Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullsafety #7

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
35 changes: 35 additions & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ doc/api/
.idea/
*.iml
test/specs/kubernetes.json
test/specs/stripe.json
test/specs/stripe.json
test/specs/petstore-simple.json
20 changes: 0 additions & 20 deletions .travis.yml

This file was deleted.

7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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());
```
Expand Down
16 changes: 1 addition & 15 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -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
20 changes: 11 additions & 9 deletions lib/src/object.dart
Original file line number Diff line number Diff line change
@@ -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<String, dynamic> extensions = {};
Expand All @@ -12,21 +10,25 @@ 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) {
throw new ArgumentError(
final invalidKeys = extensions.keys
.where((key) => !key.startsWith("x-"))
.map((key) => "'$key'")
.toList();
if (invalidKeys.isNotEmpty) {
throw ArgumentError(
"extension keys must start with 'x-'. The following keys are invalid: ${invalidKeys.join(", ")}");
}

extensions.forEach((key, value) {
object.encode(key, value);
});
}
}
}
9 changes: 9 additions & 0 deletions lib/src/util/list_helper.dart
Original file line number Diff line number Diff line change
@@ -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<String>? removeNullsFromList(List<String?>? list) {
if (list == null) return null;

// remove nulls and convert to List<String>
return List.from(list.where((c) => c != null));
}
16 changes: 16 additions & 0 deletions lib/src/util/map_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// Remove any entries with a null value from the list and convert the type.
Map<K, V> removeNullsFromMap<K, V>(Map<K, V?>? map) {
if (map == null) return <K, V>{};

final fixed = <K, V>{};

// remove nulls
for (final key in map.keys) {
final value = map[key];
if (value != null) {
fixed[key] = value;
}
}

return fixed;
}
87 changes: 48 additions & 39 deletions lib/src/v2/document.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -18,54 +20,61 @@ class APIDocument extends APIObject {
}

String version = "2.0";
APIInfo info = new APIInfo();
String host;
String basePath;
APIInfo? info = APIInfo();
String? host;
String? basePath;

List<APITag> tags = [];
List<String> schemes = [];
List<String> consumes = [];
List<String> produces = [];
List<Map<String, List<String>>> security = [];
List<APITag?>? tags = [];
List<String>? schemes = [];
List<String>? consumes = [];
List<String>? produces = [];
List<Map<String, List<String?>>?>? security = [];

Map<String, APIPath> paths = {};
Map<String, APIResponse> responses = {};
Map<String, APIParameter> parameters = {};
Map<String, APISchemaObject> definitions = {};
Map<String, APISecurityScheme> securityDefinitions = {};
Map<String, APIPath?>? paths = {};
Map<String, APIResponse?>? responses = {};
Map<String, APIParameter?>? parameters = {};
Map<String, APISchemaObject?>? definitions = {};
Map<String, APISecurityScheme?>? securityDefinitions = {};

Map<String, dynamic> asMap() {
return KeyedArchive.archive(this, allowReferences: true);
}

@override
Map<String, cast.Cast> 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<String?>?);

/// remove
consumes = removeNullsFromList(object["consumes"] as List<String?>?);
produces = removeNullsFromList(object["produces"] as List<String?>?);
security = object["security"] as List<Map<String, List<String?>>?>;

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());
}

@override
void encode(KeyedArchive object) {
super.encode(object);

Expand Down
14 changes: 8 additions & 6 deletions lib/src/v2/header.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
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 {
APIHeader();

String description;
APIProperty items;
String? description;
APIProperty? items;

@override
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());
}
}

@override
void encode(KeyedArchive json) {
super.encode(json);
json.encode("description", description);
Expand Down
Loading