Skip to content

Commit

Permalink
Merge pull request #1024 from nextcloud/fix/dynamite/header_type_reso…
Browse files Browse the repository at this point in the history
…lver

Fix/dynamite/header type resolver
  • Loading branch information
Leptopoda authored Oct 27, 2023
2 parents f78feb1 + 16eb212 commit 514a6dd
Show file tree
Hide file tree
Showing 10 changed files with 795 additions and 62 deletions.
38 changes: 25 additions & 13 deletions packages/dynamite/dynamite/lib/src/builder/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,19 @@ Iterable<Method> buildTags(
for (final operationEntry in pathEntry.value.operations.entries) {
final httpMethod = operationEntry.key.name;
final operation = operationEntry.value;
final operationId = operation.operationId ?? toDartName('$httpMethod-${pathEntry.key}');
final operationName = operation.operationId ?? toDartName('$httpMethod-${pathEntry.key}');
final parameters = [
...?pathEntry.value.parameters,
...?operation.parameters,
]..sort(sortRequiredParameters);
final name = toDartName(filterMethodName(operationId, tag ?? ''));
final name = toDartName(filterMethodName(operationName, tag ?? ''));

var responses = <openapi.Response, List<int>>{};
if (operation.responses != null) {
for (final responseEntry in operation.responses!.entries) {
final statusCode = int.tryParse(responseEntry.key);
if (statusCode == null) {
print('Default responses are not supported right now. Skipping it for $operationId');
print('Default responses are not supported right now. Skipping it for $operationName');
continue;
}
final response = responseEntry.value;
Expand All @@ -205,7 +205,7 @@ Iterable<Method> buildTags(
}

if (responses.length > 1) {
print('$operationId uses more than one response schema but we only generate the first one');
print('$operationName uses more than one response schema but we only generate the first one');
responses = Map.fromEntries([responses.entries.first]);
}
}
Expand Down Expand Up @@ -251,7 +251,7 @@ Iterable<Method> buildTags(
spec,
state,
toDartName(
'$operationId-${parameter.name}',
'$operationName-${parameter.name}',
uppercaseFirstCharacter: true,
),
parameter.schema!,
Expand All @@ -277,20 +277,26 @@ Iterable<Method> buildTags(
buildPatternCheck(result, parameter).forEach(code.writeln);
buildParameterSerialization(result, parameter).forEach(code.writeln);
}
resolveMimeTypeEncode(operation, spec, state, operationId, operationParameters).forEach(code.writeln);
resolveMimeTypeEncode(operation, spec, state, operationName, operationParameters).forEach(code.writeln);

for (final responseEntry in responses.entries) {
final response = responseEntry.key;
final statusCodes = responseEntry.value;

TypeResult? headersType;

if (response.headers != null) {
final identifier =
'${tag != null ? toDartName(tag, uppercaseFirstCharacter: true) : null}${toDartName(operationId, uppercaseFirstCharacter: true)}Headers';
final identifierBuilder = StringBuffer();
if (tag != null) {
identifierBuilder.write(toDartName(tag, uppercaseFirstCharacter: true));
}
identifierBuilder
..write(toDartName(operationName, uppercaseFirstCharacter: true))
..write('Headers');
headersType = resolveObject(
spec,
state,
identifier,
identifierBuilder.toString(),
openapi.Schema(
(final b) => b
..properties.replace(
Expand All @@ -306,14 +312,20 @@ Iterable<Method> buildTags(
);
}

final identifierBuilder = StringBuffer()
..write(operationName)
..write('-response');
if (responses.entries.length > 1) {
identifierBuilder
..write('-')
..write(responses.entries.toList().indexOf(responseEntry));
}

final dataType = resolveMimeTypeDecode(
response,
spec,
state,
toDartName(
'$operationId-response${responses.entries.length > 1 ? '-${responses.entries.toList().indexOf(responseEntry)}' : ''}',
uppercaseFirstCharacter: true,
),
toDartName(identifierBuilder.toString(), uppercaseFirstCharacter: true),
);

var path = pathEntry.key;
Expand Down
25 changes: 25 additions & 0 deletions packages/dynamite/dynamite/lib/src/builder/generate_schemas.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/models/openapi.dart' as openapi;

Iterable<Spec> generateSchemas(
final openapi.OpenAPI spec,
final State state,
) sync* {
if (spec.components?.schemas != null) {
for (final schema in spec.components!.schemas!.entries) {
final identifier = toDartName(schema.key, uppercaseFirstCharacter: true);

resolveType(
spec,
state,
identifier,
schema.value,
);
}
}

yield* state.output;
}
2 changes: 1 addition & 1 deletion packages/dynamite/dynamite/lib/src/builder/imports.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ List<Spec> generateImports(final AssetId outputId, final State state) => [
Directive.import('package:meta/meta.dart'),
Directive.import('package:universal_io/io.dart'),
const Code(''),
if (state.resolvedTypes.isNotEmpty) ...[
if (state.hasResolvedBuiltTypes) ...[
Directive.part(p.basename(outputId.changeExtension('.g.dart').path)),
const Code(''),
],
Expand Down
7 changes: 7 additions & 0 deletions packages/dynamite/dynamite/lib/src/builder/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@ class State {
final output = <Spec>[];
final resolvedTypes = <TypeResult>{};
final resolvedInterfaces = <TypeResult>{};

/// Wether the state contains resolved types that need the built_value generator.
bool get hasResolvedBuiltTypes => resolvedTypes
.where(
(final type) => type is TypeResultEnum || (type is TypeResultObject && type.className != 'ContentString'),
)
.isNotEmpty;
}
51 changes: 6 additions & 45 deletions packages/dynamite/dynamite/lib/src/openapi_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import 'package:checked_yaml/checked_yaml.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:dynamite/src/builder/client.dart';
import 'package:dynamite/src/builder/generate_schemas.dart';
import 'package:dynamite/src/builder/imports.dart';
import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/serializer.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/models/openapi.dart' as openapi;
import 'package:dynamite/src/models/type_result.dart';
import 'package:version/version.dart';

class OpenAPIBuilder implements Builder {
Expand Down Expand Up @@ -58,50 +56,13 @@ class OpenAPIBuilder implements Builder {

final state = State();

final output = ListBuilder<Spec>();

if (spec.components?.schemas != null) {
for (final schema in spec.components!.schemas!.entries) {
final identifier = toDartName(schema.key, uppercaseFirstCharacter: true);
if (schema.value.type == null && schema.value.ref == null && schema.value.ofs == null) {
output.add(
TypeDef(
(final b) => b
..name = identifier
..definition = refer('dynamic'),
),
);
} else {
final result = resolveType(
spec,
state,
identifier,
schema.value,
);
if (result is TypeResultBase) {
output.add(
TypeDef(
(final b) => b
..name = identifier
..definition = refer(result.name),
),
);
}
}
}
}

// Imports need to be generated after everything else so we know if we need the local part directive,
// but they need to be added to the beginning of the output.
final clients = generateClients(spec, state);
final serializer = buildSerializer(state);
final imports = generateImports(outputId, state);

output
..addAll(imports)
..addAll(clients)
..addAll(state.output)
..addAll(serializer);
final output = ListBuilder<Spec>()
..addAll(generateClients(spec, state))
..addAll(generateSchemas(spec, state))
..addAll(buildSerializer(state))
..insertAll(0, generateImports(outputId, state));

final patterns = [
RegExp(
Expand Down
Loading

0 comments on commit 514a6dd

Please sign in to comment.