-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Set up the routes layer for the backend server * add first draft of Pages routes * add first draft of Files and Images routes * fix typo in commit messages * add CollectionPages and ResourcePages routes * rename files routes to documents to minimize confusion * ensure that all ages routes endpoints work * refactor routes layer into File class + use Strategy pattern * clean up + add list pages for Collection and Resource routes * make routes functional * remove console log * add GET collections route * remove collections reorder endpoint * lint file * set up Config class * add navigation routes * update collections routes * create a Collection class + refactor Config * move endpoint to list pages in collection to collectionPages routes * remove hbs view engine * return empty object if folder is not found * add autogenerated permalink to collections * add cors module * fix typo in documents routes * fix typo + ensure that routes return a response
- Loading branch information
1 parent
99d429c
commit 69f70f7
Showing
23 changed files
with
1,354 additions
and
300 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
const yaml = require('js-yaml') | ||
const base64 = require('base-64') | ||
const Bluebird = require('bluebird') | ||
const _ = require('lodash') | ||
|
||
const { Config } = require('./Config.js') | ||
const { File, CollectionPageType } = require('./File.js') | ||
|
||
class Collection { | ||
constructor(accessToken, siteName) { | ||
this.accessToken = accessToken | ||
this.siteName = siteName | ||
} | ||
|
||
async list() { | ||
try { | ||
const config = new Config(this.accessToken, this.siteName) | ||
const { content, sha } = await config.read() | ||
const contentObject = yaml.safeLoad(base64.decode(content)) | ||
|
||
return Object.keys(contentObject.collections) | ||
|
||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async create(collectionName) { | ||
try { | ||
const config = new Config(this.accessToken, this.siteName) | ||
const { content, sha } = await config.read() | ||
const contentObject = yaml.safeLoad(base64.decode(content)) | ||
|
||
// TO-DO: Verify that collection doesn't already exist | ||
|
||
contentObject.collections[`${collectionName}`] = { | ||
permalink: '/:collection/:path/:title', | ||
output: true | ||
} | ||
const newContent = base64.encode(yaml.safeDump(contentObject)) | ||
|
||
await config.update(newContent, sha) | ||
|
||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async delete(collectionName) { | ||
try { | ||
// Delete collection in config | ||
const config = new Config(this.accessToken, this.siteName) | ||
const { content, sha } = await config.read() | ||
const contentObject = yaml.safeLoad(base64.decode(content)) | ||
|
||
delete contentObject.collections[`${collectionName}`] | ||
const newContent = base64.encode(yaml.safeDump(contentObject)) | ||
|
||
await config.update(newContent, sha) | ||
|
||
// Get all collectionPages | ||
const GitHubFile = new File(this.accessToken, this.siteName) | ||
const collectionPageType = new CollectionPageType(collectionName) | ||
GitHubFile.setFileType(collectionPageType) | ||
const collectionPages = await GitHubFile.list() | ||
|
||
// Delete all collectionPages | ||
await Bluebird.map(collectionPages, async(collectionPage) => { | ||
let pageName = collectionPage.pageName | ||
const { sha } = await GitHubFile.read(pageName) | ||
return GitHubFile.delete(pageName, sha) | ||
}) | ||
|
||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async rename(oldCollectionName, newCollectionName) { | ||
try { | ||
// Rename collection in config | ||
const config = new Config(this.accessToken, this.siteName) | ||
const { content, sha } = await config.read() | ||
const contentObject = yaml.safeLoad(base64.decode(content)) | ||
|
||
contentObject.collections[`${newCollectionName}`] = { | ||
permalink: '/:collection/:path/:title', | ||
output: true | ||
} | ||
delete contentObject.collections[`${oldCollectionName}`] | ||
const newContent = base64.encode(yaml.safeDump(contentObject)) | ||
|
||
await config.update(newContent, sha) | ||
|
||
// Get all collectionPages | ||
const OldGitHubFile = new File(this.accessToken, this.siteName) | ||
const oldCollectionPageType = new CollectionPageType(oldCollectionName) | ||
OldGitHubFile.setFileType(oldCollectionPageType) | ||
const collectionPages = await OldGitHubFile.list() | ||
|
||
// If the object is empty (there are no pages in the collection), do nothing | ||
if (_.isEmpty(collectionPages)) return | ||
|
||
// Set up new collection File instance | ||
const NewGitHubFile = new File(this.accessToken, this.siteName) | ||
const newCollectionPageType = new CollectionPageType(newCollectionName) | ||
NewGitHubFile.setFileType(newCollectionPageType) | ||
|
||
// Rename all collectionPages | ||
await Bluebird.map(collectionPages, async(collectionPage) => { | ||
let pageName = collectionPage.fileName | ||
const { content, sha } = await OldGitHubFile.read(pageName) | ||
await OldGitHubFile.delete(pageName, sha) | ||
return NewGitHubFile.create(pageName, content) | ||
}) | ||
|
||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
} | ||
|
||
module.exports = { Collection } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const axios = require('axios'); | ||
|
||
const GITHUB_ORG_NAME = 'isomerpages' | ||
|
||
// validateStatus allows axios to handle a 404 HTTP status without rejecting the promise. | ||
// This is necessary because GitHub returns a 404 status when the file does not exist. | ||
const validateStatus = (status) => { | ||
return (status >= 200 && status < 300) || status === 404 | ||
} | ||
|
||
class Config { | ||
constructor(accessToken, siteName) { | ||
this.accessToken = accessToken | ||
this.siteName = siteName | ||
} | ||
|
||
async read() { | ||
try { | ||
const endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml` | ||
|
||
const resp = await axios.get(endpoint, { | ||
validateStatus: validateStatus, | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
|
||
if (resp.status === 404) throw new Error ('Page does not exist') | ||
|
||
const { content, sha } = resp.data | ||
|
||
return { content, sha } | ||
|
||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async update(newContent, sha) { | ||
try { | ||
const endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml` | ||
|
||
let params = { | ||
"message": 'Edit config', | ||
"content": newContent, | ||
"branch": "staging", | ||
"sha": sha | ||
} | ||
|
||
await axios.put(endpoint, params, { | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
} | ||
|
||
module.exports = { Config } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
const axios = require('axios'); | ||
const _ = require('lodash') | ||
|
||
const GITHUB_ORG_NAME = 'isomerpages' | ||
|
||
// validateStatus allows axios to handle a 404 HTTP status without rejecting the promise. | ||
// This is necessary because GitHub returns a 404 status when the file does not exist. | ||
const validateStatus = (status) => { | ||
return (status >= 200 && status < 300) || status === 404 | ||
} | ||
|
||
class File { | ||
constructor(accessToken, siteName) { | ||
this.accessToken = accessToken | ||
this.siteName = siteName | ||
this.baseEndpoint = null | ||
} | ||
|
||
setFileType(fileType) { | ||
const folderPath = fileType.getFolderName() | ||
this.baseEndpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/${folderPath}` | ||
} | ||
|
||
async list() { | ||
try { | ||
const endpoint = `${this.baseEndpoint}` | ||
|
||
const resp = await axios.get(endpoint, { | ||
validateStatus: validateStatus, | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
|
||
if (resp.status !== 200) return {} | ||
|
||
const files = resp.data.map(object => { | ||
const pathNameSplit = object.path.split("/") | ||
const fileName = pathNameSplit[pathNameSplit.length - 1] | ||
if (object.type === 'file') { | ||
return { | ||
path: encodeURIComponent(object.path), | ||
fileName | ||
} | ||
} | ||
}) | ||
|
||
return _.compact(files) | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async create(fileName, content) { | ||
try { | ||
const endpoint = `${this.baseEndpoint}/${fileName}` | ||
|
||
let params = { | ||
"message": `Create file: ${fileName}`, | ||
"content": content, | ||
"branch": "staging", | ||
} | ||
|
||
await axios.put(endpoint, params, { | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async read(fileName) { | ||
try { | ||
const endpoint = `${this.baseEndpoint}/${fileName}` | ||
|
||
const resp = await axios.get(endpoint, { | ||
validateStatus: validateStatus, | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
|
||
if (resp.status === 404) throw new Error ('Page does not exist') | ||
|
||
const { content, sha } = resp.data | ||
|
||
return { content, sha } | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async update(fileName, content, sha) { | ||
try { | ||
const endpoint = `${this.baseEndpoint}/${fileName}` | ||
|
||
let params = { | ||
"message": `Update file: ${fileName}`, | ||
"content": content, | ||
"branch": "staging", | ||
"sha": sha | ||
} | ||
|
||
await axios.put(endpoint, params, { | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
|
||
async delete (fileName, sha) { | ||
try { | ||
const endpoint = `${this.baseEndpoint}/${fileName}` | ||
|
||
let params = { | ||
"message": `Delete file: ${fileName}`, | ||
"branch": "staging", | ||
"sha": sha | ||
} | ||
|
||
await axios.delete(endpoint, { | ||
data: params, | ||
headers: { | ||
Authorization: `token ${this.accessToken}`, | ||
"Content-Type": "application/json" | ||
} | ||
}) | ||
} catch (err) { | ||
throw err | ||
} | ||
} | ||
} | ||
|
||
class PageType { | ||
constructor() { | ||
this.folderName = 'pages' | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
class CollectionPageType { | ||
constructor(collectionName) { | ||
this.folderName = `_${collectionName}` | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
class ResourcePageType { | ||
constructor(resourceName) { | ||
this.folderName = `_${resourceName}` | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
class ImageType { | ||
constructor() { | ||
this.folderName = 'images' | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
class DocumentType { | ||
constructor() { | ||
this.folderName = 'files' | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
class DataType { | ||
constructor() { | ||
this.folderName = '_data' | ||
} | ||
getFolderName() { | ||
return this.folderName | ||
} | ||
} | ||
|
||
module.exports = { File, PageType, CollectionPageType, ResourcePageType, ImageType, DocumentType, DataType } |
Oops, something went wrong.