Skip to content

Commit 97c7a1a

Browse files
feat: Add support for raw schema name replacement rules in configuration (#416)
1 parent 36327f1 commit 97c7a1a

File tree

7 files changed

+96
-12
lines changed

7 files changed

+96
-12
lines changed

swagger_parser/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.37.2
2+
- Add `replacement_rules_for_raw_schema` option for raw schema objects replacement rules
3+
14
## 1.37.1
25
- Fix missing import for MultipartFile([#408](https://github.com/Carapacik/swagger_parser/issues/408))
36
- Fix refs in components.responses

swagger_parser/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,19 @@ swagger_parser:
146146
- pattern: "[0-9]+"
147147
replacement: ""
148148

149+
# Optional. Set raw regex replacement rules for the names of the raw schema objects.
150+
# Applies to the raw schema objects before the generator will try to parse them into a Dart class.
151+
# Useful when raw schema objects have names that are not valid Dart class names (e.g. "filters[name]")
152+
replacement_rules_for_raw_schema:
153+
- pattern: "\\]\\["
154+
replacement: "_"
155+
- pattern: "\\[]\\["
156+
replacement: "_"
157+
- pattern: "\\["
158+
replacement: "_"
159+
- pattern: "\\]"
160+
replacement: "_"
161+
149162
# Optional. Skip parameters with names.
150163
skipped_parameters:
151164
- "X-Some-Token"
@@ -240,6 +253,7 @@ swagger_parser:
240253
json_serializer: freezed
241254
put_in_folder: true
242255
replacement_rules: []
256+
replacement_rules_for_raw_schema: []
243257

244258
- schema_url: https://petstore.swagger.io/v2/swagger.json
245259
name: pet_service_dart_mappable

swagger_parser/lib/src/config/swp_config.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class SWPConfig {
2626
this.markFilesAsGenerated = true,
2727
this.originalHttpResponse = false,
2828
this.replacementRules = const [],
29+
this.replacementRulesForRawSchema = const [],
2930
this.defaultContentType = 'application/json',
3031
this.extrasParameterByDefault = false,
3132
this.dioOptionsParameterByDefault = false,
@@ -67,6 +68,7 @@ class SWPConfig {
6768
required this.markFilesAsGenerated,
6869
required this.originalHttpResponse,
6970
required this.replacementRules,
71+
required this.replacementRulesForRawSchema,
7072
required this.defaultContentType,
7173
required this.extrasParameterByDefault,
7274
required this.dioOptionsParameterByDefault,
@@ -229,6 +231,32 @@ class SWPConfig {
229231
replacementRules = List.from(rootConfig!.replacementRules);
230232
}
231233

234+
final rawReplacementRulesForRawSchema =
235+
yamlMap['replacement_rules_for_raw_schema'] as YamlList?;
236+
List<ReplacementRule>? replacementRulesForRawSchema;
237+
if (rawReplacementRulesForRawSchema != null) {
238+
replacementRulesForRawSchema = [];
239+
for (final r in rawReplacementRulesForRawSchema) {
240+
if (r is! YamlMap ||
241+
r['pattern'] is! String ||
242+
r['replacement'] is! String) {
243+
throw const ConfigException(
244+
"Config parameter 'replacement_rules_for_raw_schema' values must be maps of strings "
245+
"and contain 'pattern' and 'replacement'.",
246+
);
247+
}
248+
replacementRulesForRawSchema.add(
249+
ReplacementRule(
250+
pattern: RegExp(r['pattern'].toString()),
251+
replacement: r['replacement'].toString(),
252+
),
253+
);
254+
}
255+
} else if (rootConfig?.replacementRulesForRawSchema != null) {
256+
replacementRulesForRawSchema =
257+
List.from(rootConfig!.replacementRulesForRawSchema);
258+
}
259+
232260
final generateValidator =
233261
yamlMap['generate_validator'] as bool? ?? rootConfig?.generateValidator;
234262

@@ -327,6 +355,8 @@ class SWPConfig {
327355
markFilesAsGenerated: markFilesAsGenerated ?? dc.markFilesAsGenerated,
328356
originalHttpResponse: originalHttpResponse ?? dc.originalHttpResponse,
329357
replacementRules: replacementRules ?? dc.replacementRules,
358+
replacementRulesForRawSchema:
359+
replacementRulesForRawSchema ?? dc.replacementRulesForRawSchema,
330360
generateValidator: generateValidator ?? dc.generateValidator,
331361
useXNullable: useXNullable ?? dc.useXNullable,
332362
useFreezed3: useFreezed3 ?? dc.useFreezed3,
@@ -431,6 +461,15 @@ class SWPConfig {
431461
/// All rules are applied in order.
432462
final List<ReplacementRule> replacementRules;
433463

464+
/// {@template replacement_rules_for_raw_schema}
465+
/// Optional. Set raw regex replacement rules for the names of the raw schema objects.
466+
///
467+
/// Applies to the raw schema objects before the generator will try to parse them into a Dart class.
468+
///
469+
/// Useful when raw schema objects have names are not valid Dart class names (e.g. "filters[name]")
470+
/// {@endtemplate}
471+
final List<ReplacementRule> replacementRulesForRawSchema;
472+
434473
/// DART ONLY
435474
/// Default content type for all requests and responses.
436475
///
@@ -589,6 +628,7 @@ class SWPConfig {
589628
replacementRules: replacementRules,
590629
useXNullable: useXNullable,
591630
excludeTags: excludeTags,
631+
replacementRulesForRawSchema: replacementRulesForRawSchema,
592632
includeTags: includeTags,
593633
fallbackClient: fallbackClient,
594634
inferRequiredFromNullable: inferRequiredFromNullable,

swagger_parser/lib/src/parser/config/parser_config.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class ParserConfig {
1313
this.enumsParentPrefix = true,
1414
this.skippedParameters = const <String>[],
1515
this.replacementRules = const [],
16+
this.replacementRulesForRawSchema = const [],
1617
this.useXNullable = false,
1718
this.excludeTags = const <String>[],
1819
this.includeTags = const <String>[],
@@ -55,6 +56,9 @@ class ParserConfig {
5556
/// All rules are applied in order.
5657
final List<ReplacementRule> replacementRules;
5758

59+
/// {@macro replacement_rules_for_raw_schema}
60+
final List<ReplacementRule> replacementRulesForRawSchema;
61+
5862
/// Does the Schema use x-nullable to indicate nullable fields
5963
/// Only used for OpenApi v2
6064
final bool useXNullable;

swagger_parser/lib/src/parser/parser/open_api_parser.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,10 @@ class OpenApiParser {
674674
} else {
675675
rawOperationId = requestPath[_operationIdConst]?.toString();
676676
operationIdName = rawOperationId?.toCamel;
677-
final (_, nameDescription) = protectName(operationIdName);
677+
final (_, nameDescription) = protectName(
678+
operationIdName,
679+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
680+
);
678681
if (nameDescription != null) {
679682
description = '$description\n\n$nameDescription';
680683
requestName = (key + path).toCamel;
@@ -1310,6 +1313,7 @@ class OpenApiParser {
13101313
final (newName, description) = protectName(
13111314
name,
13121315
description: map[_descriptionConst]?.toString(),
1316+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
13131317
);
13141318

13151319
// Nullability of the array itself.
@@ -1381,6 +1385,7 @@ class OpenApiParser {
13811385
final (newName, description) = protectName(
13821386
name,
13831387
description: map[_descriptionConst]?.toString(),
1388+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
13841389
);
13851390

13861391
// Nullability of the map itself.
@@ -1437,6 +1442,7 @@ class OpenApiParser {
14371442
isEnum: true,
14381443
uniqueIfNull: true,
14391444
description: map[_descriptionConst]?.toString(),
1445+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
14401446
);
14411447

14421448
var newName = variableName;
@@ -1520,6 +1526,7 @@ class OpenApiParser {
15201526
originalName,
15211527
uniqueIfNull: true,
15221528
description: map[_descriptionConst]?.toString(),
1529+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
15231530
);
15241531

15251532
final (parameters, imports) = _findParametersAndImports(map);
@@ -1646,10 +1653,11 @@ class OpenApiParser {
16461653
// Create a base union class for the discriminated types
16471654
final baseClassName =
16481655
'${additionalName ?? ''} ${name ?? ''} Union'.toPascal;
1649-
final (newName, description) = protectName(
1656+
final (newName, _) = protectName(
16501657
baseClassName,
16511658
uniqueIfNull: true,
16521659
description: map[_descriptionConst]?.toString(),
1660+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
16531661
);
16541662

16551663
// Create a sealed class to represent the discriminated union
@@ -1805,6 +1813,8 @@ class OpenApiParser {
18051813
baseClassName,
18061814
uniqueIfNull: true,
18071815
description: map[_descriptionConst]?.toString(),
1816+
replacementRulesForRawSchema:
1817+
config.replacementRulesForRawSchema,
18081818
);
18091819

18101820
// Create a class to represent the allOf composition
@@ -1851,6 +1861,8 @@ class OpenApiParser {
18511861
baseClassName,
18521862
uniqueIfNull: true,
18531863
description: map[_descriptionConst]?.toString(),
1864+
replacementRulesForRawSchema:
1865+
config.replacementRulesForRawSchema,
18541866
);
18551867

18561868
final unionName = newName!.toPascal;
@@ -1915,6 +1927,7 @@ class OpenApiParser {
19151927
name,
19161928
description:
19171929
ofType.description ?? map[_descriptionConst]?.toString(),
1930+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
19181931
);
19191932
final enumType = map.containsKey(_defaultConst) && ofImport != null
19201933
? ofType.type
@@ -1949,6 +1962,7 @@ class OpenApiParser {
19491962
final (newNameForReturn, descriptionForReturn) = protectName(
19501963
name, // Use original name for top-level naming
19511964
description: ofType?.description ?? map[_descriptionConst]?.toString(),
1965+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
19521966
);
19531967

19541968
return (
@@ -2001,6 +2015,7 @@ class OpenApiParser {
20012015
final (newName, description) = protectName(
20022016
name,
20032017
description: map[_descriptionConst]?.toString(),
2018+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
20042019
);
20052020

20062021
final enumType = defaultValue != null && import != null ? type : null;
@@ -2219,6 +2234,7 @@ class OpenApiParser {
22192234
baseClassName,
22202235
uniqueIfNull: true,
22212236
description: unionDescription,
2237+
replacementRulesForRawSchema: config.replacementRulesForRawSchema,
22222238
);
22232239
final unionName = newName!.toPascal;
22242240
final (foundImports, variantRefToProps) = _getImportsAndProps(

swagger_parser/lib/src/parser/utils/type_utils.dart

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:collection/collection.dart';
22
import 'package:swagger_parser/src/parser/model/normalized_identifier.dart';
3-
import 'package:swagger_parser/src/parser/model/universal_data_class.dart';
43
import 'package:swagger_parser/src/parser/utils/dart_keywords.dart';
4+
import 'package:swagger_parser/swagger_parser.dart';
55

66
/// Extension for utils
77
extension StringTypeX on String {
@@ -192,10 +192,17 @@ final _nameRegExp = RegExp(r'^[a-zA-Z_-][a-zA-Z\d_-]*$');
192192
String? name, {
193193
String? description,
194194
bool uniqueIfNull = false,
195+
List<ReplacementRule> replacementRulesForRawSchema = const [],
195196
bool isEnum = false,
196197
bool isMethod = false,
197198
}) {
198-
final (newName, error) = switch (name) {
199+
var replacedName = name;
200+
if (replacedName != null) {
201+
for (final rule in replacementRulesForRawSchema) {
202+
replacedName = rule.apply(replacedName);
203+
}
204+
}
205+
final (newName, error) = switch (replacedName) {
199206
null || '' => uniqueIfNull
200207
? (
201208
uniqueName(isEnum: isEnum),
@@ -204,27 +211,27 @@ final _nameRegExp = RegExp(r'^[a-zA-Z_-][a-zA-Z\d_-]*$');
204211
: (null, null),
205212
// https://github.com/Carapacik/swagger_parser/issues/262
206213
_
207-
when name.startsWith(r'$') &&
208-
name
214+
when replacedName.startsWith(r'$') &&
215+
replacedName
209216
.split('')
210217
.where(
211218
(e) => e == r'$',
212219
)
213220
.length ==
214221
1 =>
215222
(
216-
name.substring(1),
223+
replacedName.substring(1),
217224
'Incorrect name has been replaced. Original name: `$name`.',
218225
),
219-
_ when !_nameRegExp.hasMatch(name) => (
226+
_ when !_nameRegExp.hasMatch(replacedName) => (
220227
uniqueName(isEnum: isEnum),
221228
'Incorrect name has been replaced. Original name: `$name`.',
222229
),
223-
_ when dartKeywords.contains(name.toCamel) => (
224-
'$name ${isEnum ? _enumConst : _valueConst}',
230+
_ when dartKeywords.contains(replacedName.toCamel) => (
231+
'$replacedName ${isEnum ? _enumConst : _valueConst}',
225232
'The name has been replaced because it contains a keyword. Original name: `$name`.',
226233
),
227-
_ => (name, null),
234+
_ => (replacedName, null),
228235
};
229236

230237
return (

swagger_parser/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: swagger_parser
22
description: Package that generates REST clients and data classes from OpenApi definition file
3-
version: 1.37.1
3+
version: 1.37.2
44
repository: https://github.com/Carapacik/swagger_parser/tree/main/swagger_parser
55
topics:
66
- swagger

0 commit comments

Comments
 (0)