@team-supercharge/oasg 14.1.0-fix-android-publishing-group-998336b0.0 → 15.0.0-feature-nestjs-auth-guard-d8ac30be.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
@@ -683,6 +683,47 @@ TBD
683
683
 
684
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.
685
685
 
686
+
687
+ #### `python-fastapi`
688
+
689
+ ```json
690
+ {
691
+ "id": "server-python",
692
+ "type": "python-fastapi",
693
+ "source": "source-merged",
694
+ "packageName": "oasg_example",
695
+ "repositoryUrl": "https://gitlab.supercharge.io/api/v4/projects/1226/packages/pypi"
696
+ }
697
+ ```
698
+
699
+ |Parameter| Description| Required | Default |
700
+ |-|-|-|-|
701
+ | packageName | Package nem for the project (convention: snake_case) | Y | - |
702
+ | repositoryUrl | URL of the PyPI repository | Y | - |
703
+
704
+ 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.
706
+
707
+ #### `python-fastapi-raw-request`
708
+
709
+ ```json
710
+ {
711
+ "id": "server-python",
712
+ "type": "python-fastapi-raw-request",
713
+ "source": "source-merged",
714
+ "packageName": "oasg_example",
715
+ "repositoryUrl": "https://gitlab.supercharge.io/api/v4/projects/1226/packages/pypi"
716
+ }
717
+ ```
718
+
719
+ |Parameter| Description| Required | Default |
720
+ |-|-|-|-|
721
+ | packageName | Package nem for the project (convention: snake_case) | Y | - |
722
+ | repositoryUrl | URL of the PyPI repository | Y | - |
723
+
724
+ 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.
726
+
686
727
  #### `contract-testing`
687
728
 
688
729
  ```json
@@ -780,6 +821,11 @@ describe('Auth', function () {
780
821
  | packageName | Name of the generated NPM package | Y | - |
781
822
  | repository | URL of the NPM package registry | Y | - |
782
823
 
824
+ ##### Authentication
825
+ 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
+
828
+
783
829
  **Known limitations:**
784
830
  - array of enums in query/header/path/form parameters are not validated (stay as string)
785
831
  - no multidimensional array validation in DTOs
package/bin/oasg CHANGED
@@ -34,24 +34,26 @@ const PROXY_PORT = '9999';
34
34
 
35
35
  const DEFAULT_GENERATOR_MAPPING = {
36
36
  // client targets
37
- "android": { version: '7.0.1', generator: 'kotlin' },
38
- "angular": { version: '7.0.1', generator: 'typescript-angular' },
39
- "feign": { version: '7.0.1', generator: 'spring' },
40
- "feign-kotlin": { version: '7.0.1', generator: 'kotlin-spring' },
41
- "flutter": { version: '7.0.1', generator: 'dart-dio' },
42
- "ios": { version: '7.0.1', generator: 'swift5' },
43
- "kmp": { version: '7.8.0', generator: 'kotlin' },
44
- "python": { version: '7.0.1', generator: 'python' },
45
- "react": { version: '7.0.1', generator: 'typescript-fetch' },
37
+ "android": { version: '7.0.1', generator: 'kotlin' },
38
+ "angular": { version: '7.0.1', generator: 'typescript-angular' },
39
+ "feign": { version: '7.0.1', generator: 'spring' },
40
+ "feign-kotlin": { version: '7.0.1', generator: 'kotlin-spring' },
41
+ "flutter": { version: '7.0.1', generator: 'dart-dio' },
42
+ "ios": { version: '7.0.1', generator: 'swift5' },
43
+ "kmp": { version: '7.8.0', generator: 'kotlin' },
44
+ "python": { version: '7.0.1', generator: 'python' },
45
+ "react": { version: '7.0.1', generator: 'typescript-fetch' },
46
46
  // server targets
47
- "nestjs": { version: '7.0.1', generator: 'typescript-angular' },
48
- "spring": { version: '7.0.1', generator: 'spring' },
49
- "spring-kotlin": { version: '7.0.1', generator: 'kotlin-spring' },
50
- "dotnet": { version: '7.8.0', generator: 'csharp-functions' },
47
+ "nestjs": { version: '7.0.1', generator: 'typescript-angular' },
48
+ "spring": { version: '7.0.1', generator: 'spring' },
49
+ "spring-kotlin": { version: '7.0.1', generator: 'kotlin-spring' },
50
+ "python-fastapi": { version: '7.8.0', generator: 'python-fastapi' },
51
+ "python-fastapi-raw-request": { version: '7.0.1', generator: 'python-fastapi' },
52
+ "dotnet": { version: '7.8.0', generator: 'csharp-functions' },
51
53
  // misc targets
52
- "contract-testing": { version: '4.3.1', generator: 'typescript-node' },
53
- "openapi": { version: undefined, generator: undefined },
54
- "stubby": { version: '4.3.1', generator: 'stubby' },
54
+ "contract-testing": { version: '4.3.1', generator: 'typescript-node' },
55
+ "openapi": { version: undefined, generator: undefined },
56
+ "stubby": { version: '4.3.1', generator: 'stubby' },
55
57
  };
56
58
  const DEFAULT_KTLINT_VERSION = '1.0.0';
57
59
  const BIN_FOLDER = 'out/.bin';
package/config.schema.yml CHANGED
@@ -20,6 +20,8 @@ properties:
20
20
  - $ref: '#/targets/Android'
21
21
  - $ref: '#/targets/ios'
22
22
  - $ref: '#/targets/Python'
23
+ - $ref: '#/targets/PythonFastApi'
24
+ - $ref: '#/targets/PythonFastApiRawRequest'
23
25
  - $ref: '#/targets/ContractTesting'
24
26
  - $ref: '#/targets/NestJS'
25
27
  - $ref: '#/targets/OpenAPI'
@@ -297,6 +299,34 @@ targets:
297
299
  - packageName
298
300
  - repositoryUrl
299
301
 
302
+ PythonFastApi:
303
+ allOf:
304
+ - $ref: '#/targets/Base'
305
+ - properties:
306
+ type:
307
+ pattern: "^python-fastapi$"
308
+ packageName:
309
+ type: string
310
+ repositoryUrl:
311
+ type: string
312
+ required:
313
+ - packageName
314
+ - repositoryUrl
315
+
316
+ PythonFastApiRawRequest:
317
+ allOf:
318
+ - $ref: '#/targets/Base'
319
+ - properties:
320
+ type:
321
+ pattern: "^python-fastapi-raw-request$"
322
+ packageName:
323
+ type: string
324
+ repositoryUrl:
325
+ type: string
326
+ required:
327
+ - packageName
328
+ - repositoryUrl
329
+
300
330
  ContractTesting:
301
331
  allOf:
302
332
  - $ref: '#/targets/Base'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-supercharge/oasg",
3
- "version": "14.1.0-fix-android-publishing-group-998336b0.0",
3
+ "version": "15.0.0-feature-nestjs-auth-guard-d8ac30be.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",
@@ -15,6 +15,9 @@
15
15
  "pipes.ts": {
16
16
  "templateType": "SupportingFiles"
17
17
  },
18
+ "auth.guard.ts": {
19
+ "templateType": "SupportingFiles"
20
+ },
18
21
  "exceptions.ts": {
19
22
  "templateType": "SupportingFiles"
20
23
  },
@@ -5,11 +5,12 @@
5
5
  import { Get, Post, Put, Delete, Patch, Options, Head } from '@nestjs/common';
6
6
  import { Query, Param, Headers, Body } from '@nestjs/common';
7
7
  import { HttpCode, Request } from '@nestjs/common';
8
- import { UseInterceptors, UploadedFile, StreamableFile } from '@nestjs/common';
8
+ import { UseInterceptors, UseGuards, UploadedFile, StreamableFile } from '@nestjs/common';
9
9
  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
14
 
14
15
  {{#imports}}
15
16
  // @ts-ignore
@@ -31,19 +32,22 @@ export abstract class {{classname}} {
31
32
  // ||||||||||
32
33
  /**
33
34
  {{#summary}}
34
- * {{.}}
35
+ * {{.}}
35
36
  {{/summary}}
36
37
  {{#notes}}
37
- * {{.}}
38
+ * {{.}}
38
39
  {{/notes}}
39
-
40
40
  {{#allParams}}
41
- * @param {{paramName}} {{description}}
41
+ * @param {{paramName}} {{description}}
42
42
  {{/allParams}}
43
43
  {{#isDeprecated}}
44
- * @deprecated
44
+ * @deprecated
45
45
  {{/isDeprecated}}
46
- */
46
+ */
47
+
48
+ {{#authMethods}}{{#-first}}
49
+ @AuthSchemes([{{#authMethods}}'{{name}}'{{^-last}}, {{/-last}}{{/authMethods}}])
50
+ @UseGuards(AuthGuard) {{/-first}}{{/authMethods}}
47
51
  //// @{{httpMethod}}('{{path}}'){{#isMultipart}}
48
52
  @UseInterceptors(FileInterceptor({{#formParams}}{{#isFile}}'{{paramName}}'{{/isFile}}{{/formParams}})){{/isMultipart}}
49
53
  @HttpCode({{#responses.0}}{{code}}{{/responses.0}})
@@ -0,0 +1,38 @@
1
+ import {
2
+ CanActivate,
3
+ ExecutionContext,
4
+ Inject,
5
+ Injectable,
6
+ } from "@nestjs/common";
7
+ import { Reflector } from "@nestjs/core";
8
+ import { Observable } from "rxjs";
9
+
10
+ export const AuthSchemes = Reflector.createDecorator<string[]>();
11
+
12
+ export interface AuthServiceInterface {
13
+ validate: (
14
+ scheme: string,
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ request: any
17
+ ) => boolean | Promise<boolean> | Observable<boolean>;
18
+ }
19
+
20
+ export const AUTH_SERVICE_TOKEN = "OASG_AUTH_SERVICE";
21
+
22
+ @Injectable()
23
+ export class AuthGuard implements CanActivate {
24
+ constructor(
25
+ @Inject(AUTH_SERVICE_TOKEN)
26
+ private readonly authService: AuthServiceInterface,
27
+ private readonly reflector: Reflector
28
+ ) {}
29
+
30
+ canActivate(
31
+ context: ExecutionContext
32
+ ): boolean | Promise<boolean> | Observable<boolean> {
33
+ const schemes = this.reflector.get(AuthSchemes, context.getHandler());
34
+ const request = context.switchToHttp().getRequest();
35
+
36
+ return schemes.some((scheme) => this.authService.validate(scheme, request));
37
+ }
38
+ }
@@ -13,10 +13,9 @@
13
13
  },
14
14
  "main": "dist/index.js",
15
15
  "peerDependencies": {
16
- "@nestjs/common": "^9.0.0 || ^10.0.0",
17
- "@nestjs/core": "^9.0.0 || ^10.0.0",
18
- "@nestjs/platform-express": "^9.0.0 || ^10.0.0",
19
- "@types/validator": "13.11.7"
16
+ "@nestjs/common": "^10.0.0",
17
+ "@nestjs/core": "^10.0.0",
18
+ "@nestjs/platform-express": "^10.0.0",
20
19
  },
21
20
  "dependencies": {
22
21
  "class-transformer": "^0.5.1",
@@ -2,9 +2,12 @@
2
2
 
3
3
  source $(dirname "$0")/../common.sh
4
4
 
5
- # change npm pre-release syntax (with - after semver) to python local syntax (with + after semver)
6
- version=$(echo $version | sed 's/-/\+/')
7
- echo "[NOTE] version updated to: ${version}"
5
+ # if the version contains a `-` character, then use PEP 440 beta version syntax
6
+ # example: 1.0.0-beta.1 -> 1.0.0b0+beta.1
7
+ if [[ $version == *"-"* ]]; then
8
+ version=$(echo $version | sed 's/-/b0+/')
9
+ echo "[NOTE] version updated to: ${version}"
10
+ fi
8
11
 
9
12
  rm -rf out/$targetId
10
13
  mkdir -p out/$targetId
@@ -0,0 +1,32 @@
1
+ #/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+
5
+ # if the version contains a `-` character, then use PEP 440 beta version syntax
6
+ # example: 1.0.0-beta.1 -> 1.0.0b0+beta.1
7
+ if [[ $version == *"-"* ]]; then
8
+ version=$(echo $version | sed 's/-/b0+/')
9
+ echo "[NOTE] version updated to: ${version}"
10
+ fi
11
+
12
+ rm -rf out/$targetId
13
+ mkdir -p out/$targetId
14
+
15
+ java -jar $binary generate \
16
+ -g $generatorId \
17
+ -i $openApiFile \
18
+ -t $templateDir \
19
+ -o out/$targetId \
20
+ -c $(dirname "$0")/generator-config.json \
21
+ -p "packageVersion=$version,packageName=$packageName" $generatorCustomArgs
22
+
23
+ cd out/$targetId
24
+
25
+ touch "src/$packageName/__init__.py"
26
+ python3 -m venv venv
27
+ source venv/bin/activate
28
+ pip install --upgrade pip
29
+ pip install flit==3.9.0
30
+ flit build --no-use-vcs
31
+
32
+ cd ../../
@@ -0,0 +1,7 @@
1
+ {
2
+ "inlineSchemaOptions": {
3
+ "ARRAY_ITEM_SUFFIX": "",
4
+ "MAP_ITEM_SUFFIX": "",
5
+ "SKIP_SCHEMA_REUSE": "true"
6
+ }
7
+ }
@@ -0,0 +1,9 @@
1
+ #/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+
5
+ cd out/$targetId
6
+ source venv/bin/activate
7
+ pip install importlib_metadata==7.2.1 twine==4.0.2
8
+ python3 -m twine upload --repository-url $repositoryUrl dist/*
9
+ cd ../..
@@ -0,0 +1,32 @@
1
+ #/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+
5
+ # if the version contains a `-` character, then use PEP 440 beta version syntax
6
+ # example: 1.0.0-beta.1 -> 1.0.0b0+beta.1
7
+ if [[ $version == *"-"* ]]; then
8
+ version=$(echo $version | sed 's/-/b0+/')
9
+ echo "[NOTE] version updated to: ${version}"
10
+ fi
11
+
12
+ rm -rf out/$targetId
13
+ mkdir -p out/$targetId
14
+
15
+ java -jar $binary generate \
16
+ -g $generatorId \
17
+ -i $openApiFile \
18
+ -t $templateDir \
19
+ -o out/$targetId \
20
+ -c $(dirname "$0")/generator-config.json \
21
+ -p "packageVersion=$version,packageName=$packageName" $generatorCustomArgs
22
+
23
+ cd out/$targetId
24
+
25
+ touch "src/$packageName/__init__.py"
26
+ python3 -m venv venv
27
+ source venv/bin/activate
28
+ pip install --upgrade pip
29
+ pip install flit==3.9.0
30
+ flit build --no-use-vcs
31
+
32
+ cd ../../
@@ -0,0 +1,7 @@
1
+ {
2
+ "inlineSchemaOptions": {
3
+ "ARRAY_ITEM_SUFFIX": "",
4
+ "MAP_ITEM_SUFFIX": "",
5
+ "SKIP_SCHEMA_REUSE": "true"
6
+ }
7
+ }
@@ -0,0 +1,9 @@
1
+ #/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+
5
+ cd out/$targetId
6
+ source venv/bin/activate
7
+ pip install importlib_metadata==7.2.1 twine==4.0.2
8
+ python3 -m twine upload --repository-url $repositoryUrl dist/*
9
+ cd ../..
@@ -0,0 +1,43 @@
1
+ # OpenAPI generated FastAPI server
2
+
3
+ This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
4
+
5
+ - API version: {{appVersion}}
6
+ {{^hideGenerationTimestamp}}
7
+ - Build date: {{generatedDate}}
8
+ {{/hideGenerationTimestamp}}
9
+ - Build package: {{generatorClass}}
10
+
11
+ ## Requirements.
12
+
13
+ Python >= {{{generatorLanguageVersion}}}
14
+
15
+ ## Installation & Usage
16
+
17
+ To run the server, please execute the following from the root directory:
18
+
19
+ ```bash
20
+ pip3 install -r requirements.txt
21
+ pip3 install uvicorn
22
+ uvicorn --app-dir src {{packageName}}.main:app --host 0.0.0.0 --port {{serverPort}}
23
+ ```
24
+
25
+ and open your browser at `http://localhost:{{serverPort}}/docs/` to see the docs.
26
+
27
+ ## Running with Docker
28
+
29
+ To run the server on a Docker container, please execute the following from the root directory:
30
+
31
+ ```bash
32
+ docker-compose up --build
33
+ ```
34
+
35
+ ## Tests
36
+
37
+ To run the tests:
38
+
39
+ ```bash
40
+ pip3 install pytest
41
+ pip3 install httpx
42
+ PYTHONPATH=src pytest tests
43
+ ```
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+
3
+ from typing import Dict, List # noqa: F401
4
+ import importlib
5
+ import pkgutil
6
+
7
+ from {{apiPackage}}.{{classFilename}}_{{baseSuffix}} import Base{{classname}}
8
+
9
+ from fastapi import ( # noqa: F401
10
+ APIRouter,
11
+ Body,
12
+ Cookie,
13
+ Depends,
14
+ Form,
15
+ Header,
16
+ Path,
17
+ Query,
18
+ Request,
19
+ Response,
20
+ Security,
21
+ status,
22
+ )
23
+
24
+ from {{modelPackage}}.extra_models import TokenModel # noqa: F401
25
+ {{#imports}}
26
+ {{import}}
27
+ {{/imports}}
28
+
29
+ router = APIRouter()
30
+
31
+ {{#operations}}
32
+ {{#operation}}
33
+ @router.{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}(
34
+ "{{path}}",
35
+ responses={
36
+ {{#responses}}
37
+ {{code}}: {{=<% %>=}}{<%#dataType%>"model": <%dataType%>, "description": "<%message%>"<%/dataType%><%^dataType%>"description": "<%message%>"<%/dataType%>}<%={{ }}=%>,
38
+ {{/responses}}
39
+ },
40
+ tags=[{{#tags}}"{{name}}"{{^-last}},{{/-last}}{{/tags}}],
41
+ {{#summary}}
42
+ summary="{{.}}",
43
+ {{/summary}}
44
+ {{#description}}
45
+ description = "{{.}}",
46
+ {{/description}}
47
+ response_model_by_alias=True,
48
+ )
49
+ async def {{operationId}}(
50
+ request: Request,
51
+ {{#allParams}}
52
+ {{>endpoint_argument_definition}},
53
+ {{/allParams}}
54
+ ) -> Response: # -> {{returnType}}{{^returnType}}None{{/returnType}}
55
+ {{#notes}}"""{{.}}"""
56
+ return await Base{{classname}}.subclasses[0]().{{operationId}}(request{{#allParams}}, {{>impl_argument}}{{/allParams}}){{/notes}}{{^notes}}...{{/notes}}
57
+ {{^-last}}
58
+
59
+
60
+ {{/-last}}
61
+ {{/operation}}
62
+ {{/operations}}
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+
3
+ from typing import ClassVar, Dict, List, Tuple # noqa: F401
4
+ from fastapi import Request, Response
5
+
6
+ {{#imports}}
7
+ {{import}}
8
+ {{/imports}}
9
+
10
+ class Base{{classname}}:
11
+ subclasses: ClassVar[Tuple] = ()
12
+
13
+ def __init_subclass__(cls, **kwargs):
14
+ super().__init_subclass__(**kwargs)
15
+ Base{{classname}}.subclasses = Base{{classname}}.subclasses + (cls,)
16
+
17
+ {{#operations}}
18
+ {{#operation}}
19
+ async def {{operationId}}(
20
+ self,
21
+ request: Request,
22
+ {{#allParams}}
23
+ {{>impl_argument_definition}},
24
+ {{/allParams}}
25
+ ) -> Response: # -> {{returnType}}{{^returnType}}None{{/returnType}}
26
+ {{#notes}}"""{{.}}"""
27
+ ...{{/notes}}{{^notes}}...{{/notes}}
28
+ {{^-last}}
29
+
30
+ {{/-last}}
31
+ {{/operation}}
32
+ {{/operations}}
@@ -0,0 +1 @@
1
+ {{#isPathParam}}{{baseName}}{{/isPathParam}}{{^isPathParam}}{{paramName}}{{/isPathParam}}: {{>param_type}} = {{#isPathParam}}Path{{/isPathParam}}{{#isHeaderParam}}Header{{/isHeaderParam}}{{#isFormParam}}Form{{/isFormParam}}{{#isQueryParam}}Query{{/isQueryParam}}{{#isCookieParam}}Cookie{{/isCookieParam}}{{#isBodyParam}}Body{{/isBodyParam}}({{&defaultValue}}{{^defaultValue}}{{#isPathParam}}...{{/isPathParam}}{{^isPathParam}}None{{/isPathParam}}{{/defaultValue}}, description="{{description}}"{{#isQueryParam}}, alias="{{baseName}}"{{/isQueryParam}}{{#isLong}}{{#minimum}}, ge={{.}}{{/minimum}}{{#maximum}}, le={{.}}{{/maximum}}{{/isLong}}{{#isInteger}}{{#minimum}}, ge={{.}}{{/minimum}}{{#maximum}}, le={{.}}{{/maximum}}{{/isInteger}}{{#pattern}}, regex=r"{{.}}"{{/pattern}}{{#minLength}}, min_length={{.}}{{/minLength}}{{#maxLength}}, max_length={{.}}{{/maxLength}})
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "{{packageName}}"
3
+ version = "{{packageVersion}}"
4
+ description = "{{appDescription}}"
5
+
6
+ [build-system]
7
+ requires = ["setuptools", "wheel"]
8
+ build-backend = "setuptools.build_meta"
9
+
10
+ [tool.black]
11
+ line-length = 88
12
+ exclude = '''
13
+ (
14
+ /(
15
+ \.eggs # exclude a few common directories in the
16
+ | \.git # root of the project
17
+ | \.hg
18
+ | \.mypy_cache
19
+ | \.tox
20
+ | \.venv
21
+ | _build
22
+ | buck-out
23
+ | build
24
+ | dist
25
+ )/
26
+ )
27
+ '''
28
+
29
+ [tool.isort]
30
+ profile = "black"
31
+ skip = [
32
+ '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.tox',
33
+ '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv',
34
+ ]
35
+ skip_gitignore = true
@@ -0,0 +1,2 @@
1
+ fastapi==0.109.2
2
+ pydantic==2.6