Skip to content

Commit

Permalink
Feat/move files (#124)
Browse files Browse the repository at this point in the history
* Nit: config indentation

* Feat: update list for collection to not use _config.yml file

* Refactor: use new list method when retrieving folders

* Feat: add page moving endpoints for collections and unlinked pages

* Fix: missing import in config class

* Feat: add check for special directory names

* Feat: add third nav parameter to front matter

* Fix: remove unused import
  • Loading branch information
alexanderleegs authored Mar 10, 2021
1 parent 3d95653 commit 1cf0c07
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 142 deletions.
33 changes: 21 additions & 12 deletions classes/Collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ const base64 = require('base-64')
const Bluebird = require('bluebird')
const _ = require('lodash')

const { Config, CollectionConfig } = require('./Config.js')
const { CollectionConfig } = require('./Config.js')
const { File, CollectionPageType, DataType } = require('./File.js')
const { Directory, RootType } = require('./Directory.js')
const { ConflictError, protectedFolderConflictErrorMsg } = require('../errors/ConflictError')
const { getCommitAndTreeSha, getTree, sendTree, deslugifyCollectionName } = require('../utils/utils.js')

const NAV_FILE_NAME = 'navigation.yml'
const ISOMER_TEMPLATE_DIRS = ['_data', '_includes', '_site', '_layouts']
const ISOMER_TEMPLATE_PROTECTED_DIRS = ['data', 'includes', 'site', 'layouts', 'files', 'images', 'misc', 'pages']

class Collection {
constructor(accessToken, siteName) {
Expand All @@ -16,19 +20,23 @@ class Collection {
}

async list() {
// to be removed in future PRs as collection data is no longer stored in _config.yml
try {
const config = new Config(this.accessToken, this.siteName)
const { content, sha } = await config.read()
const contentObject = yaml.safeLoad(base64.decode(content))
const collections = contentObject.collections ? Object.keys(contentObject.collections) : []
return collections
} catch (err) {
throw err
}
const IsomerDirectory = new Directory(this.accessToken, this.siteName)
const folderType = new RootType()
IsomerDirectory.setDirType(folderType)
const repoRootContent = await IsomerDirectory.list()

const allFolders = repoRootContent.reduce((acc, curr) => {
if (
curr.type === 'dir'
&& !ISOMER_TEMPLATE_DIRS.includes(curr.name)
&& curr.name.slice(0, 1) === '_'
) acc.push(curr.path.slice(1))
return acc
}, [])
return allFolders
}

async create(collectionName) {
async create(collectionName, orderArray) {
try {
const collectionConfig = new CollectionConfig(this.accessToken, this.siteName, collectionName)
const contentObject = {
Expand All @@ -39,6 +47,7 @@ class Collection {
},
}
}
if (ISOMER_TEMPLATE_PROTECTED_DIRS.includes(collectionName)) throw new ConflictError(protectedFolderConflictErrorMsg(collectionName))
const newContent = base64.encode(yaml.safeDump(contentObject))
await collectionConfig.create(newContent)

Expand Down
222 changes: 110 additions & 112 deletions classes/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const _ = require('lodash')
const logger = require('../logger/logger');

// Import error
const { NotFoundError } = require('../errors/NotFoundError')
const { NotFoundError } = require('../errors/NotFoundError')
const { ConflictError, inputNameConflictErrorMsg } = require('../errors/ConflictError')

const GITHUB_ORG_NAME = process.env.GITHUB_ORG_NAME
const BRANCH_REF = process.env.BRANCH_REF
Expand All @@ -17,142 +18,139 @@ class Config {
constructor(accessToken, siteName) {
this.accessToken = accessToken
this.siteName = siteName
this.endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml`
this.endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/_config.yml`
}

async read() {
try {
const params = {
"ref": BRANCH_REF,
}
const resp = await axios.get(this.endpoint, {
validateStatus,
params,
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})
const params = {
"ref": BRANCH_REF,
}
const resp = await axios.get(this.endpoint, {
validateStatus,
params,
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})

if (resp.status === 404) throw new NotFoundError ('Config page does not exist')
if (resp.status === 404) throw new NotFoundError ('Config page does not exist')

const { content, sha } = resp.data
const { content, sha } = resp.data

return { content, sha }
return { content, sha }

} catch (err) {
throw err
}
}

async update(newContent, sha) {
const params = {
"message": 'Edit config',
"content": newContent,
"branch": BRANCH_REF,
"sha": sha
}

await axios.put(this.endpoint, params, {
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})
const params = {
"message": 'Edit config',
"content": newContent,
"branch": BRANCH_REF,
"sha": sha
}

await axios.put(this.endpoint, params, {
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})
}
}

class CollectionConfig extends Config {
constructor(accessToken, siteName, collectionName) {
super(accessToken, siteName)
this.collectionName = collectionName
this.endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${siteName}/contents/_${collectionName}/collection.yml`
}

async create(content) {
try {
const params = {
"message": `Create file: _${this.collectionName}/collection.yml`,
"content": content,
"branch": BRANCH_REF,
}

const resp = await axios.put(this.endpoint, params, {
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})

return { sha: resp.data.content.sha }
} catch (err) {
const { status } = err.response
if (status === 422 || status === 409) throw new ConflictError(inputNameConflictErrorMsg(fileName))
throw err.response
constructor(accessToken, siteName, collectionName) {
super(accessToken, siteName)
this.collectionName = collectionName
this.endpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${siteName}/contents/_${collectionName}/collection.yml`
}

async create(content) {
try {
const params = {
"message": `Create file: _${this.collectionName}/collection.yml`,
"content": content,
"branch": BRANCH_REF,
}
}


async delete (sha) {
try {
const params = {
"message": `Delete file: _${this.collectionName}/collection.yml`,
"branch": BRANCH_REF,
"sha": sha

const resp = await axios.put(this.endpoint, params, {
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})

await axios.delete(this.endpoint, {
params,
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})
} catch (err) {
const status = err.response.status
if (status === 404) throw new NotFoundError ('File does not exist')
throw err
return { sha: resp.data.content.sha }
} catch (err) {
const { status } = err.response
if (status === 422 || status === 409) throw new ConflictError(inputNameConflictErrorMsg(fileName))
throw err.response
}
}

async delete (sha) {
try {
const params = {
"message": `Delete file: _${this.collectionName}/collection.yml`,
"branch": BRANCH_REF,
"sha": sha
}

await axios.delete(this.endpoint, {
params,
headers: {
Authorization: `token ${this.accessToken}`,
"Content-Type": "application/json"
}
})
} catch (err) {
const status = err.response.status
if (status === 404) throw new NotFoundError ('File does not exist')
throw err
}
}

async addItemToOrder(item) {
const collectionName = this.collectionName

const { content, sha } = await this.read()
const contentObject = yaml.safeLoad(base64.decode(content))

let index
if (item.split('/').length === 2) {
// if file in subfolder, get index of last file in subfolder
index = _.findLastIndex(
contentObject.collections[collectionName].order,
(f) => f.split('/')[0] === item.split('/')[0]
) + 1
} else {
// get index of last file in collection
index = contentObject.collections[collectionName].order.length
}
contentObject.collections[collectionName].order.splice(index, 0, item)
const newContent = base64.encode(yaml.safeDump(contentObject))

await this.update(newContent, sha)
}

async deleteItemFromOrder(item) {
const collectionName = this.collectionName

const { content, sha } = await this.read()
const contentObject = yaml.safeLoad(base64.decode(content))

const index = contentObject.collections[collectionName].order.indexOf(item);
contentObject.collections[collectionName].order.splice(index, 1)
const newContent = base64.encode(yaml.safeDump(contentObject))

await this.update(newContent, sha)
}
}
async addItemToOrder(item) {
const collectionName = this.collectionName

const { content, sha } = await this.read()
const contentObject = yaml.safeLoad(base64.decode(content))

let index
if (item.split('/').length === 2) {
// if file in subfolder, get index of last file in subfolder
index = _.findLastIndex(
contentObject.collections[collectionName].order,
(f) => f.split('/')[0] === item.split('/')[0]
) + 1
} else {
// get index of last file in collection
index = contentObject.collections[collectionName].order.length
}
contentObject.collections[collectionName].order.splice(index, 0, item)
const newContent = base64.encode(yaml.safeDump(contentObject))

await this.update(newContent, sha)
}

async deleteItemFromOrder(item) {
const collectionName = this.collectionName

const { content, sha } = await this.read()
const contentObject = yaml.safeLoad(base64.decode(content))

const index = contentObject.collections[collectionName].order.indexOf(item);
contentObject.collections[collectionName].order.splice(index, 1)
const newContent = base64.encode(yaml.safeDump(contentObject))

await this.update(newContent, sha)
}
}

module.exports = { Config, CollectionConfig }
3 changes: 3 additions & 0 deletions errors/ConflictError.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const { BaseIsomerError } = require('./BaseError')

const inputNameConflictErrorMsg = (fileName) => `A file with ${fileName} already exists.`

const protectedFolderConflictErrorMsg = (folderName) => `${folderName} is a protected folder name.`

class ConflictError extends BaseIsomerError {
constructor (message) {
super(
Expand All @@ -14,4 +16,5 @@ class ConflictError extends BaseIsomerError {
module.exports = {
ConflictError,
inputNameConflictErrorMsg,
protectedFolderConflictErrorMsg,
}
2 changes: 2 additions & 0 deletions middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ auth.get('/v1/sites/:siteName/collections', verifyJwt)
auth.post('/v1/sites/:siteName/collections', verifyJwt)
auth.delete('/v1/sites/:siteName/collections/:collectionName', verifyJwt)
auth.post('/v1/sites/:siteName/collections/:collectionName/rename/:newCollectionName', verifyJwt)
auth.post('/v1/sites/:siteName/collections/:collectionPath/move/:targetPath', verifyJwt)

// Documents
auth.get('/v1/sites/:siteName/documents', verifyJwt)
Expand Down Expand Up @@ -101,6 +102,7 @@ auth.get('/v1/sites/:siteName/pages/:pageName', verifyJwt)
auth.post('/v1/sites/:siteName/pages/:pageName', verifyJwt)
auth.delete('/v1/sites/:siteName/pages/:pageName', verifyJwt)
auth.post('/v1/sites/:siteName/pages/:pageName/rename/:newPageName', verifyJwt)
auth.post('/v1/sites/:siteName/pages/move/:newPagePath', verifyJwt)

// Resource pages
auth.get('/v1/sites/:siteName/resources/:resourceName', verifyJwt)
Expand Down
Loading

0 comments on commit 1cf0c07

Please sign in to comment.