Skip to content

Commit

Permalink
feat: add example code snippet to README (#809)
Browse files Browse the repository at this point in the history
* refactor: move getExampleCodeSnippet to its own file

there's probably an opportunity here to consolidate stuff since we call this twice

* feat: add example usage to README

* test: update fixtures

* fix: lint

* refactor: move getExampleCodeSnippet to class function

* docs: add JSDocs for CodeGenerator properties
  • Loading branch information
kanadgupta authored Nov 2, 2023
1 parent 0dfd14c commit aa22328
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 20 deletions.
32 changes: 32 additions & 0 deletions packages/api/src/codegen/codegenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,43 @@ import { findLicense } from 'license';
import { PACKAGE_NAME, PACKAGE_VERSION } from '../packageInfo.js';

export default abstract class CodeGenerator {
/** The associated API definition */
spec: Oas;

/**
* The path to the API definion (might be a local path, a URL, or an API registry identifier)
* @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
* @example @petstore/v1.0#n6kvf10vakpemvplx
* @example ./petstore.json
*/
specPath: string;

/**
* The user-specified identifier for the SDK,
* used as the directory name in the `.api/apis` directory
* where the SDK source code is located.
*/
identifier: string;

/** The user agent which is set for all outgoing fetch requests */
userAgent: string;

/**
* The license associated with the SDK.
* This is extrapolated from the API definition file.
*/
spdxLicense?: string;

/**
* Contact info for the API and/or SDK author in case users need support.
* This is extrapolated from the API definition file.
*/
apiContact: { name?: string; url?: string } = {};

/**
* An object containing any downstream packages that are required
* for building/executing the SDK.
*/
requiredPackages!: Record<
string,
{
Expand All @@ -30,6 +55,11 @@ export default abstract class CodeGenerator {
}
>;

/**
* An example code snippet that a user can run to get started with the SDK.
*/
exampleCodeSnippet?: string | false;

constructor(spec: Oas, specPath: string, identifier: string) {
this.spec = spec;
this.specPath = specPath;
Expand Down Expand Up @@ -112,6 +142,8 @@ export default abstract class CodeGenerator {

abstract compile(storage: Storage, opts?: InstallerOptions): Promise<void>;

abstract getExampleCodeSnippet(): Promise<string | false>;

hasRequiredPackages() {
return Boolean(Object.keys(this.requiredPackages));
}
Expand Down
37 changes: 34 additions & 3 deletions packages/api/src/codegen/languages/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import setWith from 'lodash.setwith';
import semver from 'semver';
import { IndentationText, Project, QuoteKind, ScriptTarget, VariableDeclarationKind } from 'ts-morph';

import { buildCodeSnippetForOperation, getSuggestedOperation } from '../../../lib/suggestedOperations.js';
import logger from '../../../logger.js';
import { PACKAGE_VERSION } from '../../../packageInfo.js';
import Storage from '../../../storage.js';
Expand Down Expand Up @@ -213,7 +214,7 @@ export default class TSGenerator extends CodeGenerator {
this.createGitIgnore();
this.createPackageJSON();
this.createTSConfig();
this.createREADME();
await this.createREADME();

if (Object.keys(this.schemas).length) {
this.createSchemasFile(srcDirectory);
Expand Down Expand Up @@ -260,6 +261,23 @@ export default class TSGenerator extends CodeGenerator {
].reduce((prev, next) => Object.assign(prev, next));
}

async getExampleCodeSnippet() {
// if we've already built the code snippet, return it instead of re-building it!
if (typeof this.exampleCodeSnippet !== 'undefined') {
return this.exampleCodeSnippet;
}

const operation = getSuggestedOperation(this.spec);
if (!operation) {
this.exampleCodeSnippet = false;
return false;
}

const snippet = await buildCodeSnippetForOperation(this.spec, operation, { identifier: this.identifier });
this.exampleCodeSnippet = snippet;
return snippet;
}

/**
* Create our main SDK source file.
*
Expand Down Expand Up @@ -641,15 +659,28 @@ dist/
* Create a placeholder `README.md` file in the repository, with information on how to use/administer the SDK.
*
*/
createREADME() {
async createREADME() {
let createdAt = new Date().toISOString();
const currentAPI = Storage.getLockfile().apis.find(api => api.identifier === this.identifier);
if (currentAPI) createdAt = currentAPI.createdAt;

let exampleUsage = 'Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀';
const exampleSnippet = await this.getExampleCodeSnippet();
if (exampleSnippet) {
exampleUsage = `
## Example Usage 🚀
\`\`\`js
${exampleSnippet}
\`\`\`
`.trim();
}

const file = `# \`@api/${this.identifier}\`
This SDK was autogenerated by the [\`api\` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉
Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
${exampleUsage}
<!---
Expand Down
12 changes: 1 addition & 11 deletions packages/api/src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import uslug from 'uslug';
import { SupportedLanguages, codegenFactory } from '../codegen/factory.js';
import Fetcher from '../fetcher.js';
import promptTerminal from '../lib/prompt.js';
import { buildCodeSnippetForOperation, getSuggestedOperation } from '../lib/suggestedOperations.js';
import logger, { oraOptions } from '../logger.js';
import Storage from '../storage.js';

Expand Down Expand Up @@ -71,15 +70,6 @@ async function getIdentifier(oas: Oas, uri: string, options: Options) {
return identifier;
}

async function getExampleCodeSnippet(oas: Oas, identifier: string) {
const operation = getSuggestedOperation(oas);
if (!operation) {
return false;
}

return buildCodeSnippetForOperation(oas, operation, { identifier });
}

// @todo log logs to `.api/.logs` and have `.logs` ignored
const cmd = new Command();
cmd
Expand Down Expand Up @@ -213,7 +203,7 @@ cmd
)} package.`,
);

const exampleSnippet = await getExampleCodeSnippet(oas, identifier);
const exampleSnippet = await generator.getExampleCodeSnippet();
if (exampleSnippet) {
logger('');
logger(chalk.bold("👇 Here's an example code snippet you can try out 👇"));
Expand Down
11 changes: 10 additions & 1 deletion packages/test-utils/sdks/alby/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import alby from '@api/alby';

alby.auth('token');
alby.getMe()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down
10 changes: 9 additions & 1 deletion packages/test-utils/sdks/metrotransit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import metrotransit from '@api/metrotransit';

metrotransit.getNextripAgencies()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down
10 changes: 9 additions & 1 deletion packages/test-utils/sdks/operationid-quirks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import operationidQuirks from '@api/operationid-quirks';

operationidQuirks.quirky_OperationId_string()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down
11 changes: 10 additions & 1 deletion packages/test-utils/sdks/petstore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import petstore from '@api/petstore';

petstore.auth('token');
petstore.getInventory()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down
11 changes: 10 additions & 1 deletion packages/test-utils/sdks/readme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import readme from '@api/readme';

readme.auth('username', 'password');
readme.getAPISpecification()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down
10 changes: 9 additions & 1 deletion packages/test-utils/sdks/star-trek/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

This SDK was autogenerated by the [`api` SDK generator](https://api.readme.dev), powered by [ReadMe](https://readme.com) 🦉

Add SDK setup information and usage examples here so your users get started in a jiffy! 🚀
## Example Usage 🚀

```js
import starTrek from '@api/star-trek';

starTrek.getAnimalSearch()
.then(({ data }) => console.log(data))
.catch(err => console.error(err));
```

<!---
Expand Down

0 comments on commit aa22328

Please sign in to comment.