Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ environment:

scripts:
# To generate .g files:
build:runner: dart run build_runner watch --delete-conflicting-outputs --verbose
runner:watch: dart run build_runner watch --delete-conflicting-outputs --verbose

dependencies:
shelf: ^1.4.0
shelf_router: ^1.1.3
shelf_static: ^1.1.1
shelf_swagger_ui: ^1.0.0+2

shelf_open_api:
path: ../shelf_open_api
shelf_open_api: ^1.1.0

mek_data_class: ^1.0.1
json_annotation: ^4.8.0
Expand All @@ -28,13 +27,12 @@ dev_dependencies:
mek_data_class_generator: ^1.1.1
json_serializable: ^6.6.1
shelf_router_generator: ^1.0.5
shelf_open_api_generator:
path: ../shelf_open_api_generator

dependency_overrides:
open_api_specification:
path: ../open_api_spec
shelf_open_api:
path: ../shelf_open_api
shelf_open_api_generator:
path: ../shelf_open_api_generator
shelf_open_api_generator: ^2.0.0

#dependency_overrides:
# open_api_specification:
# path: ../open_api_spec
# shelf_open_api:
# path: ../shelf_open_api
# shelf_open_api_generator:
# path: ../shelf_open_api_generator
2 changes: 2 additions & 0 deletions open_api_client_generator/example/paypal/.openapiignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/v2/checkout/orders
/v2/checkout/orders/{id}/capture
29 changes: 29 additions & 0 deletions open_api_client_generator/example/paypal/build_paypal_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'dart:io';

import 'package:open_api_client_generator/open_api_client_generator.dart';
import 'package:open_api_client_generator/src/plugins/open_api_ignore.dart';

void main(List<String> args) async {
const root = './example/paypal';
final codeDir = Directory('$root/api');
if (!codeDir.existsSync()) codeDir.createSync();

final options = Options(
// input: Uri.file('$root/specification/index.yml'),
input: Uri.parse(
'https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json'),
outputFolder: codeDir.path,
apiClassName: 'PayPalApi',
);
await generateApi(
options: options,
clientCodec: const DioClientCodec(),
serializationCodec: const JsonSerializableSerializationCodec(
classFieldRename: FieldRename.snake,
),
plugins: [
OpenApiIgnore(overrideFilePath: '$root/.openapiignore'),
WriteOpenApiPlugin(options: options)
],
);
}
33 changes: 17 additions & 16 deletions open_api_client_generator/lib/src/builders/build_api_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ class BuildApiClass with ContextMixin {
required String name,
required String path,
}) {
if (id != null) return id;
if (id != null) return codecs.encodeName(id);
return '$name${path.replaceAll('/', '_').replaceAll('{', '').replaceAll('}', '').pascalCase}';
}

String encodePath(String path) {
return path.replaceAllMapped(RegExp(r'(\{\w*\})'), (match) {
return '\$${codecs.encodeFieldName(match.group(0)!)}';
return path.replaceAllMapped(RegExp(r'\{(\w*)\}'), (match) {
return '\$${codecs.encodeName(match.group(1)!)}';
});
}

Expand All @@ -56,7 +56,10 @@ class BuildApiClass with ContextMixin {
if (queryParameters.isNotEmpty) {
b.write('final _queryParameters = <String, Object?>{\n');
b.writeAll(queryParameters.map((e) {
return '${codecs.encodeDartValue(e.name)}: ${e.name.camelCase}${collectionCodec.encodeToCore(schemaToType(e.schema!))},\n';
final key = codecs.encodeDartValue(e.name);
final varName = codecs.encodeName(e.name);
final varEncoder = collectionCodec.encodeToCore(ref(e.schema!).toNullable(!e.required));
return '$key: $varName$varEncoder,\n';
}));
b.write('};\n');
}
Expand Down Expand Up @@ -154,7 +157,7 @@ class BuildApiClass with ContextMixin {
description: operation.description,
params: operation.parameters.expand((param) {
return Docs.documentField(
name: codecs.encodeFieldName(param.name),
name: codecs.encodeName(param.name),
description: param.description,
example: param.example,
);
Expand All @@ -164,24 +167,22 @@ class BuildApiClass with ContextMixin {
..name = methodName
..requiredParameters.addAll(pathParameters.map((param) {
return Parameter((b) => b
..type = schemaToType(param.schema!) //.toNull(!param.required)
..name = codecs.encodeFieldName(param.name));
..type = ref(param.schema!).toNullable(!param.required)
..name = codecs.encodeName(param.name));
}))
..requiredParameters.addAll([
if (request != null && requestSchema != null)
Parameter((b) => b
..type = requestType
..name = '_request')
])
..optionalParameters.addAll([
...queryParameters.map((e) {
return Parameter((b) => b
..named = true
..required = e.required
..type = schemaToType(e.schema!).toNullable(!e.required)
..name = e.name.camelCase);
}),
])
..optionalParameters.addAll(queryParameters.map((e) {
return Parameter((b) => b
..named = true
..required = e.required
..type = ref(e.schema!).toNullable(!e.required)
..name = codecs.encodeName(e.name));
}))
..modifier = MethodModifier.async
..body = Code(operationCode));
}
Expand Down
25 changes: 9 additions & 16 deletions open_api_client_generator/lib/src/builders/build_schema_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class BuildSchemaClass with ContextMixin {

Reference call(String name, SchemaOpenApi schema) {
// ignore: parameter_assignments
name = schema.title ?? name;
name = schema.name ?? name;

final cacheEntry = _cache[name];
if (cacheEntry != null) return cacheEntry.type;
Expand All @@ -35,14 +35,6 @@ class BuildSchemaClass with ContextMixin {
return builtSchema.type;
}

Iterable<Reference> _buildImplements(Iterable<String> refs) sync* {
for (final ref in refs) {
final reference = Reference(codecs.encodeType(ref.split('/').last));
// if (ref.split('/').last == 'Bundle') print('$schemaOrRef');
yield reference;
}
}

_BuiltSchema _build(String name, SchemaOpenApi schema) {
final docs = Docs.format(Docs.documentClass(
description: schema.description,
Expand All @@ -53,7 +45,7 @@ class BuildSchemaClass with ContextMixin {
if (items != null) {
return _BuiltSchema(
// docs: docs.followedBy(built.docs ?? const []),
type: schemaToType(schema),
type: ref(schema),
children: {name: items},
);
}
Expand Down Expand Up @@ -81,7 +73,8 @@ class BuildSchemaClass with ContextMixin {
if (schema.isClass) {
final properties = schema.allProperties;
final allOf = schema.allOf ?? const [];
final implements = allOf.map((e) => e.title).nonNulls;
final implements = allOf.where((e) => e.name != null).toList();
final implementsNames = implements.map((e) => codecs.encodeType(e.name!)).toList();

final className = codecs.encodeType(name);

Expand All @@ -91,23 +84,23 @@ class BuildSchemaClass with ContextMixin {
schema: schema,
docs: docs,
name: className,
implements: _buildImplements(implements).map((e) => e.symbol!).toList(),
implements: implementsNames,
fields: properties.entries.map((entry) {
final MapEntry(key: name, value: prop) = entry;

return ApiField(
key: name,
docs: const [], // TODO: prop.docs ??
isRequired: schema.isRequired(name),
type: schemaToType(prop).toNullable(schema.canNull(name, properties[name]!)),
name: codecs.encodeFieldName(name),
type: ref(prop).toNullable(schema.canNull(name, properties[name]!)),
name: codecs.encodeName(name),
);
}).toList(),
),
children: properties,
children: {...Map.fromEntries(implements.map((e) => MapEntry(e.name!, e))), ...properties},
);
}
final reference = schemaToType(schema);
final reference = ref(schema);
if (!reference.isDartCore) {
// ignore: avoid_print
print('$name $schema');
Expand Down
99 changes: 55 additions & 44 deletions open_api_client_generator/lib/src/code_utils/codecs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,31 @@ class Codecs {
const Codecs();

@protected
String encodeType(String name) => name;

static final _keywords = {
'else',
'enum',
'in',
'assert',
'super',
'extends',
'is',
'switch',
'break',
'this',
'case',
'throw',
'catch',
'false',
'new',
'true',
'class',
'final',
'null',
'try',
'const',
'finally',
'continue',
'for',
'var',
'void',
'default',
'while',
'rethrow',
'with',
'do',
'if',
'return',
};

@protected
String encodeFieldName(String str) {
str = str.replaceAll('@', '');
return _keywords.contains(str) ? '$str\$' : str;
String encodeType(String name) {
// Don't start the string with a number
if (name.startsWith(RegExp('[0-9]'))) name = '\$$name';
return name;
}

String encodeName(String str) => _encodeName(str);

String encodeDartValue(Object value) {
if (value is String) return "'${value.replaceAll(r'$', r'\$')}'";
return '$value';
}

String encodeEnumValue(Object value) => value is String ? encodeFieldName(value) : 'vl$value';
String encodeEnumValue(Object value) => value is String ? encodeName(value) : 'vl$value';

/// Encode variable name, field name and method name
@protected
String _encodeName(String str) {
if (_keywords.contains(str)) return '$str\$';
// Remove symbols
str = str.replaceAllMapped(RegExp(r'([^0-9\w])'), (match) => '_');
// Don't start the string with a number
if (str.startsWith(RegExp('[0-9]'))) str = '\$$str';
return str;
}
}

class ApiCodecs extends Codecs {
Expand All @@ -69,5 +44,41 @@ class ApiCodecs extends Codecs {
'${removeDiacritics(super.encodeType(name)).pascalCase}${options.dataClassesPostfix ?? ''}';

@override
String encodeFieldName(String str) => super.encodeFieldName(removeDiacritics(str).camelCase);
String _encodeName(String str) => super._encodeName(removeDiacritics(str).camelCase);
}

final _keywords = {
'else',
'enum',
'in',
'assert',
'super',
'extends',
'is',
'switch',
'break',
'this',
'case',
'throw',
'catch',
'false',
'new',
'true',
'class',
'final',
'null',
'try',
'const',
'finally',
'continue',
'for',
'var',
'void',
'default',
'while',
'rethrow',
'with',
'do',
'if',
'return',
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ import 'package:open_api_client_generator/src/code_utils/reference_utils.dart';
import 'package:open_api_client_generator/src/options/context.dart';
import 'package:open_api_specification/open_api_spec.dart';

extension SchemaToType on ContextMixin {
Reference schemaToType(SchemaOpenApi schema, {Reference? target}) {
extension SchemaToRef on ContextMixin {
Reference ref(SchemaOpenApi schema) {
if (schema.isEnum) return Reference(codecs.encodeType(schema.name!));

switch (schema.format) {
case FormatOpenApi.int32:
case FormatOpenApi.int64:
return target ?? References.int;
return References.int;
case FormatOpenApi.float:
case FormatOpenApi.double:
return target ?? References.double;
return References.double;
case FormatOpenApi.string:
return References.string;
case FormatOpenApi.date:
case FormatOpenApi.dateTime:
return target ?? References.dateTime;
return References.dateTime;
case FormatOpenApi.uuid:
case FormatOpenApi.email:
return target ?? References.string;
return References.string;

case FormatOpenApi.url:
case FormatOpenApi.uri:
Expand All @@ -32,22 +36,21 @@ extension SchemaToType on ContextMixin {

switch (schema.type) {
case TypeOpenApi.boolean:
return target ?? References.bool;
return References.bool;
case TypeOpenApi.integer:
return target ?? References.int;
return References.int;
case TypeOpenApi.number:
return target ?? References.num;
return References.num;
case TypeOpenApi.string:
return target ?? References.string;
return References.string;
case TypeOpenApi.array:
return References.list(schemaToType(schema.items!));
return References.list(ref(schema.items!));
case TypeOpenApi.object:
if (schema.title != null) return Reference(schema.title!);
if (schema.name != null) return Reference(codecs.encodeType(schema.name!));

final additionalProperties = schema.additionalProperties;
return References.map(
key: References.string,
value: additionalProperties != null ? schemaToType(additionalProperties) : null,
value: schema.additionalProperties != null ? ref(schema.additionalProperties!) : null,
);
case null:
return References.jsonValue;
Expand Down
Loading