# `oats-generator`

[![npm](https://img.shields.io/npm/v/oats-generator.svg)](https://www.npmjs.com/package/oats-generator)

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [`oats-generator`](#oats-generator)
  - [Getting Started](#getting-started)
  - [Features](#features)
    - [Code Generation](#code-generation)
      - [Usage](#usage)
      - [Validation of the OpenAPI specification](#validation-of-the-openapi-specification)
      - [Import from URL](#import-from-url)
      - [Import from GitHub](#import-from-github)
      - [Transforming an Original Spec](#transforming-an-original-spec)
      - [Advanced configuration](#advanced-configuration)
        - [Config File Format](#config-file-format)
        - [Config File Example](#config-file-example)
  - [Contributing](#contributing)
    - [Code](#code)
  - [Next Steps](#next-steps)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Getting Started

To install and use this library, install it by running `yarn add @tpdewolf/oats-generator -D`, or `npm i @tpdewolf/oats-generator -D` and you should be good to go.

## Features

`oats-generator` ships with the following features that we think might be useful.

### Code Generation

`oats-generator` is able to generate React hooks with appropriate type-signatures (TypeScript) from any valid OpenAPI v3 or Swagger v2 specification, either in `yaml` or `json` formats.

#### Usage

Type-safe React data fetchers can be generated from an OpenAPI specification using the following command:

- `oats-generator import --file MY_OPENAPI_SPEC.yaml --output my-awesome-generated-types.tsx`

This command can be invoked by _either_:

- Installing `oats-generator` globally and running it in the terminal: `npm i -g oats-generator`, or
- Adding a `script` to your `package.json` like so:

```diff
      "scripts": {
        "start": "webpack-dev-server",
        "build": "webpack -p",
+       "generate-fetcher": "oats-generator import --file MY_SWAGGER_DOCS.json --output FETCHERS.tsx"
      }
```

Your components can then be generated by running `npm run generate-fetcher`. Optionally, we recommend linting/prettifying the output for readability like so:

```diff
      "scripts": {
        "start": "webpack-dev-server",
        "build": "webpack -p",
        "generate-fetcher": "oats-generator import --file MY_SWAGGER_DOCS.json --output FETCHERS.tsx",
+       "postgenerate-fetcher": "prettier FETCHERS.tsx --write"
      }
```

#### Validation of the OpenAPI specification

To enforce the best quality as possible of specification, we have integrated the amazing [OpenAPI linter from IBM](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).

To activate this, add a `--validation` flag to your `oats-generator` call.

#### Import from URL

Adding the `--url` flag to `oats-generator import` instead of using the `--file` flag will attempt to fetch the spec from that endpoint.

- `oats-generator import --url https://api.mine.com/openapi.json --output my-awesome-generated-types.tsx`

#### Import from GitHub

Adding the `--github` flag to `oats-generator import` instead of using the `--file` flag allows us to **create React components from an OpenAPI spec _remotely hosted on GitHub._** <sup>_(how is this real life_ 🔥 _)_</sup>

To generate components from remote specifications, you'll need to follow the following steps:

1.  Visit [your GitHub settings](https://github.com/settings/tokens).
1.  Click **Generate New Token** and choose the following:

        Token Description: (enter anything)
        Scopes:
            [X] repo
                [X] repo:status
                [X] repo_deployment
                [X] public_repo
                [X] repo:invite

1.  Click **Generate token**.
1.  Copy the generated string.
1.  Open a terminal and run `oats-generator import --github username:repo:branch:path/to/openapi.yaml --output MY_FETCHERS.tsx`, substituting things where necessary.
1.  You will be prompted for a token.
1.  Paste your token.
1.  You will be asked if you'd like to save it for later. This is _entirely_ up to you and completely safe: it is saved in your `node_modules` folder and _not_ committed to version control or sent to us or anything: the source code of this whole thing is public so you're safe.

    **Caveat:** _Since_ your token is stored in `node_modules`, your token will be removed on each `npm install` of `oats-generator`.

1.  You're done! 🎉

#### Transforming an Original Spec

In some cases, you might need to augment an existing OpenAPI specification on the fly, for code-generation purposes. Our CLI makes this quite straightforward:

```bash
  oats-generator import --file myspec.yaml --output mybettercomponents.tsx --transformer path/to/my-transformer.js
```

The function specified in `--transformer` is pure: it imports your `--file`, transforms it, and passes the augmented OpenAPI specification to `oats-generator`'s generator. Here's how it can be used:

```ts
// /path/to/my-transformer.js

/**
 * Transformer function for oats-generator.
 *
 * @param {OpenAPIObject} schema
 * @return {OpenAPIObject}
 */
module.exports = inputSchema => ({
  ...inputSchema,
  // Place your augmentations here
  paths: Object.entries(schema.paths).reduce(
    (mem, [path, pathItem]) => ({
      ...mem,
      [path]: Object.entries(pathItem).reduce(
        (pathItemMem, [verb, operation]) => ({
          ...pathItemMem,
          [verb]: {
            ...fixOperationId(path, verb, operation),
          },
        }),
        {},
      ),
    }),
    {},
  ),
});
```

#### Advanced configuration

`oats-generator` supports the concept of "schema stitching" in a RESTful ecosystem as well. We are able to tie multiple backends together and generate code using a single configuration file, `oats-generator.config.js`

To activate this "advanced mode", replace all flags from your `oats-generator` call with the config flag: `--config oats-generator.config.js` (or any filename that you want).

⚠️ **Note:** using a config file makes use of all of the options contained therein, and ignores all other CLI flags.

##### Config File Format

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

    // advanced configuration
    customImport?: string;
    customGenerator?: (data: {
      componentName: string;
      verb: string;
      route: string;
      description: string;
      genericsTypes: string;
      operation: OperationObject;
      paramsInPath: string[];
      paramsTypes: string;
    }) => string;
  };
}
```

##### Config File Example

```js
// oats-generator.config.js
module.exports = {
  myFirstBackend: {
    output: "src/queries/myFirstBackend.tsx",
    file: "specs/my-first-backend.yaml",
  },
  configurableBackend: {
    output: "src/queries/configurableBackend.tsx",
    github: "contiamo:oats-generator:master:docs/swagger.json",
    customImport: `import { getConfig } from "../components/Config.tsx";`,
  },
};
```

```json
// package.json
{
  "scripts": {
    "gen": "oats-generator import --config oats-generator.config.js",
    "gen-first": "oats-generator import --config oats-generator.config.js myFirstBackend"
  }
}
```

##### Custom generator

To support even more advanced usecases (like a promise base API, mock generator or anything else that can infer from your specs), you can define your own template in `customGenerator`. This function will be call for each route with some useful computed values (see the types above) and the resulted string will be added to the generated file.

You can see a concrete usage inside the `examples` folder and try yourself in this repository with the following command:

- `yarn build`
- `yarn example:advanced petstore-custom-fetch`

You can inspect the result inside `/examples/petstoreFromFileSpecWithCustomFetch.tsx`

## Contributing

All contributions are welcome – especially:

- documentation,
- bug reports and issues,
- code contributions.

### Code

If you'd like to actively develop or help maintain this project then there are existing tests against which you can test the library with. Typically, this looks like

- `git clone git@github.com:contiamo/oats-generator.git`
- `cd oats-generator`
- `yarn install`
- `yarn test --watch`

From there, you should be able to start developing without problems.

### How to publish to npm

Just update the `version` in `package.json`!

As soon as your branch will be merged to master, a new npm version will be automatically published for you.

## `@without-cli` npm package

If for any reasons you don't want to use our CLI to generate oats-generator components, we provide a `without-cli` version of the package.

Just `npm install oats-generator@without-cli` to have this light version.

This version will follow `latest` but without the cli part (more details into `publish-without-cli.js`).

## Next Steps

We're actively developing this at Contiamo to meet our use cases as they arise. If you have a use case that you'd like to implement, do it! Open an issue, submit a Pull Request, have fun! We're friendly.
