Skip to content
This repository has been archived by the owner on Nov 11, 2023. It is now read-only.

Commit

Permalink
Add basic configuration support
Browse files Browse the repository at this point in the history
  • Loading branch information
fabien0102 committed Sep 13, 2019
1 parent baa5f9c commit 0db9d09
Show file tree
Hide file tree
Showing 7 changed files with 349 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ lib

package-lock.json

# examples artefacts
examples/*.tsx

# Files managed by operational-scripts
tslint.json
.prettierrc
66 changes: 65 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,13 @@ Your components can then be generated by running `npm run generate-fetcher`. Opt
}
```

You have some command examples in `package.json` (commands than begin with `example:`), just replace `node lib/bin/restful-react.js` with `restful-react`. These examples are used to try the CLI locally 😉 (please also note that a `yarn build` is necessary before running any example)

#### Validation of the specification

To enforce the best quality as possible of specification, we have integrate the amazing open-api linter from ibm ([OpenAPI Validator](https://github.com/IBM/openapi-validator)). We strongly encourage you to setup your custom rules with a `.validaterc` file, you can find all useful information about this configuration [here](https://github.com/IBM/openapi-validator/#configuration).

If it's too noisy, you don't have the time or can't control the open-api specification: just add `--no-validation` flag to the command and this validation step will be skipped :wink:
To activate this, just add `--validation` flag.

#### Import from GitHub

Expand Down Expand Up @@ -611,6 +613,68 @@ module.exports = inputSchema => ({
});
```

#### Advanced configuration

You have multiple backends? Need more control on the generated code? We have the solution!

-> The `restful-react.config.js` (Note, this will make all other flags not relevant).

To activate this "advanced mode", just replace all flags with `--config restful-react.config.js` (or any filename that you want)

Format of the config:

```ts
interface RestfulReactConfig {
[backend: string]: {
// classic configuration
output: string;
file?: string;
github?: string;
transformer?: string;
validation?: boolean;

// advanced configuration
customImport?: string;
customProperties?: {
base?: string;
};
};
}
```
Example:
```js
// restful-react.config.js

module.exports = {
myFirstBackend: {
output: "src/queries/myFirstBackend.tsx",
file: "specs/my-first-backend.yaml",
customProperties: {
base: `"http://my-first-backend.com"`,
},
},
configurableBackend: {
output: "src/queries/configurableBackend.tsx",
github: "contiamo:restful-react:master:docs/swagger.json",
customImport: `import { getConfig } from "../components/Config.tsx";`,
customProperties: {
base: `{getConfig("backendBasePath")}`,
},
},
};
```
```json
// package.json
{
"scripts": {
"gen": "restful-react import --config restful-react.config.js"
}
}
```
### Caching
This doesn't exist yet.
Expand Down
111 changes: 111 additions & 0 deletions examples/petstore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
14 changes: 14 additions & 0 deletions examples/restful-react.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Integration config for `yarn integration:advanced`
*/

module.exports = {
"petstore-file": {
file: "integration/petstore.yaml",
output: "integration/petstoreFromFileSpecWithConfig.tsx",
},
"petstore-github": {
github: "OAI:OpenAPI-Specification:master:examples/v3.0/petstore.yaml",
output: "integration/petstoreFromGithubSpecWithConfig.tsx",
},
};
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@
"version": "auto-changelog -p && git add CHANGELOG.md",
"lint": "tslint src/**/*{ts,tsx} --project .",
"prepublishOnly": "operational-scripts prepare && rollup -c rollup.config.js",
"ci": "[ ! -z $DANGER_GITHUB_API_TOKEN ] && yarn danger ci || echo \"Skipping Danger for External Contributor\""
"ci": "[ ! -z $DANGER_GITHUB_API_TOKEN ] && yarn danger ci || echo \"Skipping Danger for External Contributor\"",
"examples": "run-p example:*",
"example:github": "node lib/bin/restful-react.js import --github OAI:OpenAPI-Specification:master:examples/v3.0/petstore.yaml --output examples/petstoreFromGithubSpec.tsx",
"example:file": "node lib/bin/restful-react.js import --file examples/petstore.yaml --output examples/petstoreFromFileSpec.tsx",
"example:advanced": "node lib/bin/restful-react.js import --config examples/restful-react.config.js"
},
"devDependencies": {
"@operational/scripts": "1.4.0-1c795b9",
Expand All @@ -70,6 +74,7 @@
"jest-dom": "^3.1.3",
"lint-staged": "^7.2.0",
"nock": "^10.0.6",
"npm-run-all": "^4.1.5",
"prettier": "^1.16.4",
"react-dom": "^16.8.5",
"react-hooks-testing-library": "^0.5.0",
Expand Down
89 changes: 67 additions & 22 deletions src/bin/restful-react-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,50 @@ import importOpenApi from "../scripts/import-open-api";

const log = console.log; // tslint:disable-line:no-console

interface Options {
// classic configuration
output: string;
file?: string;
github?: string;
transformer?: string;
validation?: boolean;
}

interface ExternalConfigFile {
[backend: string]: Options & {
// advanced configuration
customImport?: string;
customProperties?: {
base?: string;
};
};
}

program.option("-o, --output [value]", "output file destination");
program.option("-f, --file [value]", "input file (yaml or json openapi specs)");
program.option("-g, --github [value]", "github path (format: `owner:repo:branch:path`)");
program.option("-t, --transformer [value]", "transformer function path");
program.option("--no-validation", "skip the validation step (provided by ibm-openapi-validator)");
program.option("--validation", "add the validation step (provided by ibm-openapi-validator)");
program.option("--config [value]", "override flags by a config file");
program.parse(process.argv);

(async () => {
const transformer = program.transformer ? require(join(process.cwd(), program.transformer)) : undefined;
const importSpecs = async (options: Options) => {
const transformer = options.transformer ? require(join(process.cwd(), options.transformer)) : undefined;

if (!program.output) {
if (!options.output) {
throw new Error("You need to provide an output file with `--output`");
}
if (!program.file && !program.github) {
if (!options.file && !options.github) {
throw new Error("You need to provide an input specification with `--file` or `--github`");
}

if (program.file) {
const data = readFileSync(join(process.cwd(), program.file), "utf-8");
const { ext } = parse(program.file);
if (options.file) {
const data = readFileSync(join(process.cwd(), options.file), "utf-8");
const { ext } = parse(options.file);
const format = [".yaml", ".yml"].includes(ext.toLowerCase()) ? "yaml" : "json";

return importOpenApi(data, format, transformer, program.validation);
} else if (program.github) {
return importOpenApi(data, format, transformer, options.validation);
} else if (options.github) {
let accessToken: string;
const githubTokenPath = join(__dirname, ".githubToken");
if (existsSync(githubTokenPath)) {
Expand All @@ -56,9 +76,9 @@ program.parse(process.argv);
}
accessToken = answers.githubToken;
}
const [owner, repo, branch, path] = program.github.split(":");
const [owner, repo, branch, path] = options.github.split(":");

const options = {
const githubSpecReq = {
method: "POST",
url: "https://api.github.com/graphql",
headers: {
Expand All @@ -80,7 +100,7 @@ program.parse(process.argv);
};

return new Promise((resolve, reject) => {
request(options, async (error, _, rawBody) => {
request(githubSpecReq, async (error, _, rawBody) => {
if (error) {
return reject(error);
}
Expand All @@ -103,20 +123,45 @@ program.parse(process.argv);
}

const format =
program.github.toLowerCase().includes(".yaml") || program.github.toLowerCase().includes(".yml")
options.github!.toLowerCase().includes(".yaml") || options.github!.toLowerCase().includes(".yml")
? "yaml"
: "json";
resolve(importOpenApi(body.data.repository.object.text, format, transformer, program.validation));
resolve(importOpenApi(body.data.repository.object.text, format, transformer, options.validation));
});
});
} else {
return Promise.reject("Please provide a file (--file) or a github (--github) input");
}
})()
.then(data => {
writeFileSync(join(process.cwd(), program.output), data);
log(chalk.green(`🎉 Your OpenAPI spec has been converted into ready to use restful-react components!`));
})
.catch(err => {
log(chalk.red(err));
};

if (program.config) {
// Use config file as configuration (advanced usage)

// tslint:disable-next-line: no-var-requires
const config: ExternalConfigFile = require(join(process.cwd(), program.config));

Object.entries(config).forEach(([backend, options]) => {
importSpecs(options)
.then(data => {
writeFileSync(join(process.cwd(), options.output), data);
log(
chalk.green(
`[${backend}] 🎉 Your OpenAPI spec has been converted into ready to use restful-react components!`,
),
);
})
.catch(err => {
log(chalk.red(err));
});
});
} else {
// Use flags as configuration
importSpecs((program as any) as Options)
.then(data => {
writeFileSync(join(process.cwd(), program.output), data);
log(chalk.green(`🎉 Your OpenAPI spec has been converted into ready to use restful-react components!`));
})
.catch(err => {
log(chalk.red(err));
});
}
Loading

0 comments on commit 0db9d09

Please sign in to comment.