From 9cf65bff4836b788501a7432705bd9307a6f1105 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 00:33:07 -0800 Subject: [PATCH 01/15] nullsafety --- README.md | 4 +- lib/src/object.dart | 9 +- lib/src/v2/document.dart | 34 ++-- lib/src/v2/header.dart | 6 +- lib/src/v2/metadata.dart | 12 +- lib/src/v2/operation.dart | 10 +- lib/src/v2/parameter.dart | 27 +-- lib/src/v2/path.dart | 8 +- lib/src/v2/property.dart | 45 ++--- lib/src/v2/response.dart | 8 +- lib/src/v2/schema.dart | 26 +-- lib/src/v2/security.dart | 44 +++-- lib/src/v2/types.dart | 4 +- lib/src/v3/callback.dart | 9 +- lib/src/v3/components.dart | 92 ++++++---- lib/src/v3/document.dart | 31 ++-- lib/src/v3/encoding.dart | 37 ++-- lib/src/v3/header.dart | 2 +- lib/src/v3/media_type.dart | 8 +- lib/src/v3/metadata.dart | 40 ++--- lib/src/v3/operation.dart | 63 +++---- lib/src/v3/parameter.dart | 137 +++++++-------- lib/src/v3/path.dart | 33 +++- lib/src/v3/request_body.dart | 26 +-- lib/src/v3/response.dart | 38 ++--- lib/src/v3/schema.dart | 119 +++++++------ lib/src/v3/security.dart | 93 ++++++---- lib/src/v3/server.dart | 26 +-- lib/src/v3/types.dart | 11 +- pubspec.yaml | 8 +- test/v2_test.dart | 81 +++++---- test/v3_test.dart | 320 ++++++++++++++++++++--------------- 32 files changed, 793 insertions(+), 618 deletions(-) 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/lib/src/object.dart b/lib/src/object.dart index 22eccfa..effa9ba 100644 --- a/lib/src/object.dart +++ b/lib/src/object.dart @@ -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..5d50ebe 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -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 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..4aefd79 100644 --- a/lib/src/v2/header.dart +++ b/lib/src/v2/header.dart @@ -6,14 +6,14 @@ import 'package:open_api/src/v2/types.dart'; 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..c9f0fac 100644 --- a/lib/src/v2/metadata.dart +++ b/lib/src/v2/metadata.dart @@ -9,8 +9,8 @@ class APIInfo extends APIObject { String description = "Description"; String version = "1.0"; String termsOfServiceURL = ""; - APIContact contact = new APIContact(); - APILicense license = new APILicense(); + 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..2d64a6b 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -18,16 +18,16 @@ class APIOperation extends APIObject { String summary = ""; String description = ""; - String id; + String? id; bool deprecated = false; List tags = []; List schemes = []; List consumes = []; List produces = []; - List parameters = []; + List parameters = []; List>> security = []; - Map responses = {}; + 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..0fa22e7 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -7,7 +7,7 @@ import 'package:open_api/src/v2/types.dart'; 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,17 +46,17 @@ class APIParameterLocationCodec { class APIParameter extends APIProperty { APIParameter(); - String name; - String description; + String? name; + String? description; bool required = false; - APIParameterLocation location; + 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"); @@ -68,12 +69,12 @@ class APIParameter extends APIProperty { } 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()); } } } @@ -81,7 +82,7 @@ class APIParameter extends APIProperty { void encode(KeyedArchive json) { json.encode("name", name); json.encode("description", description); - json.encode("in", APIParameterLocationCodec.encode(location)); + json.encode("in", APIParameterLocationCodec.encode(location!)); json.encode("required", required); if (location == APIParameterLocation.body) { diff --git a/lib/src/v2/path.dart b/lib/src/v2/path.dart index 19dd817..535cc34 100644 --- a/lib/src/v2/path.dart +++ b/lib/src/v2/path.dart @@ -6,8 +6,8 @@ import 'package:open_api/src/v2/parameter.dart'; 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..4d87fe9 100644 --- a/lib/src/v2/property.dart +++ b/lib/src/v2/property.dart @@ -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) { @@ -96,10 +97,10 @@ class APIProperty extends APIObject { void encode(KeyedArchive object) { super.encode(object); - object.encode("type", APITypeCodec.encode(type)); + object.encode("type", APITypeCodec.encode(type!)); object.encode("format", format); object.encode( - "collectionFormat", APICollectionFormatCodec.encode(collectionFormat)); + "collectionFormat", APICollectionFormatCodec.encode(collectionFormat!)); object.encode("default", defaultValue); object.encode("maximum", maximum); object.encode("exclusiveMaximum", exclusiveMaximum); diff --git a/lib/src/v2/response.dart b/lib/src/v2/response.dart index 20063e5..161f074 100644 --- a/lib/src/v2/response.dart +++ b/lib/src/v2/response.dart @@ -7,15 +7,15 @@ class APIResponse extends APIObject { APIResponse(); String description = ""; - APISchemaObject schema; - Map headers = {}; + 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..9f1dec6 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -6,20 +6,20 @@ import 'package:open_api/src/v2/property.dart'; class APISchemaObject extends APIProperty { APISchemaObject(); - String title; - String description; - String example; - List required = []; + String? title; + String? description; + String? example; + List isRequired = []; 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 { @@ -38,14 +38,14 @@ class APISchemaObject extends APIProperty { title = json.decode("title"); description = json.decode("description"); - required = json.decode("required"); + isRequired = json.decode("required"); 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) { @@ -53,7 +53,7 @@ class APISchemaObject extends APIProperty { json.encode("title", title); json.encode("description", description); - json.encode("required", required); + json.encode("required", isRequired); json.encode("example", example); json.encode("readOnly", readOnly); diff --git a/lib/src/v2/security.dart b/lib/src/v2/security.dart index a7575c0..45cad67 100644 --- a/lib/src/v2/security.dart +++ b/lib/src/v2/security.dart @@ -3,10 +3,15 @@ import 'package:open_api/src/object.dart'; import 'package:open_api/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")); @@ -100,9 +108,9 @@ class APISecurityScheme extends APIObject { /* nothing to do */ } else if (type == "apiKey") { object.encode("name", apiKeyName); - object.encode("in", APIParameterLocationCodec.encode(apiKeyLocation)); + object.encode("in", APIParameterLocationCodec.encode(apiKeyLocation!)); } else if (type == "oauth2") { - object.encode("flow", APISecuritySchemeFlowCodec.encode(oauthFlow)); + object.encode("flow", APISecuritySchemeFlowCodec.encode(oauthFlow!)); object.encode("authorizationUrl", authorizationURL); object.encode("tokenUrl", tokenURL); diff --git a/lib/src/v2/types.dart b/lib/src/v2/types.dart index af9ae8f..6c22a18 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"; diff --git a/lib/src/v3/callback.dart b/lib/src/v3/callback.dart index 9c3c347..f76d59c 100644 --- a/lib/src/v3/callback.dart +++ b/lib/src/v3/callback.dart @@ -11,7 +11,7 @@ class APICallback extends APIObject { /// Callback paths. /// /// The key that identifies the [APIPath] is a runtime expression that can be evaluated in the context of a runtime HTTP request/response to identify the URL to be used for the callback request. A simple example might be $request.body#/url. - Map paths; + Map? paths; void decode(KeyedArchive object) { super.decode(object); @@ -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..fbc592e 100644 --- a/lib/src/v3/components.dart +++ b/lib/src/v3/components.dart @@ -15,85 +15,103 @@ 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; + 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."); + throw ArgumentError( + "Invalid reference URI: component type '${segments[1]}' does not exist."); } return namedMap[segments.last]; } - T resolve(T refObject) { + T? resolve(T refObject) { if (refObject.referenceURI == null) { - throw new ArgumentError("APIObject is not a reference to a component."); + throw ArgumentError("APIObject is not a reference to a component."); } - return resolveUri(refObject.referenceURI); + return resolveUri(refObject.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..2bab4a9 100644 --- a/lib/src/v3/document.dart +++ b/lib/src/v3/document.dart @@ -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,23 @@ 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()); + info = object.decodeObject("info", () => APIInfo.empty())!; + servers = + object.decodeObjects("servers", () => APIServerDescription.empty()); + paths = object.decodeObjectMap("paths", () => APIPath()); + components = object.decodeObject("components", () => APIComponents()); security = object.decode("security"); - tags = object.decodeObjects("tags", () => new APITag.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..ad3c2f8 100644 --- a/lib/src/v3/encoding.dart +++ b/lib/src/v3/encoding.dart @@ -4,9 +4,14 @@ import 'package:open_api/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}) { - this.allowReserved = allowReserved; - this.explode = explode; + APIEncoding( + {this.contentType, + this.headers, + this.style, + bool? allowReserved, + bool? explode}) { + this.allowReserved = allowReserved ?? false; + this.explode = explode ?? false; } APIEncoding.empty(); @@ -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..c727d46 100644 --- a/lib/src/v3/header.dart +++ b/lib/src/v3/header.dart @@ -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..d379cba 100644 --- a/lib/src/v3/media_type.dart +++ b/lib/src/v3/media_type.dart @@ -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..206d822 100644 --- a/lib/src/v3/metadata.dart +++ b/lib/src/v3/metadata.dart @@ -7,33 +7,34 @@ 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); @@ -41,8 +42,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.empty()); + contact = object.decodeObject("contact", () => APIContact()); + license = object.decodeObject("license", () => APILicense.empty()); version = object.decode("version"); } @@ -50,7 +51,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,17 +70,17 @@ 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); @@ -105,12 +107,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 +125,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 +144,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 +162,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..455ccbb 100644 --- a/lib/src/v3/operation.dart +++ b/lib/src/v3/operation.dart @@ -21,62 +21,62 @@ class APIOperation extends APIObject { this.security, this.requestBody, this.callbacks, - bool deprecated}) { - isDeprecated = deprecated; + bool? deprecated}) { + isDeprecated = deprecated ?? false; } /// 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) { _deprecated = f; @@ -85,7 +85,8 @@ class APIOperation extends APIObject { bool _deprecated = false; /// 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]. /// @@ -93,7 +94,7 @@ class APIOperation extends APIObject { /// Otherwise, [parameter] is added to [parameters]. void addParameter(APIParameter parameter) { parameters ??= []; - parameters.add(parameter); + parameters!.add(parameter); } /// Adds [requirement] to [security]. @@ -103,7 +104,7 @@ class APIOperation extends APIObject { void addSecurityRequirement(APISecurityRequirement requirement) { security ??= []; - security.add(requirement); + security!.add(requirement); } /// Adds [response] to [responses], merging schemas if necessary. @@ -118,22 +119,22 @@ class APIOperation extends APIObject { final key = "$statusCode"; - final existingResponse = responses[key]; + final existingResponse = responses![key]; if (existingResponse == null) { - responses[key] = response; + responses![key] = response; 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 +145,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..f39f410 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -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,16 +72,16 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired ?? false; + this.isDeprecated = deprecated ?? false; + this.allowEmptyValue = allowEmptyValue ?? false; + this.allowReserved = allowReserved ?? false; + this.explode = explode ?? false; } APIParameter.header(this.name, @@ -88,16 +89,16 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired ?? false; + this.isDeprecated = deprecated ?? false; + this.allowEmptyValue = allowEmptyValue ?? false; + this.allowReserved = allowReserved ?? false; + this.explode = explode ?? false; this.location = APIParameterLocation.header; } @@ -106,22 +107,22 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired ?? false; + this.isDeprecated = deprecated ?? false; + this.allowEmptyValue = allowEmptyValue ?? false; + this.allowReserved = allowReserved ?? false; + this.explode = explode ?? false; this.location = APIParameterLocation.query; } APIParameter.path(this.name) : location = APIParameterLocation.path, - schema = new APISchemaObject.string(), + schema = APISchemaObject.string(), _required = true; APIParameter.cookie(this.name, @@ -129,16 +130,16 @@ class APIParameter extends APIObject { this.schema, this.content, this.style, - bool required, - bool deprecated, - bool allowEmptyValue, - bool explode, - bool allowReserved}) { - this.isRequired = required; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; + bool? isRequired, + bool? deprecated, + bool? allowEmptyValue, + bool? explode, + bool? allowReserved}) { + this.isRequired = isRequired ?? false; + this.isDeprecated = deprecated ?? false; + this.allowEmptyValue = allowEmptyValue ?? false; + this.allowReserved = allowReserved ?? false; + this.explode = explode ?? false; this.location = APIParameterLocation.cookie; } @@ -148,83 +149,84 @@ 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); 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; + bool get isDeprecated => _deprecated; 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; + 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) { _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) { _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) { _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 @@ -234,29 +236,30 @@ class APIParameter extends APIObject { name = object.decode("name"); description = object.decode("description"); - location = APIParameterLocationCodec.decode(object.decode("in")); + location = APIParameterLocationCodec.decode(object.decode("in"))!; _required = object.decode("required"); _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); object.encode("description", description); - object.encode("in", APIParameterLocationCodec.encode(location)); + object.encode("in", APIParameterLocationCodec.encode(location!)); if (location == APIParameterLocation.path) { object.encode("required", true); diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index df52e4b..2b3213e 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -10,29 +10,33 @@ class APIPath extends APIObject { APIPath({this.summary, this.description, this.parameters, this.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 +51,26 @@ 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()); }); } @@ -66,7 +81,7 @@ class APIPath extends APIObject { object.encode("description", description); object.encodeObjects("parameters", parameters); - operations.forEach((opName, op) { + operations!.forEach((opName, op) { object.encodeObject(opName.toLowerCase(), op); }); } diff --git a/lib/src/v3/request_body.dart b/lib/src/v3/request_body.dart index 1f54bb5..dcf9c8f 100644 --- a/lib/src/v3/request_body.dart +++ b/lib/src/v3/request_body.dart @@ -6,14 +6,17 @@ import 'package:open_api/src/v3/schema.dart'; 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; + 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] = new APIMediaType(schema: schema); + prev![elem] = APIMediaType(schema: schema); return prev; }); } @@ -21,37 +24,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()); + 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..2cbde39 100644 --- a/lib/src/v3/response.dart +++ b/lib/src/v3/response.dart @@ -7,9 +7,11 @@ import 'package:open_api/src/v3/schema.dart'; 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}) { + APIResponse.schema(this.description, APISchemaObject schema, + {Iterable contentTypes: const ["application/json"], + this.headers}) { content = contentTypes.fold({}, (prev, elem) { - prev[elem] = new APIMediaType(schema: schema); + 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,10 +38,10 @@ 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) { + void addHeader(String name, APIHeader? header) { headers ??= {}; - if (!headers.containsKey(name)) { - headers[name] = header; + if (!headers!.containsKey(name)) { + headers![name] = header; } } @@ -49,23 +51,21 @@ 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) { + void addContent(String contentType, APISchemaObject? bodyObject) { content ??= {}; final key = contentType; - final existingContent = content[key]; + 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); + schema!.oneOf!.add(bodyObject); } else { - final container = new APISchemaObject()..oneOf = [ - schema, bodyObject - ]; + final container = APISchemaObject()..oneOf = [schema, bodyObject]; existingContent.schema = container; } } @@ -74,18 +74,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..427af54 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -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? isRequired; /// 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,23 +196,23 @@ 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; @@ -233,10 +241,10 @@ class APISchemaObject extends APIObject { _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)}; @@ -259,18 +267,18 @@ class APISchemaObject extends APIObject { enumerated = object.decode("enum"); minProperties = object.decode("minProperties"); maxProperties = object.decode("maxProperties"); - required = object.decode("required"); + isRequired = object.decode("required"); // - 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()); + type = APITypeCodec.decode(object.decode("type"))!; + 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"); @@ -314,11 +323,11 @@ class APISchemaObject extends APIObject { object.encode("enum", enumerated); object.encode("minProperties", minProperties); object.encode("maxProperties", maxProperties); - object.encode("required", required); + object.encode("required", isRequired); // - object.encode("type", APITypeCodec.encode(type)); + object.encode("type", APITypeCodec.encode(type!)); object.encodeObjects("allOf", allOf); object.encodeObjects("anyOf", anyOf); object.encodeObjects("oneOf", oneOf); @@ -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..914d24f 100644 --- a/lib/src/v3/security.dart +++ b/lib/src/v3/security.dart @@ -7,7 +7,7 @@ import 'package:open_api/src/v3/parameter.dart'; enum APISecuritySchemeType { apiKey, http, oauth2, openID } class APISecuritySchemeTypeCodec { - static APISecuritySchemeType decode(String type) { + static APISecuritySchemeType? decode(String type) { switch (type) { case "apiKey": return APISecuritySchemeType.apiKey; @@ -17,11 +17,12 @@ class APISecuritySchemeTypeCodec { return APISecuritySchemeType.oauth2; case "openID": return APISecuritySchemeType.openID; + default: + return null; } - return null; } - static String encode(APISecuritySchemeType type) { + static String? encode(APISecuritySchemeType type) { switch (type) { case APISecuritySchemeType.apiKey: return "apiKey"; @@ -31,8 +32,9 @@ class APISecuritySchemeTypeCodec { return "oauth2"; case APISecuritySchemeType.openID: return "openID"; + default: + return null; } - return null; } } @@ -45,63 +47,65 @@ class APISecurityScheme extends APIObject { 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); - type = APISecuritySchemeTypeCodec.decode(object.decode("type")); + type = APISecuritySchemeTypeCodec.decode(object.decode("type"))!; description = object.decode("description"); switch (type) { @@ -113,7 +117,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: @@ -127,6 +132,9 @@ class APISecurityScheme extends APIObject { connectURL = object.decode("openIdConnectUrl"); } break; + default: + throw ArgumentError( + "APISecurityScheme must have non-null values for: 'type'."); } } @@ -134,28 +142,30 @@ 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)); + object.encode("type", APISecuritySchemeTypeCodec.encode(type!)); object.encode("description", description); switch (type) { 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'."); } object.encode("name", name); - object.encode("in", APIParameterLocationCodec.encode(location)); + object.encode("in", APIParameterLocationCodec.encode(location!)); } break; 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 +174,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 +185,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 +201,34 @@ 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 +247,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")); } } @@ -246,12 +265,12 @@ class APISecurityRequirement extends APIObject { /// 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); - requirements.forEach((key, value) { + requirements?.forEach((key, value) { object.encode(key, value); }); } @@ -260,8 +279,8 @@ class APISecurityRequirement extends APIObject { super.decode(object); object.keys.forEach((key) { - final req = new List.from(object.decode(key)); - requirements[key] = req; + 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..7944cac 100644 --- a/lib/src/v3/server.dart +++ b/lib/src/v3/server.dart @@ -4,20 +4,21 @@ import 'package:open_api/src/object.dart'; 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; + late 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); @@ -25,14 +26,15 @@ class APIServerDescription extends APIObject { url = object.decode("url"); 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 +46,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 +74,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..67c178a 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/pubspec.yaml b/pubspec.yaml index e459a45..3b2c1a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,11 +5,11 @@ homepage: https://github.com/stablekernel/open-api-dart author: stable|kernel environment: - sdk: ">=2.0.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: - meta: ^1.1.5 - codable: ^1.0.0 + codable: + git: https://github.com/j4qfrost/dart-codable.git dev_dependencies: - test: ^1.3.0 \ No newline at end of file + test: ^1.16.4 \ No newline at end of file diff --git a/test/v2_test.dart b/test/v2_test.dart index 97b300f..2215fd6 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -8,79 +8,90 @@ 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.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")); - expect(schema.required, ["versions", "serverAddressByClientCIDRs"]); - expect(schema.properties["serverAddressByClientCIDRs"].items.properties["clientCIDR"].description, contains("The CIDR")); + 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.isRequired, ["versions", "serverAddressByClientCIDRs"]); + 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()), (object) => object.runtimeType is String); }); }); } diff --git a/test/v3_test.dart b/test/v3_test.dart index 2eb4bea..17ad24b 100644 --- a/test/v3_test.dart +++ b/test/v3_test.dart @@ -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,175 @@ 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); + expect(p.operations!.length, 2); - final getOp = p.operations["get"]; - final getParams = getOp.parameters; + final getOp = p.operations!["get"]; + 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); + expect(doc!.security, isNull); }); }); 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 +227,121 @@ 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); }); - });; + }); + ; } From 89d2c386e7d5ba5512646ee034807a9140b933d4 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 00:38:47 -0800 Subject: [PATCH 02/15] update travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 966f6a3a5ebc938bdf363cd73edfa9886ad3144a Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 01:09:06 -0800 Subject: [PATCH 03/15] check null type return --- lib/src/v2/document.dart | 8 ++++---- lib/src/v3/metadata.dart | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/v2/document.dart b/lib/src/v2/document.dart index 5d50ebe..b46bf8d 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -52,10 +52,10 @@ class APIDocument extends APIObject { version = object["swagger"]; host = object["host"]; basePath = object["basePath"]; - schemes = object["schemes"]; - consumes = object["consumes"]; - produces = object["produces"]; - security = object["security"]; + schemes = object["schemes"] ?? []; + consumes = object["consumes"] ?? []; + produces = object["produces"] ?? []; + security = object["security"] ?? []; info = object.decodeObject("info", () => APIInfo())!; tags = object.decodeObjects("tags", () => APITag())!; diff --git a/lib/src/v3/metadata.dart b/lib/src/v3/metadata.dart index 206d822..3bf12fd 100644 --- a/lib/src/v3/metadata.dart +++ b/lib/src/v3/metadata.dart @@ -41,7 +41,7 @@ class APIInfo extends APIObject { title = object.decode("title"); description = object.decode("description"); - termsOfServiceURL = object.decode("termsOfService"); + termsOfServiceURL = object.decode("termsOfService") as Uri?; contact = object.decodeObject("contact", () => APIContact()); license = object.decodeObject("license", () => APILicense.empty()); version = object.decode("version"); From 322ebec8e9ab537fef77da68f218c36c10f80eb7 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 01:57:11 -0800 Subject: [PATCH 04/15] test large configs --- lib/src/v2/document.dart | 40 +++++++++++++++++++-------------------- lib/src/v2/metadata.dart | 12 +++++++----- lib/src/v2/operation.dart | 22 ++++++++++----------- lib/src/v2/property.dart | 8 ++++---- lib/src/v2/response.dart | 2 +- lib/src/v2/schema.dart | 2 +- lib/src/v2/types.dart | 8 ++++---- lib/src/v3/encoding.dart | 4 ++-- lib/src/v3/metadata.dart | 8 ++++++-- lib/src/v3/operation.dart | 2 +- lib/src/v3/parameter.dart | 10 +++++----- lib/src/v3/path.dart | 3 +-- lib/src/v3/schema.dart | 6 +++--- lib/src/v3/server.dart | 6 ++++-- lib/src/v3/types.dart | 4 ++-- test/v2_test.dart | 28 +++++++++++++-------------- test/v3_test.dart | 1 - 17 files changed, 86 insertions(+), 80 deletions(-) diff --git a/lib/src/v2/document.dart b/lib/src/v2/document.dart index b46bf8d..5d2f094 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -22,17 +22,17 @@ class APIDocument extends APIObject { 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); @@ -52,20 +52,20 @@ class APIDocument extends APIObject { version = object["swagger"]; host = object["host"]; basePath = object["basePath"]; - schemes = object["schemes"] ?? []; - consumes = object["consumes"] ?? []; - produces = object["produces"] ?? []; + schemes = object["schemes"]; + consumes = object["consumes"]; + produces = object["produces"]; security = object["security"] ?? []; - 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())!; + info = object.decodeObject("info", () => APIInfo()) ?? 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())!; + object.decodeObjectMap("definitions", () => APISchemaObject()); securityDefinitions = object.decodeObjectMap( - "securityDefinitions", () => APISecurityScheme())!; + "securityDefinitions", () => APISecurityScheme()); } void encode(KeyedArchive object) { diff --git a/lib/src/v2/metadata.dart b/lib/src/v2/metadata.dart index c9f0fac..cf2e3de 100644 --- a/lib/src/v2/metadata.dart +++ b/lib/src/v2/metadata.dart @@ -6,9 +6,9 @@ class APIInfo extends APIObject { APIInfo(); String title = "API"; - String description = "Description"; - String version = "1.0"; - String termsOfServiceURL = ""; + String? description = "Description"; + String? version = "1.0"; + String? termsOfServiceURL = ""; APIContact contact = APIContact(); APILicense license = APILicense(); @@ -18,8 +18,10 @@ class APIInfo extends APIObject { title = object.decode("title"); description = object.decode("description"); termsOfServiceURL = object.decode("termsOfService"); - contact = object.decodeObject("contact", () => APIContact())!; - license = object.decodeObject("license", () => APILicense())!; + contact = + object.decodeObject("contact", () => APIContact()) ?? APIContact(); + license = + object.decodeObject("license", () => APILicense()) ?? APILicense(); version = object.decode("version"); } diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 2d64a6b..6cffd47 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -21,26 +21,26 @@ class APIOperation extends APIObject { 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); tags = object.decode("tags"); - summary = object.decode("summary"); - description = object.decode("description"); + summary = object.decode("summary") ?? ""; + description = object.decode("description") ?? ""; id = object.decode("operationId"); consumes = object.decode("consumes"); produces = object.decode("produces"); deprecated = object.decode("deprecated") ?? false; - parameters = object.decodeObjects("parameters", () => APIParameter())!; - responses = object.decodeObjectMap("responses", () => 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/property.dart b/lib/src/v2/property.dart index 4d87fe9..73ebcba 100644 --- a/lib/src/v2/property.dart +++ b/lib/src/v2/property.dart @@ -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; @@ -27,7 +27,7 @@ class APICollectionFormatCodec { } } - static String? encode(APICollectionFormat location) { + static String? encode(APICollectionFormat? location) { switch (location) { case APICollectionFormat.csv: return "csv"; @@ -97,10 +97,10 @@ class APIProperty extends APIObject { void encode(KeyedArchive object) { super.encode(object); - object.encode("type", APITypeCodec.encode(type!)); + object.encode("type", APITypeCodec.encode(type)); object.encode("format", format); object.encode( - "collectionFormat", APICollectionFormatCodec.encode(collectionFormat!)); + "collectionFormat", APICollectionFormatCodec.encode(collectionFormat)); object.encode("default", defaultValue); object.encode("maximum", maximum); object.encode("exclusiveMaximum", exclusiveMaximum); diff --git a/lib/src/v2/response.dart b/lib/src/v2/response.dart index 161f074..77afad6 100644 --- a/lib/src/v2/response.dart +++ b/lib/src/v2/response.dart @@ -15,7 +15,7 @@ class APIResponse extends APIObject { description = object.decode("description"); schema = object.decodeObject("schema", () => APISchemaObject()); - headers = object.decodeObjectMap("headers", () => APIHeader())!; + headers = object.decodeObjectMap("headers", () => APIHeader()) ?? {}; } void encode(KeyedArchive object) { diff --git a/lib/src/v2/schema.dart b/lib/src/v2/schema.dart index 9f1dec6..a7daff8 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -9,7 +9,7 @@ class APISchemaObject extends APIProperty { String? title; String? description; String? example; - List isRequired = []; + List? isRequired = []; bool readOnly = false; /// Valid when type == array diff --git a/lib/src/v2/types.dart b/lib/src/v2/types.dart index 6c22a18..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/encoding.dart b/lib/src/v3/encoding.dart index ad3c2f8..4efe8a4 100644 --- a/lib/src/v3/encoding.dart +++ b/lib/src/v3/encoding.dart @@ -56,8 +56,8 @@ class APIEncoding extends APIObject { contentType = object.decode("contentType"); headers = object.decodeObjectMap("headers", () => APIHeader()); - _allowReserved = object.decode("allowReserved"); - _explode = object.decode("explode"); + _allowReserved = object.decode("allowReserved") ?? false; + _explode = object.decode("explode") ?? false; style = object.decode("style"); } diff --git a/lib/src/v3/metadata.dart b/lib/src/v3/metadata.dart index 3bf12fd..cf5f77a 100644 --- a/lib/src/v3/metadata.dart +++ b/lib/src/v3/metadata.dart @@ -41,7 +41,9 @@ class APIInfo extends APIObject { title = object.decode("title"); description = object.decode("description"); - termsOfServiceURL = object.decode("termsOfService") as Uri?; + 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"); @@ -86,7 +88,9 @@ class APIContact extends APIObject { 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"); } diff --git a/lib/src/v3/operation.dart b/lib/src/v3/operation.dart index 455ccbb..0cc4b3f 100644 --- a/lib/src/v3/operation.dart +++ b/lib/src/v3/operation.dart @@ -150,7 +150,7 @@ class APIOperation extends APIObject { object.decodeObject("requestBody", () => APIRequestBody.empty()); responses = object.decodeObjectMap("responses", () => APIResponse.empty()); callbacks = object.decodeObjectMap("callbacks", () => APICallback()); - _deprecated = object.decode("deprecated"); + _deprecated = object.decode("deprecated") ?? false; security = object.decodeObjects("security", () => APISecurityRequirement.empty()); servers = diff --git a/lib/src/v3/parameter.dart b/lib/src/v3/parameter.dart index f39f410..837144a 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -239,13 +239,13 @@ class APIParameter extends APIObject { location = APIParameterLocationCodec.decode(object.decode("in"))!; _required = object.decode("required"); - _deprecated = object.decode("deprecated"); - _allowEmptyValue = object.decode("allowEmptyValue"); + _deprecated = object.decode("deprecated") ?? false; + _allowEmptyValue = object.decode("allowEmptyValue") ?? false; - schema = object.decodeObject("schema", () => APISchemaObject())!; + schema = object.decodeObject("schema", () => APISchemaObject()); style = object.decode("style"); - _explode = object.decode("explode"); - _allowReserved = object.decode("allowReserved"); + _explode = object.decode("explode") ?? false; + _allowReserved = object.decode("allowReserved") ?? false; content = object.decodeObjectMap("content", () => APIMediaType()); } diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index 2b3213e..70362f5 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -51,8 +51,7 @@ class APIPath extends APIObject { summary = object.decode("summary"); description = object.decode("description"); - parameters = - object.decodeObjects("parameters", () => APIParameter.empty())!; + parameters = object.decodeObjects("parameters", () => APIParameter.empty()); final methodNames = [ "get", diff --git a/lib/src/v3/schema.dart b/lib/src/v3/schema.dart index 427af54..8f3b8e2 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -187,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? isRequired; + List? isRequired; /// The value of this keyword MUST be an array. This array SHOULD have /// at least one element. Elements in the array SHOULD be unique. @@ -271,7 +271,7 @@ class APISchemaObject extends APIObject { // - type = APITypeCodec.decode(object.decode("type"))!; + type = APITypeCodec.decode(object.decode("type")); allOf = object.decodeObjects("allOf", () => APISchemaObject()); anyOf = object.decodeObjects("anyOf", () => APISchemaObject()); oneOf = object.decodeObjects("oneOf", () => APISchemaObject()); @@ -327,7 +327,7 @@ class APISchemaObject extends APIObject { // - object.encode("type", APITypeCodec.encode(type!)); + object.encode("type", APITypeCodec.encode(type)); object.encodeObjects("allOf", allOf); object.encodeObjects("anyOf", anyOf); object.encodeObjects("oneOf", oneOf); diff --git a/lib/src/v3/server.dart b/lib/src/v3/server.dart index 7944cac..4bf18e5 100644 --- a/lib/src/v3/server.dart +++ b/lib/src/v3/server.dart @@ -8,7 +8,7 @@ class APIServerDescription extends APIObject { /// 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}. - late Uri url; + Uri? url; /// An optional string describing the host designated by the URL. /// @@ -23,7 +23,9 @@ class APIServerDescription extends APIObject { 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", () => APIServerVariable.empty()); diff --git a/lib/src/v3/types.dart b/lib/src/v3/types.dart index 67c178a..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; @@ -20,7 +20,7 @@ class APITypeCodec { } } - static String? encode(APIType type) { + static String? encode(APIType? type) { switch (type) { case APIType.string: return "string"; diff --git a/test/v2_test.dart b/test/v2_test.dart index 2215fd6..c519f71 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -40,46 +40,46 @@ void main() { }); 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) { + 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")); expect(getNamespace.consumes, ["*/*"]); expect(getNamespace.produces, contains("application/json")); expect(getNamespace.produces, contains("application/yaml")); - expect(getNamespace.parameters.length, 8); + expect(getNamespace.parameters!.length, 8); expect( - getNamespace.parameters + getNamespace.parameters! .firstWhere((p) => p!.name == "limit")! .location, APIParameterLocation.query); expect( - getNamespace.parameters.firstWhere((p) => p!.name == "limit")!.type, + getNamespace.parameters!.firstWhere((p) => p!.name == "limit")!.type, APIType.integer); - expect(getNamespace.responses.keys, contains("401")); - expect(getNamespace.responses.keys, contains("200")); + 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!.length, 1); + expect(postNamespace.parameters!.first!.name, "body"); expect( - postNamespace.parameters.first!.location, APIParameterLocation.body); + postNamespace.parameters!.first!.location, APIParameterLocation.body); }); test("Sample - Reference", () { - var apiPath = doc!.paths["/api/"]; + var apiPath = doc!.paths!["/api/"]; var apiPathGet = apiPath!.operations["get"]; - var response = apiPathGet!.responses["200"]; + var response = apiPathGet!.responses!["200"]; var schema = response!.schema; expect(schema!.description, contains("APIVersions lists the")); expect(schema.isRequired, ["versions", "serverAddressByClientCIDRs"]); diff --git a/test/v3_test.dart b/test/v3_test.dart index 17ad24b..39052be 100644 --- a/test/v3_test.dart +++ b/test/v3_test.dart @@ -343,5 +343,4 @@ void main() { 2); }); }); - ; } From aa9c66c38cfc926a41d01657fac4c6f8927d3e0a Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 02:18:23 -0800 Subject: [PATCH 05/15] test kube config --- lib/src/v2/operation.dart | 12 +++--- lib/src/v2/parameter.dart | 12 +++--- lib/src/v2/schema.dart | 4 +- lib/src/v3/encoding.dart | 20 +++++----- lib/src/v3/operation.dart | 10 ++--- lib/src/v3/parameter.dart | 78 +++++++++++++++++++-------------------- lib/src/v3/schema.dart | 16 ++++---- 7 files changed, 76 insertions(+), 76 deletions(-) diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 6cffd47..7d7c469 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -19,12 +19,12 @@ class APIOperation extends APIObject { String summary = ""; String description = ""; String? id; - bool deprecated = false; + bool? deprecated; - List tags = []; - List schemes = []; - List consumes = []; - List produces = []; + List? tags = []; + List? schemes = []; + List? consumes = []; + List? produces = []; List? parameters = []; List>?>? security = []; Map? responses = {}; @@ -38,7 +38,7 @@ class APIOperation extends APIObject { id = object.decode("operationId"); consumes = object.decode("consumes"); produces = object.decode("produces"); - deprecated = object.decode("deprecated") ?? false; + deprecated = object.decode("deprecated"); parameters = object.decodeObjects("parameters", () => APIParameter()); responses = object.decodeObjectMap("responses", () => APIResponse()); schemes = object.decode("schemes"); diff --git a/lib/src/v2/parameter.dart b/lib/src/v2/parameter.dart index 0fa22e7..099a24c 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -48,14 +48,14 @@ class APIParameter extends APIProperty { String? name; String? description; - bool required = false; + bool? isRequired = false; APIParameterLocation? location; // Valid if location is body. APISchemaObject? schema; // Valid if location is not body. - bool allowEmptyValue = false; + bool? allowEmptyValue = false; APIProperty? items; void decode(KeyedArchive json) { @@ -63,16 +63,16 @@ class APIParameter extends APIProperty { 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"); } if (location == APIParameterLocation.body) { schema = json.decodeObject("schema", () => APISchemaObject()); } else { super.decode(json); - allowEmptyValue = json.decode("allowEmptyValue") ?? false; + allowEmptyValue = json.decode("allowEmptyValue"); if (type == APIType.array) { items = json.decodeObject("items", () => APIProperty()); } @@ -83,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/schema.dart b/lib/src/v2/schema.dart index a7daff8..3e55ab9 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -10,7 +10,7 @@ class APISchemaObject extends APIProperty { String? description; String? example; List? isRequired = []; - bool readOnly = false; + bool? readOnly = false; /// Valid when type == array APISchemaObject? items; @@ -40,7 +40,7 @@ class APISchemaObject extends APIProperty { description = json.decode("description"); isRequired = json.decode("required"); example = json.decode("example"); - readOnly = json.decode("readOnly") ?? false; + readOnly = json.decode("readOnly"); items = json.decodeObject("items", () => APISchemaObject()); additionalProperties = diff --git a/lib/src/v3/encoding.dart b/lib/src/v3/encoding.dart index 4efe8a4..5d19c3a 100644 --- a/lib/src/v3/encoding.dart +++ b/lib/src/v3/encoding.dart @@ -10,8 +10,8 @@ class APIEncoding extends APIObject { this.style, bool? allowReserved, bool? explode}) { - this.allowReserved = allowReserved ?? false; - this.explode = explode ?? false; + this.allowReserved = allowReserved; + this.explode = explode; } APIEncoding.empty(); @@ -29,22 +29,22 @@ class APIEncoding extends APIObject { /// 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; - set allowReserved(bool f) { + bool? get allowReserved => _allowReserved; + set allowReserved(bool? f) { _allowReserved = f; } - bool _allowReserved = false; + 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; - set explode(bool f) { + bool? get explode => _explode; + set explode(bool? f) { _explode = f; } - bool _explode = false; + bool? _explode = false; /// Describes how a specific property value will be serialized depending on its type. /// @@ -56,8 +56,8 @@ class APIEncoding extends APIObject { contentType = object.decode("contentType"); headers = object.decodeObjectMap("headers", () => APIHeader()); - _allowReserved = object.decode("allowReserved") ?? false; - _explode = object.decode("explode") ?? false; + _allowReserved = object.decode("allowReserved"); + _explode = object.decode("explode"); style = object.decode("style"); } diff --git a/lib/src/v3/operation.dart b/lib/src/v3/operation.dart index 0cc4b3f..2574077 100644 --- a/lib/src/v3/operation.dart +++ b/lib/src/v3/operation.dart @@ -22,7 +22,7 @@ class APIOperation extends APIObject { this.requestBody, this.callbacks, bool? deprecated}) { - isDeprecated = deprecated ?? false; + isDeprecated = deprecated; } /// A list of tags for API documentation control. @@ -76,13 +76,13 @@ class APIOperation extends APIObject { /// Declares this operation to be deprecated. /// /// Consumers SHOULD refrain from usage of the declared operation. Default value is false. - bool get isDeprecated => _deprecated; + 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) => @@ -150,7 +150,7 @@ class APIOperation extends APIObject { object.decodeObject("requestBody", () => APIRequestBody.empty()); responses = object.decodeObjectMap("responses", () => APIResponse.empty()); callbacks = object.decodeObjectMap("callbacks", () => APICallback()); - _deprecated = object.decode("deprecated") ?? false; + _deprecated = object.decode("deprecated"); security = object.decodeObjects("security", () => APISecurityRequirement.empty()); servers = diff --git a/lib/src/v3/parameter.dart b/lib/src/v3/parameter.dart index 837144a..895fd24 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -77,11 +77,11 @@ class APIParameter extends APIObject { bool? allowEmptyValue, bool? explode, bool? allowReserved}) { - this.isRequired = isRequired ?? false; - this.isDeprecated = deprecated ?? false; - this.allowEmptyValue = allowEmptyValue ?? false; - this.allowReserved = allowReserved ?? false; - this.explode = explode ?? false; + this.isRequired = isRequired; + this.isDeprecated = deprecated; + this.allowEmptyValue = allowEmptyValue; + this.allowReserved = allowReserved; + this.explode = explode; } APIParameter.header(this.name, @@ -94,11 +94,11 @@ class APIParameter extends APIObject { bool? allowEmptyValue, bool? explode, bool? allowReserved}) { - this.isRequired = isRequired ?? false; - this.isDeprecated = deprecated ?? false; - this.allowEmptyValue = allowEmptyValue ?? false; - this.allowReserved = allowReserved ?? false; - this.explode = explode ?? false; + this.isRequired = isRequired; + this.isDeprecated = deprecated; + this.allowEmptyValue = allowEmptyValue; + this.allowReserved = allowReserved; + this.explode = explode; this.location = APIParameterLocation.header; } @@ -112,11 +112,11 @@ class APIParameter extends APIObject { bool? allowEmptyValue, bool? explode, bool? allowReserved}) { - this.isRequired = isRequired ?? false; - this.isDeprecated = deprecated ?? false; - this.allowEmptyValue = allowEmptyValue ?? false; - this.allowReserved = allowReserved ?? false; - this.explode = explode ?? false; + this.isRequired = isRequired; + this.isDeprecated = deprecated; + this.allowEmptyValue = allowEmptyValue; + this.allowReserved = allowReserved; + this.explode = explode; this.location = APIParameterLocation.query; } @@ -135,11 +135,11 @@ class APIParameter extends APIObject { bool? allowEmptyValue, bool? explode, bool? allowReserved}) { - this.isRequired = isRequired ?? false; - this.isDeprecated = deprecated ?? false; - this.allowEmptyValue = allowEmptyValue ?? false; - this.allowReserved = allowReserved ?? false; - this.explode = explode ?? false; + this.isRequired = isRequired; + this.isDeprecated = deprecated; + this.allowEmptyValue = allowEmptyValue; + this.allowReserved = allowReserved; + this.explode = explode; this.location = APIParameterLocation.cookie; } @@ -159,23 +159,23 @@ class APIParameter extends APIObject { /// 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 => + bool? get isRequired => (location == APIParameterLocation.path ? true : _required); - set isRequired(bool f) { + set isRequired(bool? f) { _required = f; } - bool _required = false; + bool? _required = false; /// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage. - bool get isDeprecated => _deprecated; + bool? get isDeprecated => _deprecated; - set isDeprecated(bool f) { + set isDeprecated(bool? f) { _deprecated = f; } - bool _deprecated = false; + bool? _deprecated = false; /// The location of the parameter. /// @@ -188,13 +188,13 @@ class APIParameter extends APIObject { // 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; + bool? get allowEmptyValue => _allowEmptyValue; - set allowEmptyValue(bool f) { + set allowEmptyValue(bool? f) { _allowEmptyValue = f; } - bool _allowEmptyValue = false; + bool? _allowEmptyValue = false; /// Describes how the parameter value will be serialized depending on the type of the parameter value. /// @@ -204,24 +204,24 @@ class APIParameter extends APIObject { /// 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; + bool? get explode => _explode; - set explode(bool f) { + set explode(bool? f) { _explode = f; } - bool _explode = false; + 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; + bool? get allowReserved => _allowReserved; - set allowReserved(bool f) { + set allowReserved(bool? f) { _allowReserved = f; } - bool _allowReserved = false; + bool? _allowReserved = false; /// A map containing the representations for the parameter. /// @@ -239,13 +239,13 @@ class APIParameter extends APIObject { location = APIParameterLocationCodec.decode(object.decode("in"))!; _required = object.decode("required"); - _deprecated = object.decode("deprecated") ?? false; - _allowEmptyValue = object.decode("allowEmptyValue") ?? false; + _deprecated = object.decode("deprecated"); + _allowEmptyValue = object.decode("allowEmptyValue"); schema = object.decodeObject("schema", () => APISchemaObject()); style = object.decode("style"); - _explode = object.decode("explode") ?? false; - _allowReserved = object.decode("allowReserved") ?? false; + _explode = object.decode("explode"); + _allowReserved = object.decode("allowReserved"); content = object.decodeObjectMap("content", () => APIMediaType()); } diff --git a/lib/src/v3/schema.dart b/lib/src/v3/schema.dart index 8f3b8e2..4d00be1 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -215,29 +215,29 @@ class APISchemaObject extends APIObject { String? format; dynamic defaultValue; - bool get isNullable => _nullable ?? false; + bool? get isNullable => _nullable; - set isNullable(bool n) { + set isNullable(bool? n) { _nullable = n; } // APIDiscriminator discriminator; - bool get isReadOnly => _readOnly ?? false; + bool? get isReadOnly => _readOnly; - set isReadOnly(bool n) { + set isReadOnly(bool? n) { _readOnly = n; } - bool get isWriteOnly => _writeOnly ?? false; + bool? get isWriteOnly => _writeOnly; - set isWriteOnly(bool n) { + set isWriteOnly(bool? n) { _writeOnly = n; } - bool get isDeprecated => _deprecated ?? false; + bool? get isDeprecated => _deprecated; - set isDeprecated(bool n) { + set isDeprecated(bool? n) { _deprecated = n; } From c237f9a2428683097a1ba6e5b920d8eed931c328 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 02:32:13 -0800 Subject: [PATCH 06/15] check changes --- lib/src/v2/document.dart | 6 +++--- lib/src/v2/metadata.dart | 10 ++++------ lib/src/v2/operation.dart | 8 ++++---- lib/src/v2/response.dart | 6 +++--- test/v2_test.dart | 4 ++-- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/src/v2/document.dart b/lib/src/v2/document.dart index 5d2f094..c79948b 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -18,7 +18,7 @@ class APIDocument extends APIObject { } String version = "2.0"; - APIInfo info = APIInfo(); + APIInfo? info = APIInfo(); String? host; String? basePath; @@ -55,9 +55,9 @@ class APIDocument extends APIObject { schemes = object["schemes"]; consumes = object["consumes"]; produces = object["produces"]; - security = object["security"] ?? []; + security = object["security"]; - info = object.decodeObject("info", () => APIInfo()) ?? APIInfo(); + info = object.decodeObject("info", () => APIInfo()); tags = object.decodeObjects("tags", () => APITag()); paths = object.decodeObjectMap("paths", () => APIPath()); responses = object.decodeObjectMap("responses", () => APIResponse()); diff --git a/lib/src/v2/metadata.dart b/lib/src/v2/metadata.dart index cf2e3de..8b674d8 100644 --- a/lib/src/v2/metadata.dart +++ b/lib/src/v2/metadata.dart @@ -9,8 +9,8 @@ class APIInfo extends APIObject { String? description = "Description"; String? version = "1.0"; String? termsOfServiceURL = ""; - APIContact contact = APIContact(); - APILicense license = APILicense(); + APIContact? contact = APIContact(); + APILicense? license = APILicense(); void decode(KeyedArchive object) { super.decode(object); @@ -18,10 +18,8 @@ class APIInfo extends APIObject { title = object.decode("title"); description = object.decode("description"); termsOfServiceURL = object.decode("termsOfService"); - contact = - object.decodeObject("contact", () => APIContact()) ?? APIContact(); - license = - object.decodeObject("license", () => APILicense()) ?? APILicense(); + contact = object.decodeObject("contact", () => APIContact()); + license = object.decodeObject("license", () => APILicense()); version = object.decode("version"); } diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 7d7c469..f195615 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -16,8 +16,8 @@ class APIOperation extends APIObject { "security": cast.List(cast.Map(cast.String, cast.List(cast.String))), }; - String summary = ""; - String description = ""; + String? summary = ""; + String? description = ""; String? id; bool? deprecated; @@ -33,8 +33,8 @@ class APIOperation extends APIObject { super.decode(object); tags = object.decode("tags"); - summary = object.decode("summary") ?? ""; - description = object.decode("description") ?? ""; + summary = object.decode("summary"); + description = object.decode("description"); id = object.decode("operationId"); consumes = object.decode("consumes"); produces = object.decode("produces"); diff --git a/lib/src/v2/response.dart b/lib/src/v2/response.dart index 77afad6..8056706 100644 --- a/lib/src/v2/response.dart +++ b/lib/src/v2/response.dart @@ -6,16 +6,16 @@ import 'package:open_api/src/v2/schema.dart'; class APIResponse extends APIObject { APIResponse(); - String description = ""; + String? description = ""; APISchemaObject? schema; - Map headers = {}; + Map? headers = {}; void decode(KeyedArchive object) { super.decode(object); description = object.decode("description"); schema = object.decodeObject("schema", () => APISchemaObject()); - headers = object.decodeObjectMap("headers", () => APIHeader()) ?? {}; + headers = object.decodeObjectMap("headers", () => APIHeader()); } void encode(KeyedArchive object) { diff --git a/test/v2_test.dart b/test/v2_test.dart index c519f71..bba8d8d 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -23,8 +23,8 @@ void main() { test("Has all metadata", () { expect(doc!.version, "2.0"); - expect(doc!.info.title, "Kubernetes"); - expect(doc!.info.version, isNotNull); + expect(doc!.info!.title, "Kubernetes"); + expect(doc!.info!.version, isNotNull); expect(doc!.host, isNull); expect(doc!.basePath, isNull); expect(doc!.tags, isNull); From ea7a14696f570d05ae2f0ba60142d06333b5bd8f Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 02:50:15 -0800 Subject: [PATCH 07/15] add more nulls --- lib/src/v2/operation.dart | 4 ++-- lib/src/v2/parameter.dart | 8 ++++---- lib/src/v2/schema.dart | 2 +- lib/src/v2/security.dart | 4 ++-- lib/src/v3/components.dart | 8 +++----- lib/src/v3/parameter.dart | 4 ++-- lib/src/v3/security.dart | 4 ++-- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index f195615..0978b9d 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -19,7 +19,7 @@ class APIOperation extends APIObject { String? summary = ""; String? description = ""; String? id; - bool? deprecated; + bool? deprecated = false; List? tags = []; List? schemes = []; @@ -38,7 +38,7 @@ class APIOperation extends APIObject { id = object.decode("operationId"); consumes = object.decode("consumes"); produces = object.decode("produces"); - deprecated = object.decode("deprecated"); + deprecated = object.decode("deprecated") ?? false; parameters = object.decodeObjects("parameters", () => APIParameter()); responses = object.decodeObjectMap("responses", () => APIResponse()); schemes = object.decode("schemes"); diff --git a/lib/src/v2/parameter.dart b/lib/src/v2/parameter.dart index 099a24c..33842f9 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -7,7 +7,7 @@ import 'package:open_api/src/v2/types.dart'; 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; @@ -24,7 +24,7 @@ class APIParameterLocationCodec { } } - static String? encode(APIParameterLocation location) { + static String? encode(APIParameterLocation? location) { switch (location) { case APIParameterLocation.query: return "query"; @@ -65,14 +65,14 @@ class APIParameter extends APIProperty { if (location == APIParameterLocation.path) { isRequired = true; } else { - isRequired = json.decode("required"); + isRequired = json.decode("required") ?? false; } if (location == APIParameterLocation.body) { schema = json.decodeObject("schema", () => APISchemaObject()); } else { super.decode(json); - allowEmptyValue = json.decode("allowEmptyValue"); + allowEmptyValue = json.decode("allowEmptyValue") ?? false; if (type == APIType.array) { items = json.decodeObject("items", () => APIProperty()); } diff --git a/lib/src/v2/schema.dart b/lib/src/v2/schema.dart index 3e55ab9..3d5ff8d 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -40,7 +40,7 @@ class APISchemaObject extends APIProperty { description = json.decode("description"); isRequired = json.decode("required"); example = json.decode("example"); - readOnly = json.decode("readOnly"); + readOnly = json.decode("readOnly") ?? false; items = json.decodeObject("items", () => APISchemaObject()); additionalProperties = diff --git a/lib/src/v2/security.dart b/lib/src/v2/security.dart index 45cad67..90b3f84 100644 --- a/lib/src/v2/security.dart +++ b/lib/src/v2/security.dart @@ -11,7 +11,7 @@ enum APISecuritySchemeFlow { } class APISecuritySchemeFlowCodec { - static APISecuritySchemeFlow? decode(String flow) { + static APISecuritySchemeFlow? decode(String? flow) { switch (flow) { case "accessCode": return APISecuritySchemeFlow.authorizationCode; @@ -26,7 +26,7 @@ class APISecuritySchemeFlowCodec { } } - static String? encode(APISecuritySchemeFlow flow) { + static String? encode(APISecuritySchemeFlow? flow) { switch (flow) { case APISecuritySchemeFlow.authorizationCode: return "accessCode"; diff --git a/lib/src/v3/components.dart b/lib/src/v3/components.dart index fbc592e..fcfba66 100644 --- a/lib/src/v3/components.dart +++ b/lib/src/v3/components.dart @@ -78,11 +78,9 @@ class APIComponents extends APIObject { case "callbacks": namedMap = callbacks; break; - } - - if (namedMap == null) { - throw ArgumentError( - "Invalid reference URI: component type '${segments[1]}' does not exist."); + default: + throw ArgumentError( + "Invalid reference URI: component type '${segments[1]}' does not exist."); } return namedMap[segments.last]; diff --git a/lib/src/v3/parameter.dart b/lib/src/v3/parameter.dart index 895fd24..df82829 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -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; @@ -45,7 +45,7 @@ class APIParameterLocationCodec { } } - static String? encode(APIParameterLocation location) { + static String? encode(APIParameterLocation? location) { switch (location) { case APIParameterLocation.query: return "query"; diff --git a/lib/src/v3/security.dart b/lib/src/v3/security.dart index 914d24f..6f5ee8d 100644 --- a/lib/src/v3/security.dart +++ b/lib/src/v3/security.dart @@ -7,7 +7,7 @@ import 'package:open_api/src/v3/parameter.dart'; enum APISecuritySchemeType { apiKey, http, oauth2, openID } class APISecuritySchemeTypeCodec { - static APISecuritySchemeType? decode(String type) { + static APISecuritySchemeType? decode(String? type) { switch (type) { case "apiKey": return APISecuritySchemeType.apiKey; @@ -22,7 +22,7 @@ class APISecuritySchemeTypeCodec { } } - static String? encode(APISecuritySchemeType type) { + static String? encode(APISecuritySchemeType? type) { switch (type) { case APISecuritySchemeType.apiKey: return "apiKey"; From 789d1cedbf9b76aa29bc51e572163333310a6203 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 02:55:56 -0800 Subject: [PATCH 08/15] i am a dumb dumb --- test/v2_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/v2_test.dart b/test/v2_test.dart index bba8d8d..1f2885b 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -90,8 +90,7 @@ void main() { }); test("Can encode as JSON", () { - expect( - json.encode(doc!.asMap()), (object) => object.runtimeType is String); + expect(json.encode(doc!.asMap()), (object) => isA()); }); }); } From eff3ce3763969ed0110ecb745f2266d666f2fa3a Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 02:58:02 -0800 Subject: [PATCH 09/15] still dumb and should run locally --- test/v2_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/v2_test.dart b/test/v2_test.dart index 1f2885b..f56f66c 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -90,7 +90,7 @@ void main() { }); test("Can encode as JSON", () { - expect(json.encode(doc!.asMap()), (object) => isA()); + expect(json.encode(doc!.asMap()), isA()); }); }); } From 9cf53f00606604fd8f45a44e085fe91ede3ed36f Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 03:02:48 -0800 Subject: [PATCH 10/15] revert some bool fields --- lib/src/v2/operation.dart | 2 +- lib/src/v2/parameter.dart | 4 ++-- lib/src/v2/schema.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 0978b9d..1edbf91 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -19,7 +19,7 @@ class APIOperation extends APIObject { String? summary = ""; String? description = ""; String? id; - bool? deprecated = false; + bool deprecated = false; List? tags = []; List? schemes = []; diff --git a/lib/src/v2/parameter.dart b/lib/src/v2/parameter.dart index 33842f9..a6e14a6 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -48,14 +48,14 @@ class APIParameter extends APIProperty { String? name; String? description; - bool? isRequired = false; + bool isRequired = false; APIParameterLocation? location; // Valid if location is body. APISchemaObject? schema; // Valid if location is not body. - bool? allowEmptyValue = false; + bool allowEmptyValue = false; APIProperty? items; void decode(KeyedArchive json) { diff --git a/lib/src/v2/schema.dart b/lib/src/v2/schema.dart index 3d5ff8d..a7daff8 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -10,7 +10,7 @@ class APISchemaObject extends APIProperty { String? description; String? example; List? isRequired = []; - bool? readOnly = false; + bool readOnly = false; /// Valid when type == array APISchemaObject? items; From 278d80d66e434e024fc222f04f0efe8674fde13c Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 8 Mar 2021 00:17:31 -0800 Subject: [PATCH 11/15] containsPathParameters parameterNames nullable --- lib/src/v3/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index 70362f5..4ee6ac7 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -31,7 +31,7 @@ class APIPath extends APIObject { /// /// 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) { + bool containsPathParameters(List parameterNames) { final pathParams = parameters ?.where((p) => p?.location == APIParameterLocation.path) .map((p) => p?.name) From 5f0f37c3e7d326e0634e2d4af618142bd32ed011 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 8 Mar 2021 22:44:38 -0800 Subject: [PATCH 12/15] oops --- lib/src/v3/schema.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/v3/schema.dart b/lib/src/v3/schema.dart index 4d00be1..798ef59 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -215,7 +215,7 @@ class APISchemaObject extends APIObject { String? format; dynamic defaultValue; - bool? get isNullable => _nullable; + bool? get isNullable => _nullable ?? false; set isNullable(bool? n) { _nullable = n; @@ -223,19 +223,19 @@ class APISchemaObject extends APIObject { // APIDiscriminator discriminator; - bool? get isReadOnly => _readOnly; + bool? get isReadOnly => _readOnly ?? false; set isReadOnly(bool? n) { _readOnly = n; } - bool? get isWriteOnly => _writeOnly; + bool? get isWriteOnly => _writeOnly ?? false; set isWriteOnly(bool? n) { _writeOnly = n; } - bool? get isDeprecated => _deprecated; + bool? get isDeprecated => _deprecated ?? false; set isDeprecated(bool? n) { _deprecated = n; From d5e895e0189565715a9d133fc33ac1e4806749cc Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 8 Mar 2021 23:56:06 -0800 Subject: [PATCH 13/15] try non nullable --- lib/src/v3/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index 4ee6ac7..70362f5 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -31,7 +31,7 @@ class APIPath extends APIObject { /// /// 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) { + bool containsPathParameters(List parameterNames) { final pathParams = parameters ?.where((p) => p?.location == APIParameterLocation.path) .map((p) => p?.name) From 80ba7db4953b3209c971ca438e48f7968e088a2a Mon Sep 17 00:00:00 2001 From: Brett Sutton Date: Thu, 1 Apr 2021 15:48:49 +1100 Subject: [PATCH 14/15] work on nndb migration and migration to conduit_codable (#1) * Applied standard dart formatting. * Removed deprecated method. * removed deprecated option. * upgraded to new version of conduit_codable * renamed project to conduit_open_api * Added lint package and resolved all automated fixes. * manually fixed lints. * Completed work on migrating to conduit_codable and nnbd migration. * Released 1.0.0-b1 * removed deprecated authors field. * removed null test as should never occur. * changed components so that they return a non-nullable list. * Remove the curl commands as the unit tests now fetch the required files themselves. * restored the kubenetes test file as we found a v2 version. reverted some fields to be nullable as the document uses null to know that they were not present in the original document. --- .gitignore | 3 +- .travis.yml | 2 - CHANGELOG.md | 7 +- analysis_options.yaml | 16 +--- lib/src/object.dart | 11 ++- lib/src/util/list_helper.dart | 9 +++ lib/src/util/map_helper.dart | 16 ++++ lib/src/v2/document.dart | 47 +++++++----- lib/src/v2/header.dart | 8 +- lib/src/v2/metadata.dart | 23 ++++-- lib/src/v2/operation.dart | 26 ++++--- lib/src/v2/parameter.dart | 16 ++-- lib/src/v2/path.dart | 13 ++-- lib/src/v2/property.dart | 7 +- lib/src/v2/response.dart | 9 ++- lib/src/v2/schema.dart | 11 ++- lib/src/v2/security.dart | 22 +++--- lib/src/v3/callback.dart | 7 +- lib/src/v3/components.dart | 87 ++++++++++++--------- lib/src/v3/document.dart | 33 ++++---- lib/src/v3/encoding.dart | 42 +++++------ lib/src/v3/header.dart | 6 +- lib/src/v3/media_type.dart | 9 ++- lib/src/v3/metadata.dart | 31 +++++--- lib/src/v3/operation.dart | 45 +++++------ lib/src/v3/parameter.dart | 134 ++++++++++++--------------------- lib/src/v3/path.dart | 48 +++++++----- lib/src/v3/request_body.dart | 36 ++++----- lib/src/v3/response.dart | 15 ++-- lib/src/v3/schema.dart | 28 ++++--- lib/src/v3/security.dart | 48 +++++++----- lib/src/v3/server.dart | 20 +++-- lib/src/version/version.g.dart | 3 + lib/v2.dart | 20 ++--- lib/v3.dart | 34 ++++----- pubspec.yaml | 26 +++---- test/v2_test.dart | 60 +++++++++------ test/v3_test.dart | 99 +++++++++++++++--------- 38 files changed, 594 insertions(+), 483 deletions(-) create mode 100644 lib/src/util/list_helper.dart create mode 100644 lib/src/util/map_helper.dart create mode 100644 lib/src/version/version.g.dart diff --git a/.gitignore b/.gitignore index 86c9731..7cfb9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ doc/api/ .idea/ *.iml test/specs/kubernetes.json -test/specs/stripe.json \ No newline at end of file +test/specs/stripe.json +test/specs/petstore-simple.json diff --git a/.travis.yml b/.travis.yml index 3c6fabc..3669e02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,6 @@ dart: before_script: - mkdir test/specs - - curl https://raw.githubusercontent.com/kubernetes/kubernetes/f091073b0fb4d3a550e7f182eb5465338c8b7cbf/api/openapi-spec/swagger.json -o test/specs/kubernetes.json - - curl https://raw.githubusercontent.com/stripe/openapi/33a4d4d78b0844356a332c30645ec6807d0c2e3d/openapi/spec3.json -o test/specs/stripe.json jobs: include: diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c236f..5eb72d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.0.0-b1 +Completed work on migrating to conduit_codable and nnbd migration. +Added lint package and resolved all automated fixes. +renamed project to conduit_open_api +nnbd migration # Changelog ## 2.0.1 @@ -20,7 +25,7 @@ ## 1.0.0 - Adds support for OpenAPI 3.0 -- Splits Swagger (2.0) and OpenAPI (3.0) into 'package:open_api/v2.dart' and 'package:open_api/v3.dart'. +- Splits Swagger (2.0) and OpenAPI (3.0) into 'package:conduit_open_api/v2.dart' and 'package:conduit_open_api/v3.dart'. ## 0.9.1 diff --git a/analysis_options.yaml b/analysis_options.yaml index 97f0908..26128b9 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,15 +1 @@ -analyzer: - strong-mode: true -# exclude: -# - path/to/excluded/files/** - -# Lint rules and documentation, see http://dart-lang.github.io/linter/lints -linter: - rules: - - cancel_subscriptions - - hash_and_equals - - iterable_contains_unrelated_type - - list_remove_unrelated_type - - test_types_in_equals - - unrelated_type_equality_checks - - valid_regexps +include: package:lint/analysis_options.yaml \ No newline at end of file diff --git a/lib/src/object.dart b/lib/src/object.dart index effa9ba..612bc26 100644 --- a/lib/src/object.dart +++ b/lib/src/object.dart @@ -1,7 +1,5 @@ import 'package:meta/meta.dart'; -import 'package:codable/codable.dart'; - -export 'package:codable/codable.dart'; +import 'package:conduit_codable/conduit_codable.dart'; class APIObject extends Coding { Map extensions = {}; @@ -12,18 +10,19 @@ class APIObject extends Coding { super.decode(object); final extensionKeys = object.keys.where((k) => k.startsWith("x-")); - extensionKeys.forEach((key) { + for (final key in extensionKeys) { extensions[key] = object.decode(key); - }); + } } + @override @mustCallSuper void encode(KeyedArchive object) { final invalidKeys = extensions.keys .where((key) => !key.startsWith("x-")) .map((key) => "'$key'") .toList(); - if (invalidKeys.length > 0) { + if (invalidKeys.isNotEmpty) { throw ArgumentError( "extension keys must start with 'x-'. The following keys are invalid: ${invalidKeys.join(", ")}"); } diff --git a/lib/src/util/list_helper.dart b/lib/src/util/list_helper.dart new file mode 100644 index 0000000..9a26a0b --- /dev/null +++ b/lib/src/util/list_helper.dart @@ -0,0 +1,9 @@ +/// Remove any null entries from the list and convert the type. +/// In reality I don't think the list can have nulls but we still +/// need the conversion. +List? removeNullsFromList(List? list) { + if (list == null) return null; + + // remove nulls and convert to List + return List.from(list.where((c) => c != null)); +} diff --git a/lib/src/util/map_helper.dart b/lib/src/util/map_helper.dart new file mode 100644 index 0000000..97e1928 --- /dev/null +++ b/lib/src/util/map_helper.dart @@ -0,0 +1,16 @@ +/// Remove any entries with a null value from the list and convert the type. +Map removeNullsFromMap(Map? map) { + if (map == null) return {}; + + final fixed = {}; + + // remove nulls + for (final key in map.keys) { + final value = map[key]; + if (value != null) { + fixed[key] = value; + } + } + + return fixed; +} diff --git a/lib/src/v2/document.dart b/lib/src/v2/document.dart index c79948b..1cef8e3 100644 --- a/lib/src/v2/document.dart +++ b/lib/src/v2/document.dart @@ -1,11 +1,13 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_codable/cast.dart' as cast; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/util/list_helper.dart'; +import 'package:conduit_open_api/src/v2/metadata.dart'; +import 'package:conduit_open_api/src/v2/parameter.dart'; +import 'package:conduit_open_api/src/v2/path.dart'; +import 'package:conduit_open_api/src/v2/response.dart'; +import 'package:conduit_open_api/src/v2/schema.dart'; +import 'package:conduit_open_api/src/v2/security.dart'; /// Represents an OpenAPI 2.0 specification. class APIDocument extends APIObject { @@ -26,7 +28,7 @@ class APIDocument extends APIObject { List? schemes = []; List? consumes = []; List? produces = []; - List>?> security = []; + List>?>? security = []; Map? paths = {}; Map? responses = {}; @@ -40,22 +42,26 @@ class APIDocument extends APIObject { @override Map get castMap => { - "schemes": cast.List(cast.String), - "consumes": cast.List(cast.String), - "produces": cast.List(cast.String), - "security": cast.List(cast.Map(cast.String, cast.List(cast.String))) + "schemes": const cast.List(cast.string), + "consumes": const cast.List(cast.string), + "produces": const cast.List(cast.string), + "security": + const cast.List(cast.Map(cast.string, cast.List(cast.string))) }; + @override void decode(KeyedArchive object) { super.decode(object); - version = object["swagger"]; - host = object["host"]; - basePath = object["basePath"]; - schemes = object["schemes"]; - consumes = object["consumes"]; - produces = object["produces"]; - security = object["security"]; + version = object["swagger"] as String; + host = object["host"] as String?; + basePath = object["basePath"] as String?; + schemes = removeNullsFromList(object["schemes"] as List?); + + /// remove + consumes = removeNullsFromList(object["consumes"] as List?); + produces = removeNullsFromList(object["produces"] as List?); + security = object["security"] as List>?>; info = object.decodeObject("info", () => APIInfo()); tags = object.decodeObjects("tags", () => APITag()); @@ -68,6 +74,7 @@ class APIDocument extends APIObject { "securityDefinitions", () => APISecurityScheme()); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/header.dart b/lib/src/v2/header.dart index 4aefd79..7814ede 100644 --- a/lib/src/v2/header.dart +++ b/lib/src/v2/header.dart @@ -1,6 +1,6 @@ -import 'package:codable/codable.dart'; -import 'package:open_api/src/v2/property.dart'; -import 'package:open_api/src/v2/types.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/v2/property.dart'; +import 'package:conduit_open_api/src/v2/types.dart'; /// Represents a header in the OpenAPI specification. class APIHeader extends APIProperty { @@ -9,6 +9,7 @@ class APIHeader extends APIProperty { String? description; APIProperty? items; + @override void decode(KeyedArchive json) { super.decode(json); description = json.decode("description"); @@ -17,6 +18,7 @@ class APIHeader extends APIProperty { } } + @override void encode(KeyedArchive json) { super.encode(json); json.encode("description", description); diff --git a/lib/src/v2/metadata.dart b/lib/src/v2/metadata.dart index 8b674d8..bc0d27c 100644 --- a/lib/src/v2/metadata.dart +++ b/lib/src/v2/metadata.dart @@ -1,4 +1,5 @@ -import 'package:open_api/src/object.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; /// Represents a metadata for an API in the OpenAPI specification. class APIInfo extends APIObject { @@ -12,10 +13,11 @@ class APIInfo extends APIObject { APIContact? contact = APIContact(); APILicense? license = APILicense(); + @override void decode(KeyedArchive object) { super.decode(object); - title = object.decode("title"); + title = object.decode("title") ?? ''; description = object.decode("description"); termsOfServiceURL = object.decode("termsOfService"); contact = object.decodeObject("contact", () => APIContact()); @@ -23,6 +25,7 @@ class APIInfo extends APIObject { version = object.decode("version"); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -39,18 +42,20 @@ class APIInfo extends APIObject { class APIContact extends APIObject { APIContact(); + @override void decode(KeyedArchive object) { super.decode(object); - name = object.decode("name"); - url = object.decode("url"); - email = object.decode("email"); + name = object.decode("name") ?? "default"; + url = object.decode("url") ?? "http://localhost"; + email = object.decode("email") ?? "default"; } String name = "default"; String url = "http://localhost"; String email = "default"; + @override void encode(KeyedArchive object) { super.encode(object); @@ -64,16 +69,18 @@ class APIContact extends APIObject { class APILicense extends APIObject { APILicense(); + @override void decode(KeyedArchive object) { super.decode(object); - name = object.decode("name"); - url = object.decode("url"); + name = object.decode("name") ?? "default"; + url = object.decode("url") ?? "http://localhost"; } String name = "default"; String url = "http://localhost"; + @override void encode(KeyedArchive object) { super.encode(object); @@ -85,6 +92,7 @@ class APILicense extends APIObject { class APITag extends APIObject { APITag(); + @override void decode(KeyedArchive object) { super.decode(object); @@ -95,6 +103,7 @@ class APITag extends APIObject { String? name; String? description; + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/operation.dart b/lib/src/v2/operation.dart index 1edbf91..99227dd 100644 --- a/lib/src/v2/operation.dart +++ b/lib/src/v2/operation.dart @@ -1,7 +1,8 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_codable/cast.dart' as cast; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v2/parameter.dart'; +import 'package:conduit_open_api/src/v2/response.dart'; /// Represents a HTTP operation (a path/method pair) in the OpenAPI specification. class APIOperation extends APIObject { @@ -9,17 +10,18 @@ class APIOperation extends APIObject { @override Map get castMap => { - "tags": cast.List(cast.String), - "consumes": cast.List(cast.String), - "produces": cast.List(cast.String), - "schemes": cast.List(cast.String), - "security": cast.List(cast.Map(cast.String, cast.List(cast.String))), + "tags": const cast.List(cast.string), + "consumes": const cast.List(cast.string), + "produces": const cast.List(cast.string), + "schemes": const cast.List(cast.string), + "security": + const cast.List(cast.Map(cast.string, cast.List(cast.string))), }; String? summary = ""; String? description = ""; String? id; - bool deprecated = false; + bool? deprecated; List? tags = []; List? schemes = []; @@ -29,6 +31,7 @@ class APIOperation extends APIObject { List>?>? security = []; Map? responses = {}; + @override void decode(KeyedArchive object) { super.decode(object); @@ -38,13 +41,14 @@ class APIOperation extends APIObject { id = object.decode("operationId"); consumes = object.decode("consumes"); produces = object.decode("produces"); - deprecated = object.decode("deprecated") ?? false; + deprecated = object.decode("deprecated"); parameters = object.decodeObjects("parameters", () => APIParameter()); responses = object.decodeObjectMap("responses", () => APIResponse()); schemes = object.decode("schemes"); security = object.decode("security"); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/parameter.dart b/lib/src/v2/parameter.dart index a6e14a6..d3e6605 100644 --- a/lib/src/v2/parameter.dart +++ b/lib/src/v2/parameter.dart @@ -1,7 +1,7 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/v2/property.dart'; +import 'package:conduit_open_api/src/v2/schema.dart'; +import 'package:conduit_open_api/src/v2/types.dart'; /// Represents a parameter location in the OpenAPI specification. enum APIParameterLocation { query, header, path, formData, body } @@ -58,6 +58,7 @@ class APIParameter extends APIProperty { bool allowEmptyValue = false; APIProperty? items; + @override void decode(KeyedArchive json) { name = json.decode("name"); description = json.decode("description"); @@ -79,17 +80,20 @@ class APIParameter extends APIProperty { } } + @override void encode(KeyedArchive json) { json.encode("name", name); json.encode("description", description); - json.encode("in", APIParameterLocationCodec.encode(location!)); + json.encode("in", APIParameterLocationCodec.encode(location)); json.encode("required", isRequired); if (location == APIParameterLocation.body) { json.encodeObject("schema", schema); } else { super.encode(json); - json.encode("allowEmptyValue", allowEmptyValue); + if (allowEmptyValue) { + json.encode("allowEmptyValue", allowEmptyValue); + } if (type == APIType.array) { json.encodeObject("items", items); } diff --git a/lib/src/v2/path.dart b/lib/src/v2/path.dart index 535cc34..82b6870 100644 --- a/lib/src/v2/path.dart +++ b/lib/src/v2/path.dart @@ -1,6 +1,7 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v2/operation.dart'; +import 'package:conduit_open_api/src/v2/parameter.dart'; /// Represents a path (also known as a route) in the OpenAPI specification. class APIPath extends APIObject { @@ -9,10 +10,11 @@ class APIPath extends APIObject { List parameters = []; Map operations = {}; + @override void decode(KeyedArchive object) { super.decode(object); - object.keys.forEach((k) { + for (final k in object.keys) { if (k == r"$ref") { // todo: reference } else if (k == "parameters") { @@ -20,9 +22,10 @@ class APIPath extends APIObject { } else { operations[k] = object.decodeObject(k, () => APIOperation()); } - }); + } } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/property.dart b/lib/src/v2/property.dart index 73ebcba..5f8b3b2 100644 --- a/lib/src/v2/property.dart +++ b/lib/src/v2/property.dart @@ -1,5 +1,6 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v2/types.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v2/types.dart'; enum APISchemaRepresentation { primitive, @@ -72,6 +73,7 @@ class APIProperty extends APIObject { return APISchemaRepresentation.primitive; } + @override void decode(KeyedArchive object) { super.decode(object); @@ -94,6 +96,7 @@ class APIProperty extends APIObject { enumerated = object.decode("enum"); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/response.dart b/lib/src/v2/response.dart index 8056706..0bd95e8 100644 --- a/lib/src/v2/response.dart +++ b/lib/src/v2/response.dart @@ -1,6 +1,7 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v2/header.dart'; +import 'package:conduit_open_api/src/v2/schema.dart'; /// Represents an HTTP response in the OpenAPI specification. class APIResponse extends APIObject { @@ -10,6 +11,7 @@ class APIResponse extends APIObject { APISchemaObject? schema; Map? headers = {}; + @override void decode(KeyedArchive object) { super.decode(object); @@ -18,6 +20,7 @@ class APIResponse extends APIObject { headers = object.decodeObjectMap("headers", () => APIHeader()); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v2/schema.dart b/lib/src/v2/schema.dart index a7daff8..0efdf1a 100644 --- a/lib/src/v2/schema.dart +++ b/lib/src/v2/schema.dart @@ -1,6 +1,6 @@ -import 'package:codable/cast.dart' as cast; -import 'package:codable/codable.dart'; -import 'package:open_api/src/v2/property.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_codable/cast.dart' as cast; +import 'package:conduit_open_api/src/v2/property.dart'; /// Represents a schema object in the OpenAPI specification. class APISchemaObject extends APIProperty { @@ -31,8 +31,10 @@ class APISchemaObject extends APIProperty { } @override - Map get castMap => {"required": cast.List(cast.String)}; + Map get castMap => + {"required": const cast.List(cast.string)}; + @override void decode(KeyedArchive json) { super.decode(json); @@ -48,6 +50,7 @@ class APISchemaObject extends APIProperty { properties = json.decodeObjectMap("properties", () => APISchemaObject()); } + @override void encode(KeyedArchive json) { super.encode(json); diff --git a/lib/src/v2/security.dart b/lib/src/v2/security.dart index 90b3f84..96de2bb 100644 --- a/lib/src/v2/security.dart +++ b/lib/src/v2/security.dart @@ -1,6 +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:conduit_codable/cast.dart' as cast; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v2/parameter.dart'; /// Represents a OAuth 2.0 security scheme flow in the OpenAPI specification. enum APISecuritySchemeFlow { @@ -55,7 +56,7 @@ class APISecurityScheme extends APIObject { } APISecurityScheme.oauth2(this.oauthFlow, - {this.authorizationURL, this.tokenURL, this.scopes: const {}}) { + {this.authorizationURL, this.tokenURL, this.scopes = const {}}) { type = "oauth2"; } @@ -78,12 +79,13 @@ class APISecurityScheme extends APIObject { @override Map get castMap => - {"scopes": cast.Map(cast.String, cast.String)}; + {"scopes": const cast.Map(cast.string, cast.string)}; + @override void decode(KeyedArchive object) { super.decode(object); - type = object.decode("type"); + type = object.decode("type") ?? "oauth2"; description = object.decode("description"); if (type == "basic") { @@ -91,13 +93,15 @@ class APISecurityScheme extends APIObject { oauthFlow = APISecuritySchemeFlowCodec.decode(object.decode("flow")); authorizationURL = object.decode("authorizationUrl"); tokenURL = object.decode("tokenUrl"); - scopes = Map.from(object.decode("scopes")); + final scopeMap = object.decode>("scopes")!; + scopes = Map.from(scopeMap); } else if (type == "apiKey") { apiKeyName = object.decode("name"); apiKeyLocation = APIParameterLocationCodec.decode(object.decode("in")); } } + @override void encode(KeyedArchive object) { super.encode(object); @@ -108,9 +112,9 @@ class APISecurityScheme extends APIObject { /* nothing to do */ } else if (type == "apiKey") { object.encode("name", apiKeyName); - object.encode("in", APIParameterLocationCodec.encode(apiKeyLocation!)); + object.encode("in", APIParameterLocationCodec.encode(apiKeyLocation)); } else if (type == "oauth2") { - object.encode("flow", APISecuritySchemeFlowCodec.encode(oauthFlow!)); + object.encode("flow", APISecuritySchemeFlowCodec.encode(oauthFlow)); object.encode("authorizationUrl", authorizationURL); object.encode("tokenUrl", tokenURL); diff --git a/lib/src/v3/callback.dart b/lib/src/v3/callback.dart index f76d59c..d943e66 100644 --- a/lib/src/v3/callback.dart +++ b/lib/src/v3/callback.dart @@ -1,5 +1,6 @@ -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/path.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/path.dart'; /// A map of possible out-of band callbacks related to the parent operation. /// @@ -13,6 +14,7 @@ class APICallback extends APIObject { /// The key that identifies the [APIPath] is a runtime expression that can be evaluated in the context of a runtime HTTP request/response to identify the URL to be used for the callback request. A simple example might be $request.body#/url. Map? paths; + @override void decode(KeyedArchive object) { super.decode(object); @@ -26,6 +28,7 @@ class APICallback extends APIObject { }); } + @override void encode(KeyedArchive object) { super.encode(object); throw StateError("APICallback.encode: not yet implemented."); diff --git a/lib/src/v3/components.dart b/lib/src/v3/components.dart index fcfba66..87057af 100644 --- a/lib/src/v3/components.dart +++ b/lib/src/v3/components.dart @@ -1,11 +1,13 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/util/map_helper.dart'; +import 'package:conduit_open_api/src/v3/callback.dart'; +import 'package:conduit_open_api/src/v3/header.dart'; +import 'package:conduit_open_api/src/v3/parameter.dart'; +import 'package:conduit_open_api/src/v3/request_body.dart'; +import 'package:conduit_open_api/src/v3/response.dart'; +import 'package:conduit_open_api/src/v3/schema.dart'; +import 'package:conduit_open_api/src/v3/security.dart'; /// Holds a set of reusable objects for different aspects of the OAS. /// @@ -16,29 +18,29 @@ class APIComponents extends APIObject { APIComponents.empty(); /// An object to hold reusable [APISchemaObject?]. - Map? schemas = {}; + Map schemas = {}; /// An object to hold reusable [APIResponse?]. - Map? responses = {}; + Map responses = {}; /// An object to hold reusable [APIParameter?]. - Map? parameters = {}; + Map parameters = {}; //Map examples = {}; /// An object to hold reusable [APIRequestBody?]. - Map? requestBodies = {}; + Map requestBodies = {}; /// An object to hold reusable [APIHeader]. - Map? headers = {}; + Map headers = {}; /// An object to hold reusable [APISecurityScheme?]. - Map? securitySchemes = {}; + Map securitySchemes = {}; //Map links = {}; /// An object to hold reusable [APICallback?]. - Map? callbacks = {}; + Map callbacks = {}; /// Returns a component definition for [uri]. /// @@ -55,7 +57,7 @@ class APIComponents extends APIObject { "Invalid reference URI: does not begin with /components/"); } - var namedMap = null; + Map? namedMap; switch (segments[1]) { case "schemas": namedMap = schemas; @@ -78,9 +80,11 @@ class APIComponents extends APIObject { case "callbacks": namedMap = callbacks; break; - default: - throw ArgumentError( - "Invalid reference URI: component type '${segments[1]}' does not exist."); + } + + if (namedMap == null) { + throw ArgumentError( + "Invalid reference URI: component type '${segments[1]}' does not exist."); } return namedMap[segments.last]; @@ -94,35 +98,46 @@ class APIComponents extends APIObject { return resolveUri(refObject.referenceURI!) as T?; } + @override void decode(KeyedArchive object) { super.decode(object); - schemas = object.decodeObjectMap("schemas", () => APISchemaObject())!; - responses = object.decodeObjectMap("responses", () => APIResponse.empty()); - parameters = - object.decodeObjectMap("parameters", () => APIParameter.empty()); + schemas = removeNullsFromMap( + object.decodeObjectMap("schemas", () => APISchemaObject())); + responses = removeNullsFromMap( + object.decodeObjectMap("responses", () => APIResponse.empty())); + parameters = removeNullsFromMap( + object.decodeObjectMap("parameters", () => APIParameter.empty())); // examples = object.decodeObjectMap("examples", () => APIExample()); - requestBodies = - object.decodeObjectMap("requestBodies", () => APIRequestBody.empty()); - headers = object.decodeObjectMap("headers", () => APIHeader()); + requestBodies = removeNullsFromMap( + object.decodeObjectMap("requestBodies", () => APIRequestBody.empty())); + headers = removeNullsFromMap( + object.decodeObjectMap("headers", () => APIHeader())); - securitySchemes = - object.decodeObjectMap("securitySchemes", () => APISecurityScheme()); + securitySchemes = removeNullsFromMap( + object.decodeObjectMap("securitySchemes", () => APISecurityScheme())); // links = object.decodeObjectMap("links", () => APILink()); - callbacks = object.decodeObjectMap("callbacks", () => APICallback()); + callbacks = removeNullsFromMap( + object.decodeObjectMap("callbacks", () => APICallback())); } + @override void encode(KeyedArchive object) { super.encode(object); - object.encodeObjectMap("schemas", schemas); - object.encodeObjectMap("responses", responses); - object.encodeObjectMap("parameters", parameters); + if (schemas.isNotEmpty) object.encodeObjectMap("schemas", schemas); + if (responses.isNotEmpty) object.encodeObjectMap("responses", responses); + if (parameters.isNotEmpty) object.encodeObjectMap("parameters", parameters); // object.encodeObjectMap("examples", examples); - object.encodeObjectMap("requestBodies", requestBodies); - object.encodeObjectMap("headers", headers); - object.encodeObjectMap("securitySchemes", securitySchemes); + if (requestBodies.isNotEmpty) { + object.encodeObjectMap("requestBodies", requestBodies); + } + if (headers.isNotEmpty) object.encodeObjectMap("headers", headers); + if (securitySchemes.isNotEmpty) { + object.encodeObjectMap("securitySchemes", securitySchemes); + } + // object.encodeObjectMap("links", links); - object.encodeObjectMap("callbacks", callbacks); + if (callbacks.isNotEmpty) object.encodeObjectMap("callbacks", callbacks); } } diff --git a/lib/src/v3/document.dart b/lib/src/v3/document.dart index 2bab4a9..f4bf45b 100644 --- a/lib/src/v3/document.dart +++ b/lib/src/v3/document.dart @@ -1,9 +1,10 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/components.dart'; +import 'package:conduit_open_api/src/v3/metadata.dart'; +import 'package:conduit_open_api/src/v3/path.dart'; +import 'package:conduit_open_api/src/v3/security.dart'; +import 'package:conduit_open_api/src/v3/server.dart'; /// This is the root document object of the OpenAPI document. class APIDocument extends APIObject { @@ -18,12 +19,12 @@ 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 = APIInfo.empty(); /// An array of [APIServerDescription], which provide connectivity information to a target server. /// @@ -52,25 +53,29 @@ class APIDocument extends APIObject { return KeyedArchive.archive(this, allowReferences: true); } + @override void decode(KeyedArchive object) { super.decode(object); - version = object.decode("openapi"); - info = object.decodeObject("info", () => APIInfo.empty())!; + version = object.decode("openapi") ?? "3.0.0"; + info = + object.decodeObject("info", () => APIInfo.empty()) ?? APIInfo.empty(); servers = object.decodeObjects("servers", () => APIServerDescription.empty()); paths = object.decodeObjectMap("paths", () => APIPath()); components = object.decodeObject("components", () => APIComponents()); - security = object.decode("security"); + security = + object.decodeObjects("security", () => APISecurityRequirement.empty()); tags = object.decodeObjects("tags", () => APITag.empty()); } + @override void encode(KeyedArchive object) { super.encode(object); - if (version == null || info == null || paths == null) { + if (!info.isValid || paths == null) { throw ArgumentError( - "APIDocument must have non-null values for: 'version', 'info', 'paths'."); + "APIDocument must have values for: 'version', 'info' and 'paths'."); } object.encode("openapi", version); @@ -78,7 +83,7 @@ class APIDocument extends APIObject { object.encodeObjects("servers", servers); object.encodeObjectMap("paths", paths); object.encodeObject("components", components); - object.encode("security", security); + object.encodeObjects("security", security); object.encodeObjects("tags", tags); } } diff --git a/lib/src/v3/encoding.dart b/lib/src/v3/encoding.dart index 5d19c3a..cb0cf0e 100644 --- a/lib/src/v3/encoding.dart +++ b/lib/src/v3/encoding.dart @@ -1,6 +1,7 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/header.dart'; +import 'package:conduit_open_api/src/v3/parameter.dart'; /// A single encoding definition applied to a single schema property. class APIEncoding extends APIObject { @@ -8,13 +9,12 @@ class APIEncoding extends APIObject { {this.contentType, this.headers, this.style, - bool? allowReserved, - bool? explode}) { - this.allowReserved = allowReserved; - this.explode = explode; - } + this.allowReserved = false, + this.explode = false}); - APIEncoding.empty(); + APIEncoding.empty() + : allowReserved = false, + explode = false; /// The Content-Type for encoding a specific property. /// @@ -29,45 +29,37 @@ class APIEncoding extends APIObject { /// 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; - set allowReserved(bool? f) { - _allowReserved = f; - } - - bool? _allowReserved = false; + bool? allowReserved; /// 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; - set explode(bool? f) { - _explode = f; - } - - bool? _explode = false; + bool? explode; /// 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; + @override void decode(KeyedArchive object) { super.decode(object); contentType = object.decode("contentType"); headers = object.decodeObjectMap("headers", () => APIHeader()); - _allowReserved = object.decode("allowReserved"); - _explode = object.decode("explode"); + allowReserved = object.decode("allowReserved"); + explode = object.decode("explode"); style = object.decode("style"); } + @override void encode(KeyedArchive object) { super.encode(object); object.encode("contentType", contentType); object.encodeObjectMap("headers", headers); - object.encode("allowReserved", _allowReserved); - object.encode("explode", _explode); + object.encode("allowReserved", allowReserved); + object.encode("explode", explode); object.encode("style", style); } } diff --git a/lib/src/v3/header.dart b/lib/src/v3/header.dart index c727d46..7591cd9 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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/v3/parameter.dart'; +import 'package:conduit_open_api/src/v3/schema.dart'; /// [APIHeader] follows the structure of the [APIParameter] with the following changes: /// diff --git a/lib/src/v3/media_type.dart b/lib/src/v3/media_type.dart index d379cba..e543598 100644 --- a/lib/src/v3/media_type.dart +++ b/lib/src/v3/media_type.dart @@ -1,6 +1,7 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/encoding.dart'; +import 'package:conduit_open_api/src/v3/schema.dart'; /// Each [APIMediaType] provides schema and examples for the media type identified by its key. class APIMediaType extends APIObject { @@ -15,6 +16,7 @@ class APIMediaType extends APIObject { /// 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; + @override void decode(KeyedArchive object) { super.decode(object); @@ -22,6 +24,7 @@ class APIMediaType extends APIObject { encoding = object.decodeObjectMap("encoding", () => APIEncoding()); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v3/metadata.dart b/lib/src/v3/metadata.dart index cf5f77a..77c3489 100644 --- a/lib/src/v3/metadata.dart +++ b/lib/src/v3/metadata.dart @@ -1,15 +1,16 @@ -import 'package:open_api/src/object.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; /// The object provides metadata about the API. /// /// The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience. 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.empty(); + /// The title of the application. /// /// REQUIRED. @@ -36,19 +37,21 @@ class APIInfo extends APIObject { /// The license information for the exposed API. APILicense? license; + bool get isValid => title != null && version != null; + + @override void decode(KeyedArchive object) { super.decode(object); title = object.decode("title"); description = object.decode("description"); - termsOfServiceURL = object.decode("termsOfService") != null - ? Uri.tryParse(object.decode("termsOfService")) - : null; + termsOfServiceURL = object.decode("termsOfService"); contact = object.decodeObject("contact", () => APIContact()); license = object.decodeObject("license", () => APILicense.empty()); version = object.decode("version"); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -84,16 +87,16 @@ class APIContact extends APIObject { /// MUST be in the format of an email address. String? email; + @override void decode(KeyedArchive object) { super.decode(object); name = object.decode("name"); - url = object.decode("url") != null - ? Uri.tryParse(object.decode("url")) - : null; + url = object.decode("url"); email = object.decode("email"); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -105,8 +108,8 @@ class APIContact extends APIObject { /// License information for the exposed API. class APILicense extends APIObject { - APILicense.empty(); APILicense(this.name, {this.url}); + APILicense.empty(); /// The license name used for the API. /// @@ -118,6 +121,7 @@ class APILicense extends APIObject { /// MUST be in the format of a URL. Uri? url; + @override void decode(KeyedArchive object) { super.decode(object); @@ -125,6 +129,7 @@ class APILicense extends APIObject { url = object.decode("url"); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -141,10 +146,10 @@ class APILicense extends APIObject { /// /// It is not mandatory to have a [APITag] per tag defined in the [APIOperation] instances. class APITag extends APIObject { - APITag.empty(); - APITag(this.name, {this.description}); + APITag.empty(); + /// The name of the tag. /// /// REQUIRED. @@ -155,6 +160,7 @@ class APITag extends APIObject { /// CommonMark syntax MAY be used for rich text representation. String? description; + @override void decode(KeyedArchive object) { super.decode(object); @@ -162,6 +168,7 @@ class APITag extends APIObject { description = object.decode("description"); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v3/operation.dart b/lib/src/v3/operation.dart index 2574077..91d3819 100644 --- a/lib/src/v3/operation.dart +++ b/lib/src/v3/operation.dart @@ -1,18 +1,17 @@ -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:conduit_codable/cast.dart' as cast; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/callback.dart'; +import 'package:conduit_open_api/src/v3/parameter.dart'; +import 'package:conduit_open_api/src/v3/request_body.dart'; +import 'package:conduit_open_api/src/v3/response.dart'; +import 'package:conduit_open_api/src/v3/security.dart'; +import 'package:conduit_open_api/src/v3/path.dart'; +import 'package:conduit_open_api/src/v3/document.dart'; +import 'package:conduit_open_api/src/v3/server.dart'; /// Describes a single API operation on a path. class APIOperation extends APIObject { - APIOperation.empty(); - APIOperation(this.id, this.responses, {this.tags, this.summary, @@ -21,9 +20,9 @@ class APIOperation extends APIObject { this.security, this.requestBody, this.callbacks, - bool? deprecated}) { - isDeprecated = deprecated; - } + this.deprecated}); + + APIOperation.empty(); /// A list of tags for API documentation control. /// @@ -76,13 +75,7 @@ class APIOperation extends APIObject { /// Declares this operation to be deprecated. /// /// Consumers SHOULD refrain from usage of the declared operation. Default value is false. - bool? get isDeprecated => _deprecated; - - set isDeprecated(bool? f) { - _deprecated = f; - } - - bool? _deprecated; + bool? deprecated; /// Returns the parameter named [name] or null if it doesn't exist. APIParameter? parameterNamed(String name) => @@ -136,8 +129,9 @@ class APIOperation extends APIObject { } @override - Map get castMap => {"tags": cast.List(cast.String)}; + Map get castMap => {"tags": const cast.List(cast.string)}; + @override void decode(KeyedArchive object) { super.decode(object); @@ -150,13 +144,14 @@ class APIOperation extends APIObject { object.decodeObject("requestBody", () => APIRequestBody.empty()); responses = object.decodeObjectMap("responses", () => APIResponse.empty()); callbacks = object.decodeObjectMap("callbacks", () => APICallback()); - _deprecated = object.decode("deprecated"); + deprecated = object.decode("deprecated"); security = object.decodeObjects("security", () => APISecurityRequirement.empty()); servers = object.decodeObjects("servers", () => APIServerDescription.empty()); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -173,7 +168,7 @@ class APIOperation extends APIObject { object.encodeObject("requestBody", requestBody); object.encodeObjectMap("responses", responses); object.encodeObjectMap("callbacks", callbacks); - object.encode("deprecated", _deprecated); + object.encode("deprecated", deprecated); object.encodeObjects("security", security); object.encodeObjects("servers", servers); } diff --git a/lib/src/v3/parameter.dart b/lib/src/v3/parameter.dart index df82829..d05aacf 100644 --- a/lib/src/v3/parameter.dart +++ b/lib/src/v3/parameter.dart @@ -1,7 +1,8 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/document.dart'; +import 'package:conduit_open_api/src/v3/media_type.dart'; +import 'package:conduit_open_api/src/v3/schema.dart'; /// There are four possible parameter locations specified by the in field. /// @@ -65,24 +66,19 @@ class APIParameterLocationCodec { /// /// A unique parameter is defined by a combination of a [name] and [location]. class APIParameter extends APIObject { - APIParameter.empty(); - APIParameter(this.name, this.location, {this.description, this.schema, this.content, this.style, bool? isRequired, - bool? deprecated, - bool? allowEmptyValue, - bool? explode, - bool? allowReserved}) { - this.isRequired = isRequired; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; - } + this.deprecated, + this.allowEmptyValue, + this.explode, + this.allowReserved}) + : _required = isRequired; + + APIParameter.empty(); APIParameter.header(this.name, {this.description, @@ -90,16 +86,12 @@ class APIParameter extends APIObject { this.content, this.style, bool? isRequired, - bool? deprecated, - bool? allowEmptyValue, - bool? explode, - bool? allowReserved}) { - this.isRequired = isRequired; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; - this.location = APIParameterLocation.header; + this.deprecated, + this.allowEmptyValue, + this.explode, + this.allowReserved}) + : _required = isRequired { + location = APIParameterLocation.header; } APIParameter.query(this.name, @@ -108,16 +100,12 @@ class APIParameter extends APIObject { this.content, this.style, bool? isRequired, - bool? deprecated, - bool? allowEmptyValue, - bool? explode, - bool? allowReserved}) { - this.isRequired = isRequired; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; - this.location = APIParameterLocation.query; + this.deprecated, + this.allowEmptyValue, + this.explode, + this.allowReserved}) + : _required = isRequired { + location = APIParameterLocation.query; } APIParameter.path(this.name) @@ -131,16 +119,12 @@ class APIParameter extends APIObject { this.content, this.style, bool? isRequired, - bool? deprecated, - bool? allowEmptyValue, - bool? explode, - bool? allowReserved}) { - this.isRequired = isRequired; - this.isDeprecated = deprecated; - this.allowEmptyValue = allowEmptyValue; - this.allowReserved = allowReserved; - this.explode = explode; - this.location = APIParameterLocation.cookie; + this.deprecated, + this.allowEmptyValue, + this.explode, + this.allowReserved}) + : _required = isRequired { + location = APIParameterLocation.cookie; } /// The name of the parameter. @@ -160,22 +144,18 @@ class APIParameter extends APIObject { /// /// 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); + // ignore: avoid_bool_literals_in_conditional_expressions + location == APIParameterLocation.path ? true : _required; set isRequired(bool? f) { _required = f; } - bool? _required = false; + bool? _required; /// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage. - bool? get isDeprecated => _deprecated; - - set isDeprecated(bool? f) { - _deprecated = f; - } - bool? _deprecated = false; + bool? deprecated; /// The location of the parameter. /// @@ -188,13 +168,7 @@ class APIParameter extends APIObject { // 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; - - set allowEmptyValue(bool? f) { - _allowEmptyValue = f; - } - - bool? _allowEmptyValue = false; + bool? allowEmptyValue = false; /// Describes how the parameter value will be serialized depending on the type of the parameter value. /// @@ -204,24 +178,12 @@ class APIParameter extends APIObject { /// 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; - - set explode(bool? f) { - _explode = f; - } - - bool? _explode = false; + 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; - - set allowReserved(bool? f) { - _allowReserved = f; - } - - bool? _allowReserved = false; + bool? allowReserved = false; /// A map containing the representations for the parameter. /// @@ -231,24 +193,26 @@ class APIParameter extends APIObject { // Currently missing: // example, examples + @override void decode(KeyedArchive object) { super.decode(object); name = object.decode("name"); description = object.decode("description"); - location = APIParameterLocationCodec.decode(object.decode("in"))!; + location = APIParameterLocationCodec.decode(object.decode("in")); _required = object.decode("required"); - _deprecated = object.decode("deprecated"); - _allowEmptyValue = object.decode("allowEmptyValue"); + deprecated = object.decode("deprecated"); + allowEmptyValue = object.decode("allowEmptyValue"); schema = object.decodeObject("schema", () => APISchemaObject()); style = object.decode("style"); - _explode = object.decode("explode"); - _allowReserved = object.decode("allowReserved"); + explode = object.decode("explode"); + allowReserved = object.decode("allowReserved"); content = object.decodeObjectMap("content", () => APIMediaType()); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -259,7 +223,7 @@ class APIParameter extends APIObject { object.encode("name", name); object.encode("description", description); - object.encode("in", APIParameterLocationCodec.encode(location!)); + object.encode("in", APIParameterLocationCodec.encode(location)); if (location == APIParameterLocation.path) { object.encode("required", true); @@ -267,16 +231,16 @@ class APIParameter extends APIObject { object.encode("required", _required); } - object.encode("deprecated", _deprecated); + object.encode("deprecated", deprecated); if (location == APIParameterLocation.query) { - object.encode("allowEmptyValue", _allowEmptyValue); + object.encode("allowEmptyValue", allowEmptyValue); } object.encodeObject("schema", schema); object.encode("style", style); - object.encode("explode", _explode); - object.encode("allowReserved", _allowReserved); + object.encode("explode", explode); + object.encode("allowReserved", allowReserved); object.encodeObjectMap("content", content); } } diff --git a/lib/src/v3/path.dart b/lib/src/v3/path.dart index 70362f5..a2492ad 100644 --- a/lib/src/v3/path.dart +++ b/lib/src/v3/path.dart @@ -1,13 +1,21 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/operation.dart'; +import 'package:conduit_open_api/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( + {this.summary, + this.description, + List? parameters, + Map? operations}) { + this.parameters = parameters ?? []; + this.operations = operations ?? {}; + } APIPath.empty(); - APIPath({this.summary, this.description, this.parameters, this.operations}); /// An optional, string summary, intended to apply to all operations in this path. String? summary; @@ -20,12 +28,12 @@ class APIPath extends APIObject { /// 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; + late List parameters; /// Definitions of operations on this path. /// /// Keys are lowercased HTTP methods, e.g. get, put, delete, post, etc. - Map? operations; + late final Map operations; /// Returns true if this path has path parameters [parameterNames]. /// @@ -33,10 +41,9 @@ class APIPath extends APIObject { /// 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() ?? - []; + .where((p) => p?.location == APIParameterLocation.path) + .map((p) => p?.name) + .toList(); if (pathParams.length != parameterNames.length) { return false; } @@ -46,12 +53,15 @@ class APIPath extends APIObject { // todo (joeconwaystk): alternative servers not yet implemented + @override void decode(KeyedArchive object) { super.decode(object); summary = object.decode("summary"); description = object.decode("description"); - parameters = object.decodeObjects("parameters", () => APIParameter.empty()); + parameters = + object.decodeObjects("parameters", () => APIParameter.empty()) ?? + []; final methodNames = [ "get", @@ -63,24 +73,26 @@ class APIPath extends APIObject { "patch", "trace" ]; - methodNames.forEach((methodName) { + for (final methodName in methodNames) { if (!object.containsKey(methodName)) { - return; + continue; } - operations ??= {}; - operations![methodName] = + operations[methodName] = object.decodeObject(methodName, () => APIOperation.empty()); - }); + } } + @override void encode(KeyedArchive object) { super.encode(object); object.encode("summary", summary); object.encode("description", description); - object.encodeObjects("parameters", parameters); + if (parameters.isNotEmpty) { + object.encodeObjects("parameters", parameters); + } - operations!.forEach((opName, op) { + operations.forEach((opName, op) { object.encodeObject(opName.toLowerCase(), op); }); } diff --git a/lib/src/v3/request_body.dart b/lib/src/v3/request_body.dart index dcf9c8f..7f31c22 100644 --- a/lib/src/v3/request_body.dart +++ b/lib/src/v3/request_body.dart @@ -1,21 +1,19 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/media_type.dart'; +import 'package:conduit_open_api/src/v3/schema.dart'; /// Describes a single request body. class APIRequestBody extends APIObject { - APIRequestBody.empty(); + APIRequestBody(this.content, {this.description, this.isRequired = false}); - APIRequestBody(this.content, {this.description, bool isRequired = false}) { - this.isRequired = isRequired; - } + APIRequestBody.empty(); APIRequestBody.schema(APISchemaObject schema, - {Iterable contentTypes: const ["application/json"], + {Iterable contentTypes = const ["application/json"], this.description, - bool isRequired = false}) { - this.isRequired = isRequired; - this.content = contentTypes.fold({}, (prev, elem) { + this.isRequired = false}) { + content = contentTypes.fold({}, (prev, elem) { prev![elem] = APIMediaType(schema: schema); return prev; }); @@ -34,22 +32,18 @@ class APIRequestBody extends APIObject { /// Determines if the request body is required in the request. /// /// Defaults to false. - bool get isRequired => _required; - - set isRequired(bool f) { - _required = f; - } - - bool _required = false; + bool isRequired = false; + @override void decode(KeyedArchive object) { super.decode(object); description = object.decode("description"); - _required = object.decode("required"); - content = object.decodeObjectMap("content", () => APIMediaType())!; + isRequired = object.decode("required") ?? false; + content = object.decodeObjectMap("content", () => APIMediaType()); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -59,7 +53,7 @@ class APIRequestBody extends APIObject { } object.encode("description", description); - object.encode("required", _required); + object.encode("required", isRequired); object.encodeObjectMap("content", content); } } diff --git a/lib/src/v3/response.dart b/lib/src/v3/response.dart index 2cbde39..e3ff14e 100644 --- a/lib/src/v3/response.dart +++ b/lib/src/v3/response.dart @@ -1,14 +1,15 @@ -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:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/header.dart'; +import 'package:conduit_open_api/src/v3/media_type.dart'; +import 'package:conduit_open_api/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.empty(); APIResponse.schema(this.description, APISchemaObject schema, - {Iterable contentTypes: const ["application/json"], + {Iterable contentTypes = const ["application/json"], this.headers}) { content = contentTypes.fold({}, (prev, elem) { prev![elem] = APIMediaType(schema: schema); @@ -70,6 +71,7 @@ class APIResponse extends APIObject { } } + @override void decode(KeyedArchive object) { super.decode(object); @@ -78,6 +80,7 @@ class APIResponse extends APIObject { headers = object.decodeObjectMap("headers", () => APIHeader()); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/v3/schema.dart b/lib/src/v3/schema.dart index 798ef59..f6a9184 100644 --- a/lib/src/v3/schema.dart +++ b/lib/src/v3/schema.dart @@ -1,6 +1,7 @@ -import 'package:codable/cast.dart' as cast; -import 'package:open_api/src/object.dart'; -import 'package:open_api/src/v3/types.dart'; +import 'package:conduit_codable/cast.dart' as cast; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/types.dart'; enum APISchemaAdditionalPropertyPolicy { /// When [APISchemaObject] prevents properties other than those defined by [APISchemaObject.properties] from being included @@ -23,7 +24,7 @@ class APISchemaObject extends APIObject { APISchemaObject.integer() : type = APIType.integer; APISchemaObject.boolean() : type = APIType.boolean; APISchemaObject.map( - {APIType? ofType, APISchemaObject? ofSchema, bool any: false}) + {APIType? ofType, APISchemaObject? ofSchema, bool any = false}) : type = APIType.object { if (ofType != null) { additionalPropertySchema = APISchemaObject()..type = ofType; @@ -47,7 +48,7 @@ class APISchemaObject extends APIObject { } } APISchemaObject.object(this.properties) : type = APIType.object; - APISchemaObject.file({bool isBase64Encoded: false}) + APISchemaObject.file({bool isBase64Encoded = false}) : type = APIType.string, format = isBase64Encoded ? "byte" : "binary"; @@ -235,20 +236,16 @@ class APISchemaObject extends APIObject { _writeOnly = n; } - bool? get isDeprecated => _deprecated ?? false; - - set isDeprecated(bool? n) { - _deprecated = n; - } - bool? _nullable; bool? _readOnly; bool? _writeOnly; - bool? _deprecated; + bool? deprecated; @override - Map get castMap => {"required": cast.List(cast.String)}; + Map get castMap => + {"required": const cast.List(cast.string)}; + @override void decode(KeyedArchive object) { super.decode(object); @@ -302,9 +299,10 @@ class APISchemaObject extends APIObject { _nullable = object.decode("nullable"); _readOnly = object.decode("readOnly"); _writeOnly = object.decode("writeOnly"); - _deprecated = object.decode("deprecated"); + deprecated = object.decode("deprecated"); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -354,6 +352,6 @@ class APISchemaObject extends APIObject { object.encode("nullable", _nullable); object.encode("readOnly", _readOnly); object.encode("writeOnly", _writeOnly); - object.encode("deprecated", _deprecated); + object.encode("deprecated", deprecated); } } diff --git a/lib/src/v3/security.dart b/lib/src/v3/security.dart index 6f5ee8d..ca82a46 100644 --- a/lib/src/v3/security.dart +++ b/lib/src/v3/security.dart @@ -1,8 +1,9 @@ -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'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; +import 'package:conduit_open_api/src/v3/components.dart'; +import 'package:conduit_open_api/src/v3/document.dart'; +import 'package:conduit_open_api/src/v3/operation.dart'; +import 'package:conduit_open_api/src/v3/parameter.dart'; enum APISecuritySchemeType { apiKey, http, oauth2, openID } @@ -102,10 +103,11 @@ class APISecurityScheme extends APIObject { /// For openID only. REQUIRED if so. Uri? connectURL; + @override void decode(KeyedArchive object) { super.decode(object); - type = APISecuritySchemeTypeCodec.decode(object.decode("type"))!; + type = APISecuritySchemeTypeCodec.decode(object.decode("type")); description = object.decode("description"); switch (type) { @@ -138,6 +140,7 @@ class APISecurityScheme extends APIObject { } } + @override void encode(KeyedArchive object) { super.encode(object); @@ -146,7 +149,7 @@ class APISecurityScheme extends APIObject { "APISecurityScheme must have non-null values for: 'type'."); } - object.encode("type", APISecuritySchemeTypeCodec.encode(type!)); + object.encode("type", APISecuritySchemeTypeCodec.encode(type)); object.encode("description", description); switch (type) { @@ -158,7 +161,7 @@ class APISecurityScheme extends APIObject { } object.encode("name", name); - object.encode("in", APIParameterLocationCodec.encode(location!)); + object.encode("in", APIParameterLocationCodec.encode(location)); } break; case APISecuritySchemeType.oauth2: @@ -230,6 +233,7 @@ class APISecuritySchemeOAuth2Flow extends APIObject { /// REQUIRED. A map between the scope name and a short description for it. Map? scopes; + @override void encode(KeyedArchive object) { super.encode(object); @@ -239,6 +243,7 @@ class APISecuritySchemeOAuth2Flow extends APIObject { object.encode("scopes", scopes); } + @override void decode(KeyedArchive object) { super.decode(object); @@ -246,8 +251,7 @@ class APISecuritySchemeOAuth2Flow extends APIObject { tokenURL = object.decode("tokenUrl"); refreshURL = object.decode("refreshUrl"); - - scopes = Map.from(object.decode("scopes")); + scopes = object.decode>("scopes"); } } @@ -259,28 +263,36 @@ 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); + APISecurityRequirement.empty(); /// 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; + @override void encode(KeyedArchive object) { super.encode(object); - requirements?.forEach((key, value) { - object.encode(key, value); - }); + if (requirements != null) { + requirements!.forEach((key, value) { + object.encode(key, value); + }); + } } + @override void decode(KeyedArchive object) { super.decode(object); - object.keys.forEach((key) { - final req = List.from(object.decode(key)); - requirements?[key] = req; - }); + for (final key in object.keys) { + final decoded = object.decode>(key); + if (decoded != null) { + final req = List.from(decoded); + requirements ??= >{}; + requirements![key] = req; + } + } } } diff --git a/lib/src/v3/server.dart b/lib/src/v3/server.dart index 4bf18e5..6ac4aa2 100644 --- a/lib/src/v3/server.dart +++ b/lib/src/v3/server.dart @@ -1,10 +1,12 @@ -import 'package:open_api/src/object.dart'; +import 'package:conduit_codable/conduit_codable.dart'; +import 'package:conduit_open_api/src/object.dart'; /// An object representing a Server. class APIServerDescription extends APIObject { - APIServerDescription.empty(); APIServerDescription(this.url, {this.description, this.variables}); + APIServerDescription.empty(); + /// 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}. @@ -20,17 +22,17 @@ class APIServerDescription extends APIObject { /// The value is used for substitution in the server's URL template. Map? variables; + @override void decode(KeyedArchive object) { super.decode(object); - url = object.decode("url") != null - ? Uri.tryParse(object.decode("url")) - : null; + url = object.decode("url"); description = object.decode("description"); variables = object.decodeObjectMap("variables", () => APIServerVariable.empty()); } + @override void encode(KeyedArchive object) { super.encode(object); @@ -47,10 +49,11 @@ 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.empty(); + /// An enumeration of string values to be used if the substitution options are from a limited set. List? availableValues; @@ -64,14 +67,17 @@ class APIServerVariable extends APIObject { /// CommonMark syntax MAY be used for rich text representation. String? description; + @override void decode(KeyedArchive object) { super.decode(object); - availableValues = List.from(object.decode("enum")); + final enumMap = object.decode("enum") as List; + availableValues = List.from(enumMap); defaultValue = object.decode("default"); description = object.decode("description"); } + @override void encode(KeyedArchive object) { super.encode(object); diff --git a/lib/src/version/version.g.dart b/lib/src/version/version.g.dart new file mode 100644 index 0000000..631c322 --- /dev/null +++ b/lib/src/version/version.g.dart @@ -0,0 +1,3 @@ +/// GENERATED BY pub_release do not modify. +/// conduit_open_api version +String packageVersion = '1.0.0-b1'; diff --git a/lib/v2.dart b/lib/v2.dart index 9f65623..5cb97be 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:conduit_open_api/src/v2/document.dart'; +export 'package:conduit_open_api/src/v2/header.dart'; +export 'package:conduit_open_api/src/v2/metadata.dart'; +export 'package:conduit_open_api/src/v2/operation.dart'; +export 'package:conduit_open_api/src/v2/parameter.dart'; +export 'package:conduit_open_api/src/v2/path.dart'; +export 'package:conduit_open_api/src/v2/response.dart'; +export 'package:conduit_open_api/src/v2/schema.dart'; +export 'package:conduit_open_api/src/v2/security.dart'; +export 'package:conduit_open_api/src/v2/types.dart'; diff --git a/lib/v3.dart b/lib/v3.dart index e958dff..8128c58 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:conduit_open_api/src/v3/callback.dart'; +export 'package:conduit_open_api/src/v3/components.dart'; +export 'package:conduit_open_api/src/v3/document.dart'; +export 'package:conduit_open_api/src/v3/encoding.dart'; +export 'package:conduit_open_api/src/v3/header.dart'; +export 'package:conduit_open_api/src/v3/media_type.dart'; +export 'package:conduit_open_api/src/v3/metadata.dart'; +export 'package:conduit_open_api/src/v3/operation.dart'; +export 'package:conduit_open_api/src/v3/parameter.dart'; +export 'package:conduit_open_api/src/v3/path.dart'; +export 'package:conduit_open_api/src/v3/request_body.dart'; +export 'package:conduit_open_api/src/v3/response.dart'; +export 'package:conduit_open_api/src/v3/schema.dart'; +export 'package:conduit_open_api/src/v3/security.dart'; +export 'package:conduit_open_api/src/v3/server.dart'; +export 'package:conduit_open_api/src/v3/types.dart'; +export 'package:conduit_open_api/src/object.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 3b2c1a4..9704e1a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,13 @@ -name: open_api +name: conduit_open_api +version: 1.0.0-b1 +homepage: https://github.com/conduit-dart/open-api-dart 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 - -environment: - sdk: ">=2.12.0-0 <3.0.0" - -dependencies: - codable: - git: https://github.com/j4qfrost/dart-codable.git - -dev_dependencies: - test: ^1.16.4 \ No newline at end of file +environment: + sdk: '>=2.12.0-0 <3.0.0' +dependencies: + conduit_codable: ^1.0.0-0 + meta: ^1.1.5 +dev_dependencies: + dcli: ^0.51.0 + lint: ^1.5.3 + test: ^1.3.0 diff --git a/test/v2_test.dart b/test/v2_test.dart index f56f66c..9e61f08 100644 --- a/test/v2_test.dart +++ b/test/v2_test.dart @@ -1,41 +1,40 @@ // 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:test/test.dart'; -import 'dart:io'; import 'dart:convert'; +import 'dart:io'; + +import 'package:conduit_open_api/v2.dart'; +import 'package:dcli/dcli.dart'; +import 'package:test/test.dart'; void main() { - group("Kubernetes spec", () { + group("kubenrnetes spec", () { 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 = File("test/specs/kubernetes.json"); - var contents = file.readAsStringSync(); - original = json.decode(contents); + /// download sample api document if we don't already have it. + final String config = fetchKubernetesExample(); + final file = File(config); + final contents = file.readAsStringSync(); + original = json.decode(contents) as Map; 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!.info!.version, 'v1.12.0'); 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); + test("Confirm top-level objects", () { expect(original!.containsKey("consumes"), false); - expect(doc!.produces, isNull); expect(original!.containsKey("produces"), false); }); @@ -43,16 +42,17 @@ void main() { expect(doc!.paths!.length, greaterThan(0)); expect(doc!.paths!.length, original!["paths"].length); - Map originalPaths = original!["paths"]; + final Map originalPaths = + original!["paths"] as Map; doc!.paths!.forEach((k, v) { expect(originalPaths.keys.contains(k), true); }); }); test("Sample - Namespace", () { - var namespacePath = doc!.paths!["/api/v1/namespaces"]; + final namespacePath = doc!.paths!["/api/v1/namespaces"]; - var getNamespace = namespacePath!.operations["get"]; + final getNamespace = namespacePath!.operations["get"]; expect(getNamespace!.description, contains("of kind Namespace")); expect(getNamespace.consumes, ["*/*"]); expect(getNamespace.produces, contains("application/json")); @@ -69,7 +69,7 @@ void main() { expect(getNamespace.responses!.keys, contains("401")); expect(getNamespace.responses!.keys, contains("200")); - var postNamespace = namespacePath.operations["post"]; + final postNamespace = namespacePath.operations["post"]; expect(postNamespace!.parameters!.length, 1); expect(postNamespace.parameters!.first!.name, "body"); expect( @@ -77,10 +77,10 @@ void main() { }); test("Sample - Reference", () { - var apiPath = doc!.paths!["/api/"]; - var apiPathGet = apiPath!.operations["get"]; - var response = apiPathGet!.responses!["200"]; - var schema = response!.schema; + final apiPath = doc!.paths!["/api/"]; + final apiPathGet = apiPath!.operations["get"]; + final response = apiPathGet!.responses!["200"]; + final schema = response!.schema; expect(schema!.description, contains("APIVersions lists the")); expect(schema.isRequired, ["versions", "serverAddressByClientCIDRs"]); expect( @@ -94,3 +94,19 @@ void main() { }); }); } + +String fetchKubernetesExample() { + const config = "test/specs/kubernetes.json"; + if (!exists(config)) { + if (!exists(dirname(config))) { + createDir(dirname(config), recursive: true); + } + + fetch( + url: + 'https://raw.githubusercontent.com/kubernetes/kubernetes/f091073b0fb4d3a550e7f182eb5465338c8b7cbf/api/openapi-spec/swagger.json', +// 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/0f9d3ec7c033fef184ec54e1ffc201b2d61ce023/examples/v2.0/json/petstore.json', + saveToPath: config); + } + return config; +} diff --git a/test/v3_test.dart b/test/v3_test.dart index 39052be..b20be28 100644 --- a/test/v3_test.dart +++ b/test/v3_test.dart @@ -1,13 +1,15 @@ -import 'package:open_api/v3.dart'; -import 'package:test/test.dart'; -import 'dart:io'; import 'dart:convert'; +import 'dart:io'; + +import 'package:conduit_open_api/v3.dart'; +import 'package:dcli/dcli.dart'; +import 'package:test/test.dart'; void main() { group("Components and resolution", () { test("Can resolve object against registry", () { final components = APIComponents(); - components.schemas!["foo"] = APISchemaObject.string(); + components.schemas["foo"] = APISchemaObject.string(); final ref = APISchemaObject() ..referenceURI = Uri.parse("/components/schemas/foo"); @@ -15,9 +17,10 @@ void main() { expect(orig!.type, APIType.string); expect(ref.type, isNull); - final APISchemaObject constructed = components - .resolveUri(Uri(path: "/components/schemas/foo")) as APISchemaObject; - expect(constructed.type, APIType.string); + final APISchemaObject? constructed = components + .resolveUri(Uri(path: "/components/schemas/foo")) as APISchemaObject?; + expect(constructed, isNotNull); + expect(constructed!.type, APIType.string); }); test("Invalid ref uri format throws error", () { @@ -26,6 +29,7 @@ void main() { components.resolve(APISchemaObject() ..referenceURI = Uri.parse("#/components/schemas/foo")); expect(true, false); + // ignore: avoid_catching_errors } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); } @@ -34,6 +38,7 @@ void main() { components.resolve(APISchemaObject() ..referenceURI = Uri.parse("#/components/schemas")); expect(true, false); + // ignore: avoid_catching_errors } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); } @@ -42,13 +47,14 @@ void main() { components.resolve(APISchemaObject() ..referenceURI = Uri.parse("/components/foobar/foo")); expect(true, false); + // ignore: avoid_catching_errors } on ArgumentError catch (e) { expect(e.message, contains("Invalid reference URI")); } }); test("Nonexisting component returns null", () { - APIComponents? components = APIComponents(); + final components = APIComponents(); expect( components.resolve(APISchemaObject() ..referenceURI = Uri.parse("/components/schemas/foo")), @@ -70,10 +76,10 @@ void main() { } }); - expect(doc.components!.schemas!["container"]!.referenceURI!.path, + expect(doc.components!.schemas["container"]!.referenceURI!.path, "/components/schemas/string"); - doc.components!.schemas!["other"] = APISchemaObject() + doc.components!.schemas["other"] = APISchemaObject() ..referenceURI = Uri(path: "/components/schemas/container"); final out = doc.asMap(); @@ -89,13 +95,14 @@ void main() { 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 = File("test/specs/stripe.json"); - var contents = file.readAsStringSync(); - original = json.decode(contents); - doc = APIDocument.fromMap(original!); + final String config = fetchStripExample(); + + final file = File(config); + final contents = file.readAsStringSync(); + original = json.decode(contents) as Map; + if (original != null) { + doc = APIDocument.fromMap(original!); + } }); test("Emits same document in asMap()", () { @@ -107,19 +114,20 @@ void main() { }); test("Has info", () { - expect(doc!.info!.title, "Stripe API"); - expect(doc!.info!.version, isNotNull); - expect(doc!.info!.description, + 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(), + 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.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, isNotNull); expect(doc!.servers!.length, 1); expect(doc!.servers!.first!.url.toString(), "https://api.stripe.com/"); expect(doc!.servers!.first!.description, isNull); @@ -132,17 +140,18 @@ void main() { group("Paths", () { test("Sample path 1", () { + expect(doc!.paths, isNotNull); final p = doc!.paths!["/v1/transfers/{transfer}/reversals/{id}"]; expect(p, isNotNull); expect(p!.description, isNull); - expect(p.operations!.length, 2); + expect(p.operations.length, 2); - final getOp = p.operations!["get"]; + final getOp = p.operations["get"]; final getParams = getOp!.parameters; final getResponses = getOp.responses; expect(getOp.description, contains("10 most recent reversals")); - expect(getOp.id, "TransferReversalRetrieve"); + expect(getOp.id, "GetTransfersTransferReversalsId"); expect(getParams!.length, 3); expect(getParams[0]!.location, APIParameterLocation.query); expect(getParams[0]!.description, @@ -185,7 +194,8 @@ void main() { group("Components", () {}); test("Security requirement", () { - expect(doc!.security, isNull); + expect(doc!.security, isNotNull); + expect(doc!.security!.length, 2); }); }); @@ -202,7 +212,7 @@ void main() { } }); - expect(doc.components!.schemas!["freeform"]!.additionalPropertyPolicy, + expect(doc.components!.schemas["freeform"]!.additionalPropertyPolicy, APISchemaAdditionalPropertyPolicy.freeForm); expect( @@ -227,7 +237,7 @@ void main() { } } }); - expect(doc.components!.schemas!["freeform"]!.additionalPropertyPolicy, + expect(doc.components!.schemas["freeform"]!.additionalPropertyPolicy, APISchemaAdditionalPropertyPolicy.freeForm); expect( doc.asMap()["components"]["schemas"]["freeform"]["type"], "object"); @@ -242,7 +252,7 @@ void main() { group("'add' methods", () { test("'addHeader'", () { - var resp = APIResponse("Response"); + final resp = APIResponse("Response"); // when null resp.addHeader( @@ -263,7 +273,7 @@ void main() { }); test("'addContent'", () { - var resp = APIResponse("Response"); + final resp = APIResponse("Response"); // when null resp.addContent("x/a", APISchemaObject.string(format: "initial")); @@ -283,7 +293,7 @@ void main() { }); test("'addResponse'", () { - var op = APIOperation("op", null); + final op = APIOperation("op", null); // when null op.addResponse(200, @@ -324,7 +334,7 @@ void main() { }); test("'addResponse' guards against null value", () { - var op = APIOperation("op", null); + final op = APIOperation("op", null); op.addResponse( 400, @@ -344,3 +354,22 @@ void main() { }); }); } + +String fetchStripExample() { + // 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 + + const config = "test/specs/stripe.json"; + if (!exists(config)) { + if (!exists(dirname(config))) { + createDir(dirname(config), recursive: true); + } + + fetch( + url: + 'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json', + saveToPath: config); + } + return config; +} From 2a3bdee84dadd4f4a730feca021cd28c4e33330e Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Wed, 31 Mar 2021 21:52:18 -0700 Subject: [PATCH 15/15] replace travis with github actions --- .github/workflows/dart.yml | 35 +++++++++++++++++++++++++++++++++++ .travis.yml | 18 ------------------ 2 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/dart.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml new file mode 100644 index 0000000..f66d6b5 --- /dev/null +++ b/.github/workflows/dart.yml @@ -0,0 +1,35 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Dart + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: dart-lang/setup-dart@v1 + with: + sdk: stable + + - name: Install dependencies + run: dart pub get + + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . + + - name: Analyze project source + run: dart analyze --fatal-infos + + - name: Run tests + run: dart test -j1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3669e02..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: dart -dart: - - beta - -before_script: - - mkdir test/specs - -jobs: - include: - - stage: test - script: pub get && pub run test -j1 - -stages: -- test - -branches: - only: - - master