Skip to content

Commit 8b1713f

Browse files
committed
feat(cli): Add basic OpenAPI loading/rendering
- Adds classes and methods for loading OpenAPI documents from JSON/YAML and rendering them to YAML. - Adds Dart classes for representing OpenAPI types in a spec-conformant way but which are easy to traverse.
1 parent 46dedf5 commit 8b1713f

21 files changed

+28296
-0
lines changed

apps/cli/lib/src/openapi/ast/openapi_ast.dart

Lines changed: 2304 additions & 0 deletions
Large diffs are not rendered by default.

apps/cli/lib/src/openapi/ast/openapi_ast.g.dart

Lines changed: 6561 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/cli/lib/src/openapi/ast/openapi_reference.dart

Lines changed: 444 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import 'package:celest_cli/src/openapi/ast/openapi_ast.dart';
2+
3+
abstract class OpenApiVisitor<R> {
4+
R visitDocument(OpenApiDocument document);
5+
R visitInfo(OpenApiInfo info);
6+
R visitServer(OpenApiServer server);
7+
R visitServerVariable(OpenApiServerVariable serverVariable);
8+
R visitPathItem(OpenApiPathItem pathItem);
9+
R visitOperation(OpenApiOperation operation);
10+
R visitParameter(OpenApiParameter parameter);
11+
R visitMediaType(OpenApiMediaType mediaType);
12+
R visitRequestBody(OpenApiRequestBody requestBody);
13+
R visitEncoding(OpenApiEncoding encoding);
14+
R visitResponse(OpenApiResponse response);
15+
R visitHeader(OpenApiHeader header);
16+
R visitComponents(OpenApiComponents components);
17+
R visitComponent(OpenApiComponent component) => switch (component) {
18+
OpenApiPathItem() => visitPathItem(component),
19+
OpenApiParameter() => visitParameter(component),
20+
OpenApiRequestBody() => visitRequestBody(component),
21+
OpenApiResponse() => visitResponse(component),
22+
OpenApiHeader() => visitHeader(component),
23+
OpenApiSchema() => visitSchema(component),
24+
OpenApiSecurityScheme() => visitSecurityScheme(component),
25+
};
26+
R visitSchema(OpenApiSchema schema);
27+
R visitSecurityRequirement(OpenApiSecurityRequirement securityRequirement);
28+
R visitSecurityScheme(OpenApiSecurityScheme securityScheme);
29+
R visitOAuthFlows(OpenApiOAuthFlows oauthFlows);
30+
R visitOAuthFlow(OpenApiOAuthFlow oauthFlow);
31+
R visitAdditionalProperties(OpenApiAdditionalProperties additionalProperties);
32+
R visitDiscriminator(OpenApiDiscriminator discriminator);
33+
R visitReference(OpenApiReference reference);
34+
R visitExternalDocs(OpenApiExternalDocs externalDocs);
35+
R visitTag(OpenApiTag tag);
36+
R visitContact(OpenApiContact contact);
37+
R visitLicense(OpenApiLicense license);
38+
}
39+
40+
abstract class OpenApiVisitorWithArg<R, A> {
41+
R visitDocument(OpenApiDocument document, A arg);
42+
R visitInfo(OpenApiInfo info, A arg);
43+
R visitServer(OpenApiServer server, A arg);
44+
R visitServerVariable(OpenApiServerVariable serverVariable, A arg);
45+
R visitPathItem(OpenApiPathItem pathItem, A arg);
46+
R visitOperation(OpenApiOperation operation, A arg);
47+
R visitParameter(OpenApiParameter parameter, A arg);
48+
R visitMediaType(OpenApiMediaType mediaType, A arg);
49+
R visitRequestBody(OpenApiRequestBody requestBody, A arg);
50+
R visitEncoding(OpenApiEncoding encoding, A arg);
51+
R visitResponse(OpenApiResponse response, A arg);
52+
R visitHeader(OpenApiHeader header, A arg);
53+
R visitComponents(OpenApiComponents components, A arg);
54+
R visitComponent(OpenApiComponent component, A arg) => switch (component) {
55+
OpenApiPathItem() => visitPathItem(component, arg),
56+
OpenApiParameter() => visitParameter(component, arg),
57+
OpenApiRequestBody() => visitRequestBody(component, arg),
58+
OpenApiResponse() => visitResponse(component, arg),
59+
OpenApiHeader() => visitHeader(component, arg),
60+
OpenApiSchema() => visitSchema(component, arg),
61+
OpenApiSecurityScheme() => visitSecurityScheme(component, arg),
62+
};
63+
R visitSchema(OpenApiSchema schema, A arg);
64+
R visitSecurityRequirement(
65+
OpenApiSecurityRequirement securityRequirement,
66+
A arg,
67+
);
68+
R visitSecurityScheme(OpenApiSecurityScheme securityScheme, A arg);
69+
R visitOAuthFlows(OpenApiOAuthFlows oauthFlows, A arg);
70+
R visitOAuthFlow(OpenApiOAuthFlow oauthFlow, A arg);
71+
R visitAdditionalProperties(
72+
OpenApiAdditionalProperties additionalProperties,
73+
A arg,
74+
);
75+
R visitDiscriminator(OpenApiDiscriminator discriminator, A arg);
76+
R visitReference(OpenApiReference reference, A arg);
77+
R visitExternalDocs(OpenApiExternalDocs externalDocs, A arg);
78+
R visitTag(OpenApiTag tag, A arg);
79+
R visitContact(OpenApiContact contact, A arg);
80+
R visitLicense(OpenApiLicense license, A arg);
81+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import 'package:celest_cli/src/openapi/ast/openapi_ast.dart';
2+
import 'package:celest_cli/src/openapi/loader/openapi_v3_loader.dart';
3+
import 'package:pub_semver/pub_semver.dart';
4+
import 'package:yaml/yaml.dart';
5+
6+
final _versionPattern = RegExp(r'^(\d)\.(\d)(?:\.(\d))?$');
7+
8+
final class OpenApiDocumentLoader {
9+
OpenApiDocumentLoader();
10+
11+
OpenApiDocument load({
12+
required String jsonOrYaml,
13+
required Uri sourceUri,
14+
}) {
15+
return loadOpenApiDocument(
16+
jsonOrYaml,
17+
sourceUri: sourceUri,
18+
);
19+
}
20+
}
21+
22+
OpenApiDocument loadOpenApiDocument(
23+
String jsonOrYaml, {
24+
required Uri sourceUri,
25+
}) {
26+
final document = loadYamlDocument(jsonOrYaml, sourceUrl: sourceUri);
27+
final root = document.contents as YamlMap;
28+
29+
final version = (root.nodes['swagger'] ?? root.nodes['openapi'])?.value;
30+
if (version == null || version is! String) {
31+
throw Exception('Could not detect OpenAPI version');
32+
}
33+
34+
final semverMatch = _versionPattern.firstMatch(version);
35+
final majorVersion = switch (semverMatch?.group(1)) {
36+
final match? => int.tryParse(match),
37+
null => null,
38+
};
39+
final minorVersion = switch (semverMatch?.group(2)) {
40+
final match? => int.tryParse(match),
41+
null => 0,
42+
};
43+
final patchVersion = switch (semverMatch?.group(3)) {
44+
final match? => int.tryParse(match),
45+
null => 0,
46+
};
47+
if (majorVersion == null || minorVersion == null || patchVersion == null) {
48+
throw Exception('Invalid OpenAPI version string: $version');
49+
}
50+
final semver = Version(
51+
majorVersion,
52+
minorVersion,
53+
patchVersion,
54+
);
55+
return switch (majorVersion) {
56+
3 => OpenApiV3Loader(version: semver, rootNode: root).load(),
57+
2 => throw Exception('OpenAPI 2.0 is not supported'),
58+
_ => throw Exception('Unknown OpenAPI version: $version'),
59+
};
60+
}

0 commit comments

Comments
 (0)