import cms from 'lib/cms' import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' import path from 'path'; import { promises as fs } from 'fs'; //-----misc helpers function datocmsDateToIng(date : string) : number { let dateInt = 0; date = date.replace(/\D/g,''); dateInt = parseInt(date); if (Number.isInteger(dateInt) !== true) { dateInt = 0; } return dateInt; } //-----graphql query interception export async function datocmsQueryIntercept(query : any) { let parent = pluckFirstParentFromQuery(query); let pageQueryWhitelist = getPageQueryBypassWhitelist(); let listingQueryWhitelist = getListingQueryDirectories(); let interceptData = {}; if (listingQueryWhitelist.includes(parent)) { interceptData = await datocmsListingQueryIntercept(query); } else if (pageQueryWhitelist.includes(parent)) { interceptData = await datocmsPageQueryIntercept(query); } else { let errorText = 'Unable to intercept datocms query. No viable JSON alternative was defined.'; interceptData = {error: errorText}; } return interceptData; } function getPageQueryBypassWhitelist() : string[] { //-----stuff that has been (to the best of my knowledge) completley pulled over into JSON //-----does not include iterative stuff like blog posts or events etc... //-----this may only end up being scaffolding. let whitelist : string [] = []; whitelist.push('aboutPage'); whitelist.push('careersPage'); whitelist.push('communityPage'); whitelist.push('contactPage'); whitelist.push('homePage'); whitelist.push('partnersPage'); whitelist.push('pressPage'); whitelist.push('privacyPage'); whitelist.push('productsPage'); whitelist.push('termsPage'); whitelist.push('header'); whitelist.push('footer'); return whitelist; } function getListingQueryDirectories() : string[] { //-----whitelist the listying types of content... let directories : string[] = []; directories.push('allEvents'); directories.push('allPositions'); directories.push('allPressReleases'); directories.push('allTeams'); directories.push('allTestimonials'); return directories; } function pluckFirstParentFromQuery(query : any) : string { //-----only plucks the FIRST parent, if there are multiple parents in the query they will be ignored. let parent : string = ((query.replace(/[\W_]+/g," ")).trim()).split(" ")[0]; return parent; } async function datocmsPageQueryIntercept(query : any) { let parent : string = pluckFirstParentFromQuery(query); let jsonDirectory : string = path.join(process.cwd(), 'json/site_content'); let jsonPath : string = jsonDirectory + '/' + parent + '.json'; let fileRawContents : string = ''; let fileJson : any = {} let jsonData : any = {} try { fileRawContents = await fs.readFile(jsonPath, 'utf8'); fileJson = JSON.parse(fileRawContents); jsonData = fileJson.data; } catch (e) { console.log('Failed to parse JSON for file ' + jsonPath); console.log('JSON parse attempt resulted in the following error:'); console.log(e); } return jsonData; } async function datocmsListingQueryIntercept(query : any) { let parent : string = pluckFirstParentFromQuery(query); let jsonData : any = {}; switch(parent) { case 'allBlogPosts': //-----blog listing is handled in its own annoyingly special way //-----could build it out here to be verbose but will not in the intrest of time/budget //-----might need to add/remove some parent nodes in the JSON returned from getAllBlogPostsJson(); to match the would-be expected format. //jsonData = await getAllBlogPostsJson(); jsonData = {}; break; case 'allCategories': //-----category listing is handled in its own annoyingly special way //-----could build it out here to be verbose but will not in the intrest of time/budget //-----might need to add/remove some parent nodes in the JSON returned from getAllBlogCategoriesJson(); to match the would-be expected format. //jsonData = await getAllBlogCategoriesJson(); jsonData = {}; break; case 'allEvents': jsonData = await getAllEventsJson(); break; case 'allPositions': jsonData = await getAllPositionsJson(); break; case 'allPressReleases': jsonData = await getAllPressReleasesJson(); break; case 'allTeams': jsonData = await getAllTeamsJson(); break; case 'allTestimonials': jsonData = await getAllTestimonialsJson(); break; default: jsonData = {}; break; } return jsonData; } //-----json traversal function jsonFilePathIsValid(filePath : string) { if (filePath.startsWith("_")) { return false; } if (!filePath.endsWith(".json")) { return false; } return true; } function jsonNodeExists(jsonData : any, node : string) { //-----this condition is unneccessary... so THATS what typescript is for XD //if (typeof node === 'string' || node instanceof String) { if (node in jsonData === true) { return true; } //} return false; } function jsonNodesExist(jsonData : any, nodes : any) { //-----permit basic validation of the json we are trying to spit out by checking for existence of first order nodes let node = ''; let nodeGroup : any = []; let nodeGroupNode = ''; let nodeGroupValid = false; for (let i = 0; i < nodes.length; i++) { node = nodes[i]; if (typeof node === 'string') { //-----string means that PRIMARY node must exist... no checking for children etc... if (jsonNodeExists(jsonData, node) === false) { return false; } } else if (Array.isArray(node)) { //-----array means json is valid if ANY of the nodes in that array exist nodeGroup = node; nodeGroupValid = false; for (let j = 0; j < nodeGroup.length; j++) { nodeGroupNode = nodeGroup[j]; if (jsonNodeExists(jsonData, nodeGroupNode) === true) { nodeGroupValid = true; } } if (nodeGroupValid === false) { //-----none of the nodes in the group exist. return false; } } else { //-----lets not get too crazy here. return false; } } return true; } export async function getJsonItemsFromDirectory(jsonDirectory : string, pluckerFunction : any, validationNodes : string[]) { let jsonFiles = await fs.readdir(jsonDirectory); let returnJson = []; for (let i = 0; i < jsonFiles.length; i++) { let jsonFile = jsonFiles[i]; if (jsonFilePathIsValid(jsonFile) === true) { let jsonPath = path.join(jsonDirectory, jsonFile); let fileRawContents = await fs.readFile(jsonPath, 'utf8'); let fileJsonContents = {}; let jsonParseSuccess = false; try { fileJsonContents = JSON.parse(fileRawContents); jsonParseSuccess = true; } catch (e) { jsonParseSuccess = false; console.log('Failed to parse JSON for file ' + jsonPath); console.log('JSON parse attempt resulted in the following error:'); console.log(e); } if (jsonParseSuccess === true) { fileJsonContents = JSON.parse(fileRawContents); let jsonData = pluckerFunction(fileJsonContents); jsonData.sourceFile = jsonFile; if (jsonNodesExist(jsonData, validationNodes) === true) { returnJson.push(jsonData); } } } } return returnJson; } //-----blogPosts function getBlogJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/blogPost'); } function pluckBlogPostData(json : any) { let plucked = {}; if('data' in json){ if('blogPost' in json.data) { plucked = json.data.blogPost; } } return plucked; } function getRequiedBlogPostNodes() : string[] { let nodes = []; let contentNodes = []; contentNodes.push('content'); contentNodes.push('htmlContent'); //-----slug is now set per the JSON filename to avoid mismatches in routing //nodes.push('slug'); nodes.push('date'); nodes.push('title'); nodes.push(contentNodes); return nodes; } function forceBlogPostsJsonSlugIntegrity(json : any) { //-----this is used to force the blog post slug to match that of its parent file name. let returnJson = []; for (let i = 0; i < json.length; i++) { let blogPost = json[i]; let sourceFile = blogPost.sourceFile; let newSlug = path.parse(sourceFile).name; blogPost.slug = newSlug; returnJson.push(blogPost); } return returnJson; } function sortBlogPostsJsonByDate(json : any) { let sortedJson = json.slice().sort((a, b) => datocmsDateToIng(b.date) - datocmsDateToIng(a.date)); return sortedJson; } function getFakePaginationData(json : any) { let pagination = {}; let totalPosts = json.length; pagination.nextPage = null; pagination.page = 1; pagination.step = totalPosts; pagination.total = totalPosts; pagination.totalPages = 1; return pagination; } async function getAllBlogPostsJson() { let jsonDirectory = getBlogJsonDirectoryPath(); let validationNodes = getRequiedBlogPostNodes(); let blogPosts = await getJsonItemsFromDirectory(jsonDirectory, pluckBlogPostData, validationNodes); let pagination = getFakePaginationData(blogPosts); let processedBlogPosts = forceBlogPostsJsonSlugIntegrity(blogPosts); let sortedBlogPosts = sortBlogPostsJsonByDate(processedBlogPosts); let allBlogPostsJson = {pagination: pagination, data: sortedBlogPosts}; return allBlogPostsJson; } export async function getAllBlogPostsSlugsFromSource() { let jsonDirectory = getBlogJsonDirectoryPath(); let jsonFiles = await fs.readdir(jsonDirectory); let slugs = []; for (let i = 0; i < jsonFiles.length; i++) { let jsonFile = jsonFiles[i]; if (jsonFilePathIsValid(jsonFile) === true) { let slug = path.parse(jsonFile).name; let node = {slug : slug}; slugs.push(node); } } return slugs; } export async function getAllBlogPostsFromSource(datocmsFilters : any) { let allBlogPostsFromSource = {}; if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === "local_json") { allBlogPostsFromSource = await getAllBlogPostsJson(); } else { allBlogPostsFromSource = serverGetBlogPosts(datocmsFilters); } return allBlogPostsFromSource; } async function getSingleBlogPostJsonBySlug(slug) { let jsonDirectory = getBlogJsonDirectoryPath(); let jsonFile = slug + '.json'; let jsonPath = path.join(jsonDirectory, jsonFile); let blogPost = {}; try { let fileRawContents = await fs.readFile(jsonPath, 'utf8'); let fileJsonContents = JSON.parse(fileRawContents); blogPost = pluckBlogPostData(fileJsonContents); //-----continue to enforce standard set by forceBlogPostsJsonSlugIntegrity() blogPost.slug = slug; } catch (e) { jsonParseSuccess = false; console.log('Failed to open or parse JSON for file ' + jsonPath); console.log('JSON parse attempt resulted in the following error:'); console.log(e); } return blogPost; } export async function getSingleBlogPostBySlugFromSource(slug : string) { let blogPostJson = {}; if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === "local_json") { try { //-----old highly inefficent way, //-----used before we forced slug to be re written per filename via forceBlogPostsJsonSlugIntegrity() /* let blogPosts = await getAllBlogPostsJson(); let blogPostsData = blogPosts.data; let slugMatches = blogPostsData.filter( function(blogPostsData) { return blogPostsData.slug == slug; } ); blogPostJson = slugMatches[0]; */ blogPostJson = await getSingleBlogPostJsonBySlug(slug); } catch (e) { console.log('Failed pull blog post with slug ' + slug); console.log('The attempt resulted in the following error:'); console.log(e); } } else { blogPostJson = await (await cms().SingleBlogPost({ slug })).blogPost } return blogPostJson; } function getBlogPostCategorySlugs(blogPostJson : any) { let categories = []; try { let categoryNode = blogPostJson.category; for (let i = 0; i < categoryNode.length; i++) { let category = categoryNode[i].slug; categories.push(category); } } catch (e) { console.log('Failed pull blog post categories'); console.log('The attempt resulted in the following error:'); console.log(e); } return categories; } export async function getRelatedBlogPosts(blogPostJson : any, matchCount : number) { let relatedBlogPosts = []; let reservedSlugs = [] let matchedCount = 0; let matchCategories = getBlogPostCategorySlugs(blogPostJson); let blogPosts = await getAllBlogPostsJson(); let blogPostsData = blogPosts.data; let candidateBlogPost = {}; let candidateBlogPostSlug = ''; let slug = blogPostJson.slug; if (matchCategories.length > 0) { reservedSlugs.push(slug); for (let i = 0; i < blogPostsData.length; i++) { candidateBlogPost = blogPostsData[i]; candidateBlogPostSlug = candidateBlogPost.slug; if (matchedCount >= matchCount) { break; } if (reservedSlugs.includes(candidateBlogPostSlug) === false) { let candidateCategories = getBlogPostCategorySlugs(candidateBlogPost); for (let j = 0; j < matchCategories.length; j++) { let matchCategory = matchCategories[j]; if (candidateCategories.includes(matchCategory) === true && reservedSlugs.includes(candidateBlogPostSlug) === false) { relatedBlogPosts.push(candidateBlogPost); reservedSlugs.push(candidateBlogPostSlug) matchedCount++; break; } } } } } if (matchedCount < matchCount) { //-----if not enough matches, pull in anything thats left... for (let i = 0; i < blogPostsData.length; i++) { candidateBlogPost = blogPostsData[i]; candidateBlogPostSlug = candidateBlogPost.slug; if (matchedCount >= matchCount) { break; } if (reservedSlugs.includes(candidateBlogPostSlug) === false) { relatedBlogPosts.push(candidateBlogPost); reservedSlugs.push(candidateBlogPostSlug) matchedCount++; } } } return relatedBlogPosts; } //-----category function getBlogCategoriesJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/category'); } function pluckBlogCategoriesData(json : any) { let plucked = {}; if('data' in json){ if('category' in json.data) { plucked = json.data.category; } } return plucked; } function getRequiedBlogBlogCategoryNodes() { let nodes = []; nodes.push('slug'); nodes.push('title'); return nodes; } export async function getAllBlogCategoriesJson() : any { let jsonDirectory = getBlogCategoriesJsonDirectoryPath(); let validationNodes = getRequiedBlogBlogCategoryNodes(); let allBlogCategoriesJson = await getJsonItemsFromDirectory(jsonDirectory, pluckBlogCategoriesData, validationNodes) return allBlogCategoriesJson; } export async function getAllBlogPostsCategoriesFromSource() { let allBlogPostsCategoriesFromSource = {}; if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === "local_json") { allBlogPostsCategoriesFromSource = await getAllBlogCategoriesJson(); //let requestResults = await request(AllCategoriesQuery); //allBlogPostsCategoriesFromSource = requestResults.allCategories; } else { allBlogPostsCategoriesFromSource = serverGetBlogPostsCategories(); } return allBlogPostsCategoriesFromSource; } //-----event function getEventsJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/event'); } function pluckEventData(json : any) { let plucked = {}; if('data' in json){ if('event' in json.data) { plucked = json.data.event; } } return plucked; } function getRequiedEventNodes() { let nodes = []; nodes.push('title'); nodes.push('eventLocation'); nodes.push('eventStartdate'); nodes.push('eventEnddate'); return nodes; } function sortEventsJsonByStartDate(json : any) { let sortedJson = json.slice().sort((a, b) => datocmsDateToIng(b.eventStartdate) - datocmsDateToIng(a.eventStartdate)); return sortedJson; } async function getAllEventsJson() : any { let jsonDirectory = getEventsJsonDirectoryPath(); let validationNodes = getRequiedEventNodes(); let events = await getJsonItemsFromDirectory(jsonDirectory, pluckEventData, validationNodes); let sortedEvents = sortEventsJsonByStartDate(events); let allEventsJson = {allEvents: sortedEvents}; return allEventsJson; } //-----position function getPositionsJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/position'); } function pluckPositionData(json : any) { let plucked = {}; if('data' in json){ if('position' in json.data) { plucked = json.data.position; } } return plucked; } function getRequiedPositionNodes() { let nodes = []; nodes.push('id'); nodes.push('positionName'); nodes.push('positionLink'); return nodes; } function sortPositionsJsonById(json : any) { let sortedJson = json.slice().sort((a, b) => parseInt(a.id) - parseInt(b.id)); return sortedJson; } async function getAllPositionsJson() { let jsonDirectory = getPositionsJsonDirectoryPath(); let validationNodes = getRequiedPositionNodes(); let positions = await getJsonItemsFromDirectory(jsonDirectory, pluckPositionData, validationNodes); let sortedPositions = sortPositionsJsonById(positions); let allPositionsJson = {allPositions: sortedPositions}; return allPositionsJson; } //-----pressRelease function getPressReleasesJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/pressRelease'); } function pluckPressReleaseData(json : any) { let plucked = {}; if('data' in json){ if('pressRelease' in json.data) { plucked = json.data.pressRelease; } } return plucked; } function getRequiedPressReleaseNodes() { let nodes = []; nodes.push('id'); nodes.push('title'); nodes.push('date'); nodes.push('link'); return nodes; } function sortPressReleasesJsonByDate(json : any) { let sortedJson = json.slice().sort((a, b) => datocmsDateToIng(b.date) - datocmsDateToIng(a.date)); return sortedJson; } async function getAllPressReleasesJson() { let jsonDirectory = getPressReleasesJsonDirectoryPath(); let validationNodes = getRequiedPressReleaseNodes(); let pressReleases = await getJsonItemsFromDirectory(jsonDirectory, pluckPressReleaseData, validationNodes); let sortedPressReleases = sortPressReleasesJsonByDate(pressReleases); let allPressReleasesJson = {allPressReleases: sortedPressReleases}; return allPressReleasesJson; } //-----team function getTeamsJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/team'); } function pluckTeamData(json : any) { let plucked = {}; if('data' in json){ if('team' in json.data) { plucked = json.data.team; } } return plucked; } function getRequiedTeamNodes() { let nodes = []; nodes.push('id'); nodes.push('memberName'); return nodes; } function sortTeamsJsonById(json : any) { let sortedJson = json.slice().sort((a, b) => parseInt(a.id) - parseInt(b.id)); return sortedJson; } async function getAllTeamsJson() : any { let jsonDirectory = getTeamsJsonDirectoryPath(); let validationNodes = getRequiedTeamNodes(); let teams = await getJsonItemsFromDirectory(jsonDirectory, pluckTeamData, validationNodes); let sortedTeams = sortTeamsJsonById(teams); let allTeamsJson = {allTeams: sortedTeams}; return allTeamsJson; } //-----testimonial function getTestimonialsJsonDirectoryPath() { return path.join(process.cwd(), 'json/site_content/testimonial'); } function pluckTestimonialData(json : any) { let plucked = {}; if('data' in json){ if('testimonial' in json.data) { plucked = json.data.testimonial; } } return plucked; } function getRequiedTestimonialNodes() { let nodes = []; nodes.push('id'); nodes.push('testimonialImage'); nodes.push('testimonialText'); nodes.push('testimonialUsername'); return nodes; } function sortTestimonialsJsonById(json : any) { let sortedJson = json.slice().sort((a, b) => parseInt(a.id) - parseInt(b.id)); return sortedJson; } async function getAllTestimonialsJson() : any { let jsonDirectory = getTestimonialsJsonDirectoryPath(); let validationNodes = getRequiedTestimonialNodes(); let testimonials = await getJsonItemsFromDirectory(jsonDirectory, pluckTestimonialData, validationNodes); let sortedTestimonals = sortTestimonialsJsonById(testimonials); let allTestimonialsJson = {allTestimonials: sortedTestimonals}; return allTestimonialsJson; }