Skip to content
Open
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
2 changes: 1 addition & 1 deletion lib/src/parse/spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Header extends Equatable implements HasPointer, Parseable {
List<Object?> get props => [description, schema, pointer];
}

abstract class Parseable {}
sealed class Parseable {}

@immutable
class Ref<T extends Parseable> extends Equatable {
Expand Down
17 changes: 17 additions & 0 deletions lib/src/parse/visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ class SpecWalker {
}
}

void walk(Parseable parseable) {
switch (parseable) {
case Header():
_header(parseable);
case Parameter():
_parameter(parseable);
case RequestBody():
_requestBody(parseable);
case Response():
_response(parseable);
case Schema():
walkSchema(parseable);
case _:
throw UnimplementedError('Walking $parseable is not implemented');
}
}

void walkRoot(OpenApi root) {
visitor.visitRoot(root);
for (final path in root.paths.paths.values) {
Expand Down
49 changes: 40 additions & 9 deletions lib/src/render.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:file/file.dart';
import 'package:meta/meta.dart';
import 'package:space_gen/src/loader.dart';
Expand All @@ -15,6 +17,29 @@ import 'package:space_gen/src/string.dart';

export 'package:space_gen/src/quirks.dart';

extension RefExtension<T extends Parseable> on Ref<T> {
// TODO(eseidel): There must be a better way to do this.
T parse(Map<String, dynamic> json) {
final context = MapContext.initial(json);
if (type == Schema) {
return parseSchema(context) as T;
}
if (type == Header) {
return parseHeader(context) as T;
}
if (type == Parameter) {
return parseParameter(context) as T;
}
if (type == Paths) {
return parsePaths(context) as T;
}
if (type == RequestBody) {
return parseRequestBody(context) as T;
}
throw UnimplementedError('Parsing $T is not implemented');
}
}

class _RefCollector extends Visitor {
_RefCollector(this._refs);

Expand All @@ -28,13 +53,20 @@ class _RefCollector extends Visitor {
}
}

Iterable<Ref<Parseable>> collectRefs(OpenApi root) {
Iterable<Ref<Parseable>> collectRefsFromSpec(OpenApi root) {
final refs = <Ref<Parseable>>{};
final collector = _RefCollector(refs);
SpecWalker(collector).walkRoot(root);
return refs;
}

Iterable<Ref<Parseable>> collectRefsFromParseable(Parseable parseable) {
final refs = <Ref<Parseable>>{};
final collector = _RefCollector(refs);
SpecWalker(collector).walk(parseable);
return refs;
}

void validatePackageName(String packageName) {
// Validate that packageName is a valid dart package name.
// Should be snake_case starting with a letter.
Expand Down Expand Up @@ -66,15 +98,14 @@ Future<void> loadAndRenderSpec({
final spec = parseOpenApi(specJson);

// Pre-warm the cache. Rendering assumes all refs are present in the cache.
for (final ref in collectRefs(spec)) {
// We need to walk all the refs and get type and location.
// We load the locations, and then parse them as the types.
// And then stick them in the resolver cache.

// If any of the refs are network urls, we need to fetch them.
// The cache does not handle fragments, so we need to remove them.
final refsToLoad = Queue<Ref<Parseable>>.from(collectRefsFromSpec(spec));
while (refsToLoad.isNotEmpty) {
final ref = refsToLoad.removeFirst();
final resolved = specUrl.resolveUri(ref.uri).removeFragment();
await cache.load(resolved);
final loaded = await cache.load(resolved);
final parsed = ref.parse(loaded);
final loadedRefs = collectRefsFromParseable(parsed);
refsToLoad.addAll(loadedRefs);
}

// Resolve all references in the spec.
Expand Down
7 changes: 7 additions & 0 deletions lib/src/resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import 'package:space_gen/src/parse/spec.dart';
import 'package:space_gen/src/parse/visitor.dart';
import 'package:space_gen/src/parser.dart';

extension SchemaRefExtension on SchemaRef {
Schema parse(Map<String, dynamic> json) {
final context = MapContext.initial(json);
return parseSchema(context);
}
}

void _warn(String message, JsonPointer pointer) {
logger.warn('$message in $pointer');
}
Expand Down
Loading