Skip to content

Commit d4e312d

Browse files
author
Dom Harrington
committed
Add initial implementation of docs syncing to rdme!
1 parent 4d76dab commit d4e312d

File tree

7 files changed

+195
-1
lines changed

7 files changed

+195
-1
lines changed

lib/docs.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
const request = require('request-promise-native');
2+
const fs = require('fs');
3+
const path = require('path');
4+
const config = require('config');
5+
const frontMatter = require('gray-matter');
6+
const { promisify } = require('util');
7+
8+
const readFile = promisify(fs.readFile);
9+
10+
exports.desc = 'Sync a folder of markdown files to your ReadMe project';
11+
exports.category = 'services';
12+
exports.weight = 2;
13+
14+
exports.run = function({ args, opts }) {
15+
const { key, version } = opts;
16+
17+
if (!key) {
18+
return Promise.reject(new Error('No api key provided. Please use --key'));
19+
}
20+
21+
if (!version) {
22+
return Promise.reject(new Error('No version provided. Please use --version'));
23+
}
24+
25+
if (!args[0]) {
26+
return Promise.reject(new Error('No folder provided. Usage `rdme docs <folder>`'));
27+
}
28+
29+
const files = fs
30+
.readdirSync(args[0])
31+
.filter(file => file.endsWith('.md') || file.endsWith('.markdown'));
32+
33+
const options = {
34+
auth: { user: key },
35+
headers: {
36+
'x-readme-version': version,
37+
},
38+
};
39+
40+
function validationErrors(err) {
41+
if (err.statusCode === 400) {
42+
console.log(err.error);
43+
return Promise.reject();
44+
};
45+
46+
return Promise.reject(err);
47+
}
48+
49+
function createDoc(slug, file, err) {
50+
if (err.statusCode !== 404) return Promise.reject(err.error);
51+
52+
return request.post(`${config.host}/api/v1/docs`, {
53+
json: { slug, body: file.content, ...file.data },
54+
...options,
55+
}).catch(validationErrors);
56+
}
57+
58+
function updateDoc(slug, file, existingDoc) {
59+
return request.put(`${config.host}/api/v1/docs/${slug}`, {
60+
json: Object.assign(existingDoc, { body: file.content, ...file.data }),
61+
...options,
62+
}).catch(validationErrors);
63+
}
64+
65+
return Promise.all(
66+
files.map(async filename => {
67+
const file = frontMatter(await readFile(path.join(args[0], filename), 'utf8'));
68+
// Stripping the markdown extension from the filename
69+
const slug = filename.replace(path.extname(filename), '');
70+
71+
return request.get(`${config.host}/api/v1/docs/${slug}`, {
72+
json: true,
73+
...options,
74+
}).then(updateDoc.bind(null, slug, file), createDoc.bind(null, slug, file));
75+
}),
76+
);
77+
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"dependencies": {
3030
"colors": "^1.1.2",
3131
"config": "^1.30.0",
32+
"gray-matter": "^4.0.1",
3233
"minimist": "^1.2.0",
3334
"request": "^2.81.0",
3435
"request-promise-native": "^1.0.5"

rdme.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ const parseArgs = require('minimist')(process.argv.slice(2));
44
require('./cli')(parseArgs._[0], parseArgs._.slice(1), parseArgs)
55
.then(() => process.exit())
66
.catch(err => {
7-
console.error(err);
7+
if (err) console.error(err);
88
return process.exit(1);
99
});

test/docs.test.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
const nock = require('nock');
2+
const config = require('config');
3+
const assert = require('assert');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const frontMatter = require('gray-matter');
7+
8+
const docs = require('../cli').bind(null, 'docs');
9+
10+
const key = 'Xmw4bGctRVIQz7R7dQXqH9nQe5d0SPQs';
11+
const version = '1.0.0';
12+
13+
describe('docs command', () => {
14+
beforeAll(() => nock.disableNetConnect());
15+
afterAll(() => nock.cleanAll());
16+
17+
it('should error if no api key provided', () =>
18+
docs([], {}).catch(err => {
19+
assert.equal(err.message, 'No api key provided. Please use --key');
20+
}));
21+
22+
it('should error if no version provided', () =>
23+
docs([], { key }).catch(err => {
24+
assert.equal(err.message, 'No version provided. Please use --version');
25+
}));
26+
27+
it('should error if no folder provided', () =>
28+
docs([], { key, version: '1.0.0' }).catch(err => {
29+
assert.equal(err.message, 'No folder provided. Usage `rdme docs <folder>`');
30+
}));
31+
32+
it('should error if the argument isnt a folder');
33+
it('should error if the folder contains no markdown files');
34+
35+
describe('existing docs', () => {
36+
it('should fetch doc and merge with what is returned', () => {
37+
const slug = 'simple-doc';
38+
const doc = frontMatter(
39+
fs.readFileSync(path.join(__dirname, `./fixtures/existing-docs/${slug}.md`)),
40+
);
41+
42+
const getMock = nock(config.host, {
43+
reqheaders: {
44+
'x-readme-version': version,
45+
},
46+
})
47+
.get(`/api/v1/docs/${slug}`)
48+
.basicAuth({ user: key })
49+
.reply(200, { category: '5ae9ece93a685f47efb9a97c', slug });
50+
51+
const putMock = nock(config.host, {
52+
reqheaders: {
53+
'x-readme-version': version,
54+
},
55+
})
56+
.put(`/api/v1/docs/${slug}`, {
57+
category: '5ae9ece93a685f47efb9a97c',
58+
slug,
59+
body: doc.content,
60+
...doc.data,
61+
})
62+
.basicAuth({ user: key })
63+
.reply(200);
64+
65+
return docs(['./test/fixtures/existing-docs'], { key, version }).then(() => {
66+
getMock.done();
67+
putMock.done();
68+
});
69+
});
70+
});
71+
72+
describe('new docs', () => {
73+
it('should create new doc', () => {
74+
const slug = 'new-doc';
75+
const doc = frontMatter(
76+
fs.readFileSync(path.join(__dirname, `./fixtures/new-docs/${slug}.md`)),
77+
);
78+
79+
const getMock = nock(config.host, {
80+
reqheaders: {
81+
'x-readme-version': version,
82+
},
83+
})
84+
.get(`/api/v1/docs/${slug}`)
85+
.basicAuth({ user: key })
86+
.reply(404, {
87+
description: 'No doc found with that slug',
88+
error: 'Not Found',
89+
});
90+
91+
const postMock = nock(config.host, {
92+
reqheaders: {
93+
'x-readme-version': version,
94+
},
95+
})
96+
.post(`/api/v1/docs`, { slug, body: doc.content, ...doc.data })
97+
.basicAuth({ user: key })
98+
.reply(201);
99+
100+
return docs(['./test/fixtures/new-docs'], { key, version }).then(() => {
101+
getMock.done();
102+
postMock.done();
103+
});
104+
});
105+
});
106+
});

test/fixtures/existing-docs/not-a-markdown-file

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: This is the document title
3+
---
4+
Body

test/fixtures/new-docs/new-doc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: 5ae122e10fdf4e39bb34db6f
3+
title: This is the document title
4+
---
5+
6+
Body

0 commit comments

Comments
 (0)