diff --git a/README.md b/README.md index 82a158b..aa7f0f5 100755 --- a/README.md +++ b/README.md @@ -74,12 +74,15 @@ Annotate a dart class with @Openapi() annotation ```dart @Openapi( - additionalProperties: - AdditionalProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep'), - inputSpecFile: 'example/openapi-spec.yaml', - generatorName: Generator.dart, - outputDirectory: 'api/petstore_api') -class Example {} + additionalProperties: + DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep..'), + inputSpec: + RemoteSpec(path: 'https://petstore3.swagger.io/api/v3/openapi.json'), + typeMappings: {'Pet': 'ExamplePet'}, + generatorName: Generator.dio, + runSourceGenOnOutput: true, + outputDirectory: 'api/petstore_api', +) ``` Run @@ -99,7 +102,8 @@ The api sdk will be generated in the folder specified in the annotation. See exa ## Next Generation -There is some new functionality slated to be added to the generator. This version will have the ability to: +As of version 5.0 of this library, there is some new functionality slated to be added to the generator. This version +will have the ability to: - cache changes in the OAS spec - Rerun when there ares difference in the cached copy and current copy @@ -118,25 +122,18 @@ New: ```dart @Openapi( - additionalProperties: - AdditionalProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep'), - inputSpecFile: 'example/openapi-spec.yaml', - generatorName: Generator.dart, - outputDirectory: 'api/petstore_api', - cachePath: 'some/preferred/directory/cache.json', - useNextGen: true + additionalProperties: + DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep..'), + inputSpec: + RemoteSpec(path: 'https://petstore3.swagger.io/api/v3/openapi.json'), + typeMappings: {'Pet': 'ExamplePet'}, + generatorName: Generator.dio, + runSourceGenOnOutput: true, + outputDirectory: 'api/petstore_api', ) class Example {} ``` -**IMPORTANT** With the new changes comes 2 new annotation properties: - -- useNextGen (boolean) - - Default: `false` -- cachePath (String) - - Default: `.dart_tool/openapi-generator-cache.json` - - Must be a path to a `json` file. - - Can only be set when `useNextGen` is `true` ## Contributing diff --git a/example/api/petstore_api/pubspec.yaml b/example/api/petstore_api/pubspec.yaml index 503c965..fdcb68e 100644 --- a/example/api/petstore_api/pubspec.yaml +++ b/example/api/petstore_api/pubspec.yaml @@ -7,7 +7,7 @@ environment: sdk: '>=2.15.0 <3.0.0' dependencies: - dio: '^5.0.0' + dio: '^5.2.0' one_of: '>=1.5.0 <2.0.0' one_of_serializer: '>=1.5.0 <2.0.0' built_value: '>=8.4.0 <9.0.0' diff --git a/example/lib/main.dart b/example/lib/main.dart index 255267d..424b8af 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,4 @@ -// Openapi Generator last run: : 2023-08-20T13:11:18.157121 +// Openapi Generator last run: : 2024-01-16T01:49:21.940229 import 'package:flutter/material.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @@ -9,11 +9,11 @@ void main() { @Openapi( additionalProperties: DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep..'), - inputSpecFile: 'openapi-spec.yaml', + inputSpec: + RemoteSpec(path: 'https://petstore3.swagger.io/api/v3/openapi.json'), typeMappings: {'Pet': 'ExamplePet'}, generatorName: Generator.dio, runSourceGenOnOutput: true, - useNextGen: true, outputDirectory: 'api/petstore_api', ) class MyApp extends StatelessWidget { @@ -124,4 +124,4 @@ class _MyHomePageState extends State<MyHomePage> { ), // This trailing comma makes auto-formatting nicer for build methods. ); } -} +} \ No newline at end of file diff --git a/openapi-generator-annotations/example/example.dart b/openapi-generator-annotations/example/example.dart index 4ffd2ac..9795224 100644 --- a/openapi-generator-annotations/example/example.dart +++ b/openapi-generator-annotations/example/example.dart @@ -3,7 +3,7 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart @Openapi( additionalProperties: AdditionalProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep'), - inputSpecFile: 'example/openapi-spec.yaml', + inputSpec: InputSpec(path: 'example/openapi-spec.yaml'), generatorName: Generator.dio, outputDirectory: 'api/petstore_api', // useNextGen: true, diff --git a/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart b/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart index 007f60e..2ddc689 100644 --- a/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart +++ b/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart @@ -4,12 +4,6 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:meta/meta.dart'; -/// Config base class -/// Your annotated class must extend this config class -@Deprecated( - 'You do not need to extend this anymore (See example). This class would be removed in the next major release.') -abstract class OpenapiGeneratorConfig {} - class Openapi { /// Additional properties to pass to the compiler (CSV) /// @@ -26,12 +20,6 @@ class Openapi { /// --api-package final String? apiPackage; - /// relative path or url to spec file - /// - /// -i - @Deprecated('To be removed in the next major') - final String inputSpecFile; - /// Provides the access information to the input spec file. /// /// For use with useNextGen. @@ -43,7 +31,7 @@ class Openapi { /// to be applied to the fetch request when the spec file is in a remote /// location. There is also special handling for when the spec file lives within /// AWS. - final InputSpec? inputSpec; + final InputSpec inputSpec; /// folder containing the template files /// @@ -80,12 +68,6 @@ class Openapi { /// --reserved-words-mappings final Map<String, String>? reservedWordsMappings; - /// Tells openapi-generator to always run during the build process - /// if set to false (the default), openapi-generator will skip processing if the [outputDirectory] already exists - @Deprecated( - 'The generator will always run to determine if there are changes made on the input spec file') - final bool? alwaysRun; - /// if set to true, flutter pub get will be run on the [outputDirectory] after the code has been generated. /// Defaults to true for backwards compatibility final bool? fetchDependencies; @@ -122,21 +104,6 @@ class Openapi { /// e.g {'inline_object_2': 'SomethingMapped'} final Map<String, String>? inlineSchemaNameMappings; - /// Use the next generation of the generator. - /// - /// This annotation informs the generator to use the new generator pathway. - /// Enabling this option allows for incremental changes to the [inputSpecFile] - /// to be generated even though the annotation was unchanged. - /// - /// Due to some limitations with build_runner and it only running when the - /// asset graph has changed, a generated line get injected at the beginning of - /// the file at the end of each run. - /// - /// This will become the default behaviour in the next Major version (v5). - /// - /// Default: false - final bool useNextGen; - /// The path where to store the cached copy of the specification. /// /// For use with [useNextGen]. @@ -152,8 +119,7 @@ class Openapi { this.additionalProperties, this.overwriteExistingFiles, this.skipSpecValidation = false, - required this.inputSpecFile, - this.inputSpec, + required this.inputSpec, this.templateDirectory, required this.generatorName, this.outputDirectory, @@ -165,9 +131,7 @@ class Openapi { this.apiPackage, this.fetchDependencies = true, this.runSourceGenOnOutput = true, - this.alwaysRun = false, this.cachePath, - this.useNextGen = false, this.projectPubspecPath, this.debugLogging = false, }); @@ -179,30 +143,18 @@ class Openapi { /// Includes the option to use the default json or yaml paths. class InputSpec { final String path; - final bool defaultYaml; - final bool useYml; - const InputSpec({String? path, this.defaultYaml = true, this.useYml = false}) - : path = path ?? - 'openapi.${defaultYaml ? 'y${useYml ? '' : 'a'}ml' : 'json'}'; + const InputSpec({required this.path}); - const InputSpec.empty() : this(); + const InputSpec.json() : this(path: 'openapi.json'); - const InputSpec.emptyJson() : this(defaultYaml: false); - const InputSpec.emptyYml() : this(useYml: true); + const InputSpec.yaml({bool shortExtension = false}) + : this(path: 'openapi.y${shortExtension ? '' : 'a'}ml'); - Map<String, dynamic> toJsonMap() => { - 'path': path, - 'defaultYaml': defaultYaml, - 'useYml': useYml, - }; + Map<String, dynamic> toJsonMap() => {'path': path}; InputSpec.fromMap(Map<String, dynamic> map) - : this( - path: map['path'], - defaultYaml: map['defaultYaml'] == 'true' ? true : false, - useYml: map['useYml'] == 'true' ? true : false, - ); + : this(path: map['path']); } /// Provides the location for the remote specification. @@ -377,10 +329,13 @@ class AdditionalProperties { /// Allow the 'x-enum-values' extension for enums final bool? useEnumExtension; - /// With this option enabled, each enum will have a new case, 'unknown_default_open_api', + /// If the server adds new enum cases, that are unknown by an old spec/client, + /// the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', /// so that when the server sends an enum case that is not known by the client/spec, - /// they can safely fallback to this case - final bool? enumUnknownDefaultCase; + /// they can safely fallback to this case. + /// + /// Default: false + final bool enumUnknownDefaultCase; /// Flutter wrapper to use (none|flutterw|fvm) final Wrapper wrapper; @@ -404,7 +359,7 @@ class AdditionalProperties { this.allowUnicodeIdentifiers = false, this.ensureUniqueParams = true, this.useEnumExtension = false, - this.enumUnknownDefaultCase = true, + this.enumUnknownDefaultCase = false, this.prependFormOrBodyParameters = false, this.pubAuthor, this.pubAuthorEmail, @@ -425,7 +380,7 @@ class AdditionalProperties { allowUnicodeIdentifiers: map['allowUnicodeIdentifiers'] ?? false, ensureUniqueParams: map['ensureUniqueParams'] ?? true, useEnumExtension: map['useEnumExtension'] ?? true, - enumUnknownDefaultCase: map['enumUnknownDefaultCase'] ?? true, + enumUnknownDefaultCase: map['enumUnknownDefaultCase'] ?? false, prependFormOrBodyParameters: map['prependFormOrBodyParameters'] ?? false, pubAuthor: map['pubAuthor'], @@ -532,7 +487,7 @@ class DioProperties extends AdditionalProperties { bool sortModelPropertiesByRequiredFlag = true, bool sortParamsByRequiredFlag = true, bool useEnumExtension = true, - bool enumUnknownDefaultCase = true, + bool enumUnknownDefaultCase = false, String? sourceFolder, Wrapper wrapper = Wrapper.none}) : super( @@ -609,7 +564,7 @@ class DioAltProperties extends AdditionalProperties { bool sortModelPropertiesByRequiredFlag = true, bool sortParamsByRequiredFlag = true, bool useEnumExtension = true, - bool enumUnknownDefaultCase = true, + bool enumUnknownDefaultCase = false, String? sourceFolder, Wrapper wrapper = Wrapper.none}) : super( diff --git a/openapi-generator-annotations/test/openapi_generator_annotations_test.dart b/openapi-generator-annotations/test/openapi_generator_annotations_test.dart index a8ea25b..4c65607 100644 --- a/openapi-generator-annotations/test/openapi_generator_annotations_test.dart +++ b/openapi-generator-annotations/test/openapi_generator_annotations_test.dart @@ -9,15 +9,13 @@ void main() { group('OpenApi', () { test('defaults', () { final props = Openapi( - inputSpecFile: InputSpec.empty().path, - inputSpec: InputSpec.empty(), + inputSpec: InputSpec.json(), generatorName: Generator.dart, ); expect(props.additionalProperties, isNull); expect(props.overwriteExistingFiles, isNull); expect(props.skipSpecValidation, false); - expect(props.inputSpecFile, InputSpec.empty().path); - expect(props.inputSpec!.path, InputSpec.empty().path); + expect(props.inputSpec!.path, InputSpec.json().path); expect(props.templateDirectory, isNull); expect(props.generatorName, Generator.dart); expect(props.outputDirectory, isNull); @@ -28,38 +26,28 @@ void main() { expect(props.apiPackage, isNull); expect(props.fetchDependencies, true); expect(props.runSourceGenOnOutput, true); - expect(props.alwaysRun, false); expect(props.cachePath, isNull); - expect(props.useNextGen, false); expect(props.projectPubspecPath, isNull); expect(props.debugLogging, isFalse); }); group('NextGen', () { test('Sets cachePath', () { final api = Openapi( - inputSpecFile: InputSpec.empty().path, + inputSpec: InputSpec.json(), generatorName: Generator.dart, cachePath: 'somePath'); expect(api.cachePath, 'somePath'); }); - test('Sets useNextGenFlag', () { - final api = Openapi( - inputSpecFile: InputSpec.empty().path, - generatorName: Generator.dart, - useNextGen: true); - expect(api.useNextGen, isTrue); - }); test('Sets projectPubspecPath', () { final api = Openapi( - inputSpecFile: InputSpec.empty().path, + inputSpec: InputSpec.json(), generatorName: Generator.dart, projectPubspecPath: 'test'); expect(api.projectPubspecPath, 'test'); }); test('Set debug logging', () { final api = Openapi( - inputSpecFile: InputSpec.empty().path, - inputSpec: InputSpec.empty(), + inputSpec: InputSpec.json(), generatorName: Generator.dart, debugLogging: true); expect(api.debugLogging, isTrue); @@ -67,19 +55,15 @@ void main() { group('InputSpec', () { group('local spec', () { test('provides default yaml path', () { - expect(InputSpec.empty().path, 'openapi.yaml'); - expect(InputSpec.empty().defaultYaml, isTrue); - expect(InputSpec.empty().useYml, isFalse); + expect(InputSpec.yaml().path, 'openapi.yaml'); + expect(InputSpec.yaml(shortExtension: true).path, 'openapi.yml'); }); test('provides default yml path', () { - expect(InputSpec.emptyYml().path, 'openapi.yml'); - expect(InputSpec.emptyYml().defaultYaml, isTrue); - expect(InputSpec.emptyYml().useYml, isTrue); + expect(InputSpec.yaml(shortExtension: true).path, 'openapi.yml'); + expect(InputSpec.yaml(shortExtension: false).path, 'openapi.yaml'); }); test('provides default json path', () { - expect(InputSpec.emptyJson().path, 'openapi.json'); - expect(InputSpec.emptyJson().defaultYaml, isFalse); - expect(InputSpec.emptyJson().useYml, isFalse); + expect(InputSpec.json().path, 'openapi.json'); }); test('uses path', () { expect(InputSpec(path: 'path').path, 'path'); diff --git a/openapi-generator-cli/lib/openapi-generator.jar b/openapi-generator-cli/lib/openapi-generator.jar index b0b6d51..79fcfeb 100644 Binary files a/openapi-generator-cli/lib/openapi-generator.jar and b/openapi-generator-cli/lib/openapi-generator.jar differ diff --git a/openapi-generator-cli/pom.xml b/openapi-generator-cli/pom.xml index ac11188..d48e819 100644 --- a/openapi-generator-cli/pom.xml +++ b/openapi-generator-cli/pom.xml @@ -47,7 +47,7 @@ <dependency> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-cli</artifactId> - <version>6.6.0</version> + <version>7.2.0</version> </dependency> <dependency> diff --git a/openapi-generator/example/example.dart b/openapi-generator/example/example.dart index 16d6535..3a8104d 100644 --- a/openapi-generator/example/example.dart +++ b/openapi-generator/example/example.dart @@ -3,8 +3,8 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart @Openapi( additionalProperties: AdditionalProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep...'), - inputSpecFile: 'https://google.com', + inputSpec: InputSpec(path: 'openapi-spec.yaml'), generatorName: Generator.dio, outputDirectory: 'api/petstore_api', ) -class Example extends OpenapiGeneratorConfig {} +class Example {} diff --git a/openapi-generator/lib/src/extensions/type_methods.dart b/openapi-generator/lib/src/extensions/type_methods.dart index 6f0f422..aa8756e 100644 --- a/openapi-generator/lib/src/extensions/type_methods.dart +++ b/openapi-generator/lib/src/extensions/type_methods.dart @@ -81,6 +81,19 @@ extension ReadProperty on ConstantReader { return defaultValue; } + var property = readPropertyOrNull<T>(name); + if (property == null) { + return defaultValue; + } + return property; + } + + T? readPropertyOrNull<T>(String name) { + final v = peek(name); + if (v == null) { + return null; + } + if (isA(v, InputSpec)) { final revived = v.revive(); @@ -163,7 +176,7 @@ extension ReadProperty on ConstantReader { } else if (isA(v, Enum)) { return v.enumValue(); } else { - return defaultValue; + return null; } } } diff --git a/openapi-generator/lib/src/gen_on_spec_changes.dart b/openapi-generator/lib/src/gen_on_spec_changes.dart index e221305..5d1c061 100644 --- a/openapi-generator/lib/src/gen_on_spec_changes.dart +++ b/openapi-generator/lib/src/gen_on_spec_changes.dart @@ -29,6 +29,7 @@ final _supportedRegexes = [jsonRegex, yamlRegex]; /// WARNING: THIS DOESN'T VALIDATE THE SPECIFICATION CONTENT FutureOr<Map<String, dynamic>> loadSpec( {required InputSpec specConfig, bool isCached = false}) async { + print('loadSpec - ' + specConfig.path); // If the spec file doesn't match any of the currently supported spec formats // reject the request. if (!_supportedRegexes diff --git a/openapi-generator/lib/src/models/generator_arguments.dart b/openapi-generator/lib/src/models/generator_arguments.dart index c43c392..a5c98e1 100644 --- a/openapi-generator/lib/src/models/generator_arguments.dart +++ b/openapi-generator/lib/src/models/generator_arguments.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:logging/logging.dart'; import 'package:openapi_generator/src/extensions/type_methods.dart'; -import 'package:openapi_generator/src/models/output_message.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; import 'package:source_gen/source_gen.dart' as src_gen; @@ -17,28 +15,6 @@ final defaultCachedPath = /// Represents the Annotation fields passed to the [OpenapiGenerator]. class GeneratorArguments { - /// Informs the generator to always run on changes. - /// - /// WARNING! This will soon be noop. See [useNextGen] for more - /// details. - /// - /// Default: false - @deprecated - final bool alwaysRun; - - /// Informs the generator to follow the next generation path way. - /// - /// NextGen: - /// The next generation of the [OpenapiGenerator] will always run in the - /// event there is a change to the Openapi specification. In this version of - /// the generator the builder caches an instance of the current [inputFile], - /// if one doesn't already exist, this way in the event that are modifications - /// to the spec they can be generated. That cached copy is a translated - /// JSON copy (see [Yaml library]() about output). - /// - /// Default: false - final bool useNextGen; - /// The [cachePath] is the location of the translated copy of the [inputFile] /// before modifications. /// @@ -54,7 +30,7 @@ class GeneratorArguments { /// The directory where the generated sources will be placed. /// /// Default: Directory.current.path - final String outputDirectory; + final String? outputDirectory; /// Informs the generator to run source gen on the output. /// @@ -71,11 +47,6 @@ class GeneratorArguments { /// Default: false final bool skipValidation; - /// Use the provided spec instead of one located in [Directory.current]. - /// - /// Default: openapi.(ya?ml) | openapi.json - String _inputFile; - /// Provides an OAS spec file. /// /// When the [useNextGen] flag is set this should be the spec file configuration @@ -83,7 +54,7 @@ class GeneratorArguments { InputSpec inputSpec; /// The directory containing the template files. - final String templateDirectory; + final String? templateDirectory; /// Informs the generator what kind of library should be generated. /// @@ -94,83 +65,54 @@ class GeneratorArguments { Wrapper get wrapper => additionalProperties?.wrapper ?? Wrapper.none; /// Defines mappings between a class and the import to be used. - final Map<String, String> importMappings; + final Map<String, String>? importMappings; /// Defines mappings between OpenAPI spec types and generated types. - final Map<String, String> typeMappings; + final Map<String, String>? typeMappings; /// Adds reserved words mappings. /// /// Supported by [Generator.dio] & [Generator.dioAlt] generators. - final Map<String, String> reservedWordsMappings; + final Map<String, String>? reservedWordsMappings; /// Additional properties to be passed into the OpenAPI compiler. final AdditionalProperties? additionalProperties; /// Defines a mapping for nested (inline) schema and the generated name. - final Map<String, dynamic> inlineSchemaNameMappings; + final Map<String, dynamic>? inlineSchemaNameMappings; /// Customizes the way inline schema are handled. final InlineSchemaOptions? inlineSchemaOptions; - GeneratorArguments({ - required src_gen.ConstantReader annotations, - bool alwaysRun = false, - String inputSpecFile = '', - InputSpec inputSpec = const InputSpec.empty(), - String templateDirectory = '', - Generator generator = Generator.dart, - Map<String, String> typeMapping = const {}, - Map<String, String> importMapping = const {}, - Map<String, String> reservedWordsMapping = const {}, - Map<String, String> inlineSchemaNameMapping = const {}, - AdditionalProperties? additionalProperties, - InlineSchemaOptions? inlineSchemaOptions, - bool skipValidation = false, - bool runSourceGen = true, - String? outputDirectory, - bool fetchDependencies = true, - bool useNextGen = false, - String? cachePath, - String? pubspecPath, - bool isDebug = false, - }) : alwaysRun = annotations.readPropertyOrDefault('alwaysRun', alwaysRun), - _inputFile = - annotations.readPropertyOrDefault('inputSpecFile', inputSpecFile), - templateDirectory = annotations.readPropertyOrDefault( - 'templateDirectory', templateDirectory), + GeneratorArguments({required src_gen.ConstantReader annotations}) + : templateDirectory = annotations.readPropertyOrNull('templateDirectory'), generator = - annotations.readPropertyOrDefault('generatorName', generator), - typeMappings = - annotations.readPropertyOrDefault('typeMappings', typeMapping), - importMappings = - annotations.readPropertyOrDefault('importMappings', importMapping), - reservedWordsMappings = annotations.readPropertyOrDefault( - 'reservedWordsMappings', reservedWordsMapping), - inlineSchemaNameMappings = annotations.readPropertyOrDefault( - 'inlineSchemaNameMappings', inlineSchemaNameMapping), - additionalProperties = annotations.readPropertyOrDefault( - 'additionalProperties', additionalProperties), - inlineSchemaOptions = annotations.readPropertyOrDefault( - 'inlineSchemaOptions', inlineSchemaOptions), - skipValidation = annotations.readPropertyOrDefault( - 'skipSpecValidation', skipValidation), - runSourceGen = annotations.readPropertyOrDefault( - 'runSourceGenOnOutput', runSourceGen), - shouldFetchDependencies = annotations.readPropertyOrDefault( - 'fetchDependencies', fetchDependencies), - outputDirectory = annotations.readPropertyOrDefault( - 'outputDirectory', outputDirectory ?? Directory.current.path), - useNextGen = - annotations.readPropertyOrDefault('useNextGen', useNextGen), - cachePath = annotations.readPropertyOrDefault( - 'cachePath', cachePath ?? defaultCachedPath), + annotations.readPropertyOrDefault('generatorName', Generator.dart), + typeMappings = annotations.readPropertyOrNull('typeMappings'), + importMappings = annotations.readPropertyOrNull('importMappings'), + reservedWordsMappings = + annotations.readPropertyOrNull('reservedWordsMappings'), + inlineSchemaNameMappings = + annotations.readPropertyOrNull('inlineSchemaNameMappings'), + additionalProperties = + annotations.readPropertyOrNull('additionalProperties'), + inlineSchemaOptions = + annotations.readPropertyOrNull('inlineSchemaOptions'), + skipValidation = + annotations.readPropertyOrDefault('skipSpecValidation', false), + runSourceGen = + annotations.readPropertyOrDefault('runSourceGenOnOutput', true), + shouldFetchDependencies = + annotations.readPropertyOrDefault('fetchDependencies', true), + outputDirectory = annotations.readPropertyOrNull('outputDirectory'), + cachePath = + annotations.readPropertyOrDefault('cachePath', defaultCachedPath), pubspecPath = annotations.readPropertyOrDefault<String>( 'projectPubspecPath', - pubspecPath ?? - '${Directory.current.path}${Platform.pathSeparator}pubspec.yaml'), - isDebug = annotations.readPropertyOrDefault('debugLogging', isDebug), - inputSpec = annotations.readPropertyOrDefault('inputSpec', inputSpec); + '${Directory.current.path}${Platform.pathSeparator}pubspec.yaml'), + isDebug = annotations.readPropertyOrDefault('debugLogging', false), + inputSpec = + annotations.readPropertyOrDefault('inputSpec', InputSpec.json()); /// The stringified name of the [Generator]. String get generatorName => generator == Generator.dart @@ -191,11 +133,7 @@ class GeneratorArguments { /// Used when the specification is hosted on an external server. This will cause /// the compiler to pulls from the remote source. When this is true a cache will /// still be created but a warning will be emitted to the user. - bool get isRemote => useNextGen - ? inputSpec is RemoteSpec - : _inputFile.isNotEmpty - ? RegExp(r'^https?://').hasMatch(_inputFile) - : false; + bool get isRemote => (inputSpec is RemoteSpec); bool get hasLocalCache => File(cachePath).existsSync(); @@ -209,63 +147,26 @@ class GeneratorArguments { /// /// Subsequent calls will be able to use the [_inputFile] when successful in /// the event that a default is found. - Future<String> get inputFileOrFetch async { - if (useNextGen) { - if (isRemote) { - return (inputSpec as RemoteSpec).url.toString(); - } - if (!File(inputSpec.path).existsSync()) { - return Future.error( - OutputMessage( - message: - 'No spec file found. One must be present in the project or hosted remotely.', - level: Level.SEVERE, - stackTrace: StackTrace.current, - ), - ); - } - return inputSpec.path; - } - - final curr = Directory.current; - if (_inputFile.isNotEmpty) { - return _inputFile; - } - - try { - final entry = curr.listSync().firstWhere( - (e) => RegExp(r'^.*/(openapi\.(ya?ml|json))$').hasMatch(e.path)); - _inputFile = entry.path; - return _inputFile; - } catch (e, st) { - return Future.error( - OutputMessage( - message: - 'No spec file found. One must be present in the project or hosted remotely.', - level: Level.SEVERE, - additionalContext: e, - stackTrace: st, - ), - ); - } + String get inputFileOrFetch { + return inputSpec.path; } /// The arguments to be passed to generator jar file. FutureOr<List<String>> get jarArgs async => [ 'generate', - if (outputDirectory.isNotEmpty) '-o=$outputDirectory', + if (outputDirectory?.isNotEmpty ?? false) '-o=$outputDirectory', '-i=${await inputFileOrFetch}', - if (templateDirectory.isNotEmpty) '-t=$templateDirectory', + if (templateDirectory?.isNotEmpty ?? false) '-t=$templateDirectory', '-g=$generatorName', if (skipValidation) '--skip-validate-spec', - if (reservedWordsMappings.isNotEmpty) - '--reserved-words-mappings=${reservedWordsMappings.entries.fold('', foldStringMap())}', - if (inlineSchemaNameMappings.isNotEmpty) - '--inline-schema-name-mappings=${inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - if (importMappings.isNotEmpty) - '--import-mappings=${importMappings.entries.fold('', foldStringMap())}', - if (typeMappings.isNotEmpty) - '--type-mappings=${typeMappings.entries.fold('', foldStringMap())}', + if (reservedWordsMappings?.isNotEmpty ?? false) + '--reserved-words-mappings=${reservedWordsMappings!.entries.fold('', foldStringMap())}', + if (inlineSchemaNameMappings?.isNotEmpty ?? false) + '--inline-schema-name-mappings=${inlineSchemaNameMappings!.entries.fold('', foldStringMap())}', + if (importMappings?.isNotEmpty ?? false) + '--import-mappings=${importMappings!.entries.fold('', foldStringMap())}', + if (typeMappings?.isNotEmpty ?? false) + '--type-mappings=${typeMappings!.entries.fold('', foldStringMap())}', if (inlineSchemaOptions != null) '--inline-schema-options=${inlineSchemaOptions!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}', if (additionalProperties != null) diff --git a/openapi-generator/lib/src/openapi_generator_runner.dart b/openapi-generator/lib/src/openapi_generator_runner.dart index 7ddc15d..580c1c3 100755 --- a/openapi-generator/lib/src/openapi_generator_runner.dart +++ b/openapi-generator/lib/src/openapi_generator_runner.dart @@ -47,16 +47,6 @@ class OpenapiGenerator extends GeneratorForAnnotation<annots.Openapi> { todo: 'Remove the [Openapi] annotation from `$friendlyName`.', ); } else { - if (!(annotations.read('useNextGen').literalValue as bool)) { - if (annotations.read('cachePath').literalValue != null) { - throw InvalidGenerationSourceError( - 'useNextGen must be set when using cachePath', - todo: - 'Either set useNextGen: true on the annotation or remove the custom cachePath', - ); - } - } - // Transform the annotations. final args = GeneratorArguments(annotations: annotations); @@ -66,34 +56,10 @@ class OpenapiGenerator extends GeneratorForAnnotation<annots.Openapi> { ? 'flutter' : 'dart'; - if (!args.useNextGen) { - final path = - '${args.outputDirectory}${Platform.pathSeparator}lib${Platform.pathSeparator}api.dart'; - if (await File(path).exists()) { - if (!args.alwaysRun) { - logOutputMessage( - log: log, - communication: OutputMessage( - message: - 'Generated client already exists at [$path] and configuration is annotated with alwaysRun: [${args.alwaysRun}]. Therefore, skipping this build. Note that the "alwaysRun" config will be removed in future versions.', - level: Level.INFO, - ), - ); - return ''; - } - } - } else { - // If the flag to use the next generation of the generator is applied - // use the new functionality. - return generatorV2( - args: args, - baseCommand: baseCommand, - annotatedPath: buildStep.inputId.path); - } - - await runOpenApiJar(arguments: args); - await fetchDependencies(baseCommand: baseCommand, args: args); - await generateSources(baseCommand: baseCommand, args: args); + return generatorV2( + args: args, + baseCommand: baseCommand, + annotatedPath: buildStep.inputId.path); } } catch (e, st) { late OutputMessage communication; diff --git a/openapi-generator/pubspec.yaml b/openapi-generator/pubspec.yaml index 60ed7e7..41f4207 100755 --- a/openapi-generator/pubspec.yaml +++ b/openapi-generator/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: source_gen: '>=1.0.0 <=2.0.0' path: '>=1.0.0 <=2.0.0' openapi_generator_annotations: '>=4.13.0 <6.0.0' - analyzer: '>=2.0.0 <=6.1.0' + analyzer: '>=2.0.0 <=6.9.9' openapi_generator_cli: '>=4.13.0 <6.0.0' yaml: ^3.1.2 http: '>=0.13.1 <=2.0.0' @@ -25,6 +25,6 @@ dev_dependencies: pedantic: coverage: ^1.6.3 -#dependency_overrides: +# dependency_overrides: # openapi_generator_annotations: # path: ../openapi-generator-annotations diff --git a/openapi-generator/test/builder_test.dart b/openapi-generator/test/builder_test.dart index c97b621..e95efc5 100644 --- a/openapi-generator/test/builder_test.dart +++ b/openapi-generator/test/builder_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -15,101 +16,112 @@ import 'utils.dart'; /// We test the build runner by mocking the specs and then checking the output /// content for the expected generate command. void main() { - group('generator dio', () { + group('dio generator', () { test('to generate appropriate openapi cli command', () async { - expect( - await generate(''' - @Openapi( + final annotations = await getReaderForAnnotation(''' +@Openapi( additionalProperties: DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep...'), - inputSpecFile: '../openapi-spec.yaml', + inputSpec: InputSpec(path: '../openapi-spec.yaml'), typeMappings: {'Pet': 'ExamplePet'}, generatorName: Generator.dio, runSourceGenOnOutput: true, alwaysRun: true, outputDirectory: 'api/petstore_api') - '''), + '''); + final args = GeneratorArguments(annotations: annotations); + expect( + (await args.jarArgs).join(' '), contains( - 'generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none')); + 'generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,enumUnknownDefaultCase=false,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none')); }); test('to generate command with import and type mappings', () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', + final annotations = await getReaderForAnnotation(''' +@Openapi( + inputSpec: InputSpec(path: '../openapi-spec.yaml'), typeMappings: {'int-or-string':'IntOrString'}, importMappings: {'IntOrString':'./int_or_string.dart'}, generatorName: Generator.dio, outputDirectory: '${testSpecPath}output', ) - '''), + '''); + final args = GeneratorArguments(annotations: annotations); + expect( + (await args.jarArgs).join(' '), contains( 'generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart-dio --import-mappings=IntOrString=./int_or_string.dart --type-mappings=int-or-string=IntOrString')); }); test('to generate command with inline schema mappings', () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', + final annotations = await getReaderForAnnotation(''' +@Openapi( + inputSpec: InputSpec(path: '../openapi-spec.yaml'), typeMappings: {'int-or-string':'IntOrString'}, inlineSchemaNameMappings: {'inline_object_2':'SomethingMapped','inline_object_4':'nothing_new'}, generatorName: Generator.dio, outputDirectory: '${testSpecPath}output', ) - '''), - contains(''' + '''); + final args = GeneratorArguments(annotations: annotations); + expect( + (await args.jarArgs).join(' '), + equals(''' generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart-dio --inline-schema-name-mappings=inline_object_2=SomethingMapped,inline_object_4=nothing_new --type-mappings=int-or-string=IntOrString ''' .trim())); }); - - // test('to generate command with inline schema options', () async { - // expect(await generate(''' - // @Openapi( - // inputSpecFile: '../openapi-spec.yaml', - // inlineSchemaOptions: InlineSchemaOptions(skipSchemaReuse: true,refactorAllofInlineSchemas: true,resolveInlineEnums: true), - // generatorName: Generator.dio) - // '''), contains(''' - // generate -i ../openapi-spec.yaml -g dart-dio --type-mappings=int-or-string=IntOrString --inline-schema-name-mappings=inline_object_2=SomethingMapped,inline_object_4=nothing_new - // '''.trim())); - // }); }); group('generator dioAlt', () { test('to generate appropriate openapi cli command', () async { - expect( - await generate(''' - @Openapi( + final annotations = (await resolveSource( + ''' +library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + + @Openapi( additionalProperties: DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep...'), - inputSpecFile: '../openapi-spec.yaml', + inputSpec: InputSpec(path: '../openapi-spec.yaml'), typeMappings: {'Pet': 'ExamplePet'}, generatorName: Generator.dio, runSourceGenOnOutput: true, alwaysRun: true, outputDirectory: 'api/petstore_api') - '''), +class TestClassConfig extends OpenapiGeneratorConfig {} + ''', + (resolver) async => + (await resolver.findLibraryByName('test_lib'))!)) + .getClass('TestClassConfig')! + .metadata + .map((e) => ConstantReader(e.computeConstantValue()!)) + .first; + final args = GeneratorArguments(annotations: annotations); + expect( + (await args.jarArgs).join(' '), contains(''' - generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none + generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,enumUnknownDefaultCase=false,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none ''' .trim())); }); test('to generate command with import and type mappings for dioAlt', () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', + var annots = await getReaderForAnnotation(''' + @Openapi( + inputSpec: InputSpec(path:'../openapi-spec.yaml'), typeMappings: {'int-or-string':'IntOrString'}, importMappings: {'IntOrString':'./int_or_string.dart'}, generatorName: Generator.dioAlt, outputDirectory: '${testSpecPath}output', ) - '''), - contains( + '''); + var args = GeneratorArguments(annotations: annots); + expect( + (await args.jarArgs).join(' '), + equals( 'generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart2-api --import-mappings=IntOrString=./int_or_string.dart --type-mappings=int-or-string=IntOrString')); }); }); @@ -137,21 +149,6 @@ void main() { f.deleteSync(); } }); - test('fails with invalid configuration', () async { - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: '$specPath', - typeMappings: {'int-or-string':'IntOrString'}, - importMappings: {'IntOrString':'./int_or_string.dart'}, - generatorName: Generator.dioAlt, - useNextGen: false, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/invalid_config/' - ) - '''); - expect(generatedOutput, - contains('useNextGen must be set when using cachePath')); - }); test('Logs warning when using remote spec', () async { generatedOutput = await generate(''' @Openapi( @@ -409,8 +406,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} expect(args.runSourceGen, isFalse); generatedOutput = await generate(''' @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec(path: '$specPath'), generatorName: Generator.dio, useNextGen: true, @@ -450,8 +445,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} expect(args.runSourceGen, isTrue); generatedOutput = await generate(''' @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec(path: '$specPath'), generatorName: Generator.dart, useNextGen: true, @@ -468,8 +461,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} test('logs when successful', () async { generatedOutput = await generate(''' @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec(path: '$specPath'), generatorName: Generator.dio, useNextGen: true, @@ -486,8 +477,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} test('except when flag is present', () async { generatedOutput = await generate(''' @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec(path: '$specPath'), generatorName: Generator.dio, useNextGen: true, @@ -503,8 +492,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} test('succeeds', () async { generatedOutput = await generate(''' @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec(path: '$specPath'), generatorName: Generator.dio, useNextGen: true, @@ -521,7 +508,6 @@ class TestClassConfig extends OpenapiGeneratorConfig {} group('update cache', () { final src = ''' @Openapi( - inputSpecFile: '$specPath', inputSpec: RemoteSpec(path: '$specPath'), useNextGen: true, cachePath: '${f.path}', @@ -556,33 +542,22 @@ class TestClassConfig extends OpenapiGeneratorConfig {} generatedOutput, contains('Successfully cached spec changes.')); }); }); - test('uses AWS', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: '', - inputSpec: RemoteSpec( - path: - 'http://bucket.s3.us-east-1.localhost.localstack.cloud:4566/openapi.yaml', - headerDelegate: AWSRemoteSpecHeaderDelegate( - bucket: 'bucket', - accessKeyId: 'test', - secretAccessKey: 'test', - ), - ), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/uses-aws-spec', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', -) - '''); - - expect(generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - 'dart pub run build_runner build --delete-conflicting-outputs')); - }); }); }); } + +Future<ConstantReader> getReaderForAnnotation(String annotationDef) async { + final annotations = (await resolveSource(''' +library test_lib; +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +$annotationDef +class TestClassConfig {} + ''', + (resolver) async => (await resolver.findLibraryByName('test_lib'))!)) + .getClass('TestClassConfig')! + .metadata + .map((e) => ConstantReader(e.computeConstantValue()!)) + .first; + return annotations; +} diff --git a/openapi-generator/test/generator_arguments_test.dart b/openapi-generator/test/generator_arguments_test.dart index 53712e9..859c66d 100644 --- a/openapi-generator/test/generator_arguments_test.dart +++ b/openapi-generator/test/generator_arguments_test.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:build_test/build_test.dart'; import 'package:openapi_generator/src/models/generator_arguments.dart'; -import 'package:openapi_generator/src/models/output_message.dart'; import 'package:openapi_generator/src/utils.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; import 'package:source_gen/source_gen.dart' as src_gen; @@ -14,11 +13,8 @@ void main() { late GeneratorArguments args; setUpAll(() => args = GeneratorArguments(annotations: src_gen.ConstantReader(null))); - test('alwaysRun', () => expect(args.alwaysRun, isFalse)); - test('useNextGen', () => expect(args.useNextGen, isFalse)); test('cachePath', () => expect(args.cachePath, defaultCachedPath)); - test('outputDirectory', - () => expect(args.outputDirectory, Directory.current.path)); + test('outputDirectory', () => expect(args.outputDirectory, isNull)); test('runSourceGen', () => expect(args.runSourceGen, isTrue)); test('shouldFetchDependencies', () => expect(args.shouldFetchDependencies, isTrue)); @@ -28,34 +24,25 @@ void main() { () => expect( args.pubspecPath, '${Directory.current.path}/pubspec.yaml')); group('inputFile', () { - test('errors when no spec is found', () async { - await args.inputFileOrFetch.onError((e, __) { - expect((e as OutputMessage).message, - 'No spec file found. One must be present in the project or hosted remotely.'); - return ''; - }); - }); - test('updates path when one is found', () async { final f = File( Directory.current.path + '${Platform.pathSeparator}openapi.json'); f.createSync(); f.writeAsStringSync(''); final p = await args.inputFileOrFetch; - expect(p, f.path); - expect(await args.inputFileOrFetch, f.path); + expect(f.path.endsWith(p), isTrue); f.deleteSync(); }); }); - test('templateDirectory', () => expect(args.templateDirectory, isEmpty)); + test('templateDirectory', () => expect(args.templateDirectory, isNull)); test('generator', () => expect(args.generator, Generator.dart)); - test('importMappings', () => expect(args.importMappings, isEmpty)); - test('typeMappings', () => expect(args.typeMappings, isEmpty)); + test('importMappings', () => expect(args.importMappings, isNull)); + test('typeMappings', () => expect(args.typeMappings, isNull)); test('reservedWordsMappings', - () => expect(args.reservedWordsMappings, isEmpty)); + () => expect(args.reservedWordsMappings, isNull)); test('inlineSchemaNameMappings', - () => expect(args.inlineSchemaNameMappings, isEmpty)); + () => expect(args.inlineSchemaNameMappings, isNull)); test('generatorName', () => expect(args.generatorName, 'dart')); test('shouldGenerateSources', @@ -74,85 +61,81 @@ void main() { f.writeAsStringSync(''); expect(await args.jarArgs, [ 'generate', - '-o=${Directory.current.path}', '-i=${await args.inputFileOrFetch}', '-g=${args.generatorName}', ]); f.deleteSync(); }); }); - group('accepts overrides', () { - final annos = src_gen.ConstantReader(null); - final args = GeneratorArguments( - annotations: annos, - alwaysRun: true, - useNextGen: true, - cachePath: 'test', - outputDirectory: 'path', - templateDirectory: 'template', - runSourceGen: false, - generator: Generator.dioAlt, - skipValidation: true, - fetchDependencies: false, - inputSpecFile: 'test.yaml', - inputSpec: InputSpec(path: 'test/specs/openapi.test.yaml'), - importMapping: {'key': 'value'}, - typeMapping: {'package': 'type'}, - reservedWordsMapping: {'const': 'final'}, - inlineSchemaNameMapping: {'L': 'R'}, - additionalProperties: AdditionalProperties(wrapper: Wrapper.fvm), - pubspecPath: 'testing/pubspec.yaml', - ); - test('alwaysRun', () => expect(args.alwaysRun, isTrue)); - test('useNextGen', () => expect(args.useNextGen, isTrue)); - test('cachePath', () => expect(args.cachePath, 'test')); - test('outputDirectory', () => expect(args.outputDirectory, 'path')); - test('runSourceGen', () => expect(args.runSourceGen, isFalse)); - test('shouldFetchDependencies', - () => expect(args.shouldFetchDependencies, isFalse)); - test('skipValidation', () => expect(args.skipValidation, isTrue)); - test( - 'inputFile', - () async => expect( - await args.inputFileOrFetch, 'test/specs/openapi.test.yaml')); - test('templateDirectory', - () => expect(args.templateDirectory, 'template')); - test('generator', () => expect(args.generator, Generator.dioAlt)); - test('wrapper', () => expect(args.wrapper, Wrapper.fvm)); - test('importMappings', - () => expect(args.importMappings, {'key': 'value'})); - test( - 'typeMappings', () => expect(args.typeMappings, {'package': 'type'})); - test('reservedWordsMappings', - () => expect(args.reservedWordsMappings, {'const': 'final'})); - test('inlineSchemaNameMappings', - () => expect(args.inlineSchemaNameMappings, {'L': 'R'})); - test('isRemote', () => expect(args.isRemote, isFalse)); - test('generatorName', () => expect(args.generatorName, 'dart2-api')); - test('shouldGenerateSources', - () => expect(args.shouldGenerateSources, isTrue)); - test('pubspecPath', - () => expect(args.pubspecPath, 'testing/pubspec.yaml')); - test( - 'jarArgs', - () async => expect( - await args.jarArgs, - [ - 'generate', - '-o=${args.outputDirectory}', - '-i=${await args.inputFileOrFetch}', - '-t=${args.templateDirectory}', - '-g=${args.generatorName}', - '--skip-validate-spec', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties?.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' - ], - ), - ); - }); + // group('accepts overrides', () { + // final annos = src_gen.ConstantReader(null); + // final args = GeneratorArguments( + // annotations: annos, + // alwaysRun: true, + // useNextGen: true, + // cachePath: 'test', + // outputDirectory: 'path', + // templateDirectory: 'template', + // runSourceGen: false, + // generator: Generator.dioAlt, + // skipValidation: true, + // fetchDependencies: false, + // inputSpec: InputSpec(path: 'test/specs/openapi.test.yaml'), + // importMapping: {'key': 'value'}, + // typeMapping: {'package': 'type'}, + // reservedWordsMapping: {'const': 'final'}, + // inlineSchemaNameMapping: {'L': 'R'}, + // additionalProperties: AdditionalProperties(wrapper: Wrapper.fvm), + // pubspecPath: 'testing/pubspec.yaml', + // ); + // test('cachePath', () => expect(args.cachePath, 'test')); + // test('outputDirectory', () => expect(args.outputDirectory, 'path')); + // test('runSourceGen', () => expect(args.runSourceGen, isFalse)); + // test('shouldFetchDependencies', + // () => expect(args.shouldFetchDependencies, isFalse)); + // test('skipValidation', () => expect(args.skipValidation, isTrue)); + // test( + // 'inputFile', + // () async => expect( + // await args.inputFileOrFetch, 'test/specs/openapi.test.yaml')); + // test('templateDirectory', + // () => expect(args.templateDirectory, 'template')); + // test('generator', () => expect(args.generator, Generator.dioAlt)); + // test('wrapper', () => expect(args.wrapper, Wrapper.fvm)); + // test('importMappings', + // () => expect(args.importMappings, {'key': 'value'})); + // test( + // 'typeMappings', () => expect(args.typeMappings, {'package': 'type'})); + // test('reservedWordsMappings', + // () => expect(args.reservedWordsMappings, {'const': 'final'})); + // test('inlineSchemaNameMappings', + // () => expect(args.inlineSchemaNameMappings, {'L': 'R'})); + // test('isRemote', () => expect(args.isRemote, isFalse)); + // test('generatorName', () => expect(args.generatorName, 'dart2-api')); + // test('shouldGenerateSources', + // () => expect(args.shouldGenerateSources, isTrue)); + // test('pubspecPath', + // () => expect(args.pubspecPath, 'testing/pubspec.yaml')); + // test( + // 'jarArgs', + // () async => expect( + // await args.jarArgs, + // [ + // 'generate', + // '-o=${args.outputDirectory}', + // '-i=${await args.inputFileOrFetch}', + // '-t=${args.templateDirectory}', + // '-g=${args.generatorName}', + // '--skip-validate-spec', + // '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', + // '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', + // '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', + // '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', + // '--additional-properties=${args.additionalProperties?.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' + // ], + // ), + // ); + // }); group('annotation specification', () { // https://github.com/gibahjoe/openapi-generator-dart/issues/110 test('Processes annotations correctly', () async { @@ -168,8 +151,6 @@ void main() { .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) .first; final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); expect(args.cachePath, './test/specs/output/cache.json'); expect(args.outputDirectory, './test/specs/output'); expect(args.runSourceGen, isTrue); @@ -195,11 +176,16 @@ void main() { '-i=${await args.inputFileOrFetch}', '-t=${args.templateDirectory}', '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' + if (args.reservedWordsMappings?.isNotEmpty ?? false) + '--reserved-words-mappings=${args.reservedWordsMappings?.entries.fold('', foldStringMap())}', + if (args.inlineSchemaNameMappings?.isNotEmpty ?? false) + '--inline-schema-name-mappings=${args.inlineSchemaNameMappings!.entries.fold('', foldStringMap())}', + if (args.importMappings?.isNotEmpty ?? false) + '--import-mappings=${args.importMappings!.entries.fold('', foldStringMap())}', + if (args.typeMappings?.isNotEmpty ?? false) + '--type-mappings=${args.typeMappings!.entries.fold('', foldStringMap())}', + if (args.additionalProperties != null) + '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' ]); }); test('Processes annotation with DioProperties correctly', () async { @@ -215,8 +201,6 @@ void main() { .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) .first; final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); expect(args.cachePath, './test/specs/output/cache.json'); expect(args.outputDirectory, './test/specs/output'); expect(args.runSourceGen, isTrue); @@ -243,11 +227,16 @@ void main() { '-i=${await args.inputFileOrFetch}', '-t=${args.templateDirectory}', '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' + if (args.reservedWordsMappings?.isNotEmpty ?? false) + '--reserved-words-mappings=${args.reservedWordsMappings?.entries.fold('', foldStringMap())}', + if (args.inlineSchemaNameMappings?.isNotEmpty ?? false) + '--inline-schema-name-mappings=${args.inlineSchemaNameMappings!.entries.fold('', foldStringMap())}', + if (args.importMappings?.isNotEmpty ?? false) + '--import-mappings=${args.importMappings!.entries.fold('', foldStringMap())}', + if (args.typeMappings?.isNotEmpty ?? false) + '--type-mappings=${args.typeMappings!.entries.fold('', foldStringMap())}', + if (args.additionalProperties != null) + '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' ]); }); test('Processes annotation with DioAltProperties correctly', () async { @@ -263,8 +252,6 @@ void main() { .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) .first; final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); expect(args.cachePath, './test/specs/output/cache.json'); expect(args.outputDirectory, './test/specs/output'); expect(args.runSourceGen, isTrue); @@ -295,13 +282,61 @@ void main() { '-i=${await args.inputFileOrFetch}', '-t=${args.templateDirectory}', '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' + if (args.reservedWordsMappings?.isNotEmpty ?? false) + '--reserved-words-mappings=${args.reservedWordsMappings?.entries.fold('', foldStringMap())}', + if (args.inlineSchemaNameMappings?.isNotEmpty ?? false) + '--inline-schema-name-mappings=${args.inlineSchemaNameMappings!.entries.fold('', foldStringMap())}', + if (args.importMappings?.isNotEmpty ?? false) + '--import-mappings=${args.importMappings!.entries.fold('', foldStringMap())}', + if (args.typeMappings?.isNotEmpty ?? false) + '--type-mappings=${args.typeMappings!.entries.fold('', foldStringMap())}', + if (args.additionalProperties != null) + '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' ]); }); + test( + 'Processes annotation with inputSpecFile that contains url correctly', + () async { + final config = File( + '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}specs${Platform.pathSeparator}input_remote_properties_test_config.dart') + .readAsStringSync(); + final annotations = (await resolveSource( + config, + (resolver) async => + (await resolver.findLibraryByName('test_lib'))!)) + .getClass('DioAltPropertiesTestConfig')! + .metadata + .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) + .first; + final args = GeneratorArguments(annotations: annotations); + expect(args.cachePath, './test/specs/output/cache.json'); + expect(args.outputDirectory, './test/specs/output'); + expect(args.runSourceGen, isTrue); + expect(args.shouldFetchDependencies, isTrue); + expect(args.skipValidation, isFalse); + expect(await args.inputFileOrFetch, + 'https://petstore3.swagger.io/api/v3/openapi.json'); + expect(args.templateDirectory, 'template'); + expect(args.generator, Generator.dio); + expect(args.wrapper, Wrapper.fvm); + expect(args.importMappings, {'package': 'test'}); + expect(args.typeMappings, {'key': 'value'}); + expect(args.reservedWordsMappings, {'const': 'final'}); + expect(args.inlineSchemaNameMappings, {'200resp': 'OkResp'}); + expect(args.pubspecPath, './test/specs/dart_pubspec.test.yaml'); + expect(args.isRemote, isTrue); + expect(args.inputSpec.path, + 'https://petstore3.swagger.io/api/v3/openapi.json'); + expect(args.generatorName, 'dart-dio'); + expect(args.shouldGenerateSources, isTrue); + expect(args.additionalProperties?.useEnumExtension, isTrue); + expect( + (args.additionalProperties as DioAltProperties?)?.nullSafe, isTrue); + expect( + (args.additionalProperties as DioAltProperties?) + ?.nullSafeArrayDefault, + isTrue); + }); }); }); } diff --git a/openapi-generator/test/specs/dio_alt_properties_test_config.dart b/openapi-generator/test/specs/dio_alt_properties_test_config.dart index 810bbc0..02d6aa8 100644 --- a/openapi-generator/test/specs/dio_alt_properties_test_config.dart +++ b/openapi-generator/test/specs/dio_alt_properties_test_config.dart @@ -3,14 +3,11 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: './openapi.test.yaml', inputSpec: InputSpec(path: './test/specs/openapi.test.yaml'), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/output/cache.json', typeMappings: {'key': 'value'}, templateDirectory: 'template', - alwaysRun: true, outputDirectory: './test/specs/output', runSourceGenOnOutput: true, apiPackage: 'test', diff --git a/openapi-generator/test/specs/dio_properties_test_config.dart b/openapi-generator/test/specs/dio_properties_test_config.dart index c216410..05ae08f 100644 --- a/openapi-generator/test/specs/dio_properties_test_config.dart +++ b/openapi-generator/test/specs/dio_properties_test_config.dart @@ -3,14 +3,11 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: './openapi.test.yaml', inputSpec: InputSpec(path: './test/specs/openapi.test.yaml'), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/output/cache.json', typeMappings: {'key': 'value'}, templateDirectory: 'template', - alwaysRun: true, outputDirectory: './test/specs/output', runSourceGenOnOutput: true, apiPackage: 'test', diff --git a/openapi-generator/test/specs/input_remote_properties_test_config.dart b/openapi-generator/test/specs/input_remote_properties_test_config.dart new file mode 100644 index 0000000..6ab6540 --- /dev/null +++ b/openapi-generator/test/specs/input_remote_properties_test_config.dart @@ -0,0 +1,28 @@ +library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@Openapi( + inputSpec: + RemoteSpec(path: 'https://petstore3.swagger.io/api/v3/openapi.json'), + generatorName: Generator.dio, + cachePath: './test/specs/output/cache.json', + typeMappings: {'key': 'value'}, + templateDirectory: 'template', + outputDirectory: './test/specs/output', + runSourceGenOnOutput: true, + apiPackage: 'test', + skipSpecValidation: false, + importMappings: {'package': 'test'}, + reservedWordsMappings: {'const': 'final'}, + additionalProperties: DioAltProperties( + wrapper: Wrapper.fvm, + useEnumExtension: true, + pubAuthor: 'test author', + nullSafe: true, + nullSafeArrayDefault: true), + inlineSchemaNameMappings: {'200resp': 'OkResp'}, + overwriteExistingFiles: true, + projectPubspecPath: './test/specs/dart_pubspec.test.yaml', +) +class DioAltPropertiesTestConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart b/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart index b06211f..a299118 100644 --- a/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart @@ -3,11 +3,11 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml'), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/managed-cache.json', projectPubspecPath: './test/specs/flutter_pubspec.test.yaml', ) -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart b/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart index 5fd3f12..79216b3 100644 --- a/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart @@ -3,8 +3,6 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec( path: 'http://bucket.s3.us-east-1.localhost.localstack.cloud:4566/openapi.yaml', @@ -15,7 +13,6 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart ), ), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/output-nextgen/expected-args/cache.json', outputDirectory: './test/specs/output-nextgen/expected-args') -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_test_config.dart b/openapi-generator/test/specs/next_gen_builder_test_config.dart index d9f36c3..497d11e 100644 --- a/openapi-generator/test/specs/next_gen_builder_test_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_test_config.dart @@ -3,14 +3,11 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec( path: 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', ), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/output-nextgen/expected-args/cache.json', outputDirectory: './test/specs/output-nextgen/expected-args') -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/test_config.dart b/openapi-generator/test/specs/test_config.dart index fea8dd3..d142c7f 100644 --- a/openapi-generator/test/specs/test_config.dart +++ b/openapi-generator/test/specs/test_config.dart @@ -3,14 +3,11 @@ library test_lib; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; @Openapi( - inputSpecFile: './openapi.test.yaml', inputSpec: InputSpec(path: './test/specs/openapi.test.yaml'), generatorName: Generator.dio, - useNextGen: true, cachePath: './test/specs/output/cache.json', typeMappings: {'key': 'value'}, templateDirectory: 'template', - alwaysRun: true, outputDirectory: './test/specs/output', runSourceGenOnOutput: true, apiPackage: 'test', diff --git a/openapi-generator/test/test_annotations/test_configs.dart b/openapi-generator/test/test_annotations/test_configs.dart index 92622ac..8778825 100644 --- a/openapi-generator/test/test_annotations/test_configs.dart +++ b/openapi-generator/test/test_annotations/test_configs.dart @@ -4,74 +4,58 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart import 'package:source_gen_test/annotations.dart'; @ShouldGenerate(r''' -const alwaysRun = false; - const fetchDependencies = true; const generatorName = 'dio'; -const inputSpecFile = ''; +const inputSpec = ''; const runSourceGenOnOutput = true; const skipSpecValidation = false; - -const useNextGen = false; ''') -@Openapi(inputSpecFile: '', generatorName: Generator.dio) -class TestClassDefault extends OpenapiGeneratorConfig {} - -@ShouldThrow('useNextGen must be set when using cachePath', element: false) -@Openapi(inputSpecFile: '', generatorName: Generator.dio, cachePath: './') -class TestClassInvalidCachePathUsage extends OpenapiGeneratorConfig {} +@Openapi(inputSpec: InputSpec(path: ''), generatorName: Generator.dio) +class TestClassDefault {} @ShouldGenerate(r''' const additionalProperties = wrapper = 'flutterw'; -const alwaysRun = false; - const fetchDependencies = true; const generatorName = 'dart'; -const inputSpecFile = ''; +const inputSpec = ''; const runSourceGenOnOutput = true; const skipSpecValidation = false; - -const useNextGen = false; ''') @Openapi( - inputSpecFile: '', + inputSpec: InputSpec(path: ''), generatorName: Generator.dart, additionalProperties: AdditionalProperties(wrapper: Wrapper.flutterw), ) -class TestClassHasCustomAnnotations extends OpenapiGeneratorConfig {} +class TestClassHasCustomAnnotations {} @ShouldGenerate(r''' const additionalProperties = wrapper = 'flutterw', nullableFields = 'true'; -const alwaysRun = false; - const fetchDependencies = true; const generatorName = 'dart'; -const inputSpecFile = ''; +const inputSpec = ''; const runSourceGenOnOutput = true; const skipSpecValidation = false; - -const useNextGen = false; ''') @Openapi( - inputSpecFile: '', + inputSpec: InputSpec(path: ''), generatorName: Generator.dart, additionalProperties: DioProperties( wrapper: Wrapper.flutterw, nullableFields: true, ), ) -class TestClassHasDioProperties extends OpenapiGeneratorConfig {} +class TestClassHasDioProperties {} diff --git a/openapi-generator/test/test_annotations/test_generator.dart b/openapi-generator/test/test_annotations/test_generator.dart index 1bee080..6132b54 100644 --- a/openapi-generator/test/test_annotations/test_generator.dart +++ b/openapi-generator/test/test_annotations/test_generator.dart @@ -30,20 +30,13 @@ class TestGenerator extends src_gen.GeneratorForAnnotation<Openapi> { ); } - if (!(annotation.read('useNextGen').literalValue as bool)) { - if (annotation.read('cachePath').literalValue != null) { - throw src_gen.InvalidGenerationSourceError( - 'useNextGen must be set when using cachePath'); - } - } - // KEEP THIS IN LINE WITH THE FIELDS OF THE ANNOTATION CLASS final fields = [ SupportedFields(name: 'additionalProperties', type: AdditionalProperties), SupportedFields( name: 'overwriteExistingFiles', isDeprecated: true, type: bool), SupportedFields(name: 'skipSpecValidation', type: bool), - SupportedFields(name: 'inputSpecFile', isRequired: true, type: String), + SupportedFields(name: 'inputSpec', isRequired: true, type: InputSpec), SupportedFields(name: 'templateDirectory', type: String), SupportedFields(name: 'generatorName', isRequired: true, type: Generator), SupportedFields(name: 'outputDirectory', type: Map), @@ -55,16 +48,14 @@ class TestGenerator extends src_gen.GeneratorForAnnotation<Openapi> { SupportedFields(name: 'apiPackage', type: String), SupportedFields(name: 'fetchDependencies', type: bool), SupportedFields(name: 'runSourceGenOnOutput', type: bool), - SupportedFields(name: 'alwaysRun', isDeprecated: true, type: bool), SupportedFields(name: 'cachePath', type: String), - SupportedFields(name: 'useNextGen', type: bool), SupportedFields(name: 'projectPubspecPath', type: String), ]..sort((a, b) => a.name.compareTo(b.name)); for (final field in fields) { final v = annotation.read(field.name); try { if ([ - 'inputSpecFile', + 'inputSpec', 'projectPubspecPath', 'apiPackage', 'templateDirectory',