@team-supercharge/oasg 15.0.0-feature-nestjs-auth-guard-3d1f5ce1.0 → 15.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@ Design APIs in OpenAPI 3.0 format, lint them, and generate client/server package
8
8
  * [Create `.config.json`](#create-config.json)
9
9
  * [Configure Linter](#configure-linter)
10
10
  * [Use with Docker](#use-with-docker)
11
+ * [Version Overview](#version-overview)
11
12
  * [Usage](#usage)
12
13
  * [Linter Rules](#linter-rules)
13
14
  * [Custom Rules](#custom-rules)
@@ -102,6 +103,47 @@ swaggerlint:
102
103
 
103
104
  ---
104
105
 
106
+ # Version Overview
107
+
108
+ The table below gives an overview of the changes (breaking, non-breaking, bug fixes) introduced in various major versions. For the resolution of breaking changes please consult the [Migration Guide](#migration-guide).
109
+
110
+ | Component | | | | | | | | | | | | | | | |
111
+ |------------------------------|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
112
+ | **Internal** | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
113
+ | _Core_ |🐛 |💥 |✨ |🐛 |✨ |➖ |✨ |🐛 |🐛 |➖ |💥 |💥 |✨ |💥 |🆕 |
114
+ | _Linter_ |➖ |➖ |➖ |➖ |💥 |➖ |➖ |➖ |➖ |🐛 |➖ |✨ |💥 |🆕 |
115
+ | **Client Targets** | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
116
+ | `android` |➖ |🐛 |💥 |➖ |➖ |💥 |➖ |➖ |➖ |➖ |➖ |➖ |➖ |🆕 |
117
+ | `angular` |➖ |🐛 |💥 |➖ |➖ |➖ |🐛 |➖ |➖ |➖ |💥 |➖ |➖ |➖ |🆕 |
118
+ | `dotnet` |➖ |🆕 |
119
+ | `feign` |➖ |➖ |➖ |➖ |➖ |✨ |💥 |💥 |➖ |➖ |🐛 |🐛 |🆕 |
120
+ | `feign-kotlin` |➖ |➖ |➖ |➖ |➖ |🆕 |
121
+ | `flutter` |➖ |➖ |🆕 |
122
+ | `ios` |➖ |➖ |💥 |🐛 |➖ |➖ |➖ |✨ |➖ |💥 |➖ |➖ |✨ |🆕 |
123
+ | `kmp` |➖ |🆕 |
124
+ | `python` |➖ |🐛 |💥 |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |🆕 |
125
+ | `react` |➖ |➖ |💥 |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |🆕 |
126
+ | **Server Targets** | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
127
+ | `nestjs` |💥 |➖ |💥 |✨ |➖ |➖ |🐛 |➖ |➖ |✨ |🆕 |
128
+ | `python-fastapi` |➖ |🆕 |
129
+ | `python-fastapi-raw-request` |➖ |🆕 |
130
+ | `spring` |➖ |➖ |➖ |➖ |➖ |✨ |💥 |💥 |➖ |➖ |➖ |✨ |➖ |🆕 |
131
+ | `spring-kotlin` |➖ |➖ |➖ |➖ |➖ |✨ |💥 |💥 |➖ |➖ |🐛 |✨ |➖ |🆕 |
132
+ | **Misc Targets** | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
133
+ | `contract-testing` |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |🆕 |
134
+ | `openapi` |➖ |➖ |➖ |💥 |➖ |➖ |✨ |➖ |🆕 |
135
+ | `stubby` |➖ |➖ |➖ |➖ |➖ |➖ |➖ |➖ |💥 |➖ |➖ |➖ |➖ |🆕 |
136
+ | `postman` |🆕 |
137
+
138
+ **Legend:**
139
+
140
+ * 🆕 feature/target introduced
141
+ * 💥 breaking changes
142
+ * ✨ new features added
143
+ * 🐛 bug fixes only
144
+
145
+ ---
146
+
105
147
  # Usage
106
148
 
107
149
  ## `lint`
@@ -216,11 +258,11 @@ rules:
216
258
 
217
259
  The following rules are defined in the custom _OASg Ruleset_.
218
260
 
219
- > ⚠️ From the rules in the same group only one can be enabled at a time as they are colliding with eachother. Please turn off the default one and enable the other if you intend to change the default behaviour.
261
+ > ⚠️ From the rules in the same group only one can be enabled at a time as they are colliding with each other. Please turn off the default one and enable the other if you intend to change the default behaviour.
220
262
 
221
263
  | Group | Rule | Default | Description |
222
264
  |-|-|-|-|
223
- | | `oasg-object-names-pascal-case` | Y | Every object defined in the spec (schemas, requests, responses) shoud be `PascalCased`, as they are most likely to be converted into classes / enums / objects during code generation |
265
+ | | `oasg-object-names-pascal-case` | Y | Every object defined in the spec (schemas, requests, responses) should be `PascalCased`, as they are most likely to be converted into classes / enums / objects during code generation |
224
266
  | | `oasg-object-names-api-model-suffix` | | makes sure every schema, request, response has the `ApiModel` suffix (used to avoid naming collision) |
225
267
  | | `oasg-operation-ids-camel-case` | Y | The `operationId` properties for paths should be `camelCased`, as they are converted to class methods during code generation |
226
268
  | path casing | `oasg-path-casing-kebab` | Y | enforces `kebab-casing` for path segments |
@@ -242,7 +284,7 @@ The function enables to verify a value against a set of enumeration values defin
242
284
 
243
285
  Function parameters:
244
286
  * `schema` - Name of the schema to be validated against, must contain `enum` definition
245
- * `file` - (optional) YAML file with `components.schemas` defined, otherwise the currently linted documenet is used
287
+ * `file` - (optional) YAML file with `components.schemas` defined, otherwise the currently linted document is used
246
288
 
247
289
  Example usage in a custom project ruleset:
248
290
 
@@ -278,7 +320,7 @@ export default {
278
320
 
279
321
  # Template Customization
280
322
 
281
- Using the comming `templateDir` config parameter there is a possibility to customize the templates used during generation.
323
+ Using the coming `templateDir` config parameter there is a possibility to customize the templates used during generation.
282
324
 
283
325
  OASg offers two levels of customization:
284
326
 
@@ -290,7 +332,7 @@ The **"project level"** customization can be configured using the `templateDir`
290
332
  * if you specify a directory there, the contents of it will be __copied over__ the templates from the previous level
291
333
 
292
334
  > ⚠️ At any level if you want to introduce a customized template, always make sure you start from the **right version**:
293
- > 1. for **OASG level** customizations always theck the version field in `DEFAULT_GENERATOR_MAPPING` for your selected [Target](#target) and copy the [original template](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources) from the right tag!
335
+ > 1. for **OASG level** customizations always check the version field in `DEFAULT_GENERATOR_MAPPING` for your selected [Target](#target) and copy the [original template](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources) from the right tag!
294
336
  > 2. for **project level** customizations check first if the selected template is already customized on **OASG level**, in that case copy the template from the OASg source code
295
337
 
296
338
  ---
@@ -315,7 +357,7 @@ Common source parameters
315
357
 
316
358
  The source specification is by default bundled into a single file (resolving external dependencies) using the [@redocly/cli package's](https://www.npmjs.com/package/@redocly/cli) `bundle` command.
317
359
 
318
- > ⚠️ It is higly recommended to keep `bundle` on if you plan to use the `openapi` target type, as external refs won't be part of the target artifact.
360
+ > ⚠️ It is highly recommended to keep `bundle` on if you plan to use the `openapi` target type, as external refs won't be part of the target artifact.
319
361
 
320
362
  Decorator files must export a `decorate(document)` function which transforms the parsed OpenAPI document and returns it at the end of the function.
321
363
 
@@ -454,6 +496,20 @@ Common target parameters
454
496
  | packageName | Name of the generated NPM package | Y | - |
455
497
  | repository | URL of the NPM package registry | Y | - |
456
498
 
499
+ ##### Known issue: breaking dependencies
500
+
501
+ The Angular target has a known issue related to dependency versioning. The generated Angular project's `package.json` currently defines multiple dependencies with flexible versions (e.g., using `^` or `~` in version constraints). This flexibility allows newer versions of dependencies (not to mention the transitive dependencies), to be pulled in, which can occasionally lead to breaking changes.
502
+
503
+ A notable instance of this issue occurred with **Angular version 12**, which introduced changes that broke the build of generated projects. To address this specific case, we implemented a **temporary fix** in the `targets/angular/generate.sh` script. This script now manually installs problematic dependencies with fixed, validated versions.
504
+
505
+ ###### Long-Term Solution
506
+
507
+ We are actively discussing a long-term solution to this issue. Potential approaches include implementing dependency version locking mechanisms (`package-lock.json`) per Angular versions.
508
+
509
+ The temporary fix may not cover all future breaking changes from other dependencies. Projects using different versions of Angular may encounter issues if the fix does not align with their specific requirements.
510
+
511
+ We welcome feedback and contributions to help address this issue.
512
+
457
513
  #### `react`
458
514
 
459
515
  ```json
@@ -681,7 +737,7 @@ TBD
681
737
  | packageName | Package nem for the project (convention: snake_case) | Y | - |
682
738
  | repositoryUrl | URL of the PyPI repository | Y | - |
683
739
 
684
- Publishing the PyPI packages is done with Twine. For authentication againts the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
740
+ Publishing the PyPI packages is done with Twine. For authentication against the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
685
741
 
686
742
 
687
743
  #### `python-fastapi`
@@ -702,7 +758,7 @@ Publishing the PyPI packages is done with Twine. For authentication againts the
702
758
  | repositoryUrl | URL of the PyPI repository | Y | - |
703
759
 
704
760
  Building the distribution package requires Flit.
705
- Publishing the PyPI packages is done with Twine. For authentication againts the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
761
+ Publishing the PyPI packages is done with Twine. For authentication against the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
706
762
 
707
763
  #### `python-fastapi-raw-request`
708
764
 
@@ -722,7 +778,7 @@ Publishing the PyPI packages is done with Twine. For authentication againts the
722
778
  | repositoryUrl | URL of the PyPI repository | Y | - |
723
779
 
724
780
  Building the distribution package requires Flit.
725
- Publishing the PyPI packages is done with Twine. For authentication againts the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
781
+ Publishing the PyPI packages is done with Twine. For authentication against the PiPI repository you need to set the `TWINE_USERNAME` and `TWINE_PASSWORD` environment variables.
726
782
 
727
783
  #### `contract-testing`
728
784
 
@@ -821,15 +877,74 @@ describe('Auth', function () {
821
877
  | packageName | Name of the generated NPM package | Y | - |
822
878
  | repository | URL of the NPM package registry | Y | - |
823
879
 
824
- ##### Authentication
880
+ **Authorisation**<br>
825
881
  The package generates an AuthGuard and applies to endpoints, where a security scheme is defined.
826
- // TODO - rest of the description and how to use it
827
882
 
883
+ AuthGuard usage:
884
+ Implement the `AuthServiceInterface`, which will receive a validation request for each security scheme added to the checked endpoint and define the validation per scheme.
885
+ Finally provide the service in the context of the module the controller is defined in with the `AUTH_SERVICE_TOKEN` token.
886
+
887
+ If there is already an authorisation service setup for the controllers, the below dummy implementation could be used as a step over until full implementation.
888
+
889
+ ```ts
890
+ @Module(
891
+ providers:[
892
+ {
893
+ provide: AUTH_SERVICE_TOKEN,
894
+ useValue: { validate: () => true },
895
+ },
896
+ ]
897
+ )
898
+ export class RootModule {}
899
+ ```
900
+ Future improvements:
901
+ - with analyzing the request in the guard, only call the validation service with the relevant scheme (i.e: both ApiKey and Bearer Auth defined, but only API_KEY is present in the header, call only with ApiKey scheme )
828
902
 
829
903
  **Known limitations:**
830
904
  - array of enums in query/header/path/form parameters are not validated (stay as string)
831
905
  - no multidimensional array validation in DTOs
832
906
 
907
+ **Validation availability:**
908
+ <br> _List is not comprehensive, missing items are definitely not currently supported_
909
+
910
+ Validations in General:
911
+ | Validation | DTO | Query | Header | Path |
912
+ | ---------------------- | :-: | :---: | :----: | :--: |
913
+ | multidimensional array | ❌ | ❌ | ❌ | ❌ |
914
+ | enum arrays | ✅ | ❌ | ❌ | ❌ |
915
+
916
+ Validations from OpenAPI spec:
917
+ | OpenApi Validation | Corresponding `class-validator` | DTO | Query | Header | Path | Workaround suggestion |
918
+ | ------------------ | ------------------------------- | :-: | :---: | :----: | :--: | ----------------------- |
919
+ | **Common** | | | | | | |
920
+ | required | IsDefined | ✅ | ✅ | ✅ | ✅ | |
921
+ | !required | IsOptional | ✅ | ✅ | ✅ | ✅ | |
922
+ | **Types** | | | | | | |
923
+ | isBoolean | IsBoolean | ✅ | ✅ | ✅ | ✅ | |
924
+ | isString | IsString | ✅ | ✅ | ✅ | ✅ | |
925
+ | isNumber | IsNumber | ✅ | ✅ | ✅ | ✅ | |
926
+ | isFloat | IsNumber | ✅ | ✅ | ✅ | ✅ | |
927
+ | isDouble | IsNumber | ✅ | ✅ | ✅ | ✅ | |
928
+ | isEnum | IsEnum | ✅ | ✅ | ✅ | ✅ | |
929
+ | isArray | IsArray | ✅ | ✅ | ✅ | ✅ | |
930
+ | isDate | IsValidISO8601Date | ✅ | ✅ | ✅ | ✅ | |
931
+ | isDateTime | IsISO8601 | ✅ | ✅ | ✅ | ✅ | |
932
+ | **String** | | | | | | |
933
+ | pattern | Matches | ✅ | ✅ | ✅ | ✅ | |
934
+ | minLength | | ❌ | ❌ | ❌ | ❌ | `pattern` |
935
+ | maxLength | | ❌ | ❌ | ❌ | ❌ | `pattern` |
936
+ | isUuid | IsUUID | ✅ | ✅ | ✅ | ✅ | |
937
+ | isUri | | ❌ | ❌ | ❌ | ❌ | `pattern` |
938
+ | isEmail | IsEmail | ✅ | ✅ | ✅ | ✅ | |
939
+ | **Number** | | | | | | |
940
+ | isInt | IsInt | ✅ | ✅ | ✅ | ✅ | |
941
+ | isLong | IsInt | ✅ | ✅ | ✅ | ✅ | |
942
+ | isShort | IsInt | ✅ | ✅ | ✅ | ✅ | |
943
+ | isUnboundedInteger | | ❌ | ❌ | ❌ | ❌ | `isInt` |
944
+ | minimum | Min | ✅ | ✅ | ✅ | ✅ | |
945
+ | maximum | Max | ✅ | ✅ | ✅ | ✅ | |
946
+
947
+
833
948
  #### `openapi`
834
949
 
835
950
  ```json
@@ -861,16 +976,36 @@ The package generates an AuthGuard and applies to endpoints, where a security sc
861
976
  |Parameter| Description| Required | Default |
862
977
  |-|-|-|-|
863
978
  | sourceUrl | Url to where the package will be published | Y | - |
864
- | apiKey | Apikey of nuget source | Y | - |
979
+ | apiKey | Api key of nuget source | Y | - |
865
980
  | packageName | Name of the generated package | Y | - |
866
981
  | generatorCustomArgs | Custom arguments of the generator (--global-property, --additional-properties) | N | - |
867
982
 
983
+ #### `postman`
984
+
985
+ ```json
986
+ {
987
+ "id": "postman-collection",
988
+ "type": "postman",
989
+ "source": "source-simple",
990
+ }
991
+ ```
992
+
993
+ |Parameter| Description| Required | Default |
994
+ |-|-|-|-|
995
+ | fileName | Name of the generated file | N | `collection.json` |
996
+
868
997
  ---
869
998
 
870
999
  # Migration Guide
871
1000
 
872
1001
  This section covers the breaking changes and their migrations across major version upgrades.
873
1002
 
1003
+ ## From `14.x.x` to `15.0.0`
1004
+
1005
+ In this version the `nestjs` generator has been upgraded to support verifying different security schemes from generated code. This means that a new [Guard](https://docs.nestjs.com/guards) has been introduced on the API oprerations which need to be implemented and checks for valid authentication credentials.
1006
+
1007
+ If you wish to change your authentication handling at a later date, use the [dummy implementation](#nestjs) mentioned above in the documentation.
1008
+
874
1009
  ## From `13.x.x` to `14.0.0`
875
1010
 
876
1011
  Several dependencies have been upgraded, and OASg now uses `node` version 20.15.0 and `npm` version 10 by default. If your project still uses `node@18`, you should upgrade first.
@@ -977,7 +1112,7 @@ Take the following schema:
977
1112
  properties: # users have a company, and firstName and lastName
978
1113
  company:
979
1114
  type: object
980
- properties: # copmanies have id and name
1115
+ properties: # companies have id and name
981
1116
  id:
982
1117
  type: string
983
1118
  name:
@@ -1089,7 +1224,7 @@ For older Angular versions it was necessary to install the `typescript@3.9.5` pa
1089
1224
 
1090
1225
  ## From `3.x.x` to `4.0.0`
1091
1226
 
1092
- Starting from version `4.0.0` OASg became open-source. Thus future packages are (only) available from the official npmjs.org registry without any authenticaiton.
1227
+ Starting from version `4.0.0` OASg became open-source. Thus future packages are (only) available from the official npmjs.org registry without any authentication.
1093
1228
 
1094
1229
  Please take the following steps:
1095
1230
 
@@ -0,0 +1,9 @@
1
+ const fs = require('fs');
2
+
3
+ async function dumpJSON(document, fileName) {
4
+ const content = JSON.stringify(document, null, 2);
5
+ fs.writeFileSync(fileName, content);
6
+ return fileName;
7
+ }
8
+
9
+ exports.dumpJSON = dumpJSON;
package/bin/oasg CHANGED
@@ -19,6 +19,7 @@ const { bash } = require(`${__dirname}/exec.js`);
19
19
  const { merge } = require(`${__dirname}/merger.js`);
20
20
  const { applyOverrides } = require(`${__dirname}/overrider.js`);
21
21
  const { openApiTarget } = require(`${__dirname}/openapi-target.js`);
22
+ const { postmanTarget } = require(`${__dirname}/postman-target.js`);
22
23
  const { processSource } = require(`${__dirname}/process-source.js`);
23
24
  const { globSync } = require('glob');
24
25
 
@@ -54,6 +55,7 @@ const DEFAULT_GENERATOR_MAPPING = {
54
55
  "contract-testing": { version: '4.3.1', generator: 'typescript-node' },
55
56
  "openapi": { version: undefined, generator: undefined },
56
57
  "stubby": { version: '4.3.1', generator: 'stubby' },
58
+ "postman": { version: undefined, generator: undefined },
57
59
  };
58
60
  const DEFAULT_KTLINT_VERSION = '1.0.0';
59
61
  const BIN_FOLDER = 'out/.bin';
@@ -483,6 +485,12 @@ async function generate(argv) {
483
485
  return;
484
486
  }
485
487
 
488
+ // handle postman target
489
+ if (target.type === 'postman') {
490
+ postmanTarget(target, sources[target.source]);
491
+ return;
492
+ }
493
+
486
494
  const binary = fetchBinary(target);
487
495
  let formatter;
488
496
 
@@ -516,6 +524,12 @@ async function publish(argv) {
516
524
  return;
517
525
  }
518
526
 
527
+ // handle postman target
528
+ if (target.type === 'postman') {
529
+ console.log('publishing need to be implemented manuallly for `postman` targets');
530
+ return;
531
+ }
532
+
519
533
  const binary = fetchBinary(target);
520
534
  let formatter;
521
535
 
@@ -0,0 +1,65 @@
1
+ const fs = require('fs');
2
+ const PostmanParser = require('openapi-to-postmanv2');
3
+ const yaml = require('js-yaml');
4
+
5
+ const { dumpJSON } = require(`${__dirname}/dump-json.js`);
6
+
7
+ const defaultFileName = 'collection.json';
8
+
9
+ async function postmanTarget(target, source) {
10
+ const outDir = `out/${target.id}`;
11
+ let outFile = target.fileName || defaultFileName;
12
+ outFile = `${outDir}/${outFile}`;
13
+
14
+ if (fs.existsSync(outDir)) {
15
+ fs.rmSync(outDir, { recursive: true })
16
+ }
17
+ fs.mkdirSync(outDir, { recursive: true });
18
+
19
+ // Conversion options
20
+ const defaultOptions = {
21
+ requestNameSource: 'fallback',
22
+ requestParametersResolution: 'schema',
23
+ exampleParametersResolution: 'schema',
24
+ parametersResolution: 'schema',
25
+ collapseFolders: false,
26
+ folderStrategy: 'tags',
27
+ schemaFaker: false,
28
+ optimizeConversion: false,
29
+ includeAuthInfoInExample: false,
30
+ };
31
+
32
+ // Parse YAML to JavaScript object
33
+ const spec = yaml.load(fs.readFileSync(source, 'utf8'));
34
+
35
+ // Add version number in collection title
36
+ spec.info.title = `${spec.info.title} (${spec.info.version})`;
37
+
38
+ // Remove responses to avoid Example generation
39
+ for (const pathKey in spec.paths) {
40
+ const path = spec.paths[pathKey];
41
+ for (const methodKey in path) {
42
+ const operation = path[methodKey];
43
+
44
+ operation.responses = [];
45
+ }
46
+ }
47
+
48
+ // target-specific functions, e.g. write as JSON
49
+ PostmanParser.convert({ type: 'string', data: spec },
50
+ defaultOptions, (err, conversionResult) => {
51
+ if (err) {
52
+ console.error('Conversion error', err);
53
+ }
54
+ else if (!conversionResult.result) {
55
+ console.log('Could not convert', conversionResult.reason);
56
+ }
57
+ else {
58
+ // write file
59
+ dumpJSON(conversionResult.output[0].data, outFile);
60
+ }
61
+ }
62
+ );
63
+ }
64
+
65
+ exports.postmanTarget = postmanTarget;
package/config.schema.yml CHANGED
@@ -28,6 +28,7 @@ properties:
28
28
  - $ref: '#/targets/Flutter'
29
29
  - $ref: '#/targets/Kmp'
30
30
  - $ref: '#/targets/Dotnet'
31
+ - $ref: '#/targets/Postman'
31
32
  required:
32
33
  - targets
33
34
  additionalProperties: false
@@ -420,6 +421,15 @@ targets:
420
421
  - artifactId
421
422
  - repository
422
423
 
424
+ Postman:
425
+ allOf:
426
+ - $ref: '#/targets/Base'
427
+ - properties:
428
+ type:
429
+ pattern: "^postman$"
430
+ fileName:
431
+ type: string
432
+
423
433
  definitions:
424
434
  # default
425
435
  SourceOverrides:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-supercharge/oasg",
3
- "version": "15.0.0-feature-nestjs-auth-guard-3d1f5ce1.0",
3
+ "version": "15.1.0",
4
4
  "description": "Node-based tool to lint OpenAPI documents and generate clients, servers and documentation from them",
5
5
  "author": "Supercharge",
6
6
  "license": "MIT",
@@ -28,9 +28,9 @@
28
28
  "dependencies": {
29
29
  "@apidevtools/json-schema-ref-parser": "^10.1.0",
30
30
  "@apidevtools/swagger-parser": "^10.1.0",
31
- "@redocly/cli": "1.17.1",
32
- "@stoplight/prism-cli": "5.8.2",
33
- "@stoplight/spectral-cli": "6.11.1",
31
+ "@redocly/cli": "1.25.11",
32
+ "@stoplight/prism-cli": "5.12.0",
33
+ "@stoplight/spectral-cli": "6.14.1",
34
34
  "ajv": "^8.12.0",
35
35
  "command-exists": "^1.2.9",
36
36
  "express": "^4.18.2",
@@ -38,6 +38,7 @@
38
38
  "glob": "^10.2.1",
39
39
  "js-yaml": "^4.1.0",
40
40
  "json-schema-merge-allof": "^0.8.1",
41
+ "openapi-to-postmanv2": "4.24.0",
41
42
  "openapi-typescript-validator-ext-ref": "^3.2.0-external-ref-support",
42
43
  "swagger-ui-express": "^5.0.1",
43
44
  "yamljs": "^0.3.0",
@@ -15,5 +15,9 @@ java -jar $binary generate \
15
15
 
16
16
  cd out/$targetId
17
17
  npm install
18
+ # This is a workaround for an issue with the angular generator. Root cause is the missing package-lock.json: https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/typescript-angular
19
+ # some of the transitive dependency has @types/node@": "*" in their package.json
20
+ # use "npm explain @types/node" in the generated folder (./out/$TARGET) to get more info
21
+ npm install --save-dev @types/node@20.15.0
18
22
  npm run build
19
23
  cd ../..
@@ -15,8 +15,10 @@
15
15
  "pipes.ts": {
16
16
  "templateType": "SupportingFiles"
17
17
  },
18
- "auth.guard.ts": {
19
- "templateType": "SupportingFiles"
18
+ "auth.guard.mustache": {
19
+ "templateType": "SupportingFiles",
20
+ "folder": "",
21
+ "destinationFilename": "auth.guard.ts"
20
22
  },
21
23
  "exceptions.ts": {
22
24
  "templateType": "SupportingFiles"
@@ -35,6 +37,9 @@
35
37
  },
36
38
  "remove-empty-param-objects-from-context.js": {
37
39
  "templateType": "SupportingFiles"
40
+ },
41
+ "is-valid-iso8601-date.validator.ts": {
42
+ "templateType": "SupportingFiles"
38
43
  }
39
44
  },
40
45
  "typeMappings": {
@@ -43,6 +48,7 @@
43
48
  "inlineSchemaOptions": {
44
49
  "ARRAY_ITEM_SUFFIX": "",
45
50
  "MAP_ITEM_SUFFIX": "",
46
- "SKIP_SCHEMA_REUSE": "true"
51
+ "SKIP_SCHEMA_REUSE": "true",
52
+ "RESOLVE_INLINE_ENUMS": "true"
47
53
  }
48
54
  }
@@ -10,7 +10,7 @@ import { FileInterceptor } from '@nestjs/platform-express';
10
10
 
11
11
  import { OptionalParseIntPipe, OptionalParseFloatPipe, OptionalParseBoolPipe, OptionalParseEnumPipe, RequiredPipe } from '../pipes';
12
12
  import { ApiParseArrayPipe, ApiValidationPipe } from '../pipes';
13
- import { AuthGuard, AuthSchemes } from '../auth.guard';
13
+ import { AuthGuard, AuthSchemes, SecurityScheme } from '../auth.guard';
14
14
 
15
15
  {{#imports}}
16
16
  // @ts-ignore
@@ -46,7 +46,7 @@ export abstract class {{classname}} {
46
46
  */
47
47
 
48
48
  {{#authMethods}}{{#-first}}
49
- @AuthSchemes([{{#authMethods}}'{{name}}'{{^-last}}, {{/-last}}{{/authMethods}}])
49
+ @AuthSchemes([{{#authMethods}}SecurityScheme.{{name}}{{^-last}}, {{/-last}}{{/authMethods}}])
50
50
  @UseGuards(AuthGuard) {{/-first}}{{/authMethods}}
51
51
  //// @{{httpMethod}}('{{path}}'){{#isMultipart}}
52
52
  @UseInterceptors(FileInterceptor({{#formParams}}{{#isFile}}'{{paramName}}'{{/isFile}}{{/formParams}})){{/isMultipart}}
@@ -3,15 +3,22 @@ import {
3
3
  ExecutionContext,
4
4
  Inject,
5
5
  Injectable,
6
+ UnauthorizedException,
6
7
  } from "@nestjs/common";
7
8
  import { Reflector } from "@nestjs/core";
8
9
  import { Observable } from "rxjs";
9
10
 
10
- export const AuthSchemes = Reflector.createDecorator<string[]>();
11
+ // export securityScheme names as enum
12
+ export enum SecurityScheme { {{#authMethods}}
13
+ {{name}} = "{{name}}",{{/authMethods}}{{^authMethods}}
14
+ NoScheme = "none",{{/authMethods}}
15
+ }
16
+
17
+ export const AuthSchemes = Reflector.createDecorator<SecurityScheme[]>();
11
18
 
12
19
  export interface AuthServiceInterface {
13
20
  validate: (
14
- scheme: string,
21
+ scheme: SecurityScheme,
15
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
23
  request: any
17
24
  ) => boolean | Promise<boolean> | Observable<boolean>;
@@ -30,9 +37,12 @@ export class AuthGuard implements CanActivate {
30
37
  canActivate(
31
38
  context: ExecutionContext
32
39
  ): boolean | Promise<boolean> | Observable<boolean> {
33
- const schemes = this.reflector.get(AuthSchemes, context.getHandler());
40
+ const schemes = this.reflector.get<SecurityScheme[]>(AuthSchemes, context.getHandler());
34
41
  const request = context.switchToHttp().getRequest();
35
42
 
36
- return schemes.some((scheme) => this.authService.validate(scheme, request));
43
+ if (schemes.some((scheme) => this.authService.validate(scheme, request)))
44
+ return true;
45
+
46
+ throw new UnauthorizedException();
37
47
  }
38
48
  }
@@ -0,0 +1,5 @@
1
+ // export securityScheme names as enum
2
+ export enum SecurityScheme { {{#authMethods}}
3
+ {{name}} = "{{name}}",{{/authMethods}}{{^authMethods}}
4
+ NoScheme = "none",{{/authMethods}}
5
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './api/api';
2
2
  export * from './model/models';
3
3
  export * from './exceptions';
4
+ export * from './auth.guard';
@@ -0,0 +1,31 @@
1
+ import { registerDecorator, buildMessage } from "class-validator";
2
+
3
+ export const IsValidISO8601Date = () => {
4
+ return (object: Object, propertyName: string) => {
5
+ registerDecorator({
6
+ name: "isValidISO8601Date",
7
+ target: object.constructor,
8
+ propertyName: propertyName,
9
+ constraints: [],
10
+ options: {},
11
+ validator: {
12
+ validate(value: string) {
13
+ const [year, month, day] = value.split("-").map(Number);
14
+ const date = new Date(year, month - 1, day);
15
+
16
+ // Check if the date components are valid
17
+ return (
18
+ date.getFullYear() === year &&
19
+ date.getMonth() === month - 1 &&
20
+ date.getDate() === day
21
+ );
22
+ },
23
+ defaultMessage: buildMessage(
24
+ (eachPrefix) =>
25
+ eachPrefix +
26
+ "$property must be a valid ISO 8601 date string (YYYY-mm-DD format)"
27
+ ),
28
+ },
29
+ });
30
+ };
31
+ };
@@ -1,8 +1,11 @@
1
1
  import { Type } from 'class-transformer';
2
2
 
3
3
  import { IsOptional, IsDefined } from 'class-validator';
4
- import { IsString, IsInt, IsNumber, IsBoolean, IsEnum, IsArray, ValidateNested } from 'class-validator';
5
- import { Matches } from 'class-validator';
4
+ import { IsString, IsNumber, IsBoolean, IsEnum, IsArray, ValidateNested } from 'class-validator';
5
+ import { Max, Min, IsInt } from 'class-validator';
6
+ import { Matches, IsEmail, IsUUID } from 'class-validator';
7
+ import { IsISO8601 } from 'class-validator';
8
+ import { IsValidISO8601Date } from '../is-valid-iso8601-date.validator';
6
9
 
7
10
  export class {{classname}}{{#allParents}}{{#-first}} extends {{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/allParents}} { {{>modelGenericAdditionalProperties}}
8
11
  {{#vars}}
@@ -11,10 +14,47 @@ export class {{classname}}{{#allParents}}{{#-first}} extends {{/-first}}{{{.}}}{
11
14
  * {{{.}}}
12
15
  */
13
16
  {{/description}}
14
- {{#required}}@IsDefined(){{/required}}{{^required}}@IsOptional(){{/required}}{{^isArray}}
15
- {{#isString}}@IsString(){{#pattern}} @Matches({{{.}}}){{/pattern}}{{/isString}}{{#isInteger}}@IsInt(){{/isInteger}}{{#isLong}}@IsInt(){{/isLong}}{{#isNumber}}@IsNumber(){{/isNumber}}{{#isFloat}}@IsNumber(){{/isFloat}}{{#isDouble}}@IsNumber(){{/isDouble}}{{#isBoolean}}@IsBoolean(){{/isBoolean}}{{#allowableValues}}{{^enumVars.empty}}@IsEnum({{{dataType}}}){{/enumVars.empty}}{{/allowableValues}}{{#isModel}}@ValidateNested() @Type(() => {{{dataType}}}){{/isModel}}{{/isArray}}{{#isArray}}
16
- @IsArray()
17
- {{#items}}{{#isString}}@IsString({ each: true }){{#pattern}} @Matches({{{.}}}, { each: true }){{/pattern}}{{/isString}}{{#isInteger}}@IsInt({ each: true }){{/isInteger}}{{#isLong}}@IsInt({ each: true }){{/isLong}}{{#isNumber}}@IsNumber({}, { each: true }){{/isNumber}}{{#isFloat}}@IsNumber({}, { each: true }){{/isFloat}}{{#isDouble}}@IsNumber({}, { each: true }){{/isDouble}}{{#isBoolean}}@IsBoolean({ each: true }){{/isBoolean}}{{#allowableValues}}{{^enumVars.empty}}@IsEnum({{{dataType}}}, { each: true }){{/enumVars.empty}}{{/allowableValues}}{{#isModel}}@ValidateNested({ each: true }) @Type(() => {{{dataType}}}){{/isModel}}{{#isArray}}// type validation not supported{{/isArray}}{{/items}}{{/isArray}}
17
+ {{#required}}@IsDefined(){{/required}}{{^required}}@IsOptional(){{/required}}{{^isArray}}{{#isString}}
18
+ @IsString(){{#pattern}}
19
+ @Matches({{{.}}}){{/pattern}}{{#isEmail}}
20
+ @IsEmail(){{/isEmail}}{{#isUuid}}
21
+ @IsUUID(){{/isUuid}}{{/isString}}{{#isDateTime}}
22
+ @IsString()
23
+ @IsISO8601({ strict: true, strictSeparator: true }){{/isDateTime}}{{#isDate}}
24
+ @IsString()
25
+ @IsISO8601()
26
+ @IsValidISO8601Date(){{/isDate}}{{#isInteger}}
27
+ @IsInt(){{/isInteger}}{{#isLong}}
28
+ @IsInt(){{/isLong}}{{#isShort}}
29
+ @IsInt(){{/isShort}}{{#isNumber}}
30
+ @IsNumber(){{/isNumber}}{{#isFloat}}
31
+ @IsNumber(){{/isFloat}}{{#isDouble}}
32
+ @IsNumber(){{/isDouble}}{{#isBoolean}}
33
+ @IsBoolean(){{/isBoolean}}{{#minimum}}
34
+ @Min({{minimum}}){{/minimum}}{{#maximum}}
35
+ @Max({{maximum}}){{/maximum}}{{#allowableValues}}{{^enumVars.empty}}
36
+ @IsEnum({{{dataType}}}){{/enumVars.empty}}{{/allowableValues}}{{#isModel}}
37
+ @ValidateNested()
38
+ @Type(() => {{{dataType}}}){{/isModel}}{{/isArray}}{{#isArray}}
39
+ @IsArray(){{#items}}{{#isString}}
40
+ @IsString({ each: true }){{#pattern}}
41
+ @Matches({{{.}}}, { each: true }){{/pattern}}{{#isEmail}}
42
+ @IsEmail(undefined, { each: true}){{/isEmail}}{{#isUuid}}
43
+ @IsUUID(undefined, { each: true}){{/isUuid}}{{/isString}}{{#isDateTime}}
44
+ @IsString({ each: true })
45
+ @IsISO8601({ strict: true, strictSeparator: true }, { each: true }){{/isDateTime}}{{#isInteger}}
46
+ @IsInt({ each: true }){{/isInteger}}{{#isLong}}
47
+ @IsInt({ each: true }){{/isLong}}{{#isShort}}
48
+ @IsInt({ each: true }){{/isShort}}{{#isNumber}}
49
+ @IsNumber(undefined, { each: true }){{/isNumber}}{{#isFloat}}
50
+ @IsNumber(undefined, { each: true }){{/isFloat}}{{#isDouble}}
51
+ @IsNumber(undefined, { each: true }){{/isDouble}}{{#isBoolean}}
52
+ @IsBoolean({ each: true }){{/isBoolean}}{{#minimum}}
53
+ @Min({{minimum}}, { each: true }){{/minimum}}{{#maximum}}
54
+ @Max({{maximum}}, { each: true }){{/maximum}}{{#allowableValues}}{{^enumVars.empty}}
55
+ @IsEnum({{{dataType}}}, { each: true }){{/enumVars.empty}}{{/allowableValues}}{{#isModel}}
56
+ @ValidateNested({ each: true }) @Type(() => {{{dataType}}}){{/isModel}}{{#isArray}}
57
+ // Multidimensional array validation not supported{{/isArray}}{{/items}}{{/isArray}}
18
58
  {{#isReadOnly}}readonly {{/isReadOnly}}{{{name}}}{{^required}}?{{/required}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}};
19
59
 
20
60
  {{/vars}}