diff --git a/__tests__/__fixtures__/changelogs/failure-docs/fail-doc.md b/__tests__/__fixtures__/changelogs/failure-docs/doc-sans-attributes.md similarity index 100% rename from __tests__/__fixtures__/changelogs/failure-docs/fail-doc.md rename to __tests__/__fixtures__/changelogs/failure-docs/doc-sans-attributes.md diff --git a/__tests__/__fixtures__/custompages/failure-docs/fail-doc.md b/__tests__/__fixtures__/custompages/failure-docs/doc-sans-attributes.md similarity index 100% rename from __tests__/__fixtures__/custompages/failure-docs/fail-doc.md rename to __tests__/__fixtures__/custompages/failure-docs/doc-sans-attributes.md diff --git a/__tests__/__fixtures__/docs/failure-docs/fail-doc.md b/__tests__/__fixtures__/docs/failure-docs/doc-sans-attributes.md similarity index 100% rename from __tests__/__fixtures__/docs/failure-docs/fail-doc.md rename to __tests__/__fixtures__/docs/failure-docs/doc-sans-attributes.md diff --git a/__tests__/cmds/changelogs/index.test.ts b/__tests__/cmds/changelogs/index.test.ts index f7e65e87f..ae6bb0cde 100644 --- a/__tests__/cmds/changelogs/index.test.ts +++ b/__tests__/cmds/changelogs/index.test.ts @@ -261,8 +261,7 @@ describe('rdme changelogs', () => { it('should fail if any changelogs are invalid', async () => { const folder = 'failure-docs'; - const slug = 'fail-doc'; - const slugTwo = 'new-doc'; + const slug = 'new-doc'; const errorObject = { error: 'CHANGELOG_INVALID', @@ -271,11 +270,10 @@ describe('rdme changelogs', () => { docs: 'fake-metrics-uuid', help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", }; + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const docTwo = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const hashTwo = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const getMocks = getAPIMock() .get(`/api/v1/changelogs/${slug}`) @@ -285,25 +283,9 @@ describe('rdme changelogs', () => { message: `The changelog with the slug '${slug}' couldn't be found`, suggestion: '...a suggestion to resolve the issue...', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }) - .get(`/api/v1/changelogs/${slugTwo}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'CHANGELOG_NOTFOUND', - message: `The changelog with the slug '${slugTwo}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }); const postMocks = getAPIMock() - .post('/api/v1/changelogs', { slug: slugTwo, body: docTwo.content, ...docTwo.data, lastUpdatedHash: hashTwo }) - .basicAuth({ user: key }) - .reply(201, { - metadata: { image: [], title: '', description: '' }, - title: 'This is the changelog title', - slug: slugTwo, - body: 'Body', - }) .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) .basicAuth({ user: key }) .reply(400, errorObject); diff --git a/__tests__/cmds/changelogs/single.test.ts b/__tests__/cmds/changelogs/single.test.ts index 032eedba0..d68b10ea4 100644 --- a/__tests__/cmds/changelogs/single.test.ts +++ b/__tests__/cmds/changelogs/single.test.ts @@ -112,52 +112,16 @@ describe('rdme changelogs (single)', () => { getMock.done(); }); - it('should fail if the changelog is invalid', async () => { - const folder = 'failure-docs'; - const slug = 'fail-doc'; + it('should skip if it does not contain any front matter attributes', async () => { + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/doc-sans-attributes.md`; - const errorObject = { - error: 'CHANGELOG_INVALID', - message: "We couldn't save this changelog (Changelog title cannot be blank).", - suggestion: 'Make sure all the data is correct, and the body is valid Markdown.', - docs: 'fake-metrics-uuid', - help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", - }; - - const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const getMock = getAPIMock() - .get(`/api/v1/changelogs/${slug}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'CHANGELOG_NOTFOUND', - message: `The changelog with the slug '${slug}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); - - const postMock = getAPIMock() - .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) - .basicAuth({ user: key }) - .reply(400, errorObject); - - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; - - const formattedErrorObject = { - ...errorObject, - message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, - }; - - await expect(changelogs.run({ filePath, key })).rejects.toStrictEqual(new APIError(formattedErrorObject)); - - getMock.done(); - postMock.done(); + await expect(changelogs.run({ filePath, key })).resolves.toBe( + `⏭️ no front matter attributes found for ${filePath}, skipping` + ); }); it('should fail if some other error when retrieving page slug', async () => { - const slug = 'fail-doc'; + const slug = 'new-doc'; const errorObject = { error: 'INTERNAL_ERROR', @@ -168,7 +132,7 @@ describe('rdme changelogs (single)', () => { const getMock = getAPIMock().get(`/api/v1/changelogs/${slug}`).basicAuth({ user: key }).reply(500, errorObject); - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/${slug}.md`; const formattedErrorObject = { ...errorObject, diff --git a/__tests__/cmds/custompages/index.test.ts b/__tests__/cmds/custompages/index.test.ts index 461f19e0d..b7611d6b0 100644 --- a/__tests__/cmds/custompages/index.test.ts +++ b/__tests__/cmds/custompages/index.test.ts @@ -293,8 +293,7 @@ describe('rdme custompages', () => { it('should fail if any custompages are invalid', async () => { const folder = 'failure-docs'; - const slug = 'fail-doc'; - const slugTwo = 'new-doc'; + const slug = 'new-doc'; const errorObject = { error: 'CUSTOMPAGE_INVALID', @@ -303,11 +302,10 @@ describe('rdme custompages', () => { docs: 'fake-metrics-uuid', help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", }; + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const docTwo = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const hashTwo = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const getMocks = getAPIMock() .get(`/api/v1/custompages/${slug}`) @@ -317,31 +315,9 @@ describe('rdme custompages', () => { message: `The custom page with the slug '${slug}' couldn't be found`, suggestion: '...a suggestion to resolve the issue...', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }) - .get(`/api/v1/custompages/${slugTwo}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'CUSTOMPAGE_NOTFOUND', - message: `The custom page with the slug '${slugTwo}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }); const postMocks = getAPIMock() - .post('/api/v1/custompages', { - slug: slugTwo, - body: docTwo.content, - htmlmode: false, - ...docTwo.data, - lastUpdatedHash: hashTwo, - }) - .basicAuth({ user: key }) - .reply(201, { - metadata: { image: [], title: '', description: '' }, - title: 'This is the custom page title', - slug: slugTwo, - body: 'Body', - }) .post('/api/v1/custompages', { slug, body: doc.content, htmlmode: false, ...doc.data, lastUpdatedHash: hash }) .basicAuth({ user: key }) .reply(400, errorObject); diff --git a/__tests__/cmds/custompages/single.test.ts b/__tests__/cmds/custompages/single.test.ts index 15c0a7c02..3462cf0bf 100644 --- a/__tests__/cmds/custompages/single.test.ts +++ b/__tests__/cmds/custompages/single.test.ts @@ -143,52 +143,16 @@ describe('rdme custompages (single)', () => { getMock.done(); }); - it('should fail if the custom page is invalid', async () => { - const folder = 'failure-docs'; - const slug = 'fail-doc'; + it('should skip if it does not contain any front matter attributes', async () => { + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/doc-sans-attributes.md`; - const errorObject = { - error: 'CUSTOMPAGE_INVALID', - message: "We couldn't save this page (Custom page title cannot be blank).", - suggestion: 'Make sure all the data is correct, and the body is valid Markdown or HTML.', - docs: 'fake-metrics-uuid', - help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", - }; - - const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const getMock = getAPIMock() - .get(`/api/v1/custompages/${slug}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'CUSTOMPAGE_NOTFOUND', - message: `The custom page with the slug '${slug}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); - - const postMock = getAPIMock() - .post('/api/v1/custompages', { slug, body: doc.content, htmlmode: false, ...doc.data, lastUpdatedHash: hash }) - .basicAuth({ user: key }) - .reply(400, errorObject); - - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; - - const formattedErrorObject = { - ...errorObject, - message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, - }; - - await expect(custompages.run({ filePath, key })).rejects.toStrictEqual(new APIError(formattedErrorObject)); - - getMock.done(); - postMock.done(); + await expect(custompages.run({ filePath, key })).resolves.toBe( + `⏭️ no front matter attributes found for ${filePath}, skipping` + ); }); it('should fail if some other error when retrieving page slug', async () => { - const slug = 'fail-doc'; + const slug = 'new-doc'; const errorObject = { error: 'INTERNAL_ERROR', @@ -199,7 +163,7 @@ describe('rdme custompages (single)', () => { const getMock = getAPIMock().get(`/api/v1/custompages/${slug}`).basicAuth({ user: key }).reply(500, errorObject); - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/${slug}.md`; const formattedErrorObject = { ...errorObject, diff --git a/__tests__/cmds/docs/index.test.ts b/__tests__/cmds/docs/index.test.ts index a8ac1cc21..c28692d2e 100644 --- a/__tests__/cmds/docs/index.test.ts +++ b/__tests__/cmds/docs/index.test.ts @@ -24,7 +24,6 @@ const fullFixturesDir = `${__dirname}./../../${fixturesBaseDir}`; const key = 'API_KEY'; const version = '1.0.0'; const category = 'CATEGORY_ID'; -const apiSetting = 'API_SETTING_ID'; const testWorkingDir = process.cwd(); @@ -350,8 +349,7 @@ describe('rdme docs', () => { it('should fail if any docs are invalid', async () => { const folder = 'failure-docs'; - const slug = 'fail-doc'; - const slugTwo = 'new-doc'; + const slug = 'new-doc'; const errorObject = { error: 'DOC_INVALID', @@ -359,10 +357,8 @@ describe('rdme docs', () => { }; const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const docTwo = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const hashTwo = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const getMocks = getAPIMockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -372,35 +368,9 @@ describe('rdme docs', () => { message: `The doc with the slug '${slug}' couldn't be found`, suggestion: '...a suggestion to resolve the issue...', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }) - .get(`/api/v1/docs/${slugTwo}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'DOC_NOTFOUND', - message: `The doc with the slug '${slugTwo}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }); const postMocks = getAPIMockWithVersionHeader(version) - .post('/api/v1/docs', { slug: slugTwo, body: docTwo.content, ...docTwo.data, lastUpdatedHash: hashTwo }) - .basicAuth({ user: key }) - .reply(201, { - metadata: { image: [], title: '', description: '' }, - api: { - method: 'post', - url: '', - auth: 'required', - params: [], - apiSetting, - }, - title: 'This is the document title', - updates: [], - type: 'endpoint', - slug: slugTwo, - body: 'Body', - category, - }) .post('/api/v1/docs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) .basicAuth({ user: key }) .reply(400, errorObject); diff --git a/__tests__/cmds/docs/single.test.ts b/__tests__/cmds/docs/single.test.ts index e030eb854..cf81ee648 100644 --- a/__tests__/cmds/docs/single.test.ts +++ b/__tests__/cmds/docs/single.test.ts @@ -135,55 +135,23 @@ describe('rdme docs (single)', () => { versionMock.done(); }); - it('should fail if the doc is invalid', async () => { - const folder = 'failure-docs'; - const slug = 'fail-doc'; - - const errorObject = { - error: 'DOC_INVALID', - message: "We couldn't save this doc (Path `category` is required.).", - }; - - const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - - const getMock = getAPIMockWithVersionHeader(version) - .get(`/api/v1/docs/${slug}`) - .basicAuth({ user: key }) - .reply(404, { - error: 'DOC_NOTFOUND', - message: `The doc with the slug '${slug}' couldn't be found`, - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); - - const postMock = getAPIMockWithVersionHeader(version) - .post('/api/v1/docs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) - .basicAuth({ user: key }) - .reply(400, errorObject); - + it('should skip doc if it does not contain any front matter attributes', async () => { const versionMock = getAPIMock() .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/doc-sans-attributes.md`; - const formattedErrorObject = { - ...errorObject, - message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, - }; - - await expect(docs.run({ filePath, key, version })).rejects.toStrictEqual(new APIError(formattedErrorObject)); + await expect(docs.run({ filePath, key, version })).resolves.toBe( + `⏭️ no front matter attributes found for ${filePath}, skipping` + ); - getMock.done(); - postMock.done(); versionMock.done(); }); it('should fail if some other error when retrieving page slug', async () => { - const slug = 'fail-doc'; + const slug = 'new-doc'; const errorObject = { error: 'INTERNAL_ERROR', @@ -202,7 +170,7 @@ describe('rdme docs (single)', () => { .basicAuth({ user: key }) .reply(200, { version }); - const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/${slug}.md`; const formattedErrorObject = { ...errorObject, diff --git a/documentation/rdme.md b/documentation/rdme.md index a293c9ccc..a8e2f82ec 100644 --- a/documentation/rdme.md +++ b/documentation/rdme.md @@ -67,7 +67,20 @@ category: 62056dee230e07007218be06 If you're anything like us... ``` -The only required attributes are the `title` and `category`. To determine what your `category` value should be, you can use [the `Get all categories` endpoint](https://docs.readme.com/reference/getcategories) and grab the `id` value from the response. +#### Required Attributes + +See below for a table detailing the required YAML front matter attributes: + +| Attribute | Required for `changelogs`? | Required for `custompages`? | Required for `docs`? | +| :--------- | :------------------------- | :-------------------------- | :------------------- | +| `title` | Yes | Yes | Yes | +| `category` | No | No | Yes | + +To determine what your `category` value should be, you can use [the `Get all categories` endpoint](https://docs.readme.com/reference/getcategories) and grab the `id` value from the response. + +> 📘 +> +> Any Markdown/HTML files that lack YAML front matter attributes will be skipped. #### Specifying Page Slugs diff --git a/src/lib/syncDocsPath.ts b/src/lib/syncDocsPath.ts index fdf4c213a..30d0d1110 100644 --- a/src/lib/syncDocsPath.ts +++ b/src/lib/syncDocsPath.ts @@ -32,6 +32,13 @@ async function pushDoc( ) { const { content, data, hash, slug } = readDoc(filepath); + // TODO: ideally we should offer a zero-configuration approach that doesn't + // require YAML front matter, but that will have to be a breaking change + if (!Object.keys(data).length) { + debug(`No front matter attributes found for ${filepath}, not syncing`); + return `⏭️ no front matter attributes found for ${filepath}, skipping`; + } + let payload: { body?: string; html?: string;