epam-cross-platform-lab / swagger-dart-code-generator

Swagger/OpenAPI code generator based on Chopper and JsonAnnotation for Flutter
Apache License 2.0
261 stars 119 forks source link

[BUG] Invalid typedef for inlined array of objects #763

Open ipaid2win opened 2 months ago

ipaid2win commented 2 months ago

Describe the bug Response models generated from inlined arrays are named by the SwaggerModelsGenerator with getClassesFromResponses() by stripping "/" from the path.

if (itemsSchema?.properties.isNotEmpty == true) {
    final pathText = key.split('/').map((e) => e.pascalCase).join();
    final requestText = operation.pascalCase;
    results['$pathText$requestText\$Response'] = neededSchema!;
}

Parameters and rules like cut_from_model_names are not handled. This can lead to the following invalid typedef:

typedef 1NwkGateway{gweui}SpectralScan{execid}Get$Response = List<1NwkGateway{gweui}SpectralScan{execid}Get$Response$Item>;

The inlined object within the array is generated correctly as $Item.

To Reproduce

Example with inlined response array

/1/nwk/gateway/{GWEUI}/spectral-scan/{EXECID}:
    get:
      tags:
        - LoRa | spectral scan
      summary: Get spectral scan steps for specified execution

      parameters:
        - $ref: '#/components/parameters/GWEUI'
        - $ref: '#/components/parameters/EXECID'

      responses:
        400:
          description: bad request
        200:
          description: Success
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    _id:
                      type: string
                      description: unique identifier for spectral scan step
                      example: '5d934124bdcb0272b873c007'
                    gweui:
                      type: string
                      description: gateway eui
                      example: '7076FFFFFF0207B2'
                    date:
                      type: string
                      format: date-time
                      example: '2020-05-01T12:05:56.330Z'
                    execution_id:
                      type: string
                      description: scan execution unique id
                      example: '6152c9c3021eca001aeb5acf'
                    freq:
                      type: number
                      description: frequency scanned on this step
                      example: 870000000
                    rssiOffset:
                      type: number
                      description: RSSI offset
                      example: -143
                    values:
                      description: Array of 256 values that represent the number of measured points for each RSSI value, from -127dBm to 0dBm with 0.5dBm step
                      example:
                        []
                      type: array
                      items:
                        type: number

Generated model code with invalid typedef and valid object:

typedef 1NwkGateway{gweui}SpectralScan{execid}Get$Response = List<1NwkGateway{gweui}SpectralScan{execid}Get$Response$Item>; @JsonSerializable(explicitToJson: true )
class NwkGatewayGweuiSpectralScanExecidGet$Response$Item{
    const NwkGatewayGweuiSpectralScanExecidGet$Response$Item({
        this.id,
        this.gweui,
        this.date,
        this.executionId,
        this.freq,
        this.rssiOffset,
        this.values,

    });
...
};

Generated code in *.swagger.chopper.dart:

  @override
  Future<Response<List<InvalidType>>> _nwkGatewayGWEUISpectralScanEXECIDGet({
    required String? gweui,
    required String? execid,
  }) {
    final Uri $url =
        Uri.parse('/1/nwk/gateway/${gweui}/spectral-scan/${execid}');
    final Request $request = Request(
      'GET',
      $url,
      client.baseUrl,
    );
    return client.send<List<InvalidType>, List<InvalidType>>($request);
  }

Generated code in *.swagger.dart:

  ///Get spectral scan steps for specified execution
  ///@param GWEUI Gateway EUI
  ///@param EXECID GwScan execution ID
  Future<chopper.Response<List<NwkGatewayGWEUISpectralScanEXECIDGet$Response>>>
      nwkGatewayGWEUISpectralScanEXECIDGet({
    required String? gweui,
    required String? execid,
  }) {
    return _nwkGatewayGWEUISpectralScanEXECIDGet(gweui: gweui, execid: execid);
  }

  ///Get spectral scan steps for specified execution
  ///@param GWEUI Gateway EUI
  ///@param EXECID GwScan execution ID
  @Get(path: '/1/nwk/gateway/{GWEUI}/spectral-scan/{EXECID}')
  Future<chopper.Response<List<NwkGatewayGWEUISpectralScanEXECIDGet$Response>>>
      _nwkGatewayGWEUISpectralScanEXECIDGet({
    @Path('GWEUI') required String? gweui,
    @Path('EXECID') required String? execid,
  });

Expected behavior Typedef of inlined array of objects should be generated with getValidatedClassName()

if (itemsSchema?.properties.isNotEmpty == true) {
    //final pathText = key.split('/').map((e) => e.pascalCase).join();
    final pathText = getValidatedClassName('$key');
    final requestText = operation.pascalCase;
    results['$pathText$requestText\$Response'] = neededSchema!;
}

Changing pathText above results in the following valid output and a successful build:

typedef NwkGatewayGWEUISpectralScanEXECIDGet$Response
    = List<NwkGatewayGWEUISpectralScanEXECIDGet$Response$Item>;

Swagger specification link Full spec : https://s3.eu-central-1.amazonaws.com/api.loriot.io/index.html?url=https://s3.eu-central-1.amazonaws.com/api.loriot.io/apidoc/documents_v8.1/nwk/nwk-custom.yaml

Simplified (working) example with inlined array (see GET /1/nwk/gateway/{GWEUI}/spectral-scan/{EXECID} ): nwk-custom.yaml.txt

Library version used:

environment:
  sdk: ^3.4.1

dependencies:
  chopper: ^6.1.1
  json_annotation: ^4.9.0

dev_dependencies:
  build_runner: ^2.4.11
  json_serializable: ^6.8.0
  lints: ^3.0.0
  chopper_generator: ^6.0.0
  swagger_dart_code_generator: ^2.15.2

Additional context

Build.yaml

targets:
  $default:
    sources:
      - $package$
      - lib/**
      - resources/**
    builders:
      swagger_dart_code_generator:
        options:
          input_folder: "resources"
          output_folder: "lib/openapi"
          cut_from_model_names: "1"
          separate_models: true
          use_path_for_request_names: true