Skip to content

Commit

Permalink
feat: added fields skipIfSpecUnchanged and forceAlwaysRun
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `forceAlwaysRun` is used to represent if the library should run  everytime `build_runner` is executed. This is done by modifying the annotated file after `build_runner` completes. Previous versions of this library did not have this flag but they had the equivalent of `true`. However, this now defaults to `false`. To keep previous behavior, set this flag to `true`.
  • Loading branch information
gibahjoe committed Nov 5, 2024
1 parent 7f8770b commit d5555dd
Show file tree
Hide file tree
Showing 33 changed files with 927 additions and 564 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ jobs:
- name: Install Dependencies
run: melos exec -- "dart pub get"

# - name: Build runner
# run: melos exec --depends-on="build_runner" -- bash -c "cd \$MELOS_PACKAGE_PATH && dart run build_runner build --delete-conflicting-outputs"
- name: Build runner
run: melos exec --depends-on="build_runner" -- bash -c "cd \$MELOS_PACKAGE_PATH && dart run build_runner build --delete-conflicting-outputs"

- name: Validate formatting
run: melos format --set-exit-if-changed
- name: Run analyzer
Expand All @@ -68,7 +69,7 @@ jobs:
run: dart pub global activate combine_coverage

- name: format coverage
run: melos exec -- bash "$HOME/.pub-cache/bin/format_coverage --lcov --in=\$MELOS_PACKAGE_PATH/coverage/test --out=\$MELOS_PACKAGE_PATH/coverage/lcov.info --report-on=lib"
run: melos exec -- bash "$HOME/.pub-cache/bin/format_coverage --lcov --in=\$MELOS_PACKAGE_PATH/coverage/test --out=\$MELOS_PACKAGE_PATH/coverage/lcov.info --report-on=lib --report-on=bin"

- name: Combine Coverage Reports
run: dart pub global run combine_coverage --repo-path="."
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void main() {
RemoteSpec(path: 'https://petstore3.swagger.io/api/v3/openapi.json'),
typeMappings: {'Pet': 'ExamplePet'},
generatorName: Generator.dioAlt,
updateAnnotatedFile: false,
forceAlwaysRun: false,
runSourceGenOnOutput: true,
outputDirectory: 'api/petstore_api',
)
Expand Down
2 changes: 1 addition & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ scripts:
run: |
melos exec -c 6 --fail-fast -- \
"dart run test --chain-stack-traces --coverage='coverage'"
description: Run `flutter test` for a specific package.
description: Run `dart test`
packageFilters:
dirExists:
- test
Expand Down
15 changes: 15 additions & 0 deletions melos_openapi_generator_dart.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,34 @@ class Openapi {
/// Include in depth logging output from run commands.
final bool debugLogging;

/// If set to true, the annotated file will be added or updated comment lines as of the last run date on the top of the file.
/// Defaults to true
final bool updateAnnotatedFile;
/// If `true`, the annotated file will be modified after code generation completes.
///
/// This is a workaround to ensure this library runs every time you execute the `build_runner` command.
///
/// **Why modify the file?**
///
/// `build_runner` only processes files that have changed since the last run. By modifying the file, you
/// force `build_runner` to recognize it as changed and re-run the generation process.
///
/// Note: Setting this to `true` can lead to merge conflicts in team environments,
/// as each developer may end up modifying the annotated file.
///
/// This setting is different from [skipIfSpecIsUnchanged], which only regenerates
/// the client SDK if it detects changes in the OpenAPI specification.
///
/// Defaults to [false].
final bool forceAlwaysRun;

/// Whether to disable caching the spec file. Defaults to `true` if the
/// [inputSpec] is not a [RemoteSpec].
final bool disableCache;
/// Skips execution if the OpenAPI specification file is older than the output folder.
///
/// For remote specifications, the file will be downloaded and cached locally.
/// The cache is then compared to the remote file to detect any changes.
///
/// **Default behavior:**
/// - If [inputSpec] is a [RemoteSpec], this is set to `true`, meaning execution will be skipped if no changes are detected.
/// - For all other cases, this is set to `false`.
final bool skipIfSpecIsUnchanged;

const Openapi({
this.additionalProperties,
Expand All @@ -153,9 +174,79 @@ class Openapi {
this.cachePath,
this.projectPubspecPath,
this.debugLogging = false,
this.updateAnnotatedFile = true,
bool? disableCache,
}) : disableCache = disableCache ?? inputSpec is! RemoteSpec;
this.forceAlwaysRun = true,
bool? skipIfSpecIsUnchanged,
}) : skipIfSpecIsUnchanged = skipIfSpecIsUnchanged ?? inputSpec is RemoteSpec;

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('@Openapi(');
if (additionalProperties != null) {
buffer.writeln(' additionalProperties: $additionalProperties,');
}
if (apiPackage != null) {
buffer.writeln(' apiPackage: "$apiPackage",');
}
buffer.writeln(' inputSpec: $inputSpec,');
if (templateDirectory != null) {
buffer.writeln(' templateDirectory: "$templateDirectory",');
}
buffer.writeln(' generatorName: $generatorName,');
if (outputDirectory != null) {
buffer.writeln(' outputDirectory: "$outputDirectory",');
}
if (cleanSubOutputDirectory != null) {
buffer.writeln(
' cleanSubOutputDirectory: [${cleanSubOutputDirectory!.join(", ")}],');
}
if (skipSpecValidation != null) {
buffer.writeln(' skipSpecValidation: $skipSpecValidation,');
}
if (reservedWordsMappings != null) {
buffer.writeln(
' reservedWordsMappings: ${reservedWordsMappings.toString()},');
}
if (fetchDependencies != null) {
buffer.writeln(' fetchDependencies: $fetchDependencies,');
}
if (runSourceGenOnOutput != null) {
buffer.writeln(' runSourceGenOnOutput: $runSourceGenOnOutput,');
}
if (typeMappings != null) {
buffer.writeln(' typeMappings: ${_formatMap(typeMappings!)},');
}
if (nameMappings != null) {
buffer.writeln(' nameMappings: ${_formatMap(nameMappings!)},');
}
if (importMappings != null) {
buffer.writeln(' importMappings: ${_formatMap(importMappings!)},');
}
if (inlineSchemaNameMappings != null) {
buffer.writeln(
' inlineSchemaNameMappings: ${_formatMap(inlineSchemaNameMappings!)},');
}
if (cachePath != null) {
buffer.writeln(' cachePath: "$cachePath",');
}
if (projectPubspecPath != null) {
buffer.writeln(' projectPubspecPath: "$projectPubspecPath",');
}
buffer.writeln(' debugLogging: $debugLogging,');
buffer.writeln(' forceAlwaysRun: $forceAlwaysRun,');
buffer.writeln(' skipIfSpecIsUnchanged: $skipIfSpecIsUnchanged,');
buffer.write(')');
return buffer.toString();
}
}

String _formatMap(Map<String, String> map) {
final buffer = StringBuffer();
buffer.write('{');
buffer.writeAll(
map.entries.map((entry) => "'${entry.key}':'${entry.value}'"), ', ');
buffer.write('}');
return buffer.toString();
}

/// Provides the input spec file to be used.
Expand All @@ -173,6 +264,15 @@ class InputSpec {
: this(path: 'openapi.y${shortExtension ? '' : 'a'}ml');

InputSpec.fromMap(Map<String, dynamic> map) : this(path: map['path']);

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('InputSpec(');
buffer.writeln(' path: "$path"');
buffer.write(')');
return buffer.toString();
}
}

/// Provides the location for the remote specification.
Expand Down Expand Up @@ -201,6 +301,16 @@ class RemoteSpec extends InputSpec {
: headerDelegate =
map['headerDelegate'] ?? const RemoteSpecHeaderDelegate(),
super.fromMap(map);

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('RemoteSpec(');
buffer.writeln(' path: "$path",');
buffer.writeln(' headerDelegate: $headerDelegate');
buffer.write(')');
return buffer.toString();
}
}

/// Default [RemoteSpecHeaderDelegate] used when retrieving a remote OAS spec.
Expand All @@ -210,6 +320,9 @@ class RemoteSpecHeaderDelegate {
Map<String, String>? header() => null;

RemoteSpecHeaderDelegate.fromMap(Map<String, dynamic> map) : this();

@override
String toString() => 'RemoteSpecHeaderDelegate()';
}

/// Indicates whether or not the spec file live within AWS.
Expand Down Expand Up @@ -434,6 +547,42 @@ class AdditionalProperties {
if (sourceFolder != null) 'sourceFolder': sourceFolder,
'wrapper': EnumTransformer.wrapperName(wrapper)
};

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('AdditionalProperties(');
if (allowUnicodeIdentifiers != null)
buffer.writeln(' allowUnicodeIdentifiers: $allowUnicodeIdentifiers,');
if (ensureUniqueParams != null)
buffer.writeln(' ensureUniqueParams: $ensureUniqueParams,');
if (prependFormOrBodyParameters != null)
buffer.writeln(
' prependFormOrBodyParameters: $prependFormOrBodyParameters,');
if (pubAuthor != null) buffer.writeln(' pubAuthor: "$pubAuthor",');
if (pubAuthorEmail != null)
buffer.writeln(' pubAuthorEmail: "$pubAuthorEmail",');
if (pubDescription != null)
buffer.writeln(' pubDescription: "$pubDescription",');
if (pubHomepage != null) buffer.writeln(' pubHomepage: "$pubHomepage",');
if (pubName != null) buffer.writeln(' pubName: "$pubName",');
if (pubVersion != null) buffer.writeln(' pubVersion: "$pubVersion",');
if (sortModelPropertiesByRequiredFlag != null)
buffer.writeln(
' sortModelPropertiesByRequiredFlag: $sortModelPropertiesByRequiredFlag,');
if (sortParamsByRequiredFlag != null)
buffer.writeln(' sortParamsByRequiredFlag: $sortParamsByRequiredFlag,');
if (sourceFolder != null)
buffer.writeln(' sourceFolder: "$sourceFolder",');
if (useEnumExtension != null)
buffer.writeln(' useEnumExtension: $useEnumExtension,');
buffer.writeln(' enumUnknownDefaultCase: $enumUnknownDefaultCase,');
buffer.writeln(' wrapper: $wrapper,');
buffer
.writeln(' legacyDiscriminatorBehavior: $legacyDiscriminatorBehavior');
buffer.write(')');
return buffer.toString();
}
}

/// Allows you to customize how inline schemas are handled or named
Expand Down Expand Up @@ -479,6 +628,22 @@ class InlineSchemaOptions {
'refactorAllofInlineSchemas': refactorAllofInlineSchemas,
'resolveInlineEnums': resolveInlineEnums,
};

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('InlineSchemaOptions(');
if (arrayItemSuffix != null)
buffer.writeln(' arrayItemSuffix: "$arrayItemSuffix",');
if (mapItemSuffix != null)
buffer.writeln(' mapItemSuffix: "$mapItemSuffix",');
buffer.writeln(' skipSchemaReuse: $skipSchemaReuse,');
buffer
.writeln(' refactorAllofInlineSchemas: $refactorAllofInlineSchemas,');
buffer.writeln(' resolveInlineEnums: $resolveInlineEnums');
buffer.write(')');
return buffer.toString();
}
}

class DioProperties extends AdditionalProperties {
Expand Down Expand Up @@ -542,6 +707,23 @@ class DioProperties extends AdditionalProperties {
'serializationLibrary':
EnumTransformer.dioSerializationLibraryName(serializationLibrary!),
});

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('DioProperties(');
buffer.writeln(super
.toString()
.replaceAll(RegExp(r'AdditionalProperties\(|\)$'), '')
.replaceAll('\n', '\n ')); // Indent base class fields
if (dateLibrary != null) buffer.writeln(' dateLibrary: $dateLibrary,');
if (nullableFields != null)
buffer.writeln(' nullableFields: $nullableFields,');
if (serializationLibrary != null)
buffer.writeln(' serializationLibrary: $serializationLibrary,');
buffer.write(')');
return buffer.toString();
}
}

class DioAltProperties extends AdditionalProperties {
Expand Down Expand Up @@ -613,6 +795,28 @@ class DioAltProperties extends AdditionalProperties {
if (pubspecDevDependencies != null)
'pubspecDevDependencies': pubspecDevDependencies,
});

@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('DioAltProperties(');

// Indent the fields from AdditionalProperties
buffer.writeln(super
.toString()
.replaceAll(RegExp(r'AdditionalProperties\(|\)$'), '')
.replaceAll('\n', '\n '));

// Add DioAltProperties-specific fields
if (listAnyOf != null) buffer.writeln(' listAnyOf: $listAnyOf,');
if (pubspecDependencies != null)
buffer.writeln(' pubspecDependencies: "$pubspecDependencies",');
if (pubspecDevDependencies != null)
buffer.writeln(' pubspecDevDependencies: "$pubspecDevDependencies",');

buffer.write(')');
return buffer.toString();
}
}

enum DioDateLibrary {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ void main() {
debugLogging: true);
expect(api.debugLogging, isTrue);
});
test('Sets updateAnnotatedFile', () {
test('Sets forceAlwaysRun', () {
final api = Openapi(
inputSpec: InputSpec.json(),
generatorName: Generator.dart,
updateAnnotatedFile: false);
expect(api.updateAnnotatedFile, isFalse);
forceAlwaysRun: false);
expect(api.forceAlwaysRun, isFalse);
});
group('InputSpec', () {
group('local spec', () {
Expand Down
19 changes: 19 additions & 0 deletions openapi-generator-cli/melos_openapi_generator_cli.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
Loading

0 comments on commit d5555dd

Please sign in to comment.