diff --git a/swagger_parser/CHANGELOG.md b/swagger_parser/CHANGELOG.md index 8f45c2756..1c23a9819 100644 --- a/swagger_parser/CHANGELOG.md +++ b/swagger_parser/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.45.1 + +* Fixes OpenAPI 3 schemas without a `required` array generating every property as `required` + when any property uses `allOf` + ([#460](https://github.com/Carapacik/swagger_parser/issues/460)). + ## 1.45.0 * Adds `freezed_json_serialization` (default `true`) to generate Freezed models diff --git a/swagger_parser/lib/src/parser/parser/open_api_parser.dart b/swagger_parser/lib/src/parser/parser/open_api_parser.dart index 32a1b7641..257a1f7c2 100644 --- a/swagger_parser/lib/src/parser/parser/open_api_parser.dart +++ b/swagger_parser/lib/src/parser/parser/open_api_parser.dart @@ -766,16 +766,8 @@ class OpenApiParser { final imports = {}; var requiredParameters = []; - var hasAllOfKey = false; if (map case {_requiredConst: final List rawParameters}) { requiredParameters = rawParameters.map((e) => e.toString()).toList(); - } else if (map case {_propertiesConst: final Map props}) { - for (final propertyName in props.keys) { - final propertyValue = props[propertyName] as Map; - if (propertyValue.containsKey(_allOfConst)) { - hasAllOfKey = true; - } - } } if (map case {_propertiesConst: final Map props}) { @@ -820,7 +812,7 @@ class OpenApiParser { additionalName: additionalName, isRequired: (_apiInfo.schemaVersion == OAS.v2 && !config.useXNullable) ? isRequired - : isRequired || hasAllOfKey || hasDefaultKey, + : isRequired || hasDefaultKey, ); var validation = propertyValue; diff --git a/swagger_parser/test/e2e/tests/xof/all_of.3.0/expected_files/models/nullable_user_dto.dart b/swagger_parser/test/e2e/tests/xof/all_of.3.0/expected_files/models/nullable_user_dto.dart index b2659722a..1711fcf99 100644 --- a/swagger_parser/test/e2e/tests/xof/all_of.3.0/expected_files/models/nullable_user_dto.dart +++ b/swagger_parser/test/e2e/tests/xof/all_of.3.0/expected_files/models/nullable_user_dto.dart @@ -12,7 +12,7 @@ part 'nullable_user_dto.g.dart'; @Freezed() class NullableUserDto with _$NullableUserDto { const factory NullableUserDto({ - @JsonKey(includeIfNull: true) required UserDto? data, + @JsonKey(includeIfNull: false) UserDto? data, }) = _NullableUserDto; factory NullableUserDto.fromJson(Map json) => diff --git a/swagger_parser/test/parser/all_of_property_not_required_test.dart b/swagger_parser/test/parser/all_of_property_not_required_test.dart new file mode 100644 index 000000000..8cc45162c --- /dev/null +++ b/swagger_parser/test/parser/all_of_property_not_required_test.dart @@ -0,0 +1,100 @@ +import 'dart:io'; + +import 'package:path/path.dart' as p; +import 'package:swagger_parser/swagger_parser.dart'; +import 'package:test/test.dart'; + +void main() { + test( + 'properties not listed in required stay optional when a sibling uses allOf', + () async { + final root = + await Directory.systemTemp.createTemp('swagger-parser-test-'); + addTearDown(() => root.delete(recursive: true)); + + final schema = File(p.join(root.path, 'openapi.yaml')) + ..writeAsStringSync(r''' +openapi: 3.0.0 +info: + title: allOf properties without required + version: 1.0.0 +paths: + /foo: + get: + operationId: getFoo + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Foo' + /order: + get: + operationId: getOrder + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Order' +components: + schemas: + Bar: + type: object + properties: + name: + type: string + required: [name] + Status: + type: string + enum: [active, inactive] + Foo: + type: object + # No required array: every property is optional. + properties: + a: + type: string + b: + allOf: + - $ref: '#/components/schemas/Bar' + Order: + type: object + # No required array: every property is optional. + properties: + id: + type: integer + status: + nullable: true + allOf: + - $ref: '#/components/schemas/Status' +'''); + + await GenProcessor( + SWPConfig( + schemaPath: schema.path, + outputDirectory: p.join(root.path, 'generated'), + jsonSerializer: JsonSerializer.freezed, + putClientsInFolder: true, + ), + ).generateFiles(); + + final foo = File( + p.join(root.path, 'generated', 'models', 'foo.dart'), + ).readAsStringSync(); + expect(foo, contains('String? a,')); + expect(foo, isNot(contains('required String? a'))); + expect(foo, contains('Bar? b,')); + expect(foo, isNot(contains('required Bar? b'))); + + final order = File( + p.join(root.path, 'generated', 'models', 'order.dart'), + ).readAsStringSync(); + expect(order, contains('int? id,')); + expect(order, isNot(contains('required int? id'))); + expect(order, contains('Status? status,')); + expect(order, isNot(contains('required Status? status'))); + }, + ); +}