Skip to content

Commit c9a2d2b

Browse files
committed
feat(openapi): add generation for extension api
1 parent 86c3101 commit c9a2d2b

9 files changed

Lines changed: 228 additions & 25 deletions

File tree

packages/@ama-openapi/create/README.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,26 @@ my-openapi-project/
5959
└── renovate.json
6060
```
6161

62-
> [!NOTE]
63-
> The package includes several template files that are customized during project generation:
64-
>
65-
> - **API specifications**: Example OpenAPI YAML files
66-
> - **Configuration files**: Project-specific configurations
67-
> - **Development environment**: VS Code settings and extensions
68-
> - **Package management**: Pre-configured `package.json` file with common scripts
69-
> - **Renovate Configuration**: Extends the [@ama-openapi/core preset] package (<https://github.com/AmadeusITGroup/otter/tree/main/packages/@ama-openapi/core/renovate/default.json>)
62+
### Design Exntension Project
63+
64+
Create a new [OpenAPI](https://www.openapis.org/) design project extension via the following command :
65+
66+
```shell
67+
npm create @ama-openapi extension <project-name> -- [options]
68+
```
69+
70+
The following options are available:
71+
72+
| Options | Default Value | Description |
73+
| --- | --- | --- |
74+
| `--target`, `-t` | `.` | Target directory where files will be generated. |
75+
| `--dependency-base-spec`, `-b` | - | Name of the NPM artifact to use as the dependency base specification (e.g. @my-org/specification). |
76+
77+
The command will generate a directory with the same structure of the `design` command but will setup the repository to prepare the generation of 3 specification:
78+
79+
- `base` specification which is extended by the current specification
80+
- `extension` specification bundling only the specification defined in the current project
81+
- `merge` specification which is the merge of the `base` with `extension` specification.
7082

7183
## Integration
7284

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {
2+
OUTPUT_DIRECTORY,
3+
// eslint-disable-next-line import/no-unresolved -- Cannot resolve mjs file in current setup (see #3738)
4+
} from '@ama-openapi/core';
5+
import {
6+
type CreateOptions,
7+
generateTemplate,
8+
} from './generate-template';
9+
10+
/**
11+
* Generate design project
12+
* @param target
13+
* @param packageName
14+
* @param version
15+
*/
16+
export const generateDesign = (target: string, packageName: string, version: string) => {
17+
const options: CreateOptions = {
18+
target,
19+
externalModelPath: OUTPUT_DIRECTORY,
20+
version,
21+
templateDirectory: 'design',
22+
packageName,
23+
logger: console
24+
};
25+
return generateTemplate(options);
26+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
readFile,
3+
writeFile,
4+
} from 'node:fs/promises';
5+
import {
6+
resolve,
7+
} from 'node:path';
8+
import {
9+
OUTPUT_DIRECTORY,
10+
// eslint-disable-next-line import/no-unresolved -- Cannot resolve mjs file in current setup (see #3738)
11+
} from '@ama-openapi/core';
12+
import {
13+
type CreateOptions,
14+
generateTemplate,
15+
} from './generate-template';
16+
17+
/**
18+
* Generate extension project
19+
* @param target
20+
* @param packageName
21+
* @param version
22+
* @param dependencyBaseSpec
23+
*/
24+
export const generateExtension = async (target: string, packageName: string, version: string, dependencyBaseSpec?: string) => {
25+
const options = {
26+
target,
27+
externalModelPath: OUTPUT_DIRECTORY,
28+
version,
29+
dependencyBaseSpec,
30+
packageName,
31+
logger: console
32+
} as const satisfies Partial<CreateOptions>;
33+
await generateTemplate({ ...options, templateDirectory: 'design' });
34+
await generateTemplate({ ...options, templateDirectory: 'design-extension' });
35+
36+
const scripts = {
37+
build: 'redocly bundle && npm run build:merge',
38+
'build:base': 'redocly bundle base',
39+
'build:extension': 'redocly bundle server',
40+
'build:merge': 'redocly bundle filter-client && redocly join filtered-client server -o ./bundle/specification.yaml',
41+
'retrieve-externals': 'ama-openapi install',
42+
'watch:retrieve-externals': 'ama-openapi watch',
43+
postinstall: 'ama-openapi install'
44+
};
45+
46+
const packageJsonPath = resolve(target, 'package.json');
47+
return writeFile(
48+
packageJsonPath,
49+
JSON.stringify(
50+
{
51+
...JSON.parse(await readFile(packageJsonPath, { encoding: 'utf8' })),
52+
...scripts
53+
},
54+
null,
55+
2
56+
)
57+
);
58+
};

packages/@ama-openapi/create/src/generate-template.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
sync,
1919
} from 'globby';
2020

21-
type Templates = 'design';
21+
type Templates = 'design' | 'design-extension';
2222

2323
/**
2424
* Options for template generation
@@ -34,6 +34,8 @@ export interface CreateOptions {
3434
packageName: string;
3535
/** Name of the template directory */
3636
templateDirectory: Templates;
37+
/** Dependency base specification */
38+
dependencyBaseSpec?: string;
3739
/** Logger */
3840
logger: typeof console;
3941
}

packages/@ama-openapi/create/src/index.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import {
66
import {
77
resolve,
88
} from 'node:path';
9-
import {
10-
OUTPUT_DIRECTORY,
11-
// eslint-disable-next-line import/no-unresolved -- Cannot resolve mjs file in current setup (see #3738)
12-
} from '@ama-openapi/core';
139
import type {
1410
PackageJson,
1511
} from 'type-fest';
@@ -18,9 +14,11 @@ import {
1814
hideBin,
1915
} from 'yargs/helpers';
2016
import {
21-
type CreateOptions,
22-
generateTemplate,
23-
} from './generate-template';
17+
generateDesign,
18+
} from './generate-design';
19+
import {
20+
generateExtension,
21+
} from './generate-extension';
2422

2523
void (async () => {
2624
const version = (JSON.parse(await fs.readFile(resolve(__dirname, '..', 'package.json'), { encoding: 'utf8' })) as PackageJson).version!;
@@ -38,14 +36,23 @@ void (async () => {
3836
describe: 'Name of the artifact / package to generate'
3937
});
4038
}, async (argv) => {
41-
const options: CreateOptions = {
42-
target: argv.target,
43-
externalModelPath: OUTPUT_DIRECTORY,
44-
version,
45-
packageName: argv.name,
46-
logger: console
47-
};
48-
await generateTemplate(options);
39+
await generateDesign(argv.target, argv.name, version);
40+
})
41+
.command('extension <name>', 'Name of the artifact / package to generate', (y) => {
42+
return y
43+
.positional('name', {
44+
type: 'string',
45+
demandOption: true,
46+
describe: 'Name of the artifact / package to generate'
47+
})
48+
.option('dependencyBaseSpec', {
49+
type: 'string',
50+
alias: 'b',
51+
demandOption: false,
52+
describe: 'Name of the NPM artifact to use as the dependency base specification (e.g. @my-org/specification)'
53+
});
54+
}, async (argv) => {
55+
await generateExtension(argv.target, argv.name, version, argv.dependencyBaseSpec);
4956
})
5057
.version(version)
5158
.alias('h', 'help')
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<% if (standardGitignoreNode) { %>
2+
<%- standardGitignoreNode %>
3+
<% } else { %>
4+
# Dependency directory
5+
node_modules/
6+
<% } %>
7+
8+
# External models directory
9+
/<%- externalModelPath %>
10+
11+
# Bundle specifications directory
12+
/tmp
13+
/bundle
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# <%- packageName %>
2+
3+
This package exposes 3 different [OpenApi specifications](https://swagger.io/specification/) which can be used to use to generate an SDK or be referred in another specification.
4+
5+
- `<%- packageName %>/bundle/base.yam;`: The core the extension is based on.
6+
- `<%- packageName %>/bundle/extension.yaml`: The extension specification which contains only the new endpoints .
7+
- `<%- packageName %>/bundle/specification.yaml`: The merged specification of the base and the extension
8+
9+
## How to work on it
10+
11+
**Recommendations:**
12+
13+
- Code editor [Visual Studio Code](https://code.visualstudio.com/) with the recommended extension (proposed on start).
14+
- [NodeJs](https://nodejs.org/) with a odd [LTS (or latest) version](https://nodejs.org/en/about/previous-releases).
15+
16+
**Setup:**
17+
18+
```shell
19+
npm install
20+
```
21+
22+
### Add dependency
23+
24+
```shell
25+
npm install <artifact_name>
26+
```
27+
28+
and refer to its models via config in the [manifest](https://github.com/AmadeusITGroup/otter/tree/main/docs/openapi/MANIFEST_CONFIGURATION.md):
29+
30+
```yaml
31+
models:
32+
<artifact_name>:
33+
- models/first-model.yaml
34+
- models/second-model.yaml
35+
```
36+
37+
> [!NOTE]
38+
> The complete documentation is available on [Dependency concept documentation](https://github.com/AmadeusITGroup/otter/tree/main/docs/openapi/DEPENDENCY_RESOLUTION_CONCEPT.md).
39+
40+
## Repository structure
41+
42+
The current repository has the following structure:
43+
44+
```text
45+
<%- projectFolder %>/
46+
├── .vscode/ # VsCode options and recommended extension
47+
├── apis/ # List of the Apis to bundle in the final specification
48+
├── models/ # Models to be referred locally and shared
49+
├── responses/ # List of responses used in the APIs
50+
├── .gitignore
51+
├── openapi.manifest.yaml
52+
├── package.json
53+
├── redocly.yaml
54+
├── README.md
55+
└── renovate.json
56+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
extends:
2+
- recommended
3+
4+
plugins:
5+
- '@ama-openapi/redocly-plugin'
6+
7+
apis:
8+
# The base API
9+
base:
10+
root: ./node_modules/<%- dependencyBaseSpec || '@example/package' %>/bundle/specification.yaml
11+
output: bundle/base.yaml
12+
13+
# The API of the extension
14+
extension@v1:
15+
root: apis/example.v1.yaml
16+
output: bundle/extension.yaml
17+
18+
# This API is used to filter out the extended endpooints of the base API
19+
filter-base:
20+
root: ./node_modules/<%- dependencyBaseSpec || '@example/package' %>/bundle/specification.yaml
21+
output: ./tmp/filtered-dapi.yaml
22+
decorators:
23+
filter-out:
24+
property: operationId
25+
value: [] # Array of operationIds to filter
26+
27+
# This filtered base API
28+
filtered-client:
29+
root: ./tmp/filtered-dapi.yaml

packages/@ama-openapi/create/templates/design/README.md.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This package exposes [OpenApi specification](https://swagger.io/specification/)
44

55
## How to work on it
66

7-
**Requirements:**
7+
**Recommendations:**
88

99
- Code editor [Visual Studio Code](https://code.visualstudio.com/) with the recommended extension (proposed on start).
1010
- [NodeJs](https://nodejs.org/) with a odd [LTS (or latest) version](https://nodejs.org/en/about/previous-releases).

0 commit comments

Comments
 (0)