diff --git a/.travis.yml b/.travis.yml index d3eedb5..3c6fabc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: dart dart: - - stable + - beta before_script: - mkdir test/specs diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c236f..a6c178c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 3.0.0+6 + +- add openIDConnect security scheme type. + +## 3.0.0+5 + +- Remove forced null unwraps. + +## 3.0.0+3 + +- fix header parameters, update dependency on codable. + +## 3.0.0+2 + +- Rename APISchemaObject.isRequired back to required + +## 3.0.0 + +- nullsafety migration + +## 2.0.1+1 + +- forked version to fix v3 security requirements + ## 2.0.1 - Fix bug when merging APIResponse bodies diff --git a/README.md b/README.md index 704cafc..9021ff7 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Example --- ```dart -final file = new File("test/specs/kubernetes.json"); +final file = File("test/specs/kubernetes.json"); final contents = await file.readAsString(); -final doc = new APIDocument.fromJSON(contents); +final doc = APIDocument.fromJSON(contents); final output = JSON.encode(doc.asMap()); ``` diff --git a/analysis_options.yaml b/analysis_options.yaml index 97f0908..c6b53e5 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,6 @@ +include: package:lints/recommended.yaml + analyzer: - strong-mode: true # exclude: # - path/to/excluded/files/** @@ -8,8 +9,7 @@ linter: rules: - cancel_subscriptions - hash_and_equals - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type - test_types_in_equals - unrelated_type_equality_checks - valid_regexps diff --git a/lib/src/object.dart b/lib/src/object.dart index 22eccfa..9212ae4 100644 --- a/lib/src/object.dart +++ b/lib/src/object.dart @@ -1,7 +1,7 @@ import 'package:meta/meta.dart'; -import 'package:codable/codable.dart'; +import 'package:codable_forked/codable.dart'; -export 'package:codable/codable.dart'; +export 'package:codable_forked/codable.dart'; class APIObject extends Coding { Map extensions = {}; @@ -19,9 +19,12 @@ class APIObject extends Coding { @mustCallSuper void encode(KeyedArchive object) { - final invalidKeys = extensions.keys.where((key) => !key.startsWith("x-")).map((key) => "'$key'").toList(); + final invalidKeys = extensions.keys + .where((key) => !key.startsWith("x-")) + .map((key) => "'$key'") + .toList(); if (invalidKeys.length > 0) { - throw new ArgumentError( + throw ArgumentError( "extension keys must start with 'x-'. The following keys are invalid: ${invalidKeys.join(", ")}"); } @@ -29,4 +32,4 @@ class APIObject extends Coding { object.encode(key, value); }); } -} \ No newline at end of file +} diff --git a/lib/src/v2/document.dart b/lib/src/v2/document.dart index 288cec8..7c48902 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -1,11 +1,11 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/metadata.dart'; -import 'package:open_api/src/v2/parameter.dart'; -import 'package:open_api/src/v2/path.dart'; -import 'package:open_api/src/v2/response.dart'; -import 'package:open_api/src/v2/schema.dart'; -import 'package:open_api/src/v2/security.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/metadata.dart'; +import 'package:open_api_forked/src/v2/parameter.dart'; +import 'package:open_api_forked/src/v2/path.dart'; +import 'package:open_api_forked/src/v2/response.dart'; +import 'package:open_api_forked/src/v2/schema.dart'; +import 'package:open_api_forked/src/v2/security.dart'; /// Represents an OpenAPI 2.0 specification. class APIDocument extends APIObject { @@ -18,21 +18,21 @@ class APIDocument extends APIObject { } String version = "2.0"; - APIInfo info = new APIInfo(); - String host; - String basePath; + APIInfo? info = APIInfo(); + String? host; + String? basePath; - List tags = []; - List schemes = []; - List consumes = []; - List produces = []; - List>> security = []; + List? tags = []; + List? schemes = []; + List? consumes = []; + List? produces = []; + List>?> security = []; - Map paths = {}; - Map responses = {}; - Map parameters = {}; - Map definitions = {}; - Map securityDefinitions = {}; + Map? paths = {}; + Map? responses = {}; + Map? parameters = {}; + Map? definitions = {}; + Map? securityDefinitions = {}; Map asMap() { return KeyedArchive.archive(this, allowReferences: true); @@ -57,13 +57,15 @@ class APIDocument extends APIObject { produces = object["produces"]; security = object["security"]; - info = object.decodeObject("info", () => new APIInfo()); - tags = object.decodeObjects("tags", () => new APITag()); - paths = object.decodeObjectMap("paths", () => new APIPath()); - responses = object.decodeObjectMap("responses", () => new APIResponse()); - parameters = object.decodeObjectMap("parameters", () => new APIParameter()); - definitions = object.decodeObjectMap("definitions", () => new APISchemaObject()); - securityDefinitions = object.decodeObjectMap("securityDefinitions", () => new APISecurityScheme()); + info = object.decodeObject("info", () => APIInfo()); + tags = object.decodeObjects("tags", () => APITag()); + paths = object.decodeObjectMap("paths", () => APIPath()); + responses = object.decodeObjectMap("responses", () => APIResponse()); + parameters = object.decodeObjectMap("parameters", () => APIParameter()); + definitions = + object.decodeObjectMap("definitions", () => APISchemaObject()); + securityDefinitions = object.decodeObjectMap( + "securityDefinitions", () => APISecurityScheme()); } void encode(KeyedArchive object) { diff --git a/lib/src/v2/header.dart b/lib/src/v2/header.dart index bde5d29..ff572ad 100644 --- a/lib/src/v2/header.dart +++ b/lib/src/v2/header.dart @@ -1,19 +1,19 @@ -import 'package:codable/codable.dart'; -import 'package:open_api/src/v2/property.dart'; -import 'package:open_api/src/v2/types.dart'; +import 'package:codable_forked/codable.dart'; +import 'package:open_api_forked/src/v2/property.dart'; +import 'package:open_api_forked/src/v2/types.dart'; /// Represents a header in the OpenAPI specification. class APIHeader extends APIProperty { APIHeader(); - String description; - APIProperty items; + String? description; + APIProperty? items; void decode(KeyedArchive json) { super.decode(json); description = json.decode("description"); if (type == APIType.array) { - items = json.decodeObject("items", () => new APIProperty()); + items = json.decodeObject("items", () => APIProperty()); } } diff --git a/lib/src/v2/metadata.dart b/lib/src/v2/metadata.dart index eca70b6..b1d7f1e 100644 --- a/lib/src/v2/metadata.dart +++ b/lib/src/v2/metadata.dart @@ -1,4 +1,4 @@ -import 'package:open_api/src/object.dart'; +import 'package:open_api_forked/src/object.dart'; /// Represents a metadata for an API in the OpenAPI specification. class APIInfo extends APIObject { @@ -6,11 +6,11 @@ class APIInfo extends APIObject { APIInfo(); String title = "API"; - String description = "Description"; - String version = "1.0"; - String termsOfServiceURL = ""; - APIContact contact = new APIContact(); - APILicense license = new APILicense(); + String? description = "Description"; + String? version = "1.0"; + String? termsOfServiceURL = ""; + APIContact? contact = APIContact(); + APILicense? license = APILicense(); void decode(KeyedArchive object) { super.decode(object); @@ -18,8 +18,8 @@ class APIInfo extends APIObject { title = object.decode("title"); description = object.decode("description"); termsOfServiceURL = object.decode("termsOfService"); - contact = object.decodeObject("contact", () => new APIContact()); - license = object.decodeObject("license", () => new APILicense()); + contact = object.decodeObject("contact", () => APIContact()); + license = object.decodeObject("license", () => APILicense()); version = object.decode("version"); } @@ -92,8 +92,8 @@ class APITag extends APIObject { description = object.decode("description"); } - String name; - String description; + String? name; + String? description; void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 4e7c799..5a60a0e 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -1,7 +1,7 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/parameter.dart'; -import 'package:open_api/src/v2/response.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/parameter.dart'; +import 'package:open_api_forked/src/v2/response.dart'; /// Represents a HTTP operation (a path/method pair) in the OpenAPI specification. class APIOperation extends APIObject { @@ -16,18 +16,18 @@ class APIOperation extends APIObject { "security": cast.List(cast.Map(cast.String, cast.List(cast.String))), }; - String summary = ""; - String description = ""; - String id; + String? summary = ""; + String? description = ""; + String? id; bool deprecated = false; - List tags = []; - List schemes = []; - List consumes = []; - List produces = []; - List parameters = []; - List>> security = []; - Map responses = {}; + List? tags = []; + List? schemes = []; + List? consumes = []; + List? produces = []; + List? parameters = []; + List>?>? security = []; + Map? responses = {}; void decode(KeyedArchive object) { super.decode(object); @@ -39,8 +39,8 @@ class APIOperation extends APIObject { consumes = object.decode("consumes"); produces = object.decode("produces"); deprecated = object.decode("deprecated") ?? false; - parameters = object.decodeObjects("parameters", () => new APIParameter()); - responses = object.decodeObjectMap("responses", () => new APIResponse()); + parameters = object.decodeObjects("parameters", () => APIParameter()); + responses = object.decodeObjectMap("responses", () => APIResponse()); schemes = object.decode("schemes"); security = object.decode("security"); } diff --git a/lib/src/v2/parameter.dart b/lib/src/v2/parameter.dart index da11d3f..2cc41b7 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -1,13 +1,13 @@ -import 'package:codable/codable.dart'; -import 'package:open_api/src/v2/property.dart'; -import 'package:open_api/src/v2/schema.dart'; -import 'package:open_api/src/v2/types.dart'; +import 'package:codable_forked/codable.dart'; +import 'package:open_api_forked/src/v2/property.dart'; +import 'package:open_api_forked/src/v2/schema.dart'; +import 'package:open_api_forked/src/v2/types.dart'; /// Represents a parameter location in the OpenAPI specification. enum APIParameterLocation { query, header, path, formData, body } class APIParameterLocationCodec { - static APIParameterLocation decode(String location) { + static APIParameterLocation? decode(String? location) { switch (location) { case "query": return APIParameterLocation.query; @@ -19,12 +19,12 @@ class APIParameterLocationCodec { return APIParameterLocation.formData; case "body": return APIParameterLocation.body; + default: + return null; } - - return null; } - static String encode(APIParameterLocation location) { + static String? encode(APIParameterLocation? location) { switch (location) { case APIParameterLocation.query: return "query"; @@ -36,8 +36,9 @@ class APIParameterLocationCodec { return "formData"; case APIParameterLocation.body: return "body"; + default: + return null; } - return null; } } @@ -45,35 +46,35 @@ class APIParameterLocationCodec { class APIParameter extends APIProperty { APIParameter(); - String name; - String description; - bool required = false; - APIParameterLocation location; + String? name; + String? description; + bool isRequired = false; + APIParameterLocation? location; // Valid if location is body. - APISchemaObject schema; + APISchemaObject? schema; // Valid if location is not body. bool allowEmptyValue = false; - APIProperty items; + APIProperty? items; void decode(KeyedArchive json) { name = json.decode("name"); description = json.decode("description"); location = APIParameterLocationCodec.decode(json.decode("in")); if (location == APIParameterLocation.path) { - required = true; + isRequired = true; } else { - required = json.decode("required") ?? false; + isRequired = json.decode("required") ?? false; } if (location == APIParameterLocation.body) { - schema = json.decodeObject("schema", () => new APISchemaObject()); + schema = json.decodeObject("schema", () => APISchemaObject()); } else { super.decode(json); allowEmptyValue = json.decode("allowEmptyValue") ?? false; if (type == APIType.array) { - items = json.decodeObject("items", () => new APIProperty()); + items = json.decodeObject("items", () => APIProperty()); } } } @@ -82,7 +83,7 @@ class APIParameter extends APIProperty { json.encode("name", name); json.encode("description", description); json.encode("in", APIParameterLocationCodec.encode(location)); - json.encode("required", required); + json.encode("required", isRequired); if (location == APIParameterLocation.body) { json.encodeObject("schema", schema); diff --git a/lib/src/v2/path.dart b/lib/src/v2/path.dart index 19dd817..a0ba040 100644 --- a/lib/src/v2/path.dart +++ b/lib/src/v2/path.dart @@ -1,13 +1,13 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/operation.dart'; -import 'package:open_api/src/v2/parameter.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/operation.dart'; +import 'package:open_api_forked/src/v2/parameter.dart'; /// Represents a path (also known as a route) in the OpenAPI specification. class APIPath extends APIObject { APIPath(); - List parameters = []; - Map operations = {}; + List parameters = []; + Map operations = {}; void decode(KeyedArchive object) { super.decode(object); @@ -16,9 +16,9 @@ class APIPath extends APIObject { if (k == r"$ref") { // todo: reference } else if (k == "parameters") { - parameters = object.decodeObjects(k, () => new APIParameter()); + parameters = object.decodeObjects(k, () => APIParameter())!; } else { - operations[k] = object.decodeObject(k, () => new APIOperation()); + operations[k] = object.decodeObject(k, () => APIOperation()); } }); } diff --git a/lib/src/v2/property.dart b/lib/src/v2/property.dart index a6a8e4c..ec3587a 100644 --- a/lib/src/v2/property.dart +++ b/lib/src/v2/property.dart @@ -1,5 +1,5 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/types.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/types.dart'; enum APISchemaRepresentation { primitive, @@ -12,7 +12,7 @@ enum APISchemaRepresentation { enum APICollectionFormat { csv, ssv, tsv, pipes } class APICollectionFormatCodec { - static APICollectionFormat decode(String location) { + static APICollectionFormat? decode(String? location) { switch (location) { case "csv": return APICollectionFormat.csv; @@ -22,12 +22,12 @@ class APICollectionFormatCodec { return APICollectionFormat.tsv; case "pipes": return APICollectionFormat.pipes; + default: + return null; } - - return null; } - static String encode(APICollectionFormat location) { + static String? encode(APICollectionFormat? location) { switch (location) { case APICollectionFormat.csv: return "csv"; @@ -37,29 +37,30 @@ class APICollectionFormatCodec { return "tsv"; case APICollectionFormat.pipes: return "pipes"; + default: + return null; } - return null; } } class APIProperty extends APIObject { - APIType type; - String format; + APIType? type; + String? format; - APICollectionFormat collectionFormat; + APICollectionFormat? collectionFormat; dynamic defaultValue; - num maximum; - bool exclusiveMaximum; - num minimum; - bool exclusiveMinimum; - int maxLength; - int minLength; - String pattern; - int maxItems; - int minItems; - bool uniqueItems; - num multipleOf; - List enumerated; + num? maximum; + bool? exclusiveMaximum; + num? minimum; + bool? exclusiveMinimum; + int? maxLength; + int? minLength; + String? pattern; + int? maxItems; + int? minItems; + bool? uniqueItems; + num? multipleOf; + List? enumerated; APISchemaRepresentation get representation { if (type == APIType.array) { diff --git a/lib/src/v2/response.dart b/lib/src/v2/response.dart index 20063e5..703c04d 100644 --- a/lib/src/v2/response.dart +++ b/lib/src/v2/response.dart @@ -1,21 +1,21 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/header.dart'; -import 'package:open_api/src/v2/schema.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/header.dart'; +import 'package:open_api_forked/src/v2/schema.dart'; /// Represents an HTTP response in the OpenAPI specification. class APIResponse extends APIObject { APIResponse(); - String description = ""; - APISchemaObject schema; - Map headers = {}; + String? description = ""; + APISchemaObject? schema; + Map? headers = {}; void decode(KeyedArchive object) { super.decode(object); description = object.decode("description"); - schema = object.decodeObject("schema", () => new APISchemaObject()); - headers = object.decodeObjectMap("headers", () => new APIHeader()); + schema = object.decodeObject("schema", () => APISchemaObject()); + headers = object.decodeObjectMap("headers", () => APIHeader()); } void encode(KeyedArchive object) { diff --git a/lib/src/v2/schema.dart b/lib/src/v2/schema.dart index ab7837e..e07336b 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -1,25 +1,25 @@ -import 'package:codable/cast.dart' as cast; -import 'package:codable/codable.dart'; -import 'package:open_api/src/v2/property.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:codable_forked/codable.dart'; +import 'package:open_api_forked/src/v2/property.dart'; /// Represents a schema object in the OpenAPI specification. class APISchemaObject extends APIProperty { APISchemaObject(); - String title; - String description; - String example; - List required = []; + String? title; + String? description; + String? example; + List? required = []; bool readOnly = false; /// Valid when type == array - APISchemaObject items; + APISchemaObject? items; /// Valid when type == null - Map properties; + Map? properties; /// Valid when type == object - APISchemaObject additionalProperties; + APISchemaObject? additionalProperties; @override APISchemaRepresentation get representation { @@ -42,10 +42,10 @@ class APISchemaObject extends APIProperty { example = json.decode("example"); readOnly = json.decode("readOnly") ?? false; - items = json.decodeObject("items", () => new APISchemaObject()); - additionalProperties = json.decodeObject("additionalProperties", () => new APISchemaObject()); - properties = - json.decodeObjectMap("properties", () => new APISchemaObject()); + items = json.decodeObject("items", () => APISchemaObject()); + additionalProperties = + json.decodeObject("additionalProperties", () => APISchemaObject()); + properties = json.decodeObjectMap("properties", () => APISchemaObject()); } void encode(KeyedArchive json) { diff --git a/lib/src/v2/security.dart b/lib/src/v2/security.dart index a7575c0..5ca14c5 100644 --- a/lib/src/v2/security.dart +++ b/lib/src/v2/security.dart @@ -1,12 +1,17 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/parameter.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v2/parameter.dart'; /// Represents a OAuth 2.0 security scheme flow in the OpenAPI specification. -enum APISecuritySchemeFlow { implicit, password, application, authorizationCode } +enum APISecuritySchemeFlow { + implicit, + password, + application, + authorizationCode +} class APISecuritySchemeFlowCodec { - static APISecuritySchemeFlow decode(String flow) { + static APISecuritySchemeFlow? decode(String? flow) { switch (flow) { case "accessCode": return APISecuritySchemeFlow.authorizationCode; @@ -16,11 +21,12 @@ class APISecuritySchemeFlowCodec { return APISecuritySchemeFlow.implicit; case "application": return APISecuritySchemeFlow.application; + default: + return null; } - return null; } - static String encode(APISecuritySchemeFlow flow) { + static String? encode(APISecuritySchemeFlow? flow) { switch (flow) { case APISecuritySchemeFlow.authorizationCode: return "accessCode"; @@ -30,8 +36,9 @@ class APISecuritySchemeFlowCodec { return "implicit"; case APISecuritySchemeFlow.application: return "application"; + default: + return null; } - return null; } } @@ -47,22 +54,23 @@ class APISecurityScheme extends APIObject { type = "apiKey"; } - APISecurityScheme.oauth2(this.oauthFlow, {this.authorizationURL, this.tokenURL, this.scopes: const {}}) { + APISecurityScheme.oauth2(this.oauthFlow, + {this.authorizationURL, this.tokenURL, this.scopes: const {}}) { type = "oauth2"; } - String type; - String description; + late String type; + String? description; // API Key - String apiKeyName; - APIParameterLocation apiKeyLocation; + String? apiKeyName; + APIParameterLocation? apiKeyLocation; // Oauth2 - APISecuritySchemeFlow oauthFlow; - String authorizationURL; - String tokenURL; - Map scopes; + APISecuritySchemeFlow? oauthFlow; + String? authorizationURL; + String? tokenURL; + Map? scopes; bool get isOAuth2 { return type == "oauth2"; @@ -70,7 +78,7 @@ class APISecurityScheme extends APIObject { @override Map get castMap => - {"scopes": cast.Map(cast.String, cast.String)}; + {"scopes": cast.Map(cast.String, cast.String)}; void decode(KeyedArchive object) { super.decode(object); @@ -83,7 +91,7 @@ class APISecurityScheme extends APIObject { oauthFlow = APISecuritySchemeFlowCodec.decode(object.decode("flow")); authorizationURL = object.decode("authorizationUrl"); tokenURL = object.decode("tokenUrl"); - scopes = new Map.from(object.decode("scopes")); + scopes = Map.from(object.decode("scopes")); } else if (type == "apiKey") { apiKeyName = object.decode("name"); apiKeyLocation = APIParameterLocationCodec.decode(object.decode("in")); diff --git a/lib/src/v2/types.dart b/lib/src/v2/types.dart index af9ae8f..0c04e6c 100644 --- a/lib/src/v2/types.dart +++ b/lib/src/v2/types.dart @@ -1,7 +1,7 @@ enum APIType { string, number, integer, boolean, array, file, object } class APITypeCodec { - static APIType decode(String type) { + static APIType? decode(String? type) { switch (type) { case "string": return APIType.string; @@ -21,7 +21,7 @@ class APITypeCodec { return null; } - static String encode(APIType type) { + static String? encode(APIType? type) { switch (type) { case APIType.string: return "string"; @@ -37,8 +37,8 @@ class APITypeCodec { return "file"; case APIType.object: return "object"; + default: + return null; } - - return null; } } diff --git a/lib/src/v3/callback.dart b/lib/src/v3/callback.dart index 9c3c347..c980028 100644 --- a/lib/src/v3/callback.dart +++ b/lib/src/v3/callback.dart @@ -1,12 +1,12 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/path.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/path.dart'; /// A map of possible out-of band callbacks related to the parent operation. /// /// Each value in the map is a [APIPath] that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. class APICallback extends APIObject { - APICallback({this.paths}); - APICallback.empty(); + APICallback({Map? paths}) : paths = paths ?? {}; + APICallback.empty() : paths = {}; /// Callback paths. /// @@ -19,14 +19,15 @@ class APICallback extends APIObject { paths = {}; object.forEach((key, dynamic value) { if (value is! KeyedArchive) { - throw new ArgumentError("Invalid specification. Callback contains non-object value."); + throw ArgumentError( + "Invalid specification. Callback contains non-object value."); } - paths[key] = value.decode(key, inflate: () => new APIPath()); + paths[key] = value.decodeObject(key, () => APIPath())!; }); } void encode(KeyedArchive object) { super.encode(object); - throw new StateError("APICallback.encode: not yet implemented."); + throw StateError("APICallback.encode: not yet implemented."); } } diff --git a/lib/src/v3/components.dart b/lib/src/v3/components.dart index ac45d07..3e6062b 100644 --- a/lib/src/v3/components.dart +++ b/lib/src/v3/components.dart @@ -1,11 +1,11 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/callback.dart'; -import 'package:open_api/src/v3/header.dart'; -import 'package:open_api/src/v3/parameter.dart'; -import 'package:open_api/src/v3/request_body.dart'; -import 'package:open_api/src/v3/response.dart'; -import 'package:open_api/src/v3/schema.dart'; -import 'package:open_api/src/v3/security.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/callback.dart'; +import 'package:open_api_forked/src/v3/header.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; +import 'package:open_api_forked/src/v3/request_body.dart'; +import 'package:open_api_forked/src/v3/response.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; +import 'package:open_api_forked/src/v3/security.dart'; /// Holds a set of reusable objects for different aspects of the OAS. /// @@ -15,85 +15,102 @@ class APIComponents extends APIObject { APIComponents.empty(); - /// An object to hold reusable [APISchemaObject]. - Map schemas = {}; + /// An object to hold reusable [APISchemaObject?]. + Map? schemas = {}; - /// An object to hold reusable [APIResponse]. - Map responses = {}; + /// An object to hold reusable [APIResponse?]. + Map? responses = {}; - /// An object to hold reusable [APIParameter]. - Map parameters = {}; + /// An object to hold reusable [APIParameter?]. + Map? parameters = {}; //Map examples = {}; - /// An object to hold reusable [APIRequestBody]. - Map requestBodies = {}; + /// An object to hold reusable [APIRequestBody?]. + Map? requestBodies = {}; /// An object to hold reusable [APIHeader]. - Map headers = {}; + Map? headers = {}; - /// An object to hold reusable [APISecurityScheme]. - Map securitySchemes = {}; + /// An object to hold reusable [APISecurityScheme?]. + Map? securitySchemes = {}; //Map links = {}; - /// An object to hold reusable [APICallback]. - Map callbacks = {}; + /// An object to hold reusable [APICallback?]. + Map? callbacks = {}; /// Returns a component definition for [uri]. /// /// Construct [uri] as a path, e.g. `Uri(path: /components/schemas/name)`. - APIObject resolveUri(Uri uri) { + APIObject? resolveUri(Uri uri) { final segments = uri.pathSegments; if (segments.length != 3) { - throw new ArgumentError("Invalid reference URI. Must be a path URI of the form: '/components//'"); + throw ArgumentError( + "Invalid reference URI. Must be a path URI of the form: '/components//'"); } if (segments.first != "components") { - throw new ArgumentError("Invalid reference URI: does not begin with /components/"); + throw ArgumentError( + "Invalid reference URI: does not begin with /components/"); } var namedMap = null; switch (segments[1]) { - case "schemas": namedMap = schemas; break; - case "responses": namedMap = responses; break; - case "parameters": namedMap = parameters; break; - case "requestBodies": namedMap = requestBodies; break; - case "headers": namedMap = headers; break; - case "securitySchemes": namedMap = securitySchemes; break; - case "callbacks": namedMap = callbacks; break; - } - - if (namedMap == null) { - throw new ArgumentError("Invalid reference URI: component type '${segments[1]}' does not exist."); + case "schemas": + namedMap = schemas; + break; + case "responses": + namedMap = responses; + break; + case "parameters": + namedMap = parameters; + break; + case "requestBodies": + namedMap = requestBodies; + break; + case "headers": + namedMap = headers; + break; + case "securitySchemes": + namedMap = securitySchemes; + break; + case "callbacks": + namedMap = callbacks; + break; + default: + throw ArgumentError( + "Invalid reference URI: component type '${segments[1]}' does not exist."); } return namedMap[segments.last]; } - T resolve(T refObject) { - if (refObject.referenceURI == null) { - throw new ArgumentError("APIObject is not a reference to a component."); + T? resolve(T refObject) { + final referenceURI = refObject.referenceURI; + if (referenceURI == null) { + throw ArgumentError("APIObject is not a reference to a component."); } - return resolveUri(refObject.referenceURI); + return resolveUri(referenceURI) as T?; } void decode(KeyedArchive object) { super.decode(object); - schemas = object.decodeObjectMap("schemas", () => new APISchemaObject()); - responses = object.decodeObjectMap("responses", () => new APIResponse.empty()); - parameters = object.decodeObjectMap("parameters", () => new APIParameter.empty()); -// examples = object.decodeObjectMap("examples", () => new APIExample()); + schemas = object.decodeObjectMap("schemas", () => APISchemaObject())!; + responses = object.decodeObjectMap("responses", () => APIResponse.empty()); + parameters = + object.decodeObjectMap("parameters", () => APIParameter.empty()); +// examples = object.decodeObjectMap("examples", () => APIExample()); requestBodies = - object.decodeObjectMap("requestBodies", () => new APIRequestBody.empty()); - headers = object.decodeObjectMap("headers", () => new APIHeader()); + object.decodeObjectMap("requestBodies", () => APIRequestBody.empty()); + headers = object.decodeObjectMap("headers", () => APIHeader()); - securitySchemes = object.decodeObjectMap( - "securitySchemes", () => new APISecurityScheme()); -// links = object.decodeObjectMap("links", () => new APILink()); - callbacks = object.decodeObjectMap("callbacks", () => new APICallback()); + securitySchemes = + object.decodeObjectMap("securitySchemes", () => APISecurityScheme()); +// links = object.decodeObjectMap("links", () => APILink()); + callbacks = object.decodeObjectMap("callbacks", () => APICallback()); } void encode(KeyedArchive object) { diff --git a/lib/src/v3/document.dart b/lib/src/v3/document.dart index 3a3fa69..d16a95a 100644 --- a/lib/src/v3/document.dart +++ b/lib/src/v3/document.dart @@ -1,9 +1,9 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/components.dart'; -import 'package:open_api/src/v3/metadata.dart'; -import 'package:open_api/src/v3/path.dart'; -import 'package:open_api/src/v3/security.dart'; -import 'package:open_api/src/v3/server.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/components.dart'; +import 'package:open_api_forked/src/v3/metadata.dart'; +import 'package:open_api_forked/src/v3/path.dart'; +import 'package:open_api_forked/src/v3/security.dart'; +import 'package:open_api_forked/src/v3/server.dart'; /// This is the root document object of the OpenAPI document. class APIDocument extends APIObject { @@ -18,35 +18,35 @@ class APIDocument extends APIObject { /// This string MUST be the semantic version number of the OpenAPI Specification version that the OpenAPI document uses. /// /// REQUIRED. The openapi field SHOULD be used by tooling specifications and clients to interpret the OpenAPI document. This is not related to the API info.version string. - String version = "3.0.0"; + String? version = "3.0.0"; /// Provides metadata about the API. /// /// REQUIRED. The metadata MAY be used by tooling as required. - APIInfo info; + APIInfo? info; /// An array of [APIServerDescription], which provide connectivity information to a target server. /// /// If the servers property is not provided, or is an empty array, the default value would be a [APIServerDescription] with a url value of /. - List servers; + List? servers; /// The available paths and operations for the API. /// /// REQUIRED. - Map paths; + Map? paths; /// An element to hold various schemas for the specification. - APIComponents components; + APIComponents? components; /// A declaration of which security mechanisms can be used across the API. /// /// The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. Individual operations can override this definition. - List security; + List? security; /// A list of tags used by the specification with additional metadata. /// /// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used by the Operation Object must be declared. The tags that are not declared MAY be organized randomly or based on the tools' logic. Each tag name in the list MUST be unique. - List tags; + List? tags; Map asMap() { return KeyedArchive.archive(this, allowReferences: true); @@ -56,22 +56,24 @@ class APIDocument extends APIObject { super.decode(object); version = object.decode("openapi"); - info = object.decodeObject("info", () => new APIInfo.empty()); - servers = object.decodeObjects("servers", () => new APIServerDescription.empty()); - paths = object.decodeObjectMap("paths", () => new APIPath()); - components = - object.decodeObject("components", () => new APIComponents()); - security = object.decode("security"); - tags = object.decodeObjects("tags", () => new APITag.empty()); + info = object.decodeObject("info", () => APIInfo.empty())!; + servers = + object.decodeObjects("servers", () => APIServerDescription.empty()); + paths = object.decodeObjectMap("paths", () => APIPath()); + components = object.decodeObject("components", () => APIComponents()); + security = + object.decodeObjects("security", () => APISecurityRequirement.empty()); + tags = object.decodeObjects("tags", () => APITag.empty()); } void encode(KeyedArchive object) { super.encode(object); if (version == null || info == null || paths == null) { - throw new ArgumentError("APIDocument must have non-null values for: 'version', 'info', 'paths'."); + throw ArgumentError( + "APIDocument must have non-null values for: 'version', 'info', 'paths'."); } - + object.encode("openapi", version); object.encodeObject("info", info); object.encodeObjects("servers", servers); diff --git a/lib/src/v3/encoding.dart b/lib/src/v3/encoding.dart index dc23926..c9d6ece 100644 --- a/lib/src/v3/encoding.dart +++ b/lib/src/v3/encoding.dart @@ -1,10 +1,15 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/header.dart'; -import 'package:open_api/src/v3/parameter.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/header.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; /// A single encoding definition applied to a single schema property. class APIEncoding extends APIObject { - APIEncoding({this.contentType, this.headers, this.style, bool allowReserved, bool explode}) { + APIEncoding( + {this.contentType, + this.headers, + this.style, + bool? allowReserved, + bool? explode}) { this.allowReserved = allowReserved; this.explode = explode; } @@ -14,37 +19,43 @@ class APIEncoding extends APIObject { /// The Content-Type for encoding a specific property. /// /// Default value depends on the property type: for string with format being binary – application/octet-stream; for other primitive types – text/plain; for object - application/json; for array – the default is defined based on the inner type. The value can be a specific media type (e.g. application/json), a wildcard media type (e.g. image/*), or a comma-separated list of the two types. - String contentType; + String? contentType; /// A map allowing additional information to be provided as headers, for example Content-Disposition. /// /// Content-Type is described separately and SHALL be ignored in this section. This property SHALL be ignored if the request body media type is not a multipart. - Map headers; + Map? headers; /// Determines whether the parameter value SHOULD allow reserved characters, as defined by RFC3986 :/?#[]@!$&'()*+,;= to be included without percent-encoding. /// /// The default value is false. This property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded. - bool get allowReserved => _allowReserved ?? false; - set allowReserved(bool f) { _allowReserved = f; } - bool _allowReserved; + bool? get allowReserved => _allowReserved; + set allowReserved(bool? f) { + _allowReserved = f; + } + + bool? _allowReserved = false; /// When this is true, property values of type array or object generate separate parameters for each value of the array, or key-value-pair of the map. /// /// For other types of properties this property has no effect. When style is form, the default value is true. For all other styles, the default value is false. This property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded. - bool get explode => _explode ?? false; - set explode(bool f) { _explode = f; } - bool _explode; + bool? get explode => _explode; + set explode(bool? f) { + _explode = f; + } + + bool? _explode = false; /// Describes how a specific property value will be serialized depending on its type. /// /// See [APIParameter] for details on the style property. The behavior follows the same values as query parameters, including default values. This property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded. - String style; + String? style; void decode(KeyedArchive object) { super.decode(object); contentType = object.decode("contentType"); - headers = object.decodeObjectMap("headers", () => new APIHeader()); + headers = object.decodeObjectMap("headers", () => APIHeader()); _allowReserved = object.decode("allowReserved"); _explode = object.decode("explode"); style = object.decode("style"); diff --git a/lib/src/v3/header.dart b/lib/src/v3/header.dart index b01e846..91839bc 100644 --- a/lib/src/v3/header.dart +++ b/lib/src/v3/header.dart @@ -1,6 +1,6 @@ -import 'package:codable/codable.dart'; -import 'package:open_api/src/v3/parameter.dart'; -import 'package:open_api/src/v3/schema.dart'; +import 'package:codable_forked/codable.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; /// [APIHeader] follows the structure of the [APIParameter] with the following changes: /// @@ -8,7 +8,7 @@ import 'package:open_api/src/v3/schema.dart'; /// in MUST NOT be specified, it is implicitly in header. /// All traits that are affected by the location MUST be applicable to a location of header (for example, style). class APIHeader extends APIParameter { - APIHeader({APISchemaObject schema}) : super.header(null, schema: schema); + APIHeader({APISchemaObject? schema}) : super.header(null, schema: schema); APIHeader.empty() : super.header(null); @override diff --git a/lib/src/v3/media_type.dart b/lib/src/v3/media_type.dart index 3b0d775..c51c910 100644 --- a/lib/src/v3/media_type.dart +++ b/lib/src/v3/media_type.dart @@ -1,6 +1,6 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/encoding.dart'; -import 'package:open_api/src/v3/schema.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/encoding.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; /// Each [APIMediaType] provides schema and examples for the media type identified by its key. class APIMediaType extends APIObject { @@ -8,18 +8,18 @@ class APIMediaType extends APIObject { APIMediaType.empty(); /// The schema defining the type used for the request body. - APISchemaObject schema; + APISchemaObject? schema; /// A map between a property name and its encoding information. /// /// The key, being the property name, MUST exist in the schema as a property. The encoding object SHALL only apply to requestBody objects when the media type is multipart or application/x-www-form-urlencoded. - Map encoding; + Map? encoding; void decode(KeyedArchive object) { super.decode(object); - schema = object.decodeObject("schema", () => new APISchemaObject()); - encoding = object.decodeObjectMap("encoding", () => new APIEncoding()); + schema = object.decodeObject("schema", () => APISchemaObject()); + encoding = object.decodeObjectMap("encoding", () => APIEncoding()); } void encode(KeyedArchive object) { diff --git a/lib/src/v3/metadata.dart b/lib/src/v3/metadata.dart index e5ca37d..04cdd9e 100644 --- a/lib/src/v3/metadata.dart +++ b/lib/src/v3/metadata.dart @@ -1,4 +1,4 @@ -import 'package:open_api/src/object.dart'; +import 'package:open_api_forked/src/object.dart'; /// The object provides metadata about the API. /// @@ -7,42 +7,45 @@ class APIInfo extends APIObject { APIInfo.empty(); /// Creates empty metadata for specification. - APIInfo(this.title, this.version, {this.description, this.termsOfServiceURL, this.license, this.contact}); + APIInfo(this.title, this.version, + {this.description, this.termsOfServiceURL, this.license, this.contact}); /// The title of the application. /// /// REQUIRED. - String title; + String? title; /// A short description of the application. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API implementation version). /// /// REQUIRED. - String version; + String? version; /// A URL to the Terms of Service for the API. /// /// MUST be in the format of a URL. - Uri termsOfServiceURL; + Uri? termsOfServiceURL; /// The contact information for the exposed API. - APIContact contact; + APIContact? contact; /// The license information for the exposed API. - APILicense license; + APILicense? license; void decode(KeyedArchive object) { super.decode(object); title = object.decode("title"); description = object.decode("description"); - termsOfServiceURL = object.decode("termsOfService"); - contact = object.decodeObject("contact", () => new APIContact()); - license = object.decodeObject("license", () => new APILicense.empty()); + termsOfServiceURL = object.decode("termsOfService") != null + ? Uri.tryParse(object.decode("termsOfService")) + : null; + contact = object.decodeObject("contact", () => APIContact()); + license = object.decodeObject("license", () => APILicense.empty()); version = object.decode("version"); } @@ -50,7 +53,8 @@ class APIInfo extends APIObject { super.encode(object); if (title == null || version == null) { - throw new ArgumentError("APIInfo must have non-null values for: 'title', 'version'."); + throw ArgumentError( + "APIInfo must have non-null values for: 'title', 'version'."); } object.encode("title", title); @@ -68,23 +72,25 @@ class APIContact extends APIObject { APIContact.empty(); /// The identifying name of the contact person/organization. - String name; + String? name; /// The URL pointing to the contact information. /// /// MUST be in the format of a URL. - Uri url; + Uri? url; /// The email address of the contact person/organization. /// /// MUST be in the format of an email address. - String email; + String? email; void decode(KeyedArchive object) { super.decode(object); name = object.decode("name"); - url = object.decode("url"); + url = object.decode("url") != null + ? Uri.tryParse(object.decode("url")) + : null; email = object.decode("email"); } @@ -105,12 +111,12 @@ class APILicense extends APIObject { /// The license name used for the API. /// /// REQUIRED. - String name; + String? name; /// A URL to the license used for the API. /// /// MUST be in the format of a URL. - Uri url; + Uri? url; void decode(KeyedArchive object) { super.decode(object); @@ -123,7 +129,7 @@ class APILicense extends APIObject { super.encode(object); if (name == null) { - throw new ArgumentError("APILicense must have non-null values for: 'name'."); + throw ArgumentError("APILicense must have non-null values for: 'name'."); } object.encode("name", name); @@ -142,12 +148,12 @@ class APITag extends APIObject { /// The name of the tag. /// /// REQUIRED. - String name; + String? name; /// A short description for the tag. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; void decode(KeyedArchive object) { super.decode(object); @@ -160,7 +166,7 @@ class APITag extends APIObject { super.encode(object); if (name == null) { - throw new ArgumentError("APITag must have non-null values for: 'name'."); + throw ArgumentError("APITag must have non-null values for: 'name'."); } object.encode("name", name); object.encode("description", description); diff --git a/lib/src/v3/operation.dart b/lib/src/v3/operation.dart index b8729dd..10fc64d 100644 --- a/lib/src/v3/operation.dart +++ b/lib/src/v3/operation.dart @@ -1,13 +1,13 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/callback.dart'; -import 'package:open_api/src/v3/parameter.dart'; -import 'package:open_api/src/v3/request_body.dart'; -import 'package:open_api/src/v3/response.dart'; -import 'package:open_api/src/v3/security.dart'; -import 'package:open_api/src/v3/path.dart'; -import 'package:open_api/src/v3/document.dart'; -import 'package:open_api/src/v3/server.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/callback.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; +import 'package:open_api_forked/src/v3/request_body.dart'; +import 'package:open_api_forked/src/v3/response.dart'; +import 'package:open_api_forked/src/v3/security.dart'; +import 'package:open_api_forked/src/v3/path.dart'; +import 'package:open_api_forked/src/v3/document.dart'; +import 'package:open_api_forked/src/v3/server.dart'; /// Describes a single API operation on a path. class APIOperation extends APIObject { @@ -21,79 +21,79 @@ class APIOperation extends APIObject { this.security, this.requestBody, this.callbacks, - bool deprecated}) { + bool? deprecated}) { isDeprecated = deprecated; } /// A list of tags for API documentation control. /// /// Tags can be used for logical grouping of operations by resources or any other qualifier. - List tags; + List? tags; /// A short summary of what the operation does. - String summary; + String? summary; /// A verbose explanation of the operation behavior. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// Unique string used to identify the operation. /// /// The id MUST be unique among all operations described in the API. Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is RECOMMENDED to follow common programming naming conventions. - String id; + String? id; /// A list of parameters that are applicable for this operation. /// - /// If a parameter is already defined at the Path Item, the new definition will override it but can never remove it. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters. - List parameters; + /// If a parameter is already defined at the Path Item, the definition will override it but can never remove it. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters. + List? parameters; /// A declaration of which security mechanisms can be used for this operation. /// /// The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. This definition overrides any declared top-level security. To remove a top-level security declaration, an empty array can be used. - List security; + List? security; /// The request body applicable for this operation. /// /// The requestBody is only supported in HTTP methods where the HTTP 1.1 specification RFC7231 has explicitly defined semantics for request bodies. In other cases where the HTTP spec is vague, requestBody SHALL be ignored by consumers. - APIRequestBody requestBody; + APIRequestBody? requestBody; /// The list of possible responses as they are returned from executing this operation. /// /// REQUIRED. - Map responses; + Map? responses; /// A map of possible out-of band callbacks related to the parent operation. /// /// The key is a unique identifier for the [APICallback]. Each value in the map is a [APICallback] that describes a request that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. - Map callbacks; + Map? callbacks; /// An alternative server array to service this operation. /// /// If an alternative server object is specified at the [APIPath] or [APIDocument] level, it will be overridden by this value. - List servers; + List? servers; /// Declares this operation to be deprecated. /// /// Consumers SHOULD refrain from usage of the declared operation. Default value is false. - bool get isDeprecated => _deprecated ?? false; + bool? get isDeprecated => _deprecated; - set isDeprecated(bool f) { + set isDeprecated(bool? f) { _deprecated = f; } - bool _deprecated = false; + bool? _deprecated; /// Returns the parameter named [name] or null if it doesn't exist. - APIParameter parameterNamed(String name) => parameters?.firstWhere((p) => p.name == name, orElse: () => null); + APIParameter? parameterNamed(String name) => + parameters?.firstWhere((p) => p?.name == name, orElse: () => null); /// Adds [parameter] to [parameters]. /// /// If [parameters] is null, invoking this method will set it to a list containing [parameter]. /// Otherwise, [parameter] is added to [parameters]. void addParameter(APIParameter parameter) { - parameters ??= []; - parameters.add(parameter); + (parameters ??= []).add(parameter); } /// Adds [requirement] to [security]. @@ -101,9 +101,7 @@ class APIOperation extends APIObject { /// If [security] is null, invoking this method will set it to a list containing [requirement]. /// Otherwise, [requirement] is added to [security]. void addSecurityRequirement(APISecurityRequirement requirement) { - security ??= []; - - security.add(requirement); + (security ??= []).add(requirement); } /// Adds [response] to [responses], merging schemas if necessary. @@ -114,7 +112,7 @@ class APIOperation extends APIObject { /// and headers are added to the list of possible content and headers for the existing response. Descriptions /// of each response are joined together. All headers are marked as optional.. void addResponse(int statusCode, APIResponse response) { - responses ??= {}; + final responses = this.responses ??= {}; final key = "$statusCode"; @@ -124,16 +122,16 @@ class APIOperation extends APIObject { return; } - existingResponse.description = "${existingResponse.description ?? ""}\n${response.description}"; + existingResponse.description = + "${existingResponse.description ?? ""}\n${response.description}"; response.headers?.forEach((name, header) { existingResponse.addHeader(name, header); }); response.content?.forEach((contentType, mediaType) { - existingResponse.addContent(contentType, mediaType.schema); + existingResponse.addContent(contentType, mediaType?.schema); }); } - @override Map get castMap => {"tags": cast.List(cast.String)}; @@ -144,20 +142,24 @@ class APIOperation extends APIObject { summary = object.decode("summary"); description = object.decode("description"); id = object.decode("operationId"); - parameters = object.decodeObjects("parameters", () => new APIParameter.empty()); - requestBody = object.decodeObject("requestBody", () => new APIRequestBody.empty()); - responses = object.decodeObjectMap("responses", () => new APIResponse.empty()); - callbacks = object.decodeObjectMap("callbacks", () => new APICallback()); + parameters = object.decodeObjects("parameters", () => APIParameter.empty()); + requestBody = + object.decodeObject("requestBody", () => APIRequestBody.empty()); + responses = object.decodeObjectMap("responses", () => APIResponse.empty()); + callbacks = object.decodeObjectMap("callbacks", () => APICallback()); _deprecated = object.decode("deprecated"); - security = object.decodeObjects("security", () => new APISecurityRequirement.empty()); - servers = object.decodeObjects("servers", () => APIServerDescription.empty()); + security = + object.decodeObjects("security", () => APISecurityRequirement.empty()); + servers = + object.decodeObjects("servers", () => APIServerDescription.empty()); } void encode(KeyedArchive object) { super.encode(object); if (responses == null) { - throw new ArgumentError("Invalid specification. APIOperation must have non-null values for: 'responses'."); + throw ArgumentError( + "Invalid specification. APIOperation must have non-null values for: 'responses'."); } object.encode("tags", tags); diff --git a/lib/src/v3/parameter.dart b/lib/src/v3/parameter.dart index 02bbd1f..e6cb3c1 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -1,7 +1,7 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/document.dart'; -import 'package:open_api/src/v3/media_type.dart'; -import 'package:open_api/src/v3/schema.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/document.dart'; +import 'package:open_api_forked/src/v3/media_type.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; /// There are four possible parameter locations specified by the in field. /// @@ -30,7 +30,7 @@ enum APIParameterLocation { } class APIParameterLocationCodec { - static APIParameterLocation decode(String location) { + static APIParameterLocation? decode(String? location) { switch (location) { case "query": return APIParameterLocation.query; @@ -40,12 +40,12 @@ class APIParameterLocationCodec { return APIParameterLocation.path; case "cookie": return APIParameterLocation.cookie; + default: + return null; } - - return null; } - static String encode(APIParameterLocation location) { + static String? encode(APIParameterLocation? location) { switch (location) { case APIParameterLocation.query: return "query"; @@ -55,8 +55,9 @@ class APIParameterLocationCodec { return "path"; case APIParameterLocation.cookie: return "cookie"; + default: + return null; } - return null; } } @@ -71,12 +72,12 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired; this.isDeprecated = deprecated; this.allowEmptyValue = allowEmptyValue; this.allowReserved = allowReserved; @@ -88,12 +89,12 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired; this.isDeprecated = deprecated; this.allowEmptyValue = allowEmptyValue; this.allowReserved = allowReserved; @@ -106,12 +107,12 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired; this.isDeprecated = deprecated; this.allowEmptyValue = allowEmptyValue; this.allowReserved = allowReserved; @@ -121,7 +122,7 @@ class APIParameter extends APIObject { APIParameter.path(this.name) : location = APIParameterLocation.path, - schema = new APISchemaObject.string(), + schema = APISchemaObject.string(), _required = true; APIParameter.cookie(this.name, @@ -129,12 +130,12 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired; this.isDeprecated = deprecated; this.allowEmptyValue = allowEmptyValue; this.allowReserved = allowReserved; @@ -148,83 +149,85 @@ class APIParameter extends APIObject { /// If in is "path", the name field MUST correspond to the associated path segment from the path field in [APIDocument.paths]. See Path Templating for further information. /// If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the parameter definition SHALL be ignored. /// For all other cases, the name corresponds to the parameter name used by the in property. - String name; + String? name; /// A brief description of the parameter. /// /// This could contain examples of use. CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// Determines whether this parameter is mandatory. /// /// If the parameter location is "path", this property is REQUIRED and its value MUST be true. Otherwise, the property MAY be included and its default value is false. - bool get isRequired => (location == APIParameterLocation.path ? true : (_required ?? false)); + bool get isRequired => + (location == APIParameterLocation.path ? true : _required ?? false); - set isRequired(bool f) { + set isRequired(bool? f) { _required = f; } - bool _required; + bool? _required = false; /// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage. bool get isDeprecated => _deprecated ?? false; - set isDeprecated(bool f) { + set isDeprecated(bool? f) { _deprecated = f; } - bool _deprecated; + bool? _deprecated = false; /// The location of the parameter. /// /// REQUIRED. Possible values are "query", "header", "path" or "cookie". - APIParameterLocation location; + /// EXCEPT when used as response header 🤦️ + APIParameterLocation? location; /// The schema defining the type used for the parameter. - APISchemaObject schema; + APISchemaObject? schema; // Sets the ability to pass empty-valued parameters. // // This is valid only for query parameters and allows sending a parameter with an empty value. Default value is false. If style is used, and if behavior is n/a (cannot be serialized), the value of allowEmptyValue SHALL be ignored. - bool get allowEmptyValue => _allowEmptyValue ?? false; + bool? get allowEmptyValue => _allowEmptyValue; - set allowEmptyValue(bool f) { + set allowEmptyValue(bool? f) { _allowEmptyValue = f; } - bool _allowEmptyValue; + bool? _allowEmptyValue = false; /// Describes how the parameter value will be serialized depending on the type of the parameter value. /// /// Default values (based on value of in): for query - form; for path - simple; for header - simple; for cookie - form. - String style; + String? style; /// When this is true, parameter values of type array or object generate separate parameters for each value of the array or key-value pair of the map. /// /// For other types of parameters this property has no effect. When style is form, the default value is true. For all other styles, the default value is false. - bool get explode => _explode ?? false; + bool? get explode => _explode; - set explode(bool f) { + set explode(bool? f) { _explode = f; } - bool _explode; + bool? _explode = false; /// Determines whether the parameter value SHOULD allow reserved characters, as defined by RFC3986 :/?#[]@!$&'()*+,;= to be included without percent-encoding. /// /// This property only applies to parameters with an in value of query. The default value is false. - bool get allowReserved => _allowReserved ?? false; + bool? get allowReserved => _allowReserved; - set allowReserved(bool f) { + set allowReserved(bool? f) { _allowReserved = f; } - bool _allowReserved; + bool? _allowReserved = false; /// A map containing the representations for the parameter. /// /// The key is the media type and the value describes it. The map MUST only contain one entry. - Map content; + Map? content; // Currently missing: // example, examples @@ -240,18 +243,19 @@ class APIParameter extends APIObject { _deprecated = object.decode("deprecated"); _allowEmptyValue = object.decode("allowEmptyValue"); - schema = object.decodeObject("schema", () => new APISchemaObject()); + schema = object.decodeObject("schema", () => APISchemaObject()); style = object.decode("style"); _explode = object.decode("explode"); _allowReserved = object.decode("allowReserved"); - content = object.decodeObjectMap("content", () => new APIMediaType()); + content = object.decodeObjectMap("content", () => APIMediaType()); } void encode(KeyedArchive object) { super.encode(object); if (name == null || location == null) { - throw new ArgumentError("APIParameter must have non-null values for: 'name', 'location'."); + throw ArgumentError( + "APIParameter must have non-null values for: 'name', 'location'."); } object.encode("name", name); diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index df52e4b..222658b 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -1,38 +1,47 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/operation.dart'; -import 'package:open_api/src/v3/parameter.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/operation.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; /// Describes the operations available on a single path. /// /// An [APIPath] MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available. class APIPath extends APIObject { - APIPath.empty(); - APIPath({this.summary, this.description, this.parameters, this.operations}); + APIPath.empty() : operations = {}; + APIPath({ + this.summary, + this.description, + this.parameters, + Map? operations, + }) : operations = operations ?? {}; /// An optional, string summary, intended to apply to all operations in this path. - String summary; + String? summary; /// An optional, string description, intended to apply to all operations in this path. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// A list of parameters that are applicable for all the operations described under this path. /// /// These parameters can be overridden at the operation level, but cannot be removed there. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters. - List parameters; + List? parameters; /// Definitions of operations on this path. /// /// Keys are lowercased HTTP methods, e.g. get, put, delete, post, etc. - Map operations; + Map operations; /// Returns true if this path has path parameters [parameterNames]. /// /// Returns true if [parameters] contains path parameters with names that match [parameterNames] and /// both lists have the same number of elements. bool containsPathParameters(List parameterNames) { - final pathParams = parameters?.where((p) => p.location == APIParameterLocation.path)?.map((p) => p.name)?.toList() ?? []; + final pathParams = parameters + ?.where((p) => p?.location == APIParameterLocation.path) + .map((p) => p?.name) + .toList() ?? + []; if (pathParams.length != parameterNames.length) { return false; } @@ -47,15 +56,24 @@ class APIPath extends APIObject { summary = object.decode("summary"); description = object.decode("description"); - parameters = object.decodeObjects("parameters", () => new APIParameter.empty()); + parameters = object.decodeObjects("parameters", () => APIParameter.empty()); - final methodNames = ["get", "put", "post", "delete", "options", "head", "patch", "trace"]; + final methodNames = [ + "get", + "put", + "post", + "delete", + "options", + "head", + "patch", + "trace" + ]; methodNames.forEach((methodName) { if (!object.containsKey(methodName)) { return; } - operations ??= {}; - operations[methodName] = object.decodeObject(methodName, () => new APIOperation.empty()); + operations[methodName] = + object.decodeObject(methodName, () => APIOperation.empty()); }); } diff --git a/lib/src/v3/request_body.dart b/lib/src/v3/request_body.dart index 1f54bb5..a594f47 100644 --- a/lib/src/v3/request_body.dart +++ b/lib/src/v3/request_body.dart @@ -1,19 +1,23 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/media_type.dart'; -import 'package:open_api/src/v3/schema.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/media_type.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; /// Describes a single request body. class APIRequestBody extends APIObject { APIRequestBody.empty(); - APIRequestBody(this.content, {this.description, bool required}) { - this.isRequired = required; + APIRequestBody(this.content, {this.description, bool isRequired = false}) { + this.isRequired = isRequired; } - APIRequestBody.schema(APISchemaObject schema, {Iterable contentTypes : const ["application/json"], this.description, bool required}) { - this.isRequired = required; - this.content = contentTypes.fold({}, (prev, elem) { - prev[elem] = new APIMediaType(schema: schema); + APIRequestBody.schema(APISchemaObject schema, + {Iterable contentTypes: const ["application/json"], + this.description, + bool isRequired = false}) { + this.isRequired = isRequired; + this.content = + contentTypes.fold>({}, (prev, elem) { + prev[elem] = APIMediaType(schema: schema); return prev; }); } @@ -21,37 +25,38 @@ class APIRequestBody extends APIObject { /// A brief description of the request body. /// /// This could contain examples of use. CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// The content of the request body. /// /// REQUIRED. The key is a media type or media type range and the value describes it. For requests that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/* - Map content; + Map? content; /// Determines if the request body is required in the request. /// /// Defaults to false. - bool get isRequired => _required ?? false; + bool get isRequired => _required; set isRequired(bool f) { _required = f; } - bool _required; + bool _required = false; void decode(KeyedArchive object) { super.decode(object); description = object.decode("description"); - _required = object.decode("required"); - content = object.decodeObjectMap("content", () => new APIMediaType()); + _required = object.decode("required") ?? _required; + content = object.decodeObjectMap("content", () => APIMediaType())!; } void encode(KeyedArchive object) { super.encode(object); if (content == null) { - throw new ArgumentError("APIRequestBody must have non-null values for: 'content'."); + throw ArgumentError( + "APIRequestBody must have non-null values for: 'content'."); } object.encode("description", description); diff --git a/lib/src/v3/response.dart b/lib/src/v3/response.dart index 50c9f97..589c683 100644 --- a/lib/src/v3/response.dart +++ b/lib/src/v3/response.dart @@ -1,15 +1,17 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/header.dart'; -import 'package:open_api/src/v3/media_type.dart'; -import 'package:open_api/src/v3/schema.dart'; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/header.dart'; +import 'package:open_api_forked/src/v3/media_type.dart'; +import 'package:open_api_forked/src/v3/schema.dart'; /// Describes a single response from an API Operation, including design-time, static links to operations based on the response. class APIResponse extends APIObject { APIResponse.empty(); APIResponse(this.description, {this.content, this.headers}); - APIResponse.schema(this.description, APISchemaObject schema, {Iterable contentTypes: const ["application/json"], this.headers}) { - content = contentTypes.fold({}, (prev, elem) { - prev[elem] = new APIMediaType(schema: schema); + APIResponse.schema(this.description, APISchemaObject schema, + {Iterable contentTypes: const ["application/json"], + this.headers}) { + content = contentTypes.fold>({}, (prev, elem) { + prev[elem] = APIMediaType(schema: schema); return prev; }); } @@ -17,17 +19,17 @@ class APIResponse extends APIObject { /// A short description of the response. /// /// REQUIRED. CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// Maps a header name to its definition. /// /// RFC7230 states header names are case insensitive. If a response header is defined with the name "Content-Type", it SHALL be ignored. - Map headers; + Map? headers; /// A map containing descriptions of potential response payloads. /// /// The key is a media type or media type range and the value describes it. For responses that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/* - Map content; + Map? content; // Currently missing: // links @@ -36,8 +38,8 @@ class APIResponse extends APIObject { /// /// If [headers] is null, it is created. If the key does not exist in [headers], [header] is added for the key. /// If the key exists, [header] is not added. (To replace a header, access [headers] directly.) - void addHeader(String name, APIHeader header) { - headers ??= {}; + void addHeader(String name, APIHeader? header) { + final headers = this.headers ??= {}; if (!headers.containsKey(name)) { headers[name] = header; } @@ -49,23 +51,22 @@ class APIResponse extends APIObject { /// /// If [content] is null, it is created. If [contentType] does not exist in [content], [bodyObject] is added for [contentType]. /// If [contentType] exists, the [bodyObject] is added the list of possible schemas that were previously added. - void addContent(String contentType, APISchemaObject bodyObject) { - content ??= {}; + void addContent(String contentType, APISchemaObject? bodyObject) { + final content = this.content ??= {}; final key = contentType; final existingContent = content[key]; if (existingContent == null) { - content[key] = new APIMediaType(schema: bodyObject); + content[key] = APIMediaType(schema: bodyObject); return; } final schema = existingContent.schema; - if (schema?.oneOf != null) { - schema.oneOf.add(bodyObject); + final oneOf = schema?.oneOf; + if (oneOf != null) { + oneOf.add(bodyObject); } else { - final container = new APISchemaObject()..oneOf = [ - schema, bodyObject - ]; + final container = APISchemaObject()..oneOf = [schema, bodyObject]; existingContent.schema = container; } } @@ -74,18 +75,18 @@ class APIResponse extends APIObject { super.decode(object); description = object.decode("description"); - content = object.decodeObjectMap("content", () => new APIMediaType()); - headers = object.decodeObjectMap("headers", () => new APIHeader()); + content = object.decodeObjectMap("content", () => APIMediaType()); + headers = object.decodeObjectMap("headers", () => APIHeader()); } void encode(KeyedArchive object) { super.encode(object); if (description == null) { - throw new ArgumentError("APIResponse must have non-null values for: 'description'."); + throw ArgumentError( + "APIResponse must have non-null values for: 'description'."); } - object.encode("description", description); object.encodeObjectMap("headers", headers); object.encodeObjectMap("content", content); diff --git a/lib/src/v3/schema.dart b/lib/src/v3/schema.dart index e34eb0c..2d95ad0 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -1,6 +1,6 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/types.dart'; +import 'package:codable_forked/cast.dart' as cast; +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/types.dart'; enum APISchemaAdditionalPropertyPolicy { /// When [APISchemaObject] prevents properties other than those defined by [APISchemaObject.properties] from being included @@ -22,33 +22,41 @@ class APISchemaObject extends APIObject { APISchemaObject.number() : type = APIType.number; APISchemaObject.integer() : type = APIType.integer; APISchemaObject.boolean() : type = APIType.boolean; - APISchemaObject.map({APIType ofType, APISchemaObject ofSchema, bool any: false}) : type = APIType.object { + APISchemaObject.map( + {APIType? ofType, APISchemaObject? ofSchema, bool any: false}) + : type = APIType.object { if (ofType != null) { - additionalPropertySchema = new APISchemaObject()..type = ofType; + additionalPropertySchema = APISchemaObject()..type = ofType; } else if (ofSchema != null) { additionalPropertySchema = ofSchema; } else if (any) { - } else { - throw new ArgumentError("Invalid 'APISchemaObject.map' with neither 'ofType', 'any' or 'ofSchema' specified."); + throw ArgumentError( + "Invalid 'APISchemaObject.map' with neither 'ofType', 'any' or 'ofSchema' specified."); } } - APISchemaObject.array({APIType ofType, APISchemaObject ofSchema}) : type = APIType.array { + APISchemaObject.array({APIType? ofType, APISchemaObject? ofSchema}) + : type = APIType.array { if (ofType != null) { - items = new APISchemaObject()..type = ofType; + items = APISchemaObject()..type = ofType; } else if (ofSchema != null) { items = ofSchema; } else { - throw new ArgumentError("Invalid 'APISchemaObject.array' with neither 'ofType' or 'ofSchema' specified."); + throw ArgumentError( + "Invalid 'APISchemaObject.array' with neither 'ofType' or 'ofSchema' specified."); } } - APISchemaObject.object(this.properties): type = APIType.object; - APISchemaObject.file({bool isBase64Encoded: false}) : type = APIType.string, format = isBase64Encoded ? "byte" : "binary"; + APISchemaObject.object(this.properties) : type = APIType.object; + APISchemaObject.file({bool isBase64Encoded: false}) + : type = APIType.string, + format = isBase64Encoded ? "byte" : "binary"; - APISchemaObject.freeForm() : type = APIType.object, additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm; + APISchemaObject.freeForm() + : type = APIType.object, + additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm; /// A title for the object. - String title; + String? title; /// The value of "maximum" MUST be a number, representing an upper limit /// for a numeric instance. @@ -57,7 +65,7 @@ class APISchemaObject extends APIObject { /// "exclusiveMaximum" is true and instance is less than the provided /// value, or else if the instance is less than or exactly equal to the /// provided value. - num maximum; + num? maximum; /// The value of "exclusiveMaximum" MUST be a boolean, representing /// whether the limit in "maximum" is exclusive or not. @@ -68,7 +76,7 @@ class APISchemaObject extends APIObject { /// equal to the value specified in "maximum". If "exclusiveMaximum" is /// false (or not specified), then a numeric instance MAY be equal to the /// value of "maximum". - bool exclusiveMaximum; + bool? exclusiveMaximum; /// The value of "minimum" MUST be a number, representing a lower limit /// for a numeric instance. @@ -77,7 +85,7 @@ class APISchemaObject extends APIObject { /// "exclusiveMinimum" is true and instance is greater than the provided /// value, or else if the instance is greater than or exactly equal to /// the provided value. - num minimum; + num? minimum; /// The value of "exclusiveMinimum" MUST be a boolean, representing /// whether the limit in "minimum" is exclusive or not. An undefined @@ -87,7 +95,7 @@ class APISchemaObject extends APIObject { /// equal to the value specified in "minimum". If "exclusiveMinimum" is /// false (or not specified), then a numeric instance MAY be equal to the /// value of "minimum". - bool exclusiveMinimum; + bool? exclusiveMinimum; /// The value of this keyword MUST be a non-negative integer. /// @@ -99,7 +107,7 @@ class APISchemaObject extends APIObject { /// /// The length of a string instance is defined as the number of its /// characters as defined by RFC 7159 [RFC7159]. - int maxLength; + int? maxLength; /// A string instance is valid against this keyword if its length is /// greater than, or equal to, the value of this keyword. @@ -112,7 +120,7 @@ class APISchemaObject extends APIObject { /// /// "minLength", if absent, may be considered as being present with /// integer value 0. - int minLength; + int? minLength; /// The value of this keyword MUST be a string. This string SHOULD be a /// valid regular expression, according to the ECMA 262 regular @@ -121,14 +129,14 @@ class APISchemaObject extends APIObject { /// A string instance is considered valid if the regular expression /// matches the instance successfully. Recall: regular expressions are /// not implicitly anchored. - String pattern; + String? pattern; /// The value of this keyword MUST be an integer. This integer MUST be /// greater than, or equal to, 0. /// /// An array instance is valid against "maxItems" if its size is less /// than, or equal to, the value of this keyword. - int maxItems; + int? maxItems; /// The value of this keyword MUST be an integer. This integer MUST be /// greater than, or equal to, 0. @@ -138,7 +146,7 @@ class APISchemaObject extends APIObject { /// /// If this keyword is not present, it may be considered present with a /// value of 0. - int minItems; + int? minItems; /// The value of this keyword MUST be a boolean. /// @@ -148,20 +156,20 @@ class APISchemaObject extends APIObject { /// If not present, this keyword may be considered present with boolean /// value false. - bool uniqueItems; + bool? uniqueItems; /// The value of "multipleOf" MUST be a number, strictly greater than 0. /// A numeric instance is only valid if division by this keyword's value /// results in an integer. - num multipleOf; + num? multipleOf; /// The value of this keyword MUST be an integer. This integer MUST be /// greater than, or equal to, 0. /// /// An object instance is valid against "maxProperties" if its number of /// properties is less than, or equal to, the value of this keyword. - int maxProperties; + int? maxProperties; /// The value of this keyword MUST be an integer. This integer MUST be /// greater than, or equal to, 0. @@ -171,7 +179,7 @@ class APISchemaObject extends APIObject { /// /// If this keyword is not present, it may be considered present with a /// value of 0. - int minProperties; + int? minProperties; /// The value of this keyword MUST be an array. This array MUST have at /// least one element. Elements of this array MUST be strings, and MUST @@ -179,7 +187,7 @@ class APISchemaObject extends APIObject { /// /// An object instance is valid against this keyword if its property set /// contains all elements in this keyword's array value. - List required; + List? required; /// The value of this keyword MUST be an array. This array SHOULD have /// at least one element. Elements in the array SHOULD be unique. @@ -188,55 +196,55 @@ class APISchemaObject extends APIObject { /// /// An instance validates successfully against this keyword if its value /// is equal to one of the elements in this keyword's array value. - List enumerated; + List? enumerated; /* Modified JSON Schema for OpenAPI */ - APIType type; - List allOf; - List anyOf; - List oneOf; - APISchemaObject not; + APIType? type; + List? allOf; + List? anyOf; + List? oneOf; + APISchemaObject? not; - APISchemaObject items; - Map properties; - APISchemaObject additionalPropertySchema; - APISchemaAdditionalPropertyPolicy additionalPropertyPolicy; + APISchemaObject? items; + Map? properties; + APISchemaObject? additionalPropertySchema; + APISchemaAdditionalPropertyPolicy? additionalPropertyPolicy; - String description; - String format; + String? description; + String? format; dynamic defaultValue; - bool get isNullable => _nullable ?? false; + bool? get isNullable => _nullable ?? false; - set isNullable(bool n) { + set isNullable(bool? n) { _nullable = n; } // APIDiscriminator discriminator; - bool get isReadOnly => _readOnly ?? false; + bool? get isReadOnly => _readOnly ?? false; - set isReadOnly(bool n) { + set isReadOnly(bool? n) { _readOnly = n; } - bool get isWriteOnly => _writeOnly ?? false; + bool? get isWriteOnly => _writeOnly ?? false; - set isWriteOnly(bool n) { + set isWriteOnly(bool? n) { _writeOnly = n; } - bool get isDeprecated => _deprecated ?? false; + bool? get isDeprecated => _deprecated ?? false; - set isDeprecated(bool n) { + set isDeprecated(bool? n) { _deprecated = n; } - bool _nullable; - bool _readOnly; - bool _writeOnly; - bool _deprecated; + bool? _nullable; + bool? _readOnly; + bool? _writeOnly; + bool? _deprecated; @override Map get castMap => {"required": cast.List(cast.String)}; @@ -264,13 +272,13 @@ class APISchemaObject extends APIObject { // type = APITypeCodec.decode(object.decode("type")); - allOf = object.decodeObjects("allOf", () => new APISchemaObject()); - anyOf = object.decodeObjects("anyOf", () => new APISchemaObject()); - oneOf = object.decodeObjects("oneOf", () => new APISchemaObject()); - not = object.decodeObject("not", () => new APISchemaObject()); + allOf = object.decodeObjects("allOf", () => APISchemaObject()); + anyOf = object.decodeObjects("anyOf", () => APISchemaObject()); + oneOf = object.decodeObjects("oneOf", () => APISchemaObject()); + not = object.decodeObject("not", () => APISchemaObject()); - items = object.decodeObject("items", () => new APISchemaObject()); - properties = object.decodeObjectMap("properties", () => new APISchemaObject()); + items = object.decodeObject("items", () => APISchemaObject()); + properties = object.decodeObjectMap("properties", () => APISchemaObject()); final addlProps = object["additionalProperties"]; if (addlProps is bool) { @@ -283,7 +291,8 @@ class APISchemaObject extends APIObject { additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm; } else { additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.restricted; - additionalPropertySchema = object.decodeObject("additionalProperties", () => new APISchemaObject()); + additionalPropertySchema = + object.decodeObject("additionalProperties", () => APISchemaObject()); } description = object.decode("description"); @@ -326,9 +335,11 @@ class APISchemaObject extends APIObject { object.encodeObject("items", items); if (additionalPropertyPolicy != null || additionalPropertySchema != null) { - if (additionalPropertyPolicy == APISchemaAdditionalPropertyPolicy.disallowed) { + if (additionalPropertyPolicy == + APISchemaAdditionalPropertyPolicy.disallowed) { object.encode("additionalProperties", false); - } else if (additionalPropertyPolicy == APISchemaAdditionalPropertyPolicy.freeForm) { + } else if (additionalPropertyPolicy == + APISchemaAdditionalPropertyPolicy.freeForm) { object.encode("additionalProperties", true); } else { object.encodeObject("additionalProperties", additionalPropertySchema); diff --git a/lib/src/v3/security.dart b/lib/src/v3/security.dart index be4a7da..b275b21 100644 --- a/lib/src/v3/security.dart +++ b/lib/src/v3/security.dart @@ -1,13 +1,19 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/components.dart'; -import 'package:open_api/src/v3/document.dart'; -import 'package:open_api/src/v3/operation.dart'; -import 'package:open_api/src/v3/parameter.dart'; - -enum APISecuritySchemeType { apiKey, http, oauth2, openID } +import 'package:open_api_forked/src/object.dart'; +import 'package:open_api_forked/src/v3/components.dart'; +import 'package:open_api_forked/src/v3/document.dart'; +import 'package:open_api_forked/src/v3/operation.dart'; +import 'package:open_api_forked/src/v3/parameter.dart'; + +enum APISecuritySchemeType { + apiKey, + http, + oauth2, + openID, + openIdConnect, +} class APISecuritySchemeTypeCodec { - static APISecuritySchemeType decode(String type) { + static APISecuritySchemeType? decode(String? type) { switch (type) { case "apiKey": return APISecuritySchemeType.apiKey; @@ -17,11 +23,14 @@ class APISecuritySchemeTypeCodec { return APISecuritySchemeType.oauth2; case "openID": return APISecuritySchemeType.openID; + case "openIdConnect": + return APISecuritySchemeType.openIdConnect; + default: + return null; } - return null; } - static String encode(APISecuritySchemeType type) { + static String? encode(APISecuritySchemeType? type) { switch (type) { case APISecuritySchemeType.apiKey: return "apiKey"; @@ -31,8 +40,9 @@ class APISecuritySchemeTypeCodec { return "oauth2"; case APISecuritySchemeType.openID: return "openID"; + default: + return null; } - return null; } } @@ -41,62 +51,65 @@ class APISecuritySchemeTypeCodec { /// Supported schemes are HTTP authentication, an API key (either as a header or as a query parameter), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect Discovery. class APISecurityScheme extends APIObject { APISecurityScheme(); + APISecurityScheme.empty(); APISecurityScheme.http(this.scheme) : type = APISecuritySchemeType.http; - APISecurityScheme.apiKey(this.name, this.location) : type = APISecuritySchemeType.apiKey; + APISecurityScheme.apiKey(this.name, this.location) + : type = APISecuritySchemeType.apiKey; APISecurityScheme.oauth2(this.flows) : type = APISecuritySchemeType.oauth2; - APISecurityScheme.openID(this.connectURL) : type = APISecuritySchemeType.openID; + APISecurityScheme.openID(this.connectURL) + : type = APISecuritySchemeType.openID; /// The type of the security scheme. /// /// REQUIRED. Valid values are "apiKey", "http", "oauth2", "openIdConnect". - APISecuritySchemeType type; + APISecuritySchemeType? type; /// A short description for security scheme. /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// The name of the header, query or cookie parameter to be used. /// /// For apiKey only. REQUIRED if so. - String name; + String? name; /// The location of the API key. /// /// Valid values are "query", "header" or "cookie". /// /// For apiKey only. REQUIRED if so. - APIParameterLocation location; + APIParameterLocation? location; /// The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235. /// /// For http only. REQUIRED if so. - String scheme; + String? scheme; /// A hint to the client to identify how the bearer token is formatted. /// /// Bearer tokens are usually generated by an authorization server, so this information is primarily for documentation purposes. /// /// For http only. - String format; + String? format; /// An object containing configuration information for the flow types supported. /// /// Fixed keys are implicit, password, clientCredentials and authorizationCode. /// /// For oauth2 only. REQUIRED if so. - Map flows; + Map? flows; /// OpenId Connect URL to discover OAuth2 configuration values. /// /// This MUST be in the form of a URL. /// /// For openID only. REQUIRED if so. - Uri connectURL; + Uri? connectURL; void decode(KeyedArchive object) { super.decode(object); @@ -113,7 +126,8 @@ class APISecurityScheme extends APIObject { break; case APISecuritySchemeType.oauth2: { - flows = object.decodeObjectMap("flows", () => new APISecuritySchemeOAuth2Flow.empty()); + flows = object.decodeObjectMap( + "flows", () => APISecuritySchemeOAuth2Flow.empty()); } break; case APISecuritySchemeType.http: @@ -123,10 +137,14 @@ class APISecurityScheme extends APIObject { } break; case APISecuritySchemeType.openID: + case APISecuritySchemeType.openIdConnect: { connectURL = object.decode("openIdConnectUrl"); } break; + default: + throw ArgumentError( + "APISecurityScheme must have non-null values for: 'type' ${object.decode("type")}."); } } @@ -134,7 +152,8 @@ class APISecurityScheme extends APIObject { super.encode(object); if (type == null) { - throw new ArgumentError("APISecurityScheme must have non-null values for: 'type'."); + throw ArgumentError( + "APISecurityScheme must have non-null values for: 'type'."); } object.encode("type", APISecuritySchemeTypeCodec.encode(type)); @@ -144,7 +163,7 @@ class APISecurityScheme extends APIObject { case APISecuritySchemeType.apiKey: { if (name == null || location == null) { - throw new ArgumentError( + throw ArgumentError( "APISecurityScheme with 'apiKey' type must have non-null values for: 'name', 'location'."); } @@ -155,7 +174,8 @@ class APISecurityScheme extends APIObject { case APISecuritySchemeType.oauth2: { if (flows == null) { - throw new ArgumentError("APISecurityScheme with 'oauth2' type must have non-null values for: 'flows'."); + throw ArgumentError( + "APISecurityScheme with 'oauth2' type must have non-null values for: 'flows'."); } object.encodeObjectMap("flows", flows); @@ -164,7 +184,8 @@ class APISecurityScheme extends APIObject { case APISecuritySchemeType.http: { if (scheme == null) { - throw new ArgumentError("APISecurityScheme with 'http' type must have non-null values for: 'scheme'."); + throw ArgumentError( + "APISecurityScheme with 'http' type must have non-null values for: 'scheme'."); } object.encode("scheme", scheme); @@ -174,11 +195,15 @@ class APISecurityScheme extends APIObject { case APISecuritySchemeType.openID: { if (connectURL == null) { - throw new ArgumentError("APISecurityScheme with 'openID' type must have non-null values for: 'connectURL'."); + throw ArgumentError( + "APISecurityScheme with 'openID' type must have non-null values for: 'connectURL'."); } object.encode("openIdConnectUrl", connectURL); } break; + default: + throw ArgumentError( + "APISecurityScheme must have non-null values for: 'type'."); } } } @@ -186,30 +211,38 @@ class APISecurityScheme extends APIObject { /// Allows configuration of the supported OAuth Flows. class APISecuritySchemeOAuth2Flow extends APIObject { APISecuritySchemeOAuth2Flow.empty(); - APISecuritySchemeOAuth2Flow.code(this.authorizationURL, this.tokenURL, this.refreshURL, this.scopes); - APISecuritySchemeOAuth2Flow.implicit(this.authorizationURL, this.refreshURL, this.scopes); - APISecuritySchemeOAuth2Flow.password(this.tokenURL, this.refreshURL, this.scopes); - APISecuritySchemeOAuth2Flow.client(this.tokenURL, this.refreshURL, this.scopes); + + APISecuritySchemeOAuth2Flow.code(this.authorizationURL, this.tokenURL, + this.refreshURL, this.scopes); + + APISecuritySchemeOAuth2Flow.implicit(this.authorizationURL, this.refreshURL, + this.scopes); + + APISecuritySchemeOAuth2Flow.password(this.tokenURL, this.refreshURL, + this.scopes); + + APISecuritySchemeOAuth2Flow.client(this.tokenURL, this.refreshURL, + this.scopes); /// The authorization URL to be used for this flow. /// /// REQUIRED. This MUST be in the form of a URL. - Uri authorizationURL; + Uri? authorizationURL; /// The token URL to be used for this flow. /// /// REQUIRED. This MUST be in the form of a URL. - Uri tokenURL; + Uri? tokenURL; /// The URL to be used for obtaining refresh tokens. /// /// This MUST be in the form of a URL. - Uri refreshURL; + Uri? refreshURL; /// The available scopes for the OAuth2 security scheme. /// /// REQUIRED. A map between the scope name and a short description for it. - Map scopes; + Map? scopes; void encode(KeyedArchive object) { super.encode(object); @@ -228,7 +261,7 @@ class APISecuritySchemeOAuth2Flow extends APIObject { tokenURL = object.decode("tokenUrl"); refreshURL = object.decode("refreshUrl"); - scopes = new Map.from(object.decode("scopes")); + scopes = Map.from(object.decode("scopes")); } } @@ -241,12 +274,13 @@ class APISecuritySchemeOAuth2Flow extends APIObject { /// When a list of [APISecurityRequirement] is defined on the [APIDocument] or [APIOperation], only one of [APISecurityRequirement] in the list needs to be satisfied to authorize the request. class APISecurityRequirement extends APIObject { APISecurityRequirement.empty(); + APISecurityRequirement(this.requirements); /// Each name MUST correspond to a security scheme which is declared in [APIComponents.securitySchemes]. /// /// If the security scheme is of type [APISecuritySchemeType.oauth2] or [APISecuritySchemeType.openID], then the value is a list of scope names required for the execution. For other security scheme types, the array MUST be empty. - Map> requirements; + Map> requirements = {}; void encode(KeyedArchive object) { super.encode(object); @@ -260,8 +294,8 @@ class APISecurityRequirement extends APIObject { super.decode(object); object.keys.forEach((key) { - final req = new List.from(object.decode(key)); + final req = List.from(object.decode(key)); requirements[key] = req; }); } -} \ No newline at end of file +} diff --git a/lib/src/v3/server.dart b/lib/src/v3/server.dart index a7d68c2..95c34e7 100644 --- a/lib/src/v3/server.dart +++ b/lib/src/v3/server.dart @@ -1,38 +1,42 @@ -import 'package:open_api/src/object.dart'; +import 'package:open_api_forked/src/object.dart'; /// An object representing a Server. class APIServerDescription extends APIObject { APIServerDescription.empty(); APIServerDescription(this.url, {this.description, this.variables}); + /// A URL to the target host. /// /// REQUIRED. This URL supports Server Variables and MAY be relative, to indicate that the host location is relative to the location where the OpenAPI document is being served. Variable substitutions will be made when a variable is named in {brackets}. - Uri url; + Uri? url; /// An optional string describing the host designated by the URL. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; /// A map between a variable name and its value. /// /// The value is used for substitution in the server's URL template. - Map variables; + Map? variables; void decode(KeyedArchive object) { super.decode(object); - url = object.decode("url"); + url = object.decode("url") != null + ? Uri.tryParse(object.decode("url")) + : null; description = object.decode("description"); variables = - object.decodeObjectMap("variables", () => new APIServerVariable.empty()); + object.decodeObjectMap("variables", () => APIServerVariable.empty()); } void encode(KeyedArchive object) { super.encode(object); if (url == null) { - throw new ArgumentError("APIServerDescription must have non-null values for: 'url'."); + throw ArgumentError( + "APIServerDescription must have non-null values for: 'url'."); } object.encode("url", url); @@ -44,25 +48,26 @@ class APIServerDescription extends APIObject { /// An object representing a Server Variable for server URL template substitution. class APIServerVariable extends APIObject { APIServerVariable.empty(); - APIServerVariable(this.defaultValue, {this.availableValues, this.description}); + APIServerVariable(this.defaultValue, + {this.availableValues, this.description}); /// An enumeration of string values to be used if the substitution options are from a limited set. - List availableValues; + List? availableValues; /// The default value to use for substitution, and to send, if an alternate value is not supplied. /// /// REQUIRED. Unlike the Schema Object's default, this value MUST be provided by the consumer. - String defaultValue; + String? defaultValue; /// An optional description for the server variable. /// /// CommonMark syntax MAY be used for rich text representation. - String description; + String? description; void decode(KeyedArchive object) { super.decode(object); - availableValues = new List.from(object.decode("enum")); + availableValues = List.from(object.decode("enum")); defaultValue = object.decode("default"); description = object.decode("description"); } @@ -71,7 +76,8 @@ class APIServerVariable extends APIObject { super.encode(object); if (defaultValue == null) { - throw new ArgumentError("APIServerVariable must have non-null values for: 'defaultValue'."); + throw ArgumentError( + "APIServerVariable must have non-null values for: 'defaultValue'."); } object.encode("enum", availableValues); diff --git a/lib/src/v3/types.dart b/lib/src/v3/types.dart index 569ca04..55e1d44 100644 --- a/lib/src/v3/types.dart +++ b/lib/src/v3/types.dart @@ -1,7 +1,7 @@ enum APIType { string, number, integer, boolean, array, object } class APITypeCodec { - static APIType decode(String type) { + static APIType? decode(String? type) { switch (type) { case "string": return APIType.string; @@ -15,11 +15,12 @@ class APITypeCodec { return APIType.array; case "object": return APIType.object; + default: + return null; } - return null; } - static String encode(APIType type) { + static String? encode(APIType? type) { switch (type) { case APIType.string: return "string"; @@ -33,8 +34,8 @@ class APITypeCodec { return "array"; case APIType.object: return "object"; + default: + return null; } - - return null; } } diff --git a/lib/v2.dart b/lib/v2.dart index 9f65623..a5f3832 100644 --- a/lib/v2.dart +++ b/lib/v2.dart @@ -6,13 +6,13 @@ /// More dartdocs go here. library open_api_v2; -export 'package:open_api/src/v2/document.dart'; -export 'package:open_api/src/v2/header.dart'; -export 'package:open_api/src/v2/metadata.dart'; -export 'package:open_api/src/v2/operation.dart'; -export 'package:open_api/src/v2/parameter.dart'; -export 'package:open_api/src/v2/path.dart'; -export 'package:open_api/src/v2/response.dart'; -export 'package:open_api/src/v2/schema.dart'; -export 'package:open_api/src/v2/security.dart'; -export 'package:open_api/src/v2/types.dart'; +export 'package:open_api_forked/src/v2/document.dart'; +export 'package:open_api_forked/src/v2/header.dart'; +export 'package:open_api_forked/src/v2/metadata.dart'; +export 'package:open_api_forked/src/v2/operation.dart'; +export 'package:open_api_forked/src/v2/parameter.dart'; +export 'package:open_api_forked/src/v2/path.dart'; +export 'package:open_api_forked/src/v2/response.dart'; +export 'package:open_api_forked/src/v2/schema.dart'; +export 'package:open_api_forked/src/v2/security.dart'; +export 'package:open_api_forked/src/v2/types.dart'; diff --git a/lib/v3.dart b/lib/v3.dart index e958dff..68ce9d7 100644 --- a/lib/v3.dart +++ b/lib/v3.dart @@ -1,19 +1,19 @@ library open_api_v3; -export 'package:open_api/src/v3/callback.dart'; -export 'package:open_api/src/v3/components.dart'; -export 'package:open_api/src/v3/document.dart'; -export 'package:open_api/src/v3/encoding.dart'; -export 'package:open_api/src/v3/header.dart'; -export 'package:open_api/src/v3/media_type.dart'; -export 'package:open_api/src/v3/metadata.dart'; -export 'package:open_api/src/v3/operation.dart'; -export 'package:open_api/src/v3/parameter.dart'; -export 'package:open_api/src/v3/path.dart'; -export 'package:open_api/src/v3/request_body.dart'; -export 'package:open_api/src/v3/response.dart'; -export 'package:open_api/src/v3/schema.dart'; -export 'package:open_api/src/v3/security.dart'; -export 'package:open_api/src/v3/server.dart'; -export 'package:open_api/src/v3/types.dart'; -export 'package:open_api/src/object.dart'; +export 'package:open_api_forked/src/v3/callback.dart'; +export 'package:open_api_forked/src/v3/components.dart'; +export 'package:open_api_forked/src/v3/document.dart'; +export 'package:open_api_forked/src/v3/encoding.dart'; +export 'package:open_api_forked/src/v3/header.dart'; +export 'package:open_api_forked/src/v3/media_type.dart'; +export 'package:open_api_forked/src/v3/metadata.dart'; +export 'package:open_api_forked/src/v3/operation.dart'; +export 'package:open_api_forked/src/v3/parameter.dart'; +export 'package:open_api_forked/src/v3/path.dart'; +export 'package:open_api_forked/src/v3/request_body.dart'; +export 'package:open_api_forked/src/v3/response.dart'; +export 'package:open_api_forked/src/v3/schema.dart'; +export 'package:open_api_forked/src/v3/security.dart'; +export 'package:open_api_forked/src/v3/server.dart'; +export 'package:open_api_forked/src/v3/types.dart'; +export 'package:open_api_forked/src/object.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index e459a45..0799d57 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,17 @@ -name: open_api -description: Data structures for OpenAPI (Swagger) specification. Reads and writes JSON specifications. -version: 2.0.1 -homepage: https://github.com/stablekernel/open-api-dart -author: stable|kernel +name: open_api_forked +description: Data structures for OpenAPI (Swagger) specification. Reads and writes JSON specifications. Fork, with a couple of fixes and null safety by @hpoul +version: 3.0.0+6 +#homepage: https://github.com/stablekernel/open-api-dart +homepage: https://github.com/hpoul/open-api-dart +#author: stable|kernel environment: - sdk: ">=2.0.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: - meta: ^1.1.5 - codable: ^1.0.0 + codable_forked: ^1.0.0+3 + meta: ">=1.3.0 <2.0.0" dev_dependencies: - test: ^1.3.0 \ No newline at end of file + test: ">=1.16.4 <2.0.0" + lints: ^3.0.0 diff --git a/test/v2_test.dart b/test/v2_test.dart index 97b300f..a5494b2 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -1,86 +1,96 @@ // Copyright (c) 2017, joeconway. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. -import 'package:open_api/v2.dart'; +import 'package:open_api_forked/v2.dart'; import 'package:test/test.dart'; import 'dart:io'; import 'dart:convert'; void main() { group("Kubernetes spec", () { - APIDocument doc; - Map original; + APIDocument? doc; + Map? original; setUpAll(() { // Spec file is too large for pub, and no other way to remove from pub publish // than putting in .gitignore. Therefore, this file must be downloaded locally // to this path, from this path: https://github.com/kubernetes/kubernetes/blob/master/api/openapi-spec/swagger.json. - var file = new File("test/specs/kubernetes.json"); + var file = File("test/specs/kubernetes.json"); var contents = file.readAsStringSync(); original = json.decode(contents); - doc = new APIDocument.fromMap(original); + doc = APIDocument.fromMap(original!); }); test("Has all metadata", () { - expect(doc.version, "2.0"); - expect(doc.info.title, "Kubernetes"); - expect(doc.info.version, isNotNull); - expect(doc.host, isNull); - expect(doc.basePath, isNull); - expect(doc.tags, isNull); - expect(doc.schemes, isNull); + expect(doc!.version, "2.0"); + expect(doc!.info!.title, "Kubernetes"); + expect(doc!.info!.version, isNotNull); + expect(doc!.host, isNull); + expect(doc!.basePath, isNull); + expect(doc!.tags, isNull); + expect(doc!.schemes, isNull); }); test("Missing top-level objects", () { - expect(doc.consumes, isNull); - expect(original.containsKey("consumes"), false); + expect(doc!.consumes, isNull); + expect(original!.containsKey("consumes"), false); - expect(doc.produces, isNull); - expect(original.containsKey("produces"), false); + expect(doc!.produces, isNull); + expect(original!.containsKey("produces"), false); }); test("Has paths", () { - expect(doc.paths.length, greaterThan(0)); - expect(doc.paths.length, original["paths"].length); + expect(doc!.paths!.length, greaterThan(0)); + expect(doc!.paths!.length, original!["paths"].length); - Map originalPaths = original["paths"]; - doc.paths.forEach((k, v) { + Map originalPaths = original!["paths"]; + doc!.paths!.forEach((k, v) { expect(originalPaths.keys.contains(k), true); }); }); test("Sample - Namespace", () { - var namespacePath = doc.paths["/api/v1/namespaces"]; + var namespacePath = doc!.paths!["/api/v1/namespaces"]; - var getNamespace = namespacePath.operations["get"]; - expect(getNamespace.description, contains("of kind Namespace")); + var getNamespace = namespacePath!.operations["get"]; + expect(getNamespace!.description, contains("of kind Namespace")); expect(getNamespace.consumes, ["*/*"]); expect(getNamespace.produces, contains("application/json")); expect(getNamespace.produces, contains("application/yaml")); - expect(getNamespace.parameters.length, 8); - expect(getNamespace.parameters.firstWhere((p) => p.name == "limit").location, APIParameterLocation.query); - expect(getNamespace.parameters.firstWhere((p) => p.name == "limit").type, APIType.integer); - expect(getNamespace.responses.keys, contains("401")); - expect(getNamespace.responses.keys, contains("200")); + expect(getNamespace.parameters!.length, 8); + expect( + getNamespace.parameters! + .firstWhere((p) => p!.name == "limit")! + .location, + APIParameterLocation.query); + expect( + getNamespace.parameters!.firstWhere((p) => p!.name == "limit")!.type, + APIType.integer); + expect(getNamespace.responses!.keys, contains("401")); + expect(getNamespace.responses!.keys, contains("200")); var postNamespace = namespacePath.operations["post"]; - expect(postNamespace.parameters.length, 1); - expect(postNamespace.parameters.first.name, "body"); - expect(postNamespace.parameters.first.location, APIParameterLocation.body); + expect(postNamespace!.parameters!.length, 1); + expect(postNamespace.parameters!.first!.name, "body"); + expect( + postNamespace.parameters!.first!.location, APIParameterLocation.body); }); test("Sample - Reference", () { - var apiPath = doc.paths["/api/"]; - var apiPathGet = apiPath.operations["get"]; - var response = apiPathGet.responses["200"]; - var schema = response.schema; - expect(schema.description, contains("APIVersions lists the")); + var apiPath = doc!.paths!["/api/"]; + var apiPathGet = apiPath!.operations["get"]; + var response = apiPathGet!.responses!["200"]; + var schema = response!.schema; + expect(schema!.description, contains("APIVersions lists the")); expect(schema.required, ["versions", "serverAddressByClientCIDRs"]); - expect(schema.properties["serverAddressByClientCIDRs"].items.properties["clientCIDR"].description, contains("The CIDR")); + expect( + schema.properties!["serverAddressByClientCIDRs"]!.items! + .properties!["clientCIDR"]!.description, + contains("The CIDR")); }); test("Can encode as JSON", () { - expect(json.encode(doc.asMap()), new isInstanceOf()); + expect(json.encode(doc!.asMap()), isA()); }); }); } diff --git a/test/v3_test.dart b/test/v3_test.dart index 2eb4bea..b3c2958 100644 --- a/test/v3_test.dart +++ b/test/v3_test.dart @@ -1,4 +1,4 @@ -import 'package:open_api/v3.dart'; +import 'package:open_api_forked/v3.dart'; import 'package:test/test.dart'; import 'dart:io'; import 'dart:convert'; @@ -6,36 +6,41 @@ import 'dart:convert'; void main() { group("Components and resolution", () { test("Can resolve object against registry", () { - final components = new APIComponents(); - components.schemas["foo"] = new APISchemaObject.string(); + final components = APIComponents(); + components.schemas!["foo"] = APISchemaObject.string(); - final ref = new APISchemaObject()..referenceURI = Uri.parse("/components/schemas/foo"); + final ref = APISchemaObject() + ..referenceURI = Uri.parse("/components/schemas/foo"); final orig = components.resolve(ref); - expect(orig.type, APIType.string); + expect(orig!.type, APIType.string); expect(ref.type, isNull); - final APISchemaObject constructed = components.resolveUri(Uri(path: "/components/schemas/foo")); + final APISchemaObject constructed = components + .resolveUri(Uri(path: "/components/schemas/foo")) as APISchemaObject; expect(constructed.type, APIType.string); }); test("Invalid ref uri format throws error", () { - final components = new APIComponents(); + final components = APIComponents(); try { - components.resolve(new APISchemaObject()..referenceURI = Uri.parse("#/components/schemas/foo")); + components.resolve(APISchemaObject() + ..referenceURI = Uri.parse("#/components/schemas/foo")); expect(true, false); } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); } try { - components.resolve(new APISchemaObject()..referenceURI = Uri.parse("#/components/schemas")); + components.resolve(APISchemaObject() + ..referenceURI = Uri.parse("#/components/schemas")); expect(true, false); } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); } try { - components.resolve(new APISchemaObject()..referenceURI = Uri.parse("/components/foobar/foo")); + components.resolve(APISchemaObject() + ..referenceURI = Uri.parse("/components/foobar/foo")); expect(true, false); } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); @@ -43,156 +48,198 @@ void main() { }); test("Nonexisting component returns null", () { - final components = new APIComponents(); - expect(components.resolve(new APISchemaObject()..referenceURI = Uri.parse("/components/schemas/foo")), isNull); + APIComponents? components = APIComponents(); + expect( + components.resolve(APISchemaObject() + ..referenceURI = Uri.parse("/components/schemas/foo")), + isNull); }); test("URIs are paths internally, but fragments when serialized", () { - final doc = new APIDocument.fromMap({ + final doc = APIDocument.fromMap({ "openapi": "3.0.0", - "info": {"title":"x", "version":"1"}, + "info": {"title": "x", "version": "1"}, "paths": {}, "components": { "schemas": { "string": { "type": "string", }, - "container": { - "\$ref": "#/components/schemas/string" - } + "container": {"\$ref": "#/components/schemas/string"} } } }); - expect(doc.components.schemas["container"].referenceURI.path, "/components/schemas/string"); + expect(doc.components!.schemas!["container"]!.referenceURI!.path, + "/components/schemas/string"); - doc.components.schemas["other"] = new APISchemaObject()..referenceURI = Uri(path: "/components/schemas/container"); + doc.components!.schemas!["other"] = APISchemaObject() + ..referenceURI = Uri(path: "/components/schemas/container"); final out = doc.asMap(); - expect(out["components"]["schemas"]["container"][r"$ref"], "#/components/schemas/string"); - expect(out["components"]["schemas"]["other"][r"$ref"], "#/components/schemas/container"); + expect(out["components"]["schemas"]["container"][r"$ref"], + "#/components/schemas/string"); + expect(out["components"]["schemas"]["other"][r"$ref"], + "#/components/schemas/container"); }); }); group("Stripe spec", () { - APIDocument doc; - Map original; + APIDocument? doc; + Map? original; setUpAll(() { // Spec file is too large for pub, and no other way to remove from pub publish // than putting in .gitignore. Therefore, this file must be downloaded locally // to this path, from this path: https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json - var file = new File("test/specs/stripe.json"); + var file = File("test/specs/stripe.json"); var contents = file.readAsStringSync(); original = json.decode(contents); - doc = new APIDocument.fromMap(original); + doc = APIDocument.fromMap(original!); }); test("Emits same document in asMap()", () { - expect(doc.asMap(), original); + expect(doc!.asMap(), original); }); test("Has openapi version", () { - expect(doc.version, "3.0.0"); + expect(doc!.version, "3.0.0"); }); test("Has info", () { - expect(doc.info.title, "Stripe API"); - expect(doc.info.version, isNotNull); - expect(doc.info.description, "The Stripe REST API. Please see https://stripe.com/docs/api for more details."); - expect(doc.info.termsOfServiceURL.toString(), "https://stripe.com/us/terms/"); - expect(doc.info.contact.email, "dev-platform@stripe.com"); - expect(doc.info.contact.name, "Stripe Dev Platform Team"); - expect(doc.info.contact.url.toString(), "https://stripe.com"); - expect(doc.info.license, isNull); + expect(doc!.info!.title, "Stripe API"); + expect(doc!.info!.version, isNotNull); + expect(doc!.info!.description, + "The Stripe REST API. Please see https://stripe.com/docs/api for more details."); + expect(doc!.info!.termsOfServiceURL.toString(), + "https://stripe.com/us/terms/"); + expect(doc!.info!.contact!.email, "dev-platform@stripe.com"); + expect(doc!.info!.contact!.name, "Stripe Dev Platform Team"); + expect(doc!.info!.contact!.url.toString(), "https://stripe.com"); + expect(doc!.info!.license, isNull); }); test("Has servers", () { - expect(doc.servers.length, 1); - expect(doc.servers.first.url.toString(), "https://api.stripe.com/"); - expect(doc.servers.first.description, isNull); - expect(doc.servers.first.variables, isNull); + expect(doc!.servers!.length, 1); + expect(doc!.servers!.first!.url.toString(), "https://api.stripe.com/"); + expect(doc!.servers!.first!.description, isNull); + expect(doc!.servers!.first!.variables, isNull); }); test("Tags", () { - expect(doc.tags, isNull); + expect(doc!.tags, isNull); }); group("Paths", () { test("Sample path 1", () { - final p = doc.paths["/v1/transfers/{transfer}/reversals/{id}"]; + final p = doc!.paths!["/v1/transfers/{transfer}/reversals/{id}"]; expect(p, isNotNull); - expect(p.description, isNull); + expect(p!.description, isNull); expect(p.operations.length, 2); final getOp = p.operations["get"]; - final getParams = getOp.parameters; + final getParams = getOp!.parameters; final getResponses = getOp.responses; expect(getOp.description, contains("10 most recent reversals")); expect(getOp.id, "TransferReversalRetrieve"); - expect(getParams.length, 3); - expect(getParams[0].location, APIParameterLocation.query); - expect(getParams[0].description, "Specifies which fields in the response should be expanded."); - expect(getParams[0].name, "expand"); - expect(getParams[0].isRequired, false); - expect(getParams[0].schema.type, APIType.array); - expect(getParams[0].schema.items.type, APIType.string); - - expect(getParams[1].location, APIParameterLocation.path); - expect(getParams[1].name, "id"); - expect(getParams[1].isRequired, true); - expect(getParams[1].schema.type, APIType.string); - - expect(getParams[2].location, APIParameterLocation.path); - expect(getParams[2].name, "transfer"); - expect(getParams[2].isRequired, true); - expect(getParams[2].schema.type, APIType.string); - - expect(getResponses.length, 2); - expect(getResponses["200"].content.length, 1); - expect(getResponses["200"].content["application/json"].schema.referenceURI, Uri.parse(Uri.parse("#/components/schemas/transfer_reversal").fragment)); - - final resolvedElement = getResponses["200"].content["application/json"].schema.properties["balance_transaction"].anyOf; - expect(resolvedElement.last.properties["amount"].type, APIType.integer); + expect(getParams!.length, 3); + expect(getParams[0]!.location, APIParameterLocation.query); + expect(getParams[0]!.description, + "Specifies which fields in the response should be expanded."); + expect(getParams[0]!.name, "expand"); + expect(getParams[0]!.isRequired, false); + expect(getParams[0]!.schema!.type, APIType.array); + expect(getParams[0]!.schema!.items!.type, APIType.string); + + expect(getParams[1]!.location, APIParameterLocation.path); + expect(getParams[1]!.name, "id"); + expect(getParams[1]!.isRequired, true); + expect(getParams[1]!.schema!.type, APIType.string); + + expect(getParams[2]!.location, APIParameterLocation.path); + expect(getParams[2]!.name, "transfer"); + expect(getParams[2]!.isRequired, true); + expect(getParams[2]!.schema!.type, APIType.string); + + expect(getResponses!.length, 2); + expect(getResponses["200"]!.content!.length, 1); + expect( + getResponses["200"]! + .content!["application/json"]! + .schema! + .referenceURI, + Uri.parse( + Uri.parse("#/components/schemas/transfer_reversal").fragment)); + + final resolvedElement = getResponses["200"]! + .content!["application/json"]! + .schema! + .properties!["balance_transaction"]! + .anyOf; + expect(resolvedElement!.last!.properties!["amount"]!.type, + APIType.integer); }); }); - group("Components", () { + group("Components", () {}); + test("Security requirement", () { + expect(doc!.security, isNull); }); + }); - test("Security requirement", () { - expect(doc.security, isNull); + group("Security", () { + test("Can parse security requirements", () { + final doc = new APIDocument.fromMap({ + "openapi": "3.0.0", + "info": {"title": "x", "version": "1"}, + "paths": { + "/foo": { + "get": { + "summary": "Lorem Ipsum", + "security": [ + { + "test": [], + } + ], + }, + }, + }, + }); + expect(doc.paths!.values.first!.operations.values.first?.security, + hasLength(1)); }); }); group("Schema", () { test("Can read/emit schema object with additionalProperties=true", () { - final doc = new APIDocument.fromMap({ + final doc = APIDocument.fromMap({ "openapi": "3.0.0", - "info": {"title":"x", "version":"1"}, + "info": {"title": "x", "version": "1"}, "paths": {}, "components": { "schemas": { - "freeform": { - "type": "object", - "additionalProperties": true - } + "freeform": {"type": "object", "additionalProperties": true} } } }); - expect(doc.components.schemas["freeform"].additionalPropertyPolicy, APISchemaAdditionalPropertyPolicy.freeForm); + expect(doc.components!.schemas!["freeform"]!.additionalPropertyPolicy, + APISchemaAdditionalPropertyPolicy.freeForm); - expect(doc.asMap()["components"]["schemas"]["freeform"]["type"], "object"); - expect(doc.asMap()["components"]["schemas"]["freeform"]["additionalProperties"], true); + expect( + doc.asMap()["components"]["schemas"]["freeform"]["type"], "object"); + expect( + doc.asMap()["components"]["schemas"]["freeform"] + ["additionalProperties"], + true); }); test("Can read/emit schema object with additionalProperties={}", () { - final doc = new APIDocument.fromMap({ + final doc = APIDocument.fromMap({ "openapi": "3.0.0", - "info": {"title":"x", "version":"1"}, + "info": {"title": "x", "version": "1"}, "paths": {}, "components": { "schemas": { @@ -203,91 +250,120 @@ void main() { } } }); - expect(doc.components.schemas["freeform"].additionalPropertyPolicy, APISchemaAdditionalPropertyPolicy.freeForm); - expect(doc.asMap()["components"]["schemas"]["freeform"]["type"], "object"); - expect(doc.asMap()["components"]["schemas"]["freeform"]["additionalProperties"], true); + expect(doc.components!.schemas!["freeform"]!.additionalPropertyPolicy, + APISchemaAdditionalPropertyPolicy.freeForm); + expect( + doc.asMap()["components"]["schemas"]["freeform"]["type"], "object"); + expect( + doc.asMap()["components"]["schemas"]["freeform"] + ["additionalProperties"], + true); }); - }); - group("Callbacks", () { - }); + group("Callbacks", () {}); group("'add' methods", () { test("'addHeader'", () { - var resp = new APIResponse("Response"); + var resp = APIResponse("Response"); // when null - resp.addHeader("x", new APIHeader(schema: new APISchemaObject.string(format: "initial"))); - expect(resp.headers["x"].schema.format, "initial"); + resp.addHeader( + "x", APIHeader(schema: APISchemaObject.string(format: "initial"))); + expect(resp.headers!["x"]!.schema!.format, "initial"); // add more than one - resp.addHeader("y", new APIHeader(schema: new APISchemaObject.string(format: "second"))); - expect(resp.headers["x"].schema.format, "initial"); - expect(resp.headers["y"].schema.format, "second"); + resp.addHeader( + "y", APIHeader(schema: APISchemaObject.string(format: "second"))); + expect(resp.headers!["x"]!.schema!.format, "initial"); + expect(resp.headers!["y"]!.schema!.format, "second"); // cannot replace - resp.addHeader("y", new APIHeader(schema: new APISchemaObject.string(format: "third"))); - expect(resp.headers["x"].schema.format, "initial"); - expect(resp.headers["y"].schema.format, "second"); + resp.addHeader( + "y", APIHeader(schema: APISchemaObject.string(format: "third"))); + expect(resp.headers!["x"]!.schema!.format, "initial"); + expect(resp.headers!["y"]!.schema!.format, "second"); }); test("'addContent'", () { - var resp = new APIResponse("Response"); + var resp = APIResponse("Response"); // when null - resp.addContent("x/a", new APISchemaObject.string(format: "initial")); - expect(resp.content["x/a"].schema.format, "initial"); + resp.addContent("x/a", APISchemaObject.string(format: "initial")); + expect(resp.content!["x/a"]!.schema!.format, "initial"); // add more than one - resp.addContent("y/a", new APISchemaObject.string(format: "second")); - expect(resp.content["x/a"].schema.format, "initial"); - expect(resp.content["y/a"].schema.format, "second"); + resp.addContent("y/a", APISchemaObject.string(format: "second")); + expect(resp.content!["x/a"]!.schema!.format, "initial"); + expect(resp.content!["y/a"]!.schema!.format, "second"); // joins schema in oneOf if key exists - resp.addContent("y/a", new APISchemaObject.string(format: "third")); - expect(resp.content["x/a"].schema.format, "initial"); + resp.addContent("y/a", APISchemaObject.string(format: "third")); + expect(resp.content!["x/a"]!.schema!.format, "initial"); - expect(resp.content["y/a"].schema.oneOf.first.format, "second"); - expect(resp.content["y/a"].schema.oneOf.last.format, "third"); + expect(resp.content!["y/a"]!.schema!.oneOf!.first!.format, "second"); + expect(resp.content!["y/a"]!.schema!.oneOf!.last!.format, "third"); }); test("'addResponse'", () { - var op = new APIOperation("op", null); + var op = APIOperation("op", null); // when null - op.addResponse(200, new APIResponse.schema("OK", new APISchemaObject.string(format: "initial"))); - expect(op.responses["200"].content["application/json"].schema.format, "initial"); + op.addResponse(200, + APIResponse.schema("OK", APISchemaObject.string(format: "initial"))); + expect(op.responses!["200"]!.content!["application/json"]!.schema!.format, + "initial"); // add more than one - op.addResponse(400, new APIResponse.schema("KINDABAD", new APISchemaObject.string(format: "second"), headers: { - "initial": new APIHeader(schema: new APISchemaObject.string(format: "initial")) - })); - expect(op.responses["200"].content["application/json"].schema.format, "initial"); - expect(op.responses["400"].content["application/json"].schema.format, "second"); + op.addResponse( + 400, + APIResponse.schema( + "KINDABAD", APISchemaObject.string(format: "second"), headers: { + "initial": + APIHeader(schema: APISchemaObject.string(format: "initial")) + })); + expect(op.responses!["200"]!.content!["application/json"]!.schema!.format, + "initial"); + expect(op.responses!["400"]!.content!["application/json"]!.schema!.format, + "second"); // join responses when key exists - op.addResponse(400, new APIResponse.schema("REALBAD", new APISchemaObject.string(format: "third"), headers: { - "second": new APIHeader(schema: new APISchemaObject.string(format: "initial")) - })); - expect(op.responses["200"].content["application/json"].schema.format, "initial"); - - final r400 = op.responses["400"]; + op.addResponse( + 400, + APIResponse.schema("REALBAD", APISchemaObject.string(format: "third"), + headers: { + "second": + APIHeader(schema: APISchemaObject.string(format: "initial")) + })); + expect(op.responses!["200"]!.content!["application/json"]!.schema!.format, + "initial"); + + final r400 = op.responses!["400"]!; expect(r400.description, contains("KINDABAD")); expect(r400.description, contains("REALBAD")); - expect(r400.content["application/json"].schema.oneOf, isNotNull); - expect(r400.headers["initial"], isNotNull); - expect(r400.headers["second"], isNotNull); + expect(r400.content!["application/json"]!.schema!.oneOf, isNotNull); + expect(r400.headers!["initial"], isNotNull); + expect(r400.headers!["second"], isNotNull); }); test("'addResponse' guards against null value", () { - var op = new APIOperation("op", null); - - op.addResponse(400, new APIResponse.schema("KINDABAD", APISchemaObject.string(format: "second"))); - expect(op.responses["400"].content["application/json"].schema.format, "second"); - - op.addResponse(400, new APIResponse.schema("REALBAD", new APISchemaObject.string(format: "third"))); - expect(op.responses["400"].content["application/json"].schema.oneOf.length, 2); + var op = APIOperation("op", null); + + op.addResponse( + 400, + APIResponse.schema( + "KINDABAD", APISchemaObject.string(format: "second"))); + expect(op.responses!["400"]!.content!["application/json"]!.schema!.format, + "second"); + + op.addResponse( + 400, + APIResponse.schema( + "REALBAD", APISchemaObject.string(format: "third"))); + expect( + op.responses!["400"]!.content!["application/json"]!.schema!.oneOf! + .length, + 2); }); - });; + }); }