diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc5258e7a..6f5c0b131 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,39 +108,26 @@ Good practices: ### Development process - For each change / pull request, create your copy of the current documentation, where you will test changes. - Create your own branch from `master`, - - Create a draft pull request, - - Go to `Manage Versions` page in readme.io - - Click `Add New Version` from top right corner. - - Select the current public version in `Fork from` field, most likely `2018-08-01` - - Type your version name in `Create version` field following the pattern: - `2018-08-01-{your name}-{pull request number}` + - Create a draft pull request - Make changes in the repository following patterns and good practices. -- Deploy changes to test your documentation: - - Update tables in markdown tables: - - `npm run build-md-tables-from-openapi` - - `npm run update-md-tables-in-doc` - - from API Reference page in readme dashboard: - - remove Voucherify OpenAPI Definition - - refresh the page and remove `VOUCHERIFY API` category from left sidebar, DO NOT REMOVE `INTRODUCTION` catregory from left sidebar - the existing OpenAPI file and all specification files. - - Deploy OpenAPI file by command `rdme openapi ./reference/OpenAPI.json --version=2018-08-01-{your name}-{pull request number}`, choose `Create a new spec` option. Command most likely will fail with the message: `We're sorry, your upload request timed out. Please try again or split your file up into smaller chunks`, but so far, we see that this operation still works correctly. - - Deploy guides pages: `rdme docs ./docs/guides --version=2018-08-01-{your name}-{pull request number}` - - Deploy api reference pages: `rdme docs ./docs/reference-docs --version=2018-08-01-{your name}-{pull request number}` - - Fix docs order: `npm run readme-fix-docs-order -- --version=v2018-08-01-{your name}-{pull request number}` -- test changes using preview on readme.io -- if changes are fine, then: +- Run `npm run manage-project -- --vt={your name}-{pull request number} --create` - this will create new version project with your tag number. +- Visit url provided at the end of the script run to test changes using preview on readme.io +- If you need to make a change: + - Do changes... + - Run `npm run manage-project -- --vt={your name}-{pull request number} --update`- this will update your version. + - Test changes using preview on readme.io + - If needed - **REPEAT** +- If changes are fine, then: - Add a note in the changelog. - - `git commit` - - `git push` + - `git commit; git push` - publish PR - ## How to merge PR and update public documentation - Test changes on readme (you can use the version prepared by the contributor). - Ensure the changelog was updated. - Merge PR to `master` branch -- In reamde.io, change the current documentation version from `v2018-08-01` to `v2018-08-01-deprecated-mm-dd-yyyy` +- In readme.io, change the current documentation version from `v2018-08-01` to `v2018-08-01-deprecated-mm-dd-yyyy` - Change the name of your new release version from `2018-08-01-{your name}-{pull request number}` to `v2018-08-01` > [!NOTE] Readme.io cache pages for 15 minutes, for only logged out users. If you are logged in, then you will always receive the most recent content. diff --git a/docs/guides/campaign_recipes/Locking-Validation-Session.md b/docs/guides/campaign_recipes/Locking-Validation-Session.md index 687dab4d3..40ec56eb6 100644 --- a/docs/guides/campaign_recipes/Locking-Validation-Session.md +++ b/docs/guides/campaign_recipes/Locking-Validation-Session.md @@ -1,7 +1,7 @@ --- title: Locking Validation Session excerpt: Temporary lock the voucher's usage until redemption is successful. -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: locking-validation-session type: basic hidden: false diff --git a/docs/guides/campaign_recipes/Loyalty Program.md b/docs/guides/campaign_recipes/Loyalty Program.md index c53b48cbe..264a10571 100644 --- a/docs/guides/campaign_recipes/Loyalty Program.md +++ b/docs/guides/campaign_recipes/Loyalty Program.md @@ -1,7 +1,7 @@ --- title: Loyalty Program excerpt: null -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: loyalty-program type: basic hidden: false diff --git a/docs/guides/campaign_recipes/Prepaid-Gift-Cards.md b/docs/guides/campaign_recipes/Prepaid-Gift-Cards.md index 0ebbf105f..58682b91e 100644 --- a/docs/guides/campaign_recipes/Prepaid-Gift-Cards.md +++ b/docs/guides/campaign_recipes/Prepaid-Gift-Cards.md @@ -1,7 +1,7 @@ --- title: Prepaid Gift Cards excerpt: null -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: prepaid-gift-cards type: basic hidden: false diff --git a/docs/guides/campaign_recipes/Qualifications.md b/docs/guides/campaign_recipes/Qualifications.md index 518e12f26..c40da5f11 100644 --- a/docs/guides/campaign_recipes/Qualifications.md +++ b/docs/guides/campaign_recipes/Qualifications.md @@ -1,7 +1,7 @@ --- title: Qualification - Checking eligibility excerpt: null -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: checking-eligibility type: basic hidden: false diff --git a/docs/guides/campaign_recipes/Referral-Program.md b/docs/guides/campaign_recipes/Referral-Program.md index 8fa21ede1..98c677d71 100644 --- a/docs/guides/campaign_recipes/Referral-Program.md +++ b/docs/guides/campaign_recipes/Referral-Program.md @@ -1,7 +1,7 @@ --- title: Referral Program excerpt: null -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: referral-program type: link hidden: false diff --git a/docs/guides/campaign_recipes/Stacking-Promotion-Tiers.md b/docs/guides/campaign_recipes/Stacking-Promotion-Tiers.md index b1edb1464..e19939efa 100644 --- a/docs/guides/campaign_recipes/Stacking-Promotion-Tiers.md +++ b/docs/guides/campaign_recipes/Stacking-Promotion-Tiers.md @@ -1,7 +1,7 @@ --- title: Stacking Promotion Tiers excerpt: 'In this tutorial, you will learn how to validate, redeem, and manage promotion stacks using API.' -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: stacking-promotion-tiers type: basic hidden: false diff --git a/docs/guides/campaign_recipes/Transfer-Loyalty-Points.md b/docs/guides/campaign_recipes/Transfer-Loyalty-Points.md index 937388a41..44238cbc0 100644 --- a/docs/guides/campaign_recipes/Transfer-Loyalty-Points.md +++ b/docs/guides/campaign_recipes/Transfer-Loyalty-Points.md @@ -1,7 +1,7 @@ --- title: Transfer Loyalty Points excerpt: See how to transfer points between loyalty cards. -categorySlug: campaigns-1 +categorySlug: campaigns-recipes slug: transfer-loyalty-points type: basic hidden: false diff --git a/docs/guides/discounts_recipes/Discount-Effects.md b/docs/guides/discounts_recipes/Discount-Effects.md index 6529481fd..367f4833c 100644 --- a/docs/guides/discounts_recipes/Discount-Effects.md +++ b/docs/guides/discounts_recipes/Discount-Effects.md @@ -1,7 +1,7 @@ --- title: Discount Effects excerpt: The discount effect defines how the discount will be applied to the customer's cart. In this article, we're going to show you how different effects work with discounts. -categorySlug: discounts +categorySlug: discounts-recipes slug: discount-effects type: basic hidden: false diff --git a/docs/guides/discounts_recipes/Free-Shipping-Discount.md b/docs/guides/discounts_recipes/Free-Shipping-Discount.md index df908f3a8..094080735 100644 --- a/docs/guides/discounts_recipes/Free-Shipping-Discount.md +++ b/docs/guides/discounts_recipes/Free-Shipping-Discount.md @@ -1,7 +1,7 @@ --- title: Free shipping disount excerpt: How to use free shipping discounts? -categorySlug: discounts +categorySlug: discounts-recipes slug: free-shipping-discount type: basic hidden: false diff --git a/docs/guides/discounts_recipes/Give-Items-For-Free-Unit-Discount.md b/docs/guides/discounts_recipes/Give-Items-For-Free-Unit-Discount.md index c2e9ecb11..862c5c661 100644 --- a/docs/guides/discounts_recipes/Give-Items-For-Free-Unit-Discount.md +++ b/docs/guides/discounts_recipes/Give-Items-For-Free-Unit-Discount.md @@ -1,7 +1,7 @@ --- title: Give item(s) for free - unit discount excerpt: How to add free items to customers' orders? -categorySlug: discounts +categorySlug: discounts-recipes slug: give-item-for-free-unit-discount type: basic hidden: false diff --git a/docs/guides/discounts_recipes/Product-Specific-Discounts.md b/docs/guides/discounts_recipes/Product-Specific-Discounts.md index 002893d6e..71bdcdeea 100644 --- a/docs/guides/discounts_recipes/Product-Specific-Discounts.md +++ b/docs/guides/discounts_recipes/Product-Specific-Discounts.md @@ -1,7 +1,7 @@ --- title: Product-specific discounts excerpt: A recipe for using *applicable_to* validation rule -categorySlug: discounts +categorySlug: discounts-recipes slug: discount-for-particular-product type: basic hidden: false diff --git a/docs/guides/discounts_recipes/Stackable-Discounts-API.md b/docs/guides/discounts_recipes/Stackable-Discounts-API.md index 6394c6ffd..083ce7f69 100644 --- a/docs/guides/discounts_recipes/Stackable-Discounts-API.md +++ b/docs/guides/discounts_recipes/Stackable-Discounts-API.md @@ -1,7 +1,7 @@ --- title: Stackable discounts API excerpt: -categorySlug: discounts +categorySlug: discounts-recipes slug: manage-stackable-discounts type: basic hidden: false diff --git a/docs/guides/distributions_recipes/CSV-Export-With-API.md b/docs/guides/distributions_recipes/CSV-Export-With-API.md index 4852bd75e..c96128a65 100644 --- a/docs/guides/distributions_recipes/CSV-Export-With-API.md +++ b/docs/guides/distributions_recipes/CSV-Export-With-API.md @@ -1,7 +1,7 @@ --- title: CSV export with API excerpt: null -categorySlug: distribution-1 +categorySlug: distributions-recipes slug: csv-export type: basic hidden: false diff --git a/docs/guides/distributions_recipes/Geofencing.md b/docs/guides/distributions_recipes/Geofencing.md index 13f7c5ddc..4deb1e3a6 100644 --- a/docs/guides/distributions_recipes/Geofencing.md +++ b/docs/guides/distributions_recipes/Geofencing.md @@ -1,7 +1,7 @@ --- title: Geofencing excerpt: -categorySlug: distribution-1 +categorySlug: distributions-recipes slug: geofencing type: link hidden: false diff --git a/docs/guides/distributions_recipes/Import-Legacy-Codes.md b/docs/guides/distributions_recipes/Import-Legacy-Codes.md index 07efe095d..ee3df7b3d 100644 --- a/docs/guides/distributions_recipes/Import-Legacy-Codes.md +++ b/docs/guides/distributions_recipes/Import-Legacy-Codes.md @@ -1,7 +1,7 @@ --- title: Import legacy codes excerpt: null -categorySlug: distribution-1 +categorySlug: distributions-recipes slug: import-codes type: basic hidden: false diff --git a/docs/guides/distributions_recipes/Messaging-Automation.md b/docs/guides/distributions_recipes/Messaging-Automation.md index 62ebde939..ed9f15c34 100644 --- a/docs/guides/distributions_recipes/Messaging-Automation.md +++ b/docs/guides/distributions_recipes/Messaging-Automation.md @@ -1,7 +1,7 @@ --- title: Messaging automation excerpt: How to set up an automatic promotion delivery for a dynamic customer segment? -categorySlug: distribution-1 +categorySlug: distributions-recipes slug: automatic-delivery type: basic hidden: false diff --git a/package-lock.json b/package-lock.json index 89e83ad65..b987351eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "version": "1.0.0", "dependencies": { + "colors": "^1.4.0", "dotenv": "^16.3.1", "markdown-it": "^13.0.2", "minimist": "^1.2.8", @@ -105,6 +106,14 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", diff --git a/package.json b/package.json index 665b78342..077d58609 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,12 @@ "scripts": { "update-md-tables-in-doc": "ts-node ./scripts/update-md-tables-in-doc.ts", "build-md-tables-from-openapi": "ts-node ./scripts/build-md-tables-from-openapi.ts", - "readme-fix-docs-order": "ts-node ./scripts/readme-fix-docs-order.ts", - "remove-stoplight-tags-from-openapi": "ts-node ./scripts/remove-stoplight-tags-from-openapi.ts" + "readme-fix-reference-docs": "ts-node ./scripts/readme-fix-reference-docs.ts", + "remove-stoplight-tags-from-openapi": "ts-node ./scripts/remove-stoplight-tags-from-openapi.ts", + "manage-project": "ts-node ./scripts/manage-project.ts" }, "dependencies": { + "colors": "^1.4.0", "dotenv": "^16.3.1", "markdown-it": "^13.0.2", "minimist": "^1.2.8", diff --git a/scripts/build-md-tables-from-openapi.ts b/scripts/build-md-tables-from-openapi.ts index 20c29655e..4adf003c2 100644 --- a/scripts/build-md-tables-from-openapi.ts +++ b/scripts/build-md-tables-from-openapi.ts @@ -1,23 +1,34 @@ -import * as fs from 'fs/promises' -import path from 'path'; -import './globals.t' -import * as openApi from '../reference/OpenAPI.json'; -import { mdTables } from './md-tables'; -import SchemaToMarkdownTable, {RenderMode, ExamplesRenderedAs} from './src/schema-to-md-table'; +import * as fs from "fs/promises"; +import path from "path"; +import "./globals.t"; +import * as openApi from "../reference/OpenAPI.json"; +import { mdTables } from "./md-tables"; +import SchemaToMarkdownTable, { + RenderMode, + ExamplesRenderedAs, +} from "./src/schema-to-md-table"; -const PATH_TO_GERENATED_TABLES = [__dirname, './output']; +const PATH_TO_GERENATED_TABLES = [__dirname, "./output"]; -(async () => { - const stm = new SchemaToMarkdownTable(openApi.components.schemas, RenderMode.List, ExamplesRenderedAs.PartOfDescription); - for(const [objectName] of mdTables){ - try{ - const fileName = `${objectName}.md`; - await fs.writeFile(path.join(...PATH_TO_GERENATED_TABLES, fileName), stm.render(objectName)) - console.log(`Generated markdown table in ${fileName}`) - }catch(e){ - console.log(`Error for ${objectName}`, e) - - } +export const buildMdTablesFromOpenApi = async () => { + const stm = new SchemaToMarkdownTable( + openApi.components.schemas, + RenderMode.List, + ExamplesRenderedAs.PartOfDescription + ); + for (const [objectName] of mdTables) { + try { + const fileName = `${objectName}.md`; + await fs.writeFile( + path.join(...PATH_TO_GERENATED_TABLES, fileName), + stm.render(objectName) + ); + console.log(`Generated markdown table in ${fileName}`); + } catch (e) { + console.log(`Error for ${objectName}`, e); } - console.log('done') -})() \ No newline at end of file + } + console.log("done"); +}; + +buildMdTablesFromOpenApi(); diff --git a/scripts/manage-project.ts b/scripts/manage-project.ts new file mode 100644 index 000000000..0a89ea43f --- /dev/null +++ b/scripts/manage-project.ts @@ -0,0 +1,404 @@ +import dotenv from "dotenv"; +import minimist from "minimist"; +import colors from "colors"; +import { exec } from "child_process"; + +dotenv.config(); +const options = minimist(process.argv.slice(2)); +const versionTag = options.versionTag || options.vt; +const versionOption = options.version || options.v; +const { create, update } = options; +const help = options.help || options.h; +const mainVersion = "v2018-08-01"; +const version = + versionOption || versionTag ? `${mainVersion}-${versionTag}` : undefined; + +const listOfGuideCategories = [ + "Getting started", + "Development", + "Building blocks", + "Campaigns Recipes", + "Discounts Recipes", + "Distributions Recipes", + "More", +]; + +const listOfReferenceCategories = ["Introduction"]; + +const main = async ({ + help, + version, + create, + update, +}: { + help?: boolean; + version?: string; + create?: boolean; + update?: boolean; +}) => { + const valid = validateOptions({ help, version, create, update }); + if (!valid) { + return; + } + if (create) { + await createNewVersion(version); + } else if (!(await isVersionExists(version))) { + console.log( + colors.red( + `Version ${version} does not exist! Create it first. Use parameter --update instead of --create` + ) + ); + return; + } + await cleanProject(version); + await uploadOpenApiFile(version); + await buildMdTables(); + await updateMdTablesInDocs(); + await uploadGuideFiles(version); + await uploadReferenceDocsWithMaxNumberOfAttempts(version); + console.log( + colors.green(`\n\nDONE!\nVisit: https://docs.voucherify.io/${version}/`) + ); +}; + +const isVersionExists = async (version: string) => { + return ( + ( + await fetch( + `https://dash.readme.com/api/v1/api-specification?perPage=100&page=1`, + { + method: "GET", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + }, + } + ) + ).status === 200 + ); +}; + +const uploadReferenceDocsWithMaxNumberOfAttempts = async ( + version, + maxNumberOfUploadingAttempts = 6 +) => { + console.log(colors.green("UPLOADING REFERENCE DOC FILES...")); + for (let i = 1; i <= maxNumberOfUploadingAttempts; i++) { + await new Promise((r) => setTimeout(r, 5000)); + const success = await runCliProcess({ + command: `rdme docs ./docs/reference-docs --version=${version}`, + stdoutIncludes: "successfully created", + resolveErrorAsFalse: true, + }); + if (success) { + console.log(colors.green("REFERENCE DOC FILES WERE UPLOADED!")); + break; + } + if (i === maxNumberOfUploadingAttempts) { + throw new Error("REFERENCE DOC FILES WERE NOT UPLOADED!"); + } + } + return; +}; + +const runCliProcess = async ({ + command, + stdoutIncludes, + stderrIncludes, + resolveErrorAsFalse = false, +}: { + command: string; + stdoutIncludes?: string; + stderrIncludes?: string; + resolveErrorAsFalse?: boolean; +}) => { + return await new Promise((resolve) => { + exec(command, (error, stdout, stderr) => { + if ( + (stdoutIncludes && stdout?.includes(stdoutIncludes)) || + (!stdoutIncludes && stdout) || + (stderrIncludes && stderr.includes(stderrIncludes)) + ) { + return resolve(true); + } + if (resolveErrorAsFalse) { + return resolve(false); + } + if (stderr) { + console.log(stderr); + } + throw error; + }); + }); +}; +const uploadGuideFiles = async (version) => { + console.log(colors.green("UPLOADING GUIDES DOC FILES...")); + await runCliProcess({ + command: `rdme docs ./docs/guides --version=${version}`, + stdoutIncludes: "successfully created", + }); + console.log(colors.green("GUIDES DOC FILES WERE UPLOADED!")); +}; + +const updateMdTablesInDocs = async () => { + console.log(colors.green("UPDATING MD TABLES IN DOCS...")); + await runCliProcess({ + command: `npm run update-md-tables-in-doc`, + }); + console.log(colors.green("MD TABLES WERE UPDATED IN DOCS SUCCESSFULLY!")); +}; +const buildMdTables = async () => { + console.log(colors.green("BUILDING MD TABLES FROM OPEN API...")); + await runCliProcess({ + command: `npm run build-md-tables-from-openapi`, + }); + console.log(colors.green("MD TABLES WERE BUILDED SUCCESSFULLY!")); +}; + +const uploadOpenApiFile = async (version) => { + console.log( + colors.green( + "UPLOADING OPEN API FILE... PLEASE WAIT... THIS MAY TAKE UP TO A MINUTE" + ) + ); + await runCliProcess({ + command: `rdme openapi ./reference/OpenAPI.json --version=${version} --create`, + stderrIncludes: `We're sorry, your upload request timed out. Please try again or split your file up into smaller chunks.`, + }); + console.log(colors.green("OPEN API FILE WAS UPLOADED")); +}; + +const updateReferenceDocs = async (version) => { + return await new Promise((resolve, reject) => { + exec( + `rdme docs ./docs/reference-docs --version=${version}`, + (error, stdout, stderr) => { + if ( + stderr?.includes( + `We couldn't save this doc (Unable to find a category with the slug 'voucherify-api')` + ) + ) { + return resolve(false); + } + if (stdout?.includes("successfully created")) { + return resolve(true); + } + return resolve(false); + } + ); + }); +}; + +const createNewVersion = async (version) => { + //create fork + try { + const response = await fetch(`https://dash.readme.com/api/v1/version`, { + method: "POST", + headers: { + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + "content-type": "application/json", + accept: "application/json", + }, + body: JSON.stringify({ + is_beta: false, + is_stable: false, + is_hidden: true, + is_deprecated: false, + from: mainVersion, + version, + }), + }); + if (response.status !== 200) { + throw new Error( + `Response status: ${response.status}, maybe this versionTag is already created?` + ); + } + console.log(colors.green(`FORK CREATED! VERSION "${version}"`)); + } catch (error) { + console.log(colors.red(`Error while creating fork from ${mainVersion}!`)), + error; + throw new Error(error); + } +}; + +const cleanProject = async (version) => { + const categoriesToDelete = await getAllCategories(version); + //delete all categories + await asyncMap( + categoriesToDelete, + async (category) => await deleteCategory(version, category.slug) + ); + console.log(colors.green(`OLD CATEGORIES DELETED!`)); + //create categories one by one (creation order is important) + for (const categoryTitle of [ + ...listOfGuideCategories, + ...listOfReferenceCategories, + ]) { + await createCategory(version, categoryTitle); + } + console.log(colors.green(`NEW CATEGORIES CREATED!`)); + const allCategories = await getAllCategories(version); + //update reference categories types + await asyncMap( + listOfReferenceCategories, + async (categoryTitle) => + await updateCategory( + version, + allCategories.find((category) => category.title === categoryTitle).slug, + { type: "reference" } + ) + ); + console.log(colors.green(`REFERENCE CATEGORIES UPDATED!`)); + const allApiSpecifications = await getAllApiSpecifications(version); + await asyncMap(allApiSpecifications, deleteSpecification); + console.log(colors.green(`API SPECIFICATIONS DELETED!`)); + console.log(colors.green(`VERSION "${version}" IS CLEANED UP!`)); + return; +}; + +const updateCategory = async (version, slug, data = {}) => { + await fetch(`https://dash.readme.com/api/v1/categories/${slug}`, { + method: "PUT", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + "content-type": "application/json", + accept: "application/json", + }, + body: JSON.stringify(data), + }); +}; + +const getAllCategories = async (version) => + await ( + await fetch( + `https://dash.readme.com/api/v1/categories?perPage=100&page=1`, + { + method: "GET", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + }, + } + ) + ).json(); + +const deleteCategory = async (version, slug) => { + await fetch(`https://dash.readme.com/api/v1/categories/${slug}`, { + method: "DELETE", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + accept: "application/json", + }, + }); +}; + +const createCategory = async (version, title) => { + await fetch(`https://dash.readme.com/api/v1/categories`, { + method: "POST", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + "content-type": "application/json", + accept: "application/json", + }, + body: JSON.stringify({ title }), + }); +}; + +const getAllApiSpecifications = async (version) => + await ( + await fetch( + `https://dash.readme.com/api/v1/api-specification?perPage=100&page=1`, + { + method: "GET", + headers: { + "x-readme-version": version, + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + }, + } + ) + ).json(); + +const deleteSpecification = async (id) => { + await fetch(`https://dash.readme.com/api/v1/api-specification/${id}`, { + method: "DELETE", + headers: { + authorization: "Basic " + btoa(process.env.README_IO_AUTH + ":"), + accept: "application/json", + }, + }); +}; + +const asyncMap = (arr, asyncFn) => { + return Promise.all(arr.map(asyncFn)); +}; + +const validateOptions = ({ + help, + version, + create, + update, +}: { + help?: boolean; + version?: string; + create?: boolean; + update?: boolean; +}) => { + if (help || (!version && !create && !update)) { + printHelp(); + return false; + } + if (!version) { + console.log( + colors.red( + "invalid arguments, missing `version` or `versionTag`, check `help` for more information\nrun 'npm run manage-project -- --help'" + ) + ); + return false; + } + if (!create && !update) { + console.log( + colors.red( + "invalid arguments, missing `update` or `create`, check `help` for more information\nrun 'npm run manage-project -- --help'" + ) + ); + return false; + } + if (create && update) { + console.log( + colors.red( + "invalid arguments, you provided conflicting arguments `update` and `create`, check `help` for more information\nrun 'npm run manage-project -- --help'" + ) + ); + return false; + } + return true; +}; + +const printHelp = () => { + console.log( + colors.green( + `options:` + + `\n"versionTag" or "vt" for versionTag` + + `\n"version" or "v" for version` + + `\n"create" if you want to create such version` + + `\n"update" if you want to update such version` + + `\n\nversionTag or version is required!` + + `\ncreate or update option is required!` + + `\n\nexamples:` + + `\nnpm run manage-project -- --vt=piotr-123 --create` + + `\nnpm run manage-project -- --v=v2018-08-01-piotr-123 --create` + + `\nnpm run manage-project -- --vt=piotr-123 --update` + + `\nnpm run manage-project -- --v=v2018-08-01-piotr-123 --update` + ) + ); +}; + +main({ + help, + version, + create, + update, +}); diff --git a/scripts/readme-fix-docs-order.ts b/scripts/readme-fix-reference-docs.ts similarity index 76% rename from scripts/readme-fix-docs-order.ts rename to scripts/readme-fix-reference-docs.ts index 64e5926d3..ef6c0a1a8 100644 --- a/scripts/readme-fix-docs-order.ts +++ b/scripts/readme-fix-reference-docs.ts @@ -1,14 +1,45 @@ - -import * as fs from 'fs/promises' +import * as fsPromises from 'fs/promises' import path from 'path'; import dotenv from 'dotenv' import minimist from 'minimist'; -import { ok } from 'assert'; dotenv.config(); const { version } = minimist(process.argv.slice(2)); -const updateDoc = async ({ slug, order, pathToFile }) => { +const readmeFixReferenceDocs = async () => { + if (!version) { + console.log( + "`version` argument was not provided :/, next time try add `-- --version=************` at the end of file execution command" + ); + return; + } + if (process.env.README_IO_AUTH?.length < 10) { + console.log("`README_IO_AUTH` was not provided in `.env` file :/"); + return; + } + const basePath = path.join(__dirname, "../docs"); + const pathsToFiles = await getFiles(basePath); + + const dataToProcess = []; + for (const pathToFile of pathsToFiles) { + const data = await fsPromises.readFile(pathToFile, { encoding: "utf8" }); + const slug = data.match(/slug: .*/)?.[0]?.split?.("slug: ")?.[1]; + const type = data.match(/type: .*/)?.[0]?.split?.("type: ")?.[1]; + const order = parseInt( + data.match(/order: .*/)?.[0]?.split?.("order: ")?.[1] + ); + if (!slug || isNaN(order)) { + throw new Error("Invalid slug or order in " + pathToFile); + } + dataToProcess.push({ slug, order, pathToFile, type }); + } + for (const chunk of chunkArray(dataToProcess, 10)) { + await asyncMap(chunk, updateDoc); + } + console.log("Done!"); +}; + +const updateDoc = async ({ slug, order, type, pathToFile }) => { const options = { method: "PUT", headers: { @@ -17,7 +48,7 @@ const updateDoc = async ({ slug, order, pathToFile }) => { "content-type": "application/json", accept: "application/json", }, - body: JSON.stringify({ order }), + body: JSON.stringify({ order, type }), }; try{ @@ -27,18 +58,18 @@ const updateDoc = async ({ slug, order, pathToFile }) => { ); const responseJSON = await response.json(); - + if (responseJSON.error) { console.log(`Error in json response from readme for ${slug}`, { responseJSON }); throw new Error(responseJSON.error); } - + if (order === responseJSON.order) { console.log(`Updated successfully ${pathToFile}!`); } else { console.log(`Not updated ${pathToFile}!`); } - + return responseJSON; }catch(error){ console.log(`Error when reqesting readme for ${slug}`, error); @@ -53,7 +84,7 @@ const chunkArray = (list, chunkSize) => const getFiles = async (path: string) => { const pathsToFiles: string[] = []; - const items = await fs.readdir(path, { + const items = await fsPromises.readdir(path, { withFileTypes: true, }); for (const item of items) { @@ -76,38 +107,5 @@ const asyncMap = (arr, asyncFn) => { }; -const readmeFixDocsOrder = async () => { - if (!version) { - console.log( - "`version` argument was not provided :/, next time try add `-- --version=************` at the end of file execution command" - ); - return; - } - if (process.env.README_IO_AUTH?.length < 10) { - console.log("`README_IO_AUTH` was not provided in `.env` file :/"); - return; - } - const basePath = path.join(__dirname, "../docs"); - const pathsToFiles = await getFiles(basePath); - - const dataToProcess = []; - for (const pathToFile of pathsToFiles) { - const data = await fs.readFile(pathToFile, { encoding: "utf8" }); - const slug = data.match(/slug: .*/)?.[0]?.split?.("slug: ")?.[1]; - const order = parseInt( - data.match(/order: .*/)?.[0]?.split?.("order: ")?.[1] - ); - if (!slug || isNaN(order)) { - throw new Error("Invalid slug or order in " + pathToFile); - } - dataToProcess.push({ slug, order, pathToFile }); - } - for (const chunk of chunkArray(dataToProcess, 6)) { - await asyncMap(chunk, updateDoc); - } - console.log("Done!"); -}; - - -readmeFixDocsOrder() +readmeFixReferenceDocs() diff --git a/scripts/update-md-tables-in-doc.ts b/scripts/update-md-tables-in-doc.ts index ba3bc0ee7..8014e3861 100644 --- a/scripts/update-md-tables-in-doc.ts +++ b/scripts/update-md-tables-in-doc.ts @@ -1,56 +1,70 @@ -import * as fs from 'fs/promises' -import path from 'path'; -import { EOL } from 'os'; -import { mdTables } from './md-tables'; - -const PATH_TO_DOCS_REFERENCE = [__dirname, '../docs/reference-docs']; -const PATH_TO_GENERATED_TABLES = [__dirname, './output']; - -const updateMdTablesInDoc = async () => { - for (const [objectName, docFile] of mdTables) { - if (!docFile) { - continue; - } - try { - const docPath = path.join(...PATH_TO_DOCS_REFERENCE, docFile); - const fileContent = await fs.readFile(docPath) - const fileContentBlocks = fileContent - .toString() - .split(/(^---$)|(^\[block\:html\]$)/m) // Split by `---` and [block:html] that surrounds the table - .filter(e => !!e); - - // Find block with table by part of the markdown table syntax - const contentBlockIndexWithTableToReplace = fileContentBlocks.findIndex(block => block.indexOf('|:-----') >= 0) - - if (contentBlockIndexWithTableToReplace < 0) { - throw new Error(`Could not find table to replace in file ${docFile} (object: ${objectName}) `) - } - - const additionalBlockquotes = fileContentBlocks[contentBlockIndexWithTableToReplace].match(/^\>.*$/gm) - - const contentBeforeTable = fileContentBlocks.slice(0, contentBlockIndexWithTableToReplace).join('') - const contentAfterTable = fileContentBlocks.slice(contentBlockIndexWithTableToReplace + 1).join('') - - const newTable = (await fs.readFile(path.join(...PATH_TO_GENERATED_TABLES, `${objectName}.md`))) - .toString(); - // .replace((/^\# .*$/m), ''); // Remove first header as in readme.io it already exists - - const newFileContent = [ - contentBeforeTable, - additionalBlockquotes?.length? additionalBlockquotes.join(EOL) : false, - newTable, - contentAfterTable - ].filter(e => !!e) - .join(`${EOL}${EOL}`) - - await fs.writeFile(docPath, newFileContent) - console.log(`Updated table in ${docFile} `) - } catch (e) { - console.log(`Error for ${objectName}`, e) - - } +import * as fs from "fs/promises"; +import path from "path"; +import { EOL } from "os"; +import { mdTables } from "./md-tables"; + +const PATH_TO_DOCS_REFERENCE = [__dirname, "../docs/reference-docs"]; +const PATH_TO_GENERATED_TABLES = [__dirname, "./output"]; + +export const updateMdTablesInDoc = async () => { + for (const [objectName, docFile] of mdTables) { + if (!docFile) { + continue; + } + try { + const docPath = path.join(...PATH_TO_DOCS_REFERENCE, docFile); + const fileContent = await fs.readFile(docPath); + const fileContentBlocks = fileContent + .toString() + .split(/(^---$)|(^\[block\:html\]$)/m) // Split by `---` and [block:html] that surrounds the table + .filter((e) => !!e); + + // Find block with table by part of the markdown table syntax + const contentBlockIndexWithTableToReplace = fileContentBlocks.findIndex( + (block) => block.indexOf("|:-----") >= 0 + ); + + if (contentBlockIndexWithTableToReplace < 0) { + throw new Error( + `Could not find table to replace in file ${docFile} (object: ${objectName}) ` + ); + } + + const additionalBlockquotes = + fileContentBlocks[contentBlockIndexWithTableToReplace].match( + /^\>.*$/gm + ); + + const contentBeforeTable = fileContentBlocks + .slice(0, contentBlockIndexWithTableToReplace) + .join(""); + const contentAfterTable = fileContentBlocks + .slice(contentBlockIndexWithTableToReplace + 1) + .join(""); + + const newTable = ( + await fs.readFile( + path.join(...PATH_TO_GENERATED_TABLES, `${objectName}.md`) + ) + ).toString(); + // .replace((/^\# .*$/m), ''); // Remove first header as in readme.io it already exists + + const newFileContent = [ + contentBeforeTable, + additionalBlockquotes?.length ? additionalBlockquotes.join(EOL) : false, + newTable, + contentAfterTable, + ] + .filter((e) => !!e) + .join(`${EOL}${EOL}`); + + await fs.writeFile(docPath, newFileContent); + console.log(`Updated table in ${docFile} `); + } catch (e) { + console.log(`Error for ${objectName}`, e); } - console.log('done') -} + } + console.log("done"); +}; updateMdTablesInDoc();