remove(conduit): deleting conduit packages

This commit is contained in:
Patrick Stewart 2024-08-07 00:35:01 -07:00
parent 9dcc2f5282
commit ef3e955ce0
76 changed files with 0 additions and 265855 deletions

View file

@ -1,29 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library provides hashing functionality for the Protevus Platform.
///
/// It exports two main components:
/// - PBKDF2 (Password-Based Key Derivation Function 2) implementation
/// - Salt generation utilities
///
/// These components are essential for secure password hashing and storage.
library hashing;
export 'package:protevus_hashing/src/pbkdf2.dart';
export 'package:protevus_hashing/src/salt.dart';

View file

@ -1,140 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
/// Instances of this type derive a key from a password, salt, and hash function.
///
/// https://en.wikipedia.org/wiki/PBKDF2
class PBKDF2 {
/// Creates instance capable of generating a key.
///
/// [hashAlgorithm] defaults to [sha256].
PBKDF2({Hash? hashAlgorithm}) {
this.hashAlgorithm = hashAlgorithm ?? sha256;
}
Hash get hashAlgorithm => _hashAlgorithm;
set hashAlgorithm(Hash algorithm) {
_hashAlgorithm = algorithm;
_blockSize = _hashAlgorithm.convert([1, 2, 3]).bytes.length;
}
late Hash _hashAlgorithm;
late int _blockSize;
/// Hashes a [password] with a given [salt].
///
/// The length of this return value will be [keyLength].
///
/// See [generateAsBase64String] for generating a random salt.
///
/// See also [generateBase64Key], which base64 encodes the key returned from this method for storage.
List<int> generateKey(
String password,
String salt,
int rounds,
int keyLength,
) {
if (keyLength > (pow(2, 32) - 1) * _blockSize) {
throw PBKDF2Exception("Derived key too long");
}
final numberOfBlocks = (keyLength / _blockSize).ceil();
final hmac = Hmac(hashAlgorithm, utf8.encode(password));
final key = ByteData(keyLength);
var offset = 0;
final saltBytes = utf8.encode(salt);
final saltLength = saltBytes.length;
final inputBuffer = ByteData(saltBytes.length + 4)
..buffer.asUint8List().setRange(0, saltBytes.length, saltBytes);
for (var blockNumber = 1; blockNumber <= numberOfBlocks; blockNumber++) {
inputBuffer.setUint8(saltLength, blockNumber >> 24);
inputBuffer.setUint8(saltLength + 1, blockNumber >> 16);
inputBuffer.setUint8(saltLength + 2, blockNumber >> 8);
inputBuffer.setUint8(saltLength + 3, blockNumber);
final block = _XORDigestSink.generate(inputBuffer, hmac, rounds);
var blockLength = _blockSize;
if (offset + blockLength > keyLength) {
blockLength = keyLength - offset;
}
key.buffer.asUint8List().setRange(offset, offset + blockLength, block);
offset += blockLength;
}
return key.buffer.asUint8List();
}
/// Hashed a [password] with a given [salt] and base64 encodes the result.
///
/// This method invokes [generateKey] and base64 encodes the result.
String generateBase64Key(
String password,
String salt,
int rounds,
int keyLength,
) {
const converter = Base64Encoder();
return converter.convert(generateKey(password, salt, rounds, keyLength));
}
}
/// Thrown when [PBKDF2] throws an exception.
class PBKDF2Exception implements Exception {
PBKDF2Exception(this.message);
String message;
@override
String toString() => "PBKDF2Exception: $message";
}
class _XORDigestSink implements Sink<Digest> {
_XORDigestSink(ByteData inputBuffer, Hmac hmac) {
lastDigest = hmac.convert(inputBuffer.buffer.asUint8List()).bytes;
bytes = ByteData(lastDigest.length)
..buffer.asUint8List().setRange(0, lastDigest.length, lastDigest);
}
static Uint8List generate(ByteData inputBuffer, Hmac hmac, int rounds) {
final hashSink = _XORDigestSink(inputBuffer, hmac);
// If rounds == 1, we have already run the first hash in the constructor
// so this loop won't run.
for (var round = 1; round < rounds; round++) {
final hmacSink = hmac.startChunkedConversion(hashSink);
hmacSink.add(hashSink.lastDigest);
hmacSink.close();
}
return hashSink.bytes.buffer.asUint8List();
}
late ByteData bytes;
late List<int> lastDigest;
@override
void add(Digest digest) {
lastDigest = digest.bytes;
for (var i = 0; i < digest.bytes.length; i++) {
bytes.setUint8(i, bytes.getUint8(i) ^ lastDigest[i]);
}
}
@override
void close() {}
}

View file

@ -1,34 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
/// Generates a random salt of [length] bytes from a cryptographically secure random number generator.
///
/// Each element of this list is a byte.
List<int> generate(int length) {
final buffer = Uint8List(length);
final rng = Random.secure();
for (var i = 0; i < length; i++) {
buffer[i] = rng.nextInt(256);
}
return buffer;
}
/// Generates a random salt of [length] bytes from a cryptographically secure random number generator and encodes it to Base64.
///
/// [length] is the number of bytes generated, not the [length] of the base64 encoded string returned. Decoding
/// the base64 encoded string will yield [length] number of bytes.
String generateAsBase64String(int length) {
const encoder = Base64Encoder();
return encoder.convert(generate(length));
}

View file

@ -1,31 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library provides functionality for working with isolates in Dart.
/// It exports three main components:
/// 1. Executable: Defines the structure for tasks that can be executed in isolates.
/// 2. Executor: Provides mechanisms for running executables in isolates.
/// 3. SourceGenerator: Offers utilities for generating source code for isolates.
///
/// These components work together to facilitate concurrent programming and
/// improve performance in Dart applications by leveraging isolates.
library isolate;
export 'package:protevus_isolate/src/executable.dart';
export 'package:protevus_isolate/src/executor.dart';
export 'package:protevus_isolate/src/source_generator.dart';

View file

@ -1,63 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:async';
import 'dart:isolate';
import 'dart:mirrors';
abstract class Executable<T extends Object?> {
Executable(this.message) : _sendPort = message["_sendPort"];
Future<T> execute();
final Map<String, dynamic> message;
final SendPort? _sendPort;
U instanceOf<U>(
String typeName, {
List positionalArguments = const [],
Map<Symbol, dynamic> namedArguments = const {},
Symbol constructorName = Symbol.empty,
}) {
ClassMirror? typeMirror = currentMirrorSystem()
.isolate
.rootLibrary
.declarations[Symbol(typeName)] as ClassMirror?;
typeMirror ??= currentMirrorSystem()
.libraries
.values
.where((lib) => lib.uri.scheme == "package" || lib.uri.scheme == "file")
.expand((lib) => lib.declarations.values)
.firstWhere(
(decl) =>
decl is ClassMirror &&
MirrorSystem.getName(decl.simpleName) == typeName,
orElse: () => throw ArgumentError(
"Unknown type '$typeName'. Did you forget to import it?",
),
) as ClassMirror?;
return typeMirror!
.newInstance(
constructorName,
positionalArguments,
namedArguments,
)
.reflectee as U;
}
void send(dynamic message) {
_sendPort!.send(message);
}
void log(String message) {
_sendPort!.send({"_line_": message});
}
}

View file

@ -1,128 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:protevus_isolate/isolate.dart';
class IsolateExecutor<U> {
IsolateExecutor(
this.generator, {
this.packageConfigURI,
this.message = const {},
});
final SourceGenerator generator;
final Map<String, dynamic> message;
final Uri? packageConfigURI;
final Completer completer = Completer();
Stream<dynamic> get events => _eventListener.stream;
Stream<String> get console => _logListener.stream;
final StreamController<String> _logListener = StreamController<String>();
final StreamController<dynamic> _eventListener = StreamController<dynamic>();
Future<U> execute() async {
if (packageConfigURI != null &&
!File.fromUri(packageConfigURI!).existsSync()) {
throw StateError(
"Package file '$packageConfigURI' not found. Run 'pub get' and retry.",
);
}
final scriptSource = Uri.encodeComponent(await generator.scriptSource);
final onErrorPort = ReceivePort()
..listen((err) async {
if (err is List) {
final stack =
StackTrace.fromString(err.last.replaceAll(scriptSource, ""));
completer.completeError(StateError(err.first), stack);
} else {
completer.completeError(err);
}
});
final controlPort = ReceivePort()
..listen((results) {
if (results is Map && results.length == 1) {
if (results.containsKey("_result")) {
completer.complete(results['_result']);
return;
} else if (results.containsKey("_line_")) {
_logListener.add(results["_line_"]);
return;
}
}
_eventListener.add(results);
});
try {
message["_sendPort"] = controlPort.sendPort;
final dataUri = Uri.parse(
"data:application/dart;charset=utf-8,$scriptSource",
);
await Isolate.spawnUri(
dataUri,
[],
message,
onError: onErrorPort.sendPort,
packageConfig: packageConfigURI,
automaticPackageResolution: packageConfigURI == null,
);
return await completer.future;
} catch (e) {
print(e);
rethrow;
} finally {
onErrorPort.close();
controlPort.close();
_eventListener.close();
_logListener.close();
}
}
static Future<T> run<T>(
Executable<T> executable, {
List<String> imports = const [],
Uri? packageConfigURI,
String? additionalContents,
List<Type> additionalTypes = const [],
void Function(dynamic event)? eventHandler,
void Function(String line)? logHandler,
}) async {
final source = SourceGenerator(
executable.runtimeType,
imports: imports,
additionalContents: additionalContents,
additionalTypes: additionalTypes,
);
final executor = IsolateExecutor<T>(
source,
packageConfigURI: packageConfigURI,
message: executable.message,
);
if (eventHandler != null) {
executor.events.listen(eventHandler);
}
if (logHandler != null) {
executor.console.listen(logHandler);
}
return executor.execute();
}
}

View file

@ -1,104 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:io';
import 'dart:isolate';
import 'dart:mirrors';
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/context_builder.dart';
import 'package:analyzer/dart/analysis/context_locator.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:path/path.dart';
import 'package:protevus_isolate/isolate.dart';
class SourceGenerator {
SourceGenerator(
this.executableType, {
this.imports = const [],
this.additionalTypes = const [],
this.additionalContents,
});
Type executableType;
String get typeName =>
MirrorSystem.getName(reflectType(executableType).simpleName);
final List<String> imports;
final String? additionalContents;
final List<Type> additionalTypes;
Future<String> get scriptSource async {
final typeSource = (await _getClass(executableType)).toSource();
final builder = StringBuffer();
builder.writeln("import 'dart:async';");
builder.writeln("import 'dart:isolate';");
builder.writeln("import 'dart:mirrors';");
for (final anImport in imports) {
builder.writeln("import '$anImport';");
}
builder.writeln(
"""
Future main (List<String> args, Map<String, dynamic> message) async {
final sendPort = message['_sendPort'];
final executable = $typeName(message);
final result = await executable.execute();
sendPort.send({"_result": result});
}
""",
);
builder.writeln(typeSource);
builder.writeln((await _getClass(Executable)).toSource());
for (final type in additionalTypes) {
final source = await _getClass(type);
builder.writeln(source.toSource());
}
if (additionalContents != null) {
builder.writeln(additionalContents);
}
return builder.toString();
}
static Future<ClassDeclaration> _getClass(Type type) async {
final uri =
await Isolate.resolvePackageUri(reflectClass(type).location!.sourceUri);
final path =
absolute(normalize(uri!.toFilePath(windows: Platform.isWindows)));
final context = _createContext(path);
final session = context.currentSession;
final unit = session.getParsedUnit(path) as ParsedUnitResult;
final typeName = MirrorSystem.getName(reflectClass(type).simpleName);
return unit.unit.declarations
.whereType<ClassDeclaration>()
.firstWhere((classDecl) => classDecl.name.value() == typeName);
}
}
AnalysisContext _createContext(
String path, {
ResourceProvider? resourceProvider,
}) {
resourceProvider ??= PhysicalResourceProvider.INSTANCE;
final builder = ContextBuilder(resourceProvider: resourceProvider);
final contextLocator = ContextLocator(
resourceProvider: resourceProvider,
);
final root = contextLocator.locateRoots(
includedPaths: [path],
);
return builder.createContext(contextRoot: root.first);
}

View file

@ -1,37 +0,0 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
# protevus_openapi
Reads and writes OpenAPI (Swagger) specifications.
conduit_open_api supports both v2 and v3 of the open_api specification.
To use v2 import:
```dart
import 'package:conduit_open_api/v2.dart';
```
To use v3 import:
```dart
import 'package:conduit_open_api/v3.dart';
```
You can us v2 and v3 within a single project.
Example
---
```dart
import 'package:conduit_open_api/v3.dart';
final file = File("test/specs/kubernetes.json");
final contents = await file.readAsString();
final doc = APIDocument.fromJSON(contents);
final output = JSON.encode(doc.asMap());
```

View file

@ -1,20 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports the 'documentable' module from the Protevus OpenAPI package.
///
/// It provides access to the documentable-related functionality defined in the
/// 'src/documentable.dart' file of the 'protevus_openapi' package.
///
/// This library is useful for working with documentable objects within the
/// Protevus platform, allowing developers to utilize the pre-defined
/// documentable-related features and structures.
library documentable;
export 'package:protevus_openapi/src/documentable.dart';

View file

@ -1,15 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports the 'object' module from the Protevus OpenAPI package.
/// It provides access to the object-related functionality defined in the
/// 'src/object.dart' file of the 'protevus_openapi' package.
library object;
export 'package:protevus_openapi/src/object.dart';

View file

@ -1,525 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:async';
import 'package:protevus_openapi/v3.dart';
import 'package:protevus_openapi/object.dart';
/// Defines methods for documenting OpenAPI components.
///
/// The documentation process calls methods from objects of this type. You implement methods from
/// this interface to add reusable components to your OpenAPI document. You may use these components
/// when documenting other components or when implementing [APIOperationDocumenter].
///
/// You must implement [documentComponents].
///
/// ApplicationChannel, Controller, ManagedEntity, and AuthServer all implement this interface.
///
abstract class APIComponentDocumenter {
/// Instructs this object to add its components to the provided [context].
///
/// You may register components with [context] in this method. The order in which components
/// are registered does not matter.
///
/// Example:
///
/// class Car implements APIComponentDocumenter {
/// @override
/// void documentComponents(APIDocumentContext context) {
/// context.schema.register("Car", APISchemaObject.object({
/// "make": APISchemaObject.string(),
/// "model": APISchemaObject.string(),
/// "year": APISchemaObject.integer(),
/// }));
/// }
/// }
///
/// See [APIDocumentContext] for more details.
void documentComponents(APIDocumentContext context);
}
/// Defines methods for documenting OpenAPI operations in a Controller.
///
/// The documentation process calls these methods for every Controller in your ApplicationChannel.
/// You implement [documentOperations] to create or modify [APIOperation] objects that describe the
/// HTTP operations that a controller handler.
abstract class APIOperationDocumenter {
/// Returns a map of API paths handled by this object.
///
/// This method is implemented by Router to provide the paths of an OpenAPI document
/// and typically shouldn't be overridden by another controller.
Map<String, APIPath> documentPaths(APIDocumentContext context);
/// Documents the API operations handled by this object.
///
/// You implement this method to create or modify [APIOperation] objects that describe the
/// HTTP operations that a controller handles. Each controller in the channel, starting with
/// the entry point, have this method.
///
/// By default, a controller returns the operations created by its linked controllers.
///
/// Endpoint controllers should override this method to create a [Map] of [APIOperation] objects, where the
/// key is a [String] representation of the status code the response is for. Example:
///
/// @override
/// Map<String, APIOperation> documentOperations(APIDocumentContext context, APIPath path) {
/// if (path.containsPathParameters(['id'])) {
/// return {
/// "get": APIOperation("Get one thing", {
/// "200": APIResponse(...)
/// })
/// };
/// }
///
/// return {
/// "get": APIOperation("Get some things", {
/// "200": APIResponse(...)
/// })
/// };
/// }
///
/// Middleware controllers should override this method to call the superclass' implementation (which gathers
/// the operation objects from an endpoint controller) and then modify those operations before returning them.
///
/// @override
/// Map<String, APIOperation> documentOperations(APIDocumentContext context, APIPath path) {
/// final ops = super.documentOperation(context, path);
///
/// // add x-api-key header parameter to each operation
/// ops.values.forEach((op) {
/// op.addParameter(new APIParameter.header("x-api-key, schema: new APISchemaObject.string()));
/// });
///
/// return ops;
/// }
Map<String, APIOperation> documentOperations(
APIDocumentContext context,
String route,
APIPath path,
);
}
/// An object that contains information about [APIDocument] being generated.
///
/// This class serves as a context for the API documentation process, providing access to various
/// component collections and utility methods for managing the documentation generation.
///
/// Component registries for each type of component - e.g. [schema], [responses] - are used to
/// register and reference those types.
class APIDocumentContext {
/// Creates a new [APIDocumentContext] instance.
///
/// This constructor initializes the context with the provided [document] and sets up
/// various [APIComponentCollection] instances for different types of API components.
/// These collections are used to manage and reference reusable components throughout
/// the API documentation process.
///
/// The following component collections are initialized:
/// - [schema]: For reusable [APISchemaObject] components.
/// - [responses]: For reusable [APIResponse] components.
/// - [parameters]: For reusable [APIParameter] components.
/// - [requestBodies]: For reusable [APIRequestBody] components.
/// - [headers]: For reusable [APIHeader] components.
/// - [securitySchemes]: For reusable [APISecurityScheme] components.
/// - [callbacks]: For reusable [APICallback] components.
///
/// Each collection is associated with its corresponding component map in the [document].
APIDocumentContext(this.document)
: schema = APIComponentCollection<APISchemaObject>._(
"schemas",
document.components!.schemas,
),
responses = APIComponentCollection<APIResponse>._(
"responses",
document.components!.responses,
),
parameters = APIComponentCollection<APIParameter>._(
"parameters",
document.components!.parameters,
),
requestBodies = APIComponentCollection<APIRequestBody>._(
"requestBodies",
document.components!.requestBodies,
),
headers = APIComponentCollection<APIHeader>._(
"headers",
document.components!.headers,
),
securitySchemes = APIComponentCollection<APISecurityScheme>._(
"securitySchemes",
document.components!.securitySchemes,
),
callbacks = APIComponentCollection<APICallback>._(
"callbacks",
document.components!.callbacks,
);
/// The OpenAPI document being created and populated during the documentation process.
///
/// This [APIDocument] instance represents the root of the OpenAPI specification
/// structure. It contains all the components, paths, and other information
/// that will be included in the final OpenAPI document.
final APIDocument document;
/// Reusable [APISchemaObject] components.
///
/// This collection manages and provides access to reusable schema components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Schema components are used to define the structure of request and response
/// bodies, as well as other data structures used in the API.
final APIComponentCollection<APISchemaObject> schema;
/// Reusable [APIResponse] components.
///
/// This collection manages and provides access to reusable response components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Response components are used to define standard responses that can be
/// reused across multiple operations in the API, promoting consistency
/// and reducing duplication in the API specification.
final APIComponentCollection<APIResponse> responses;
/// Reusable [APIParameter] components.
///
/// This collection manages and provides access to reusable parameter components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Parameter components are used to define common parameters that can be
/// reused across multiple operations in the API, such as query parameters,
/// path parameters, or header parameters. This promotes consistency and
/// reduces duplication in the API specification.
final APIComponentCollection<APIParameter> parameters;
/// Reusable [APIRequestBody] components.
///
/// This collection manages and provides access to reusable request body components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Request body components are used to define standard request bodies that can be
/// reused across multiple operations in the API, promoting consistency
/// and reducing duplication in the API specification.
final APIComponentCollection<APIRequestBody> requestBodies;
/// Reusable [APIHeader] components.
///
/// This collection manages and provides access to reusable header components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Header components are used to define common headers that can be
/// reused across multiple operations in the API. This promotes consistency
/// and reduces duplication in the API specification. Headers can be used
/// for various purposes, such as authentication tokens, API versioning,
/// or custom metadata.
final APIComponentCollection<APIHeader> headers;
/// Reusable [APISecurityScheme] components.
///
/// This collection manages and provides access to reusable security scheme components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Security scheme components are used to define the security mechanisms that can be
/// used across the API. This includes authentication methods such as API keys,
/// HTTP authentication, OAuth2 flows, and OpenID Connect. By defining these
/// security schemes as reusable components, they can be easily applied to
/// different operations or the entire API, ensuring consistent security
/// documentation and implementation.
final APIComponentCollection<APISecurityScheme> securitySchemes;
/// Reusable [APICallback] components.
///
/// This collection manages and provides access to reusable callback components
/// in the OpenAPI document. These components can be registered, referenced,
/// and retrieved throughout the API documentation process.
///
/// Callback components are used to define asynchronous, out-of-band requests
/// that may be initiated by the API provider after the initial request has been
/// processed. They are typically used for webhooks or other event-driven
/// interactions. By defining callbacks as reusable components, they can be
/// easily referenced and applied to different operations in the API specification,
/// promoting consistency and reducing duplication.
final APIComponentCollection<APICallback> callbacks;
/// A list of deferred operations to be executed during the finalization process.
///
/// This list stores functions that represent asynchronous operations that need to be
/// performed before the API documentation is finalized. These operations are typically
/// added using the [defer] method and are executed in order during the [finalize] process.
List<Function> _deferredOperations = [];
/// Schedules an asynchronous operation to be executed during the documentation process.
///
/// Documentation methods are synchronous. Asynchronous methods may be called and awaited on
/// in [document]. All [document] closures will be executes and awaited on before finishing [document].
/// These closures are called in the order they were added.
void defer(FutureOr Function() document) {
_deferredOperations.add(document);
}
/// Finalizes the API document and returns it as a serializable [Map].
///
/// This method is invoked by the command line tool for creating OpenAPI documents.
Future<Map<String, dynamic>> finalize() async {
final dops = _deferredOperations;
_deferredOperations = [];
await Future.forEach(dops, (Function dop) => dop());
document.paths!.values
.expand((p) => p!.operations.values)
.where((op) => op!.security != null)
.expand((op) => op!.security!)
.forEach((req) {
req.requirements!.forEach((schemeName, scopes) {
final scheme = document.components!.securitySchemes[schemeName];
if (scheme!.type == APISecuritySchemeType.oauth2) {
for (final flow in scheme.flows!.values) {
for (final scope in scopes) {
if (!flow!.scopes!.containsKey(scope)) {
flow.scopes![scope] = "";
}
}
}
}
});
});
return document.asMap();
}
}
/// A collection of reusable OpenAPI objects.
///
/// This class manages a collection of reusable OpenAPI components of type [T],
/// which must extend [APIObject]. It provides methods for registering, retrieving,
/// and referencing components within an OpenAPI document.
///
/// The collection supports two ways of referencing components:
/// 1. By name: Components can be registered with a string name and retrieved using that name.
/// 2. By type: Components can be associated with a Dart Type and retrieved using that Type.
///
/// This class is typically used within an [APIDocumentContext] to manage different
/// types of OpenAPI components such as schemas, responses, parameters, etc.
///
/// Key features:
/// - Register components with [register]
/// - Retrieve components by name with [getObject] or the [] operator
/// - Retrieve components by Type with [getObjectWithType]
/// - Check if a Type has been registered with [hasRegisteredType]
///
/// The class also handles deferred resolution of Type-based references, allowing
/// components to be referenced before they are fully defined.
class APIComponentCollection<T extends APIObject> {
/// Creates a new [APIComponentCollection] instance.
///
/// This constructor is private and is used internally to initialize
/// the component collection with a specific type name and component map.
///
/// [_typeName] is a string that represents the type of components in this collection.
/// It is used to construct the reference URIs for the components.
///
/// [_componentMap] is a map that stores the actual components, with their names as keys.
/// This map is used to register and retrieve components by name.
APIComponentCollection._(this._typeName, this._componentMap);
/// The name of the component type managed by this collection.
///
/// This string is used to construct reference URIs for components in the OpenAPI document.
/// It typically corresponds to the plural form of the component type, such as "schemas",
/// "responses", "parameters", etc.
final String _typeName;
/// A map that stores the components of type [T] with their names as keys.
///
/// This map is used to store and retrieve components that have been registered
/// with the [APIComponentCollection]. The keys are the names given to the
/// components when they are registered, and the values are the actual component
/// objects of type [T].
///
/// This map is populated by the [register] method and accessed by various
/// other methods in the class to retrieve registered components.
final Map<String, T> _componentMap;
/// A map that associates Dart types with their corresponding API components.
///
/// This map is used to store references between Dart types and their registered
/// API components. When a component is registered with a specific type using
/// the [register] method, an entry is added to this map.
///
/// The keys are Dart [Type] objects representing the types associated with
/// the components, and the values are the corresponding API components of type [T].
///
/// This map is used internally to resolve type-based references and to check
/// if a specific type has been registered using [hasRegisteredType].
final Map<Type, T> _typeReferenceMap = {};
/// A map that stores [Completer] objects for deferred type resolution.
///
/// This map is used to handle cases where a component is referenced by its Type
/// before it has been registered. The keys are Dart [Type] objects, and the values
/// are [Completer] objects that will be completed when the corresponding component
/// is registered.
///
/// When a component is requested by type using [getObjectWithType] and it hasn't
/// been registered yet, a new [Completer] is added to this map. Later, when the
/// component is registered using [register], the corresponding [Completer] is
/// completed, allowing any pending references to be resolved.
///
/// This mechanism enables forward references in the API documentation process,
/// allowing components to be used before they are fully defined.
final Map<Type, Completer<T>> _resolutionMap = {};
/// Registers a component with a given name and optionally associates it with a Type.
///
/// [component] will be stored in the OpenAPI document. The component will be usable
/// by other objects by its [name].
///
/// If this component is represented by a class, provide it as [representation].
/// Objects may reference either [name] or [representation] when using a component.
void register(String name, T component, {Type? representation}) {
if (_componentMap.containsKey(name)) {
return;
}
if (representation != null &&
_typeReferenceMap.containsKey(representation)) {
return;
}
_componentMap[name] = component;
if (representation != null) {
final refObject = getObject(name);
_typeReferenceMap[representation] = refObject;
if (_resolutionMap.containsKey(representation)) {
_resolutionMap[representation]!.complete(refObject);
_resolutionMap.remove(representation);
}
}
}
/// Returns a reference object in this collection with the given [name].
///
/// See [getObject].
T operator [](String name) => getObject(name);
/// Returns an object that references a component named [name].
///
/// This method creates and returns a reference object of type [T] that points to
/// a component in the OpenAPI document with the given [name]. The returned object
/// is always a reference; it does not contain the actual values of the component.
///
/// An object is always returned, even if no component named [name] exists.
/// If after [APIDocumentContext.finalize] is called and no object
/// has been registered for [name], an error is thrown.
T getObject(String name) {
final obj = _getInstanceOf();
obj.referenceURI = Uri(path: "/components/$_typeName/$name");
return obj;
}
/// Returns an object that references a component registered for [type].
///
/// This method creates and returns a reference object of type [T] that points to
/// a component in the OpenAPI document associated with the given [type].
///
/// An object is always returned, even if no component named has been registered
/// for [type]. If after [APIDocumentContext.finalize] is called and no object
/// has been registered for [type], an error is thrown.
T getObjectWithType(Type type) {
final obj = _getInstanceOf();
obj.referenceURI =
Uri(path: "/components/$_typeName/conduit-typeref:$type");
if (_typeReferenceMap.containsKey(type)) {
obj.referenceURI = _typeReferenceMap[type]!.referenceURI;
} else {
final completer =
_resolutionMap.putIfAbsent(type, () => Completer<T>.sync());
completer.future.then((refObject) {
obj.referenceURI = refObject.referenceURI;
});
}
return obj;
}
/// Creates and returns an empty instance of type [T].
///
/// This method is used internally to create empty instances of various API components
/// based on the generic type [T]. It supports the following types:
/// - [APISchemaObject]
/// - [APIResponse]
/// - [APIParameter]
/// - [APIRequestBody]
/// - [APIHeader]
/// - [APISecurityScheme]
/// - [APICallback]
///
/// For each supported type, it calls the corresponding `empty()` constructor
/// and casts the result to type [T].
///
/// If [T] is not one of the supported types, this method throws a [StateError]
/// with a message indicating that it cannot reference an API object of that type.
///
/// Returns: An empty instance of type [T].
///
/// Throws: [StateError] if [T] is not a supported API object type.
T _getInstanceOf() {
switch (T) {
case const (APISchemaObject):
return APISchemaObject.empty() as T;
case const (APIResponse):
return APIResponse.empty() as T;
case const (APIParameter):
return APIParameter.empty() as T;
case const (APIRequestBody):
return APIRequestBody.empty() as T;
case const (APIHeader):
return APIHeader.empty() as T;
case const (APISecurityScheme):
return APISecurityScheme.empty() as T;
case const (APICallback):
return APICallback.empty() as T;
}
throw StateError("cannot reference API object of type $T");
}
/// Checks if a specific Type has been registered with this component collection.
///
/// This method returns true if a component has been registered for the given [type]
/// using the [register] method with a non-null [representation] parameter.
///
/// Parameters:
/// [type] - The Type to check for registration.
///
/// Returns:
/// A boolean value indicating whether the [type] has been registered (true) or not (false).
///
/// Example:
/// ```dart
/// final collection = APIComponentCollection<APISchemaObject>(...);
/// collection.register('User', userSchema, representation: User);
///
/// assert(collection.hasRegisteredType(User) == true);
/// assert(collection.hasRegisteredType(String) == false);
/// ```
bool hasRegisteredType(Type type) {
return _typeReferenceMap.containsKey(type);
}
}

View file

@ -1,94 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:meta/meta.dart';
/// Represents an API object with support for custom extensions.
///
/// This class extends [Coding] and provides functionality to handle
/// custom extensions in API objects. Extensions are key-value pairs
/// where keys must start with "x-".
///
/// The [extensions] map stores all custom extension data.
///
/// When decoding, it automatically extracts and stores all extension fields.
/// When encoding, it validates that all extension keys start with "x-" and
/// includes them in the encoded output.
class APIObject extends Coding {
/// A map to store custom extension data for the API object.
///
/// The keys in this map represent extension names, which must start with "x-".
/// The values can be of any type (dynamic) to accommodate various extension data.
///
/// This map is used to store and retrieve custom extensions that are not part of
/// the standard API object properties. It allows for flexibility in adding
/// custom data to API objects without modifying the core structure.
Map<String, dynamic> extensions = {};
/// Decodes the API object from a [KeyedArchive].
///
/// This method overrides the [decode] method from the superclass and adds
/// functionality to handle custom extensions.
///
/// It performs the following steps:
/// 1. Calls the superclass's decode method to handle standard fields.
/// 2. Identifies all keys in the [object] that start with "x-" as extension keys.
/// 3. For each extension key, decodes its value and stores it in the [extensions] map.
///
/// This allows the APIObject to capture and store any custom extensions
/// present in the decoded data, making them accessible via the [extensions] property.
///
/// [object]: The [KeyedArchive] containing the encoded data to be decoded.
@mustCallSuper
@override
void decode(KeyedArchive object) {
super.decode(object);
final extensionKeys = object.keys.where((k) => k.startsWith("x-"));
for (final key in extensionKeys) {
extensions[key] = object.decode(key);
}
}
/// Encodes the API object into a [KeyedArchive].
///
/// This method overrides the [encode] method from the superclass and adds
/// functionality to handle custom extensions.
///
/// It performs the following steps:
/// 1. Validates that all keys in the [extensions] map start with "x-".
/// If any invalid keys are found, it throws an [ArgumentError] with details.
/// 2. Encodes each key-value pair from the [extensions] map into the [object].
///
/// This ensures that all custom extensions are properly encoded and that
/// the extension naming convention (starting with "x-") is enforced.
///
/// Throws:
/// [ArgumentError]: If any extension key does not start with "x-".
///
/// [object]: The [KeyedArchive] where the encoded data will be stored.
@override
@mustCallSuper
void encode(KeyedArchive object) {
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);
});
}
}

View file

@ -1,30 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// Removes null entries from a list of nullable strings and returns a new list of non-nullable strings.
///
/// This function takes a nullable list of nullable strings as input and performs two operations:
/// 1. It removes any null entries from the list.
/// 2. It converts the resulting list to a List<String>.
///
/// If the input list is null, the function returns null.
///
/// Parameters:
/// [list] - The input list of nullable strings (List<String?>?) that may contain null entries.
///
/// Returns:
/// A new List<String> with all non-null entries from the input list, or null if the input list is null.
List<String>? removeNullsFromList(List<String?>? list) {
if (list == null) return null;
// Remove null entries from the list using the 'nonNulls' property
// which returns an Iterable containing only the non-null elements
// Convert the resulting Iterable to a List<String> using toList()
return list.nonNulls.toList();
}

View file

@ -1,42 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// Removes null values from a map and converts its type.
///
/// This function takes a nullable [Map] with potentially nullable values
/// and returns a new [Map] with the following characteristics:
/// - All entries with null values are removed.
/// - The resulting map is non-nullable (both for the map itself and its values).
/// - If the input map is null, an empty map is returned.
///
/// Parameters:
/// [map]: The input map of type `Map<K, V?>?` where `K` is the key type
/// and `V` is the value type.
///
/// Returns:
/// A new `Map<K, V>` with null values removed and non-nullable types.
Map<K, V> removeNullsFromMap<K, V>(Map<K, V?>? map) {
if (map == null) return <K, V>{};
final fixed = <K, V>{};
// Iterate through all keys in the input map
for (final key in map.keys) {
// Get the value associated with the current key
final value = map[key];
// Check if the value is not null
if (value != null) {
// If the value is not null, add it to the 'fixed' map
// This effectively removes all null values from the original map
fixed[key] = value;
}
}
return fixed;
}

View file

@ -1,347 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/util.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents an OpenAPI 2.0 specification document.
///
/// This class encapsulates the structure and content of an OpenAPI 2.0 (formerly known as Swagger) specification.
/// It provides methods for creating, parsing, and serializing OpenAPI documents.
///
/// Key features:
/// - Supports creation of empty documents or parsing from JSON/YAML maps.
/// - Implements the OpenAPI 2.0 structure, including info, paths, definitions, etc.
/// - Provides serialization to and deserialization from map representations.
/// - Includes type casting rules for proper data handling.
///
/// Usage:
/// - Create an empty document: `var doc = APIDocument();`
/// - Parse from a map: `var doc = APIDocument.fromMap(jsonMap);`
/// - Serialize to a map: `var map = doc.asMap();`
///
/// This class is part of the Protevus Platform and adheres to the OpenAPI 2.0 specification.
class APIDocument extends APIObject {
/// Creates an empty APIDocument instance.
///
/// This constructor initializes a new APIDocument object with default values
/// for all its properties. It can be used as a starting point for building
/// an OpenAPI 2.0 specification programmatically.
APIDocument();
/// Creates an APIDocument instance from a decoded JSON or YAML document object.
///
/// This constructor takes a Map<String, dynamic> representation of an OpenAPI 2.0
/// specification and initializes an APIDocument object with its contents.
///
/// The method uses KeyedArchive.unarchive to convert the map into a KeyedArchive,
/// allowing references within the document. It then calls the decode method to
/// populate the APIDocument instance with the data from the KeyedArchive.
///
/// @param map A Map<String, dynamic> containing the decoded JSON or YAML data
/// of an OpenAPI 2.0 specification.
APIDocument.fromMap(Map<String, dynamic> map) {
decode(KeyedArchive.unarchive(map, allowReferences: true));
}
/// The OpenAPI Specification version that this document adheres to.
///
/// This field is required and should always be set to "2.0" for OpenAPI 2.0
/// (Swagger) specifications. It indicates that this APIDocument instance
/// represents an OpenAPI 2.0 specification.
String version = "2.0";
/// The metadata about the API.
///
/// This property contains information such as the API title, description,
/// version, and other relevant metadata. It is represented by an instance
/// of the APIInfo class.
///
/// The field is nullable, but initialized with a default APIInfo instance.
APIInfo? info = APIInfo();
/// The host (name or IP) serving the API.
///
/// This optional field represents the host (name or IP) serving the API.
/// It must include the host name only and should not include the scheme
/// or sub-paths. It may include a port. If not specified, the host serving
/// the documentation is assumed to be the same as the host serving the API.
/// The value MAY be null to indicate that the host is not yet known.
String? host;
/// The base path on which the API is served, relative to the host.
///
/// This optional field represents the base path for all API operations.
/// If specified, it must start with a forward slash ("/"). If not specified,
/// the API is served directly under the host. The value MAY be null to
/// indicate that the base path is not yet known or is the root ("/").
String? basePath;
/// 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](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#operationObject)
/// 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.
///
/// This field is nullable and initialized as an empty list.
List<APITag?>? tags = [];
/// The transfer protocol(s) used by the API.
///
/// This list specifies the transfer protocol(s) that the API supports.
/// Common values include "http", "https", "ws" (WebSocket), and "wss" (secure WebSocket).
/// The order of the protocols does not matter.
///
/// If the schemes is not included, the default scheme to be used is the one used to access
/// the OpenAPI definition itself.
///
/// This field is nullable and initialized as an empty list.
List<String>? schemes = [];
/// The MIME types that the API can consume.
///
/// This list specifies the MIME types of the request payloads that the API can process.
/// Common values might include "application/json", "application/xml", "application/x-www-form-urlencoded", etc.
///
/// If this field is not specified, it is assumed that the API can consume any MIME type.
///
/// This field is nullable and initialized as an empty list.
List<String>? consumes = [];
/// The MIME types that the API can produce.
///
/// This list specifies the MIME types of the response payloads that the API can generate.
/// Common values might include "application/json", "application/xml", "text/plain", etc.
///
/// If this field is not specified, it is assumed that the API can produce any MIME type.
///
/// This field is nullable and initialized as an empty list.
List<String>? produces = [];
/// A list of security requirements for the API.
///
/// Each item in this list is a map representing a security requirement.
/// The keys of these maps are the names of security schemes (as defined in [securityDefinitions]),
/// and their values are lists of scopes required for that scheme.
///
/// An empty list means no security is required.
/// Multiple items in the list represent AND conditions, while multiple entries in a single map represent OR conditions.
///
/// Example:
/// [
/// {"api_key": []},
/// {"oauth2": ["write:pets", "read:pets"]}
/// ]
/// This would require either an API key OR OAuth2 with both write:pets and read:pets scopes.
///
/// This field is nullable and initialized as an empty list.
List<Map<String, List<String?>>?>? security = [];
/// A map of API paths, where each key is a path string and the value is an APIPath object.
///
/// This property represents all the paths available in the API, including their operations,
/// parameters, and responses. Each path is a relative path to an individual endpoint.
/// The path is appended to the basePath in order to construct the full URL.
///
/// The map is nullable and initialized as an empty map. Each APIPath object in the map
/// is also nullable, allowing for flexible path definitions.
///
/// Example:
/// {
/// "/pets": APIPath(...),
/// "/users/{userId}": APIPath(...),
/// }
Map<String, APIPath?>? paths = {};
/// A map of reusable responses that can be used across operations.
///
/// This property defines response objects that can be referenced by multiple
/// operations in the API. Each key in the map is a name for the response,
/// and the corresponding value is an APIResponse object describing the response.
///
/// These responses can be referenced using the '$ref' keyword in operation
/// responses, allowing for reuse and consistency across the API specification.
///
/// The map is nullable, and each APIResponse object within it is also nullable,
/// providing flexibility in defining and referencing responses.
///
/// Example:
/// {
/// "NotFound": APIResponse(...),
/// "InvalidInput": APIResponse(...),
/// }
Map<String, APIResponse?>? responses = {};
/// A map of reusable parameters that can be referenced from operations.
///
/// This property defines parameter objects that can be used across multiple
/// operations in the API. Each key in the map is a unique name for the parameter,
/// and the corresponding value is an APIParameter object describing the parameter.
///
/// These parameters can be referenced using the '$ref' keyword in operation
/// parameters, allowing for reuse and consistency across the API specification.
///
/// The map is nullable, and each APIParameter object within it is also nullable,
/// providing flexibility in defining and referencing parameters.
///
/// Example:
/// {
/// "userId": APIParameter(...),
/// "apiKey": APIParameter(...),
/// }
Map<String, APIParameter?>? parameters = {};
/// A map of reusable schema definitions that can be referenced throughout the API specification.
///
/// This property defines schema objects that can be used to describe complex data structures
/// used in request bodies, response payloads, or as nested properties of other schemas.
/// Each key in the map is a unique name for the schema, and the corresponding value is an
/// APISchemaObject that describes the structure and constraints of the schema.
///
/// These schema definitions can be referenced using the '$ref' keyword in other parts of the
/// API specification, allowing for reuse and simplification of complex data models.
///
/// The map is nullable, and each APISchemaObject within it is also nullable, providing
/// flexibility in defining and referencing schemas.
///
/// Example:
/// {
/// "User": APISchemaObject(...),
/// "Error": APISchemaObject(...),
/// }
Map<String, APISchemaObject?>? definitions = {};
/// A map of security schemes that can be used across the API specification.
///
/// This property defines security scheme objects that can be referenced by the
/// [security] property or an operation's security property. Each key in the map
/// is a unique name for the security scheme, and the corresponding value is an
/// APISecurityScheme object describing the security scheme.
///
/// These security definitions can be used to describe API keys, OAuth2 flows,
/// or other custom security mechanisms required by the API.
///
/// The map is nullable, and each APISecurityScheme object within it is also nullable,
/// providing flexibility in defining and referencing security schemes.
///
/// Example:
/// {
/// "api_key": APISecurityScheme(...),
/// "oauth2": APISecurityScheme(...),
/// }
Map<String, APISecurityScheme?>? securityDefinitions = {};
/// Converts the APIDocument object to a Map<String, dynamic>.
///
/// This method serializes the current APIDocument instance into a Map
/// representation. It uses the KeyedArchive.archive method to perform
/// the serialization, with the allowReferences parameter set to true.
///
/// @return A Map<String, dynamic> containing the serialized data of the APIDocument.
Map<String, dynamic> asMap() {
return KeyedArchive.archive(this, allowReferences: true);
}
/// Defines the type casting rules for specific properties of the APIDocument class.
///
/// This map provides casting instructions for the following properties:
/// - "schemes": A list of strings
/// - "consumes": A list of strings
/// - "produces": A list of strings
/// - "security": A list of maps, where each map has string keys and list of string values
///
/// These casting rules ensure that the data is properly typed when decoded from JSON or YAML.
@override
Map<String, cast.Cast> get castMap => {
"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)))
};
/// Decodes the APIDocument object from a KeyedArchive.
///
/// This method populates the properties of the APIDocument instance using
/// data from the provided KeyedArchive object. It decodes various fields
/// such as version, host, basePath, schemes, consumes, produces, security,
/// info, tags, paths, responses, parameters, definitions, and
/// securityDefinitions.
///
/// The method also removes null values from certain list properties and
/// creates instances of related API objects (e.g., APIInfo, APITag, APIPath)
/// as needed.
///
/// @param object The KeyedArchive containing the encoded APIDocument data.
@override
void decode(KeyedArchive object) {
super.decode(object);
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", () => 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(),
);
}
/// Encodes the APIDocument object into a KeyedArchive.
///
/// This method serializes the properties of the APIDocument instance into
/// the provided KeyedArchive object. It encodes various fields such as
/// version (as "swagger"), host, basePath, schemes, consumes, produces,
/// paths, info, parameters, responses, securityDefinitions, security,
/// tags, and definitions.
///
/// The method uses different encoding techniques based on the property type:
/// - Simple properties are encoded directly.
/// - Object maps are encoded using encodeObjectMap.
/// - Single objects (like info) are encoded using encodeObject.
/// - Lists of objects (like tags) are encoded using encodeObjects.
///
/// @param object The KeyedArchive to encode the APIDocument data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("swagger", version);
object.encode("host", host);
object.encode("basePath", basePath);
object.encode("schemes", schemes);
object.encode("consumes", consumes);
object.encode("produces", produces);
object.encodeObjectMap("paths", paths);
object.encodeObject("info", info);
object.encodeObjectMap("parameters", parameters);
object.encodeObjectMap("responses", responses);
object.encodeObjectMap("securityDefinitions", securityDefinitions);
object.encode("security", security);
object.encodeObjects("tags", tags);
object.encodeObjectMap("definitions", definitions);
}
}

View file

@ -1,87 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/v2.dart';
/// A class representing an API header in the OpenAPI specification.
///
/// This class extends [APIProperty] and provides additional functionality
/// specific to API headers. It includes properties for description and items
/// (for array types), as well as methods for encoding and decoding the header
/// object.
///
/// Properties:
/// - description: A string describing the header.
/// - items: An [APIProperty] object representing the items in an array (only used when type is array).
///
/// The class overrides the [decode] and [encode] methods from [APIProperty]
/// to handle the specific properties of an API header.
class APIHeader extends APIProperty {
/// Default constructor for the APIHeader class.
///
/// Creates a new instance of APIHeader without initializing any properties.
/// Properties can be set after instantiation or through the decode method.
APIHeader();
/// A string that provides a brief description of the header.
///
/// This property can be used to give more context or explanation about
/// the purpose and usage of the header in the API documentation.
String? description;
/// An [APIProperty] object representing the items in an array.
///
/// This property is only used when the [type] is set to [APIType.array].
/// It describes the structure and properties of the individual items
/// within the array. The [items] property can be null if not applicable.
APIProperty? items;
/// Decodes the APIHeader object from a [KeyedArchive].
///
/// This method overrides the [decode] method from [APIProperty] to handle
/// the specific properties of an API header.
///
/// It performs the following operations:
/// 1. Calls the superclass decode method to handle common properties.
/// 2. Decodes the 'description' field from the archive.
/// 3. If the header type is an array, it decodes the 'items' field as an APIProperty.
///
/// Parameters:
/// - object: A [KeyedArchive] containing the encoded header data.
@override
void decode(KeyedArchive object) {
super.decode(object);
description = object.decode("description");
if (type == APIType.array) {
items = object.decodeObject("items", () => APIProperty());
}
}
/// Encodes the APIHeader object into a [KeyedArchive].
///
/// This method overrides the [encode] method from [APIProperty] to handle
/// the specific properties of an API header.
///
/// It performs the following operations:
/// 1. Calls the superclass encode method to handle common properties.
/// 2. Encodes the 'description' field into the archive.
/// 3. If the header type is an array, it encodes the 'items' field as an APIProperty.
///
/// Parameters:
/// - object: A [KeyedArchive] where the encoded header data will be stored.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("description", description);
if (type == APIType.array) {
object.encodeObject("items", items);
}
}
}

View file

@ -1,363 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
/// Represents metadata for an API in the OpenAPI specification.
///
/// This class contains information about the API such as its title, description,
/// version, terms of service, contact information, and license details.
///
/// The [APIInfo] class extends [APIObject] and provides methods to decode from
/// and encode to a [KeyedArchive] object, which is useful for serialization and
/// deserialization of API metadata.
///
/// Properties:
/// - [title]: The title of the API (required).
/// - [description]: A brief description of the API (optional).
/// - [version]: The version of the API (optional).
/// - [termsOfServiceURL]: The URL to the Terms of Service for the API (optional).
/// - [contact]: The contact information for the API (optional).
/// - [license]: The license information for the API (optional).
///
/// The class provides a default constructor [APIInfo()] that initializes all
/// properties with default values. It also overrides [decode] and [encode] methods
/// to handle serialization and deserialization of the API metadata.
class APIInfo extends APIObject {
/// Creates a new instance of APIInfo with default values.
///
/// This constructor initializes an APIInfo object with predefined default
/// values for its properties. These include a default title, description,
/// version, terms of service URL, contact information, and license details.
APIInfo();
/// The title of the API.
///
/// This property represents the name or title of the API as defined in the OpenAPI specification.
/// It is a required field and provides a concise, meaningful name to the API.
String title = "API";
/// A brief description of the API.
///
/// This property provides a more detailed explanation of the API's purpose,
/// functionality, or any other relevant information. It's optional and defaults
/// to "Description" if not specified.
String? description = "Description";
/// The version of the API.
///
/// This property represents the version number of the API as defined in the OpenAPI specification.
/// It is typically a string in the format of "major.minor.patch" (e.g., "1.0.0").
/// The default value is "1.0" if not specified.
String? version = "1.0";
/// The URL to the Terms of Service for the API.
///
/// This property represents the URL where the Terms of Service for the API can be found.
/// It's an optional field in the OpenAPI specification and defaults to an empty string if not specified.
String? termsOfServiceURL = "";
/// The contact information for the API.
///
/// This property contains details about the contact person or organization
/// responsible for the API. It includes information such as name, URL, and email.
/// If not specified, it defaults to an instance of APIContact with default values.
APIContact? contact = APIContact();
/// The license information for the API.
///
/// This property contains details about the license under which the API is provided.
/// It includes information such as the license name and URL.
/// If not specified, it defaults to an instance of APILicense with default values.
APILicense? license = APILicense();
/// Decodes the APIInfo object from a KeyedArchive.
///
/// This method overrides the base decode method to populate the APIInfo
/// properties from a KeyedArchive object. It decodes the following properties:
/// - title: The API title (required, defaults to an empty string if not present)
/// - description: A brief description of the API (optional)
/// - termsOfServiceURL: URL to the terms of service (optional)
/// - contact: Contact information, decoded as an APIContact object (optional)
/// - license: License information, decoded as an APILicense object (optional)
/// - version: The API version (optional)
///
/// The method first calls the superclass decode method, then decodes each
/// specific property of the APIInfo class.
@override
void decode(KeyedArchive object) {
super.decode(object);
title = object.decode<String>("title") ?? '';
description = object.decode("description");
termsOfServiceURL = object.decode("termsOfService");
contact = object.decodeObject("contact", () => APIContact());
license = object.decodeObject("license", () => APILicense());
version = object.decode("version");
}
/// Encodes the APIInfo object into a KeyedArchive.
///
/// This method overrides the base encode method to serialize the APIInfo
/// properties into a KeyedArchive object. It encodes the following properties:
/// - title: The API title
/// - description: A brief description of the API
/// - version: The API version
/// - termsOfService: URL to the terms of service
/// - contact: Contact information, encoded as an APIContact object
/// - license: License information, encoded as an APILicense object
///
/// The method first calls the superclass encode method, then encodes each
/// specific property of the APIInfo class.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("title", title);
object.encode("description", description);
object.encode("version", version);
object.encode("termsOfService", termsOfServiceURL);
object.encodeObject("contact", contact);
object.encodeObject("license", license);
}
}
/// Represents contact information in the OpenAPI specification.
///
/// This class extends [APIObject] and provides properties and methods to
/// handle contact details for an API, including name, URL, and email.
///
/// Properties:
/// - [name]: The name of the contact person or organization.
/// - [url]: The URL pointing to the contact information.
/// - [email]: The email address of the contact person or organization.
///
/// The class provides methods to decode from and encode to a [KeyedArchive] object,
/// which is useful for serialization and deserialization of contact information.
class APIContact extends APIObject {
/// Creates a new instance of APIContact with default values.
///
/// This constructor initializes an APIContact object with predefined default
/// values for its properties. These include a default name, URL, and email address.
APIContact();
/// Decodes the APIContact object from a KeyedArchive.
///
/// This method overrides the base decode method to populate the APIContact
/// properties from a KeyedArchive object. It decodes the following properties:
/// - name: The name of the contact person or organization (defaults to "default" if not present)
/// - url: The URL pointing to the contact information (defaults to "http://localhost" if not present)
/// - email: The email address of the contact person or organization (defaults to "default" if not present)
///
/// The method first calls the superclass decode method, then decodes each
/// specific property of the APIContact class, providing default values if
/// the properties are not present in the KeyedArchive.
///
/// Parameters:
/// object: The KeyedArchive containing the encoded APIContact data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name") ?? "default";
url = object.decode("url") ?? "http://localhost";
email = object.decode("email") ?? "default";
}
/// The name of the contact person or organization.
///
/// This property represents the name associated with the API contact information.
/// It defaults to "default" if not specified.
String name = "default";
/// The URL pointing to the contact information.
///
/// This property represents the URL associated with the API contact information.
/// It provides a web address where users can find more details about the contact.
/// The default value is "http://localhost" if not specified.
String url = "http://localhost";
/// The email address of the contact person or organization.
///
/// This property represents the email address associated with the API contact information.
/// It provides a means of electronic communication for users or developers who need to
/// reach out regarding the API. The default value is "default" if not specified.
String email = "default";
/// Encodes the APIContact object into a KeyedArchive.
///
/// This method overrides the base encode method to serialize the APIContact
/// properties into a KeyedArchive object. It encodes the following properties:
/// - name: The name of the contact person or organization
/// - url: The URL pointing to the contact information
/// - email: The email address of the contact person or organization
///
/// The method first calls the superclass encode method, then encodes each
/// specific property of the APIContact class.
///
/// Parameters:
/// object: The KeyedArchive to encode the APIContact data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("name", name);
object.encode("url", url);
object.encode("email", email);
}
}
/// Represents a copyright/open source license in the OpenAPI specification.
///
/// This class extends [APIObject] and provides properties and methods to
/// handle license information for an API, including the license name and URL.
///
/// Properties:
/// - [name]: The name of the license.
/// - [url]: The URL where the license can be viewed.
///
/// The class provides methods to decode from and encode to a [KeyedArchive] object,
/// which is useful for serialization and deserialization of license information.
class APILicense extends APIObject {
/// Creates a new instance of APILicense with default values.
///
/// This constructor initializes an APILicense object with predefined default
/// values for its properties. These include a default name and URL for the license.
APILicense();
/// Decodes the APILicense object from a KeyedArchive.
///
/// This method overrides the base decode method to populate the APILicense
/// properties from a KeyedArchive object. It decodes the following properties:
/// - name: The name of the license (defaults to "default" if not present)
/// - url: The URL where the license can be viewed (defaults to "http://localhost" if not present)
///
/// The method first calls the superclass decode method, then decodes each
/// specific property of the APILicense class, providing default values if
/// the properties are not present in the KeyedArchive.
///
/// Parameters:
/// object: The KeyedArchive containing the encoded APILicense data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name") ?? "default";
url = object.decode("url") ?? "http://localhost";
}
/// The name of the license.
///
/// This property represents the name of the license associated with the API.
/// It provides a short identifier for the license type, such as "MIT", "Apache 2.0", etc.
/// The default value is "default" if not specified.
String name = "default";
/// The URL where the license can be viewed.
///
/// This property represents the URL associated with the API license information.
/// It provides a web address where users can find the full text or details of the license.
/// The default value is "http://localhost" if not specified.
String url = "http://localhost";
/// Encodes the APILicense object into a KeyedArchive.
///
/// This method overrides the base encode method to serialize the APILicense
/// properties into a KeyedArchive object. It encodes the following properties:
/// - name: The name of the license
/// - url: The URL where the license can be viewed
///
/// The method first calls the superclass encode method, then encodes each
/// specific property of the APILicense class.
///
/// Parameters:
/// object: The KeyedArchive to encode the APILicense data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("name", name);
object.encode("url", url);
}
}
/// Represents a tag in the OpenAPI specification.
///
/// This class extends [APIObject] and provides properties and methods to
/// handle tag information for an API, including the tag name and description.
///
/// Properties:
/// - [name]: The name of the tag.
/// - [description]: A short description of the tag.
///
/// The class provides methods to decode from and encode to a [KeyedArchive] object,
/// which is useful for serialization and deserialization of tag information.
class APITag extends APIObject {
/// Creates a new instance of APITag.
///
/// This constructor initializes an APITag object without setting any default values.
/// The [name] and [description] properties will be null until explicitly set or
/// populated through the [decode] method.
APITag();
/// Decodes the APITag object from a KeyedArchive.
///
/// This method overrides the base decode method to populate the APITag
/// properties from a KeyedArchive object. It decodes the following properties:
/// - name: The name of the tag
/// - description: A short description of the tag
///
/// The method first calls the superclass decode method, then decodes each
/// specific property of the APITag class.
///
/// Parameters:
/// object: The KeyedArchive containing the encoded APITag data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
description = object.decode("description");
}
/// The name of the tag.
///
/// This property represents the name of the tag associated with the API.
/// It is used to group operations in the API documentation.
/// The value can be null if not specified.
String? name;
/// A short description of the tag.
///
/// This property provides a brief explanation of the tag's purpose or meaning.
/// It can be used to give more context about how the tag is used in the API.
/// The value can be null if no description is provided.
String? description;
/// Encodes the APITag object into a KeyedArchive.
///
/// This method overrides the base encode method to serialize the APITag
/// properties into a KeyedArchive object. It encodes the following properties:
/// - name: The name of the tag
/// - description: A short description of the tag
///
/// The method first calls the superclass encode method, then encodes each
/// specific property of the APITag class.
///
/// Parameters:
/// object: The KeyedArchive to encode the APITag data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("name", name);
object.encode("description", description);
}
}

View file

@ -1,218 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents a HTTP operation (a path/method pair) in the OpenAPI specification.
///
/// This class extends [APIObject] and provides properties and methods to handle
/// various aspects of an API operation, including:
/// - Tags associated with the operation
/// - Summary and description of the operation
/// - Consumes and produces content types
/// - Deprecated status
/// - Parameters and responses
/// - Security requirements
///
/// The class implements [Codable] interface through [APIObject], allowing
/// for easy encoding and decoding of the operation data.
class APIOperation extends APIObject {
/// Creates a new instance of [APIOperation].
///
/// This constructor initializes a new [APIOperation] object without any
/// predefined values. Properties can be set after initialization.
APIOperation();
/// Defines the type casting rules for specific properties of the [APIOperation] class.
///
/// This getter method returns a [Map] where the keys are property names and the values
/// are [cast.Cast] objects that define how these properties should be type-casted.
///
/// The map includes the following castings:
/// - "tags": A list of strings
/// - "consumes": A list of strings
/// - "produces": A list of strings
/// - "schemes": A list of strings
/// - "security": A list of maps, where each map has string keys and list of string values
///
/// This casting information is used to ensure type safety when decoding JSON data
/// into [APIOperation] objects.
@override
Map<String, cast.Cast> get castMap => {
"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))),
};
/// A brief summary of the operation.
///
/// This property provides a short description of what the operation does.
/// It's typically used to give a quick overview of the operation's purpose
/// in API documentation or user interfaces.
String? summary = "";
/// A detailed description of the operation.
///
/// This property provides a more comprehensive explanation of what the operation does,
/// how it works, and any important details that users or developers should know.
/// It can include information about request/response formats, authentication requirements,
/// or any other relevant details about the operation's behavior.
String? description = "";
/// The unique identifier for this operation.
///
/// This property represents the operationId as defined in the OpenAPI specification.
/// It's used to uniquely identify an operation within an API. The operationId is often
/// used as a reference point in documentation, client libraries, and other tooling.
String? id;
/// Indicates whether this operation is deprecated.
///
/// If set to `true`, it means that the operation is still functional but its
/// use is discouraged. Clients should migrate away from using this operation.
/// If `false` or `null`, the operation is considered active and recommended for use.
bool? deprecated;
/// A list of tags associated with this operation.
///
/// Tags are used to group operations in the OpenAPI specification. They can be used
/// for logical grouping of operations by resources or any other qualifier.
/// This property is nullable and can be an empty list if no tags are specified.
List<String?>? tags = [];
/// A list of transfer protocols supported by this operation.
///
/// This property specifies the schemes (such as 'http', 'https', 'ws', 'wss')
/// that the operation supports. It's typically used to indicate which
/// protocols can be used to access the API endpoint.
/// The list can be empty if no specific schemes are defined.
List<String?>? schemes = [];
/// A list of MIME types the operation can consume.
///
/// This property specifies the MIME types of the request payload that the operation
/// can process. It indicates the content types that the client can send in the request body.
/// Common values include 'application/json', 'application/xml', 'multipart/form-data', etc.
/// The list can be empty if the operation doesn't consume any specific MIME types.
List<String?>? consumes = [];
/// A list of MIME types the operation can produce.
///
/// This property specifies the MIME types of the response payload that the operation
/// can generate. It indicates the content types that the server will send in the response body.
/// Common values include 'application/json', 'application/xml', 'text/plain', etc.
/// The list can be empty if the operation doesn't produce any specific MIME types.
List<String?>? produces = [];
/// A list of parameters for this operation.
///
/// This property contains a list of [APIParameter] objects that define the
/// parameters accepted by the operation. These parameters can include path
/// parameters, query parameters, header parameters, and body parameters.
/// Each parameter specifies details such as name, location (in), type, and
/// whether it's required.
///
/// The list can be empty if the operation doesn't require any parameters.
/// It's nullable to allow for cases where parameters are not specified.
List<APIParameter?>? parameters = [];
/// A list of security requirements for this operation.
///
/// This property defines the security schemes that apply to this operation.
/// Each item in the list is a map where:
/// - The key is the name of a security scheme defined in the global 'securityDefinitions'.
/// - The value is a list of scopes required for this operation (can be an empty list if no scopes are required).
///
/// Multiple items in the list indicate that multiple security schemes can be used (OR relationship).
/// An empty list means that no security is required for this operation.
/// If this property is null, it inherits the global security requirements.
List<Map<String, List<String>>?>? security = [];
/// A map of possible responses from this operation.
///
/// The keys of this map are HTTP status codes (as strings), and the values
/// are [APIResponse] objects describing the response for that status code.
///
/// For example, '200' might map to a successful response, '400' to a bad request
/// response, and so on. The map can include a 'default' key to describe the
/// response for any undocumented status codes.
///
/// This property is nullable, allowing for operations that don't specify
/// their responses explicitly.
Map<String, APIResponse?>? responses = {};
/// Decodes the [APIOperation] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIOperation]
/// instance from the provided [KeyedArchive] object. It decodes various fields
/// such as tags, summary, description, operationId, consumes, produces, deprecated
/// status, parameters, responses, schemes, and security requirements.
///
/// The method first calls the superclass's decode method to handle any base
/// properties, then proceeds to decode specific [APIOperation] properties.
///
/// For complex properties like parameters and responses, it uses specialized
/// decoding methods that create new instances of [APIParameter] and [APIResponse]
/// respectively.
///
/// @param object The [KeyedArchive] containing the encoded data to be decoded.
@override
void decode(KeyedArchive object) {
super.decode(object);
tags = object.decode("tags");
summary = object.decode("summary");
description = object.decode("description");
id = object.decode("operationId");
consumes = object.decode("consumes");
produces = object.decode("produces");
deprecated = object.decode("deprecated");
parameters = object.decodeObjects("parameters", () => APIParameter());
responses = object.decodeObjectMap("responses", () => APIResponse());
schemes = object.decode("schemes");
security = object.decode("security");
}
/// Encodes the [APIOperation] object into a [KeyedArchive].
///
/// This method is responsible for encoding the properties of the [APIOperation]
/// instance into the provided [KeyedArchive] object. It encodes various fields
/// such as tags, summary, description, operationId, consumes, produces, deprecated
/// status, parameters, responses, and security requirements.
///
/// The method first calls the superclass's encode method to handle any base
/// properties, then proceeds to encode specific [APIOperation] properties.
///
/// For complex properties like parameters and responses, it uses specialized
/// encoding methods that handle lists of objects and object maps respectively.
///
/// @param object The [KeyedArchive] where the encoded data will be stored.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("tags", tags);
object.encode("summary", summary);
object.encode("description", description);
object.encode("operationId", id);
object.encode("consumes", consumes);
object.encode("produces", produces);
object.encode("deprecated", deprecated);
object.encodeObjects("parameters", parameters);
object.encodeObjectMap("responses", responses);
object.encode("security", security);
}
}

View file

@ -1,241 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/v2.dart';
/// Enum representing the possible locations of a parameter in an API request.
///
/// The possible values are:
/// - [query]: Parameter is passed as a query parameter in the URL
/// - [header]: Parameter is included in the request headers
/// - [path]: Parameter is part of the URL path
/// - [formData]: Parameter is sent as form data in the request body
/// - [body]: Parameter is sent in the request body (typically as JSON)
enum APIParameterLocation { query, header, path, formData, body }
/// A utility class for encoding and decoding [APIParameterLocation] enum values.
class APIParameterLocationCodec {
/// Decodes a string representation of an API parameter location into an [APIParameterLocation] enum value.
///
/// This method takes a [String] parameter [location] and returns the corresponding
/// [APIParameterLocation] enum value. If the input string doesn't match any known
/// location, the method returns null.
///
/// Parameters:
/// [location] - A string representing the API parameter location.
///
/// Returns:
/// The corresponding [APIParameterLocation] enum value, or null if no match is found.
static APIParameterLocation? decode(String? location) {
switch (location) {
case "query":
return APIParameterLocation.query;
case "header":
return APIParameterLocation.header;
case "path":
return APIParameterLocation.path;
case "formData":
return APIParameterLocation.formData;
case "body":
return APIParameterLocation.body;
default:
return null;
}
}
/// Encodes an [APIParameterLocation] enum value into its string representation.
///
/// This method takes an [APIParameterLocation] enum value as input and returns
/// the corresponding string representation. If the input is null or doesn't
/// match any known location, the method returns null.
///
/// Parameters:
/// [location] - An [APIParameterLocation] enum value to be encoded.
///
/// Returns:
/// A [String] representing the API parameter location, or null if the input is null or invalid.
static String? encode(APIParameterLocation? location) {
switch (location) {
case APIParameterLocation.query:
return "query";
case APIParameterLocation.header:
return "header";
case APIParameterLocation.path:
return "path";
case APIParameterLocation.formData:
return "formData";
case APIParameterLocation.body:
return "body";
default:
return null;
}
}
}
/// Represents a parameter in the OpenAPI specification.
///
/// This class extends [APIProperty] and provides additional functionality
/// specific to API parameters. It includes properties such as name, description,
/// required status, and location of the parameter.
///
/// The class supports two main parameter types:
/// 1. Body parameters: When [location] is [APIParameterLocation.body], it uses
/// [schema] to define the parameter structure.
/// 2. Non-body parameters: For all other locations, it uses properties inherited
/// from [APIProperty] and adds [allowEmptyValue] and [items] for array types.
///
/// The class implements [Codable] through its superclass, providing [decode] and
/// [encode] methods for serialization and deserialization.
class APIParameter extends APIProperty {
/// Default constructor for [APIParameter].
///
/// Creates a new instance of [APIParameter] with default values.
/// All properties are initialized to their default values as defined in the class.
APIParameter();
/// The name of the API parameter.
///
/// This property represents the name of the parameter as defined in the API specification.
/// It can be null if the name is not specified or not applicable.
String? name;
/// A description of the API parameter.
///
/// This property provides additional information about the parameter,
/// explaining its purpose, expected format, or any other relevant details.
/// It can be null if no description is provided.
String? description;
/// Indicates whether the parameter is required.
///
/// This boolean property determines if the API parameter is mandatory (true) or optional (false).
/// By default, it is set to false, meaning the parameter is optional unless specified otherwise.
bool isRequired = false;
/// The location of the API parameter.
///
/// This property specifies where the parameter should be placed in the API request.
/// It can be one of the following values from the [APIParameterLocation] enum:
/// - [APIParameterLocation.query]: Parameter is included in the URL query string
/// - [APIParameterLocation.header]: Parameter is included in the request headers
/// - [APIParameterLocation.path]: Parameter is part of the URL path
/// - [APIParameterLocation.formData]: Parameter is sent as form data in the request body
/// - [APIParameterLocation.body]: Parameter is sent in the request body (typically as JSON)
///
/// The location can be null if it's not specified or not applicable.
APIParameterLocation? location;
/// The schema object for the API parameter.
///
/// This property is used when the [location] is [APIParameterLocation.body].
/// It defines the structure and validation rules for the parameter in the request body.
/// The schema is represented as an [APISchemaObject], which can describe complex
/// data structures including nested objects and arrays.
///
/// This property is null for parameters that are not in the body location.
APISchemaObject? schema;
/// Indicates whether an empty value is allowed for this parameter.
///
/// This property is only applicable for non-body parameters (i.e., when [location] is not [APIParameterLocation.body]).
/// When set to true, it allows the parameter to be sent with an empty value.
/// By default, it is set to false, meaning empty values are not allowed unless explicitly specified.
bool allowEmptyValue = false;
/// Represents the items of an array-type parameter.
///
/// This property is only used when [type] is [APIType.array]. It defines the
/// schema for the items in the array. The [APIProperty] object describes the
/// structure and validation rules for each item in the array.
///
/// This property is null for non-array parameters or when not specified.
APIProperty? items;
/// Decodes the parameter from a [KeyedArchive] object.
///
/// This method populates the properties of the [APIParameter] instance
/// by decoding values from the provided [KeyedArchive] object. It handles
/// both body and non-body parameters differently:
///
/// For all parameters:
/// - Decodes 'name', 'description', and 'in' (location) properties.
/// - Sets 'isRequired' based on the location or the 'required' field.
///
/// For body parameters ([APIParameterLocation.body]):
/// - Decodes the 'schema' object.
///
/// For non-body parameters:
/// - Calls the superclass decode method to handle common properties.
/// - Decodes 'allowEmptyValue'.
/// - For array types, decodes the 'items' property.
///
/// Parameters:
/// [object] - The [KeyedArchive] containing the encoded parameter data.
@override
void decode(KeyedArchive object) {
name = object.decode("name");
description = object.decode("description");
location = APIParameterLocationCodec.decode(object.decode("in"));
if (location == APIParameterLocation.path) {
isRequired = true;
} else {
isRequired = object.decode("required") ?? false;
}
if (location == APIParameterLocation.body) {
schema = object.decodeObject("schema", () => APISchemaObject());
} else {
super.decode(object);
allowEmptyValue = object.decode("allowEmptyValue") ?? false;
if (type == APIType.array) {
items = object.decodeObject("items", () => APIProperty());
}
}
}
/// Encodes the parameter into a [KeyedArchive] object.
///
/// This method serializes the properties of the [APIParameter] instance
/// into the provided [KeyedArchive] object. It handles both body and
/// non-body parameters differently:
///
/// For all parameters:
/// - Encodes 'name', 'description', 'in' (location), and 'required' properties.
///
/// For body parameters ([APIParameterLocation.body]):
/// - Encodes the 'schema' object.
///
/// For non-body parameters:
/// - Calls the superclass encode method to handle common properties.
/// - Encodes 'allowEmptyValue' if it's true.
/// - For array types, encodes the 'items' property.
///
/// Parameters:
/// [object] - The [KeyedArchive] to store the encoded parameter data.
@override
void encode(KeyedArchive object) {
object.encode("name", name);
object.encode("description", description);
object.encode("in", APIParameterLocationCodec.encode(location));
object.encode("required", isRequired);
if (location == APIParameterLocation.body) {
object.encodeObject("schema", schema);
} else {
super.encode(object);
if (allowEmptyValue) {
object.encode("allowEmptyValue", allowEmptyValue);
}
if (type == APIType.array) {
object.encodeObject("items", items);
}
}
}
}

View file

@ -1,103 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents an API path in the OpenAPI specification.
///
/// This class extends [APIObject] and provides functionality to decode and encode
/// API path information, including parameters and operations.
///
/// Properties:
/// - [parameters]: A list of [APIParameter] objects associated with this path.
/// - [operations]: A map of operation names to [APIOperation] objects for this path.
///
/// The [decode] method populates the object from a [KeyedArchive], handling parameters
/// and operations separately. The [encode] method serializes the object back into
/// a [KeyedArchive].
///
/// Note: The handling of '$ref' keys is currently a todo item.
class APIPath extends APIObject {
/// Creates a new instance of [APIPath].
///
/// This constructor initializes an empty [APIPath] object.
/// The [parameters] list and [operations] map are initialized as empty
/// and can be populated later using the [decode] method or by directly
/// adding elements.
APIPath();
/// A list of API parameters associated with this path.
///
/// This list contains [APIParameter] objects that define the parameters
/// applicable to all operations on this path. These parameters can include
/// path parameters, query parameters, header parameters, etc.
///
/// Note: The list can contain null values, hence the use of [APIParameter?].
List<APIParameter?> parameters = [];
/// A map of operation names to [APIOperation] objects for this path.
///
/// This map contains the HTTP methods (e.g., 'get', 'post', 'put', 'delete')
/// as keys, and their corresponding [APIOperation] objects as values.
///
/// Each [APIOperation] describes the details of the API operation for that
/// specific HTTP method on this path.
///
/// The use of [APIOperation?] allows for null values in the map.
Map<String, APIOperation?> operations = {};
/// Decodes the [APIPath] object from a [KeyedArchive].
///
/// This method populates the [APIPath] object with data from the provided [KeyedArchive].
/// It handles the following cases:
/// - If a key is "$ref", it's currently not implemented (todo).
/// - If a key is "parameters", it decodes a list of [APIParameter] objects.
/// - For all other keys, it assumes they are operation names and decodes them as [APIOperation] objects.
///
/// The decoded parameters are stored in the [parameters] list, while the operations
/// are stored in the [operations] map with their corresponding keys.
///
/// [object] is the [KeyedArchive] containing the encoded [APIPath] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
for (final k in object.keys) {
if (k == r"$ref") {
// todo: reference
} else if (k == "parameters") {
parameters = object.decodeObjects(k, () => APIParameter())!;
} else {
operations[k] = object.decodeObject(k, () => APIOperation());
}
}
}
/// Encodes the [APIPath] object into a [KeyedArchive].
///
/// This method serializes the [APIPath] object's data into the provided [KeyedArchive].
/// It performs the following operations:
/// - Calls the superclass's encode method to handle any base class properties.
/// - Encodes the [parameters] list into the archive under the key "parameters".
/// - Iterates through the [operations] map, encoding each operation into the archive
/// using its operation name as the key.
///
/// [object] is the [KeyedArchive] where the encoded data will be stored.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encodeObjects("parameters", parameters);
operations.forEach((opName, op) {
object.encodeObject(opName, op);
});
}
}

View file

@ -1,388 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents the different types of schema representations in an API.
///
/// This enumeration defines the possible structural representations of an API schema:
/// - [primitive]: Represents basic data types like strings, numbers, booleans, etc.
/// - [array]: Represents a list or collection of items.
/// - [object]: Represents a complex type with key-value pairs.
/// - [structure]: Represents a structured data type (e.g., a custom object).
/// - [unknownOrInvalid]: Represents an unknown or invalid schema representation.
enum APISchemaRepresentation {
primitive,
array,
object,
structure,
unknownOrInvalid
}
/// Represents the different formats for collection parameters in API requests.
///
/// This enumeration defines the possible formats for serializing array parameters:
/// - [csv]: Comma-separated values (e.g., "foo,bar,baz")
/// - [ssv]: Space-separated values (e.g., "foo bar baz")
/// - [tsv]: Tab-separated values (e.g., "foo\tbar\tbaz")
/// - [pipes]: Pipe-separated values (e.g., "foo|bar|baz")
enum APICollectionFormat { csv, ssv, tsv, pipes }
/// A utility class for encoding and decoding [APICollectionFormat] values.
///
/// This class provides static methods to convert between string representations
/// and [APICollectionFormat] enum values.
/// Decodes a string representation into an [APICollectionFormat] value.
///
/// Takes a [String] input and returns the corresponding [APICollectionFormat],
/// or `null` if the input doesn't match any known format.
///
/// - Returns [APICollectionFormat.csv] for "csv"
/// - Returns [APICollectionFormat.ssv] for "ssv"
/// - Returns [APICollectionFormat.tsv] for "tsv"
/// - Returns [APICollectionFormat.pipes] for "pipes"
/// - Returns `null` for any other input
class APICollectionFormatCodec {
/// Decodes a string representation into an [APICollectionFormat] value.
///
/// Takes a [String] input and returns the corresponding [APICollectionFormat],
/// or `null` if the input doesn't match any known format.
///
/// - Returns [APICollectionFormat.csv] for "csv"
/// - Returns [APICollectionFormat.ssv] for "ssv"
/// - Returns [APICollectionFormat.tsv] for "tsv"
/// - Returns [APICollectionFormat.pipes] for "pipes"
/// - Returns `null` for any other input
static APICollectionFormat? decode(String? location) {
switch (location) {
case "csv":
return APICollectionFormat.csv;
case "ssv":
return APICollectionFormat.ssv;
case "tsv":
return APICollectionFormat.tsv;
case "pipes":
return APICollectionFormat.pipes;
default:
return null;
}
}
/// Encodes an [APICollectionFormat] value into its string representation.
///
/// Takes an [APICollectionFormat] input and returns the corresponding string,
/// or `null` if the input is null or doesn't match any known format.
///
/// - Returns "csv" for [APICollectionFormat.csv]
/// - Returns "ssv" for [APICollectionFormat.ssv]
/// - Returns "tsv" for [APICollectionFormat.tsv]
/// - Returns "pipes" for [APICollectionFormat.pipes]
/// - Returns `null` for any other input or null
static String? encode(APICollectionFormat? location) {
switch (location) {
case APICollectionFormat.csv:
return "csv";
case APICollectionFormat.ssv:
return "ssv";
case APICollectionFormat.tsv:
return "tsv";
case APICollectionFormat.pipes:
return "pipes";
default:
return null;
}
}
}
/// Represents a property in an API schema.
///
/// This class extends [APIObject] and provides fields and methods to handle
/// various aspects of an API property, including its type, format, constraints,
/// and serialization behavior.
///
/// Properties:
/// - [type]: The data type of the property (e.g., string, number, array).
/// - [format]: Additional format information for the property.
/// - [collectionFormat]: Format for serializing array parameters.
/// - [defaultValue]: Default value for the property.
/// - [maximum]: Maximum value for numeric properties.
/// - [exclusiveMaximum]: Whether the maximum is exclusive.
/// - [minimum]: Minimum value for numeric properties.
/// - [exclusiveMinimum]: Whether the minimum is exclusive.
/// - [maxLength]: Maximum length for string properties.
/// - [minLength]: Minimum length for string properties.
/// - [pattern]: Regular expression pattern for string properties.
/// - [maxItems]: Maximum number of items for array properties.
/// - [minItems]: Minimum number of items for array properties.
/// - [uniqueItems]: Whether array items must be unique.
/// - [multipleOf]: Numeric properties must be multiples of this value.
/// - [enumerated]: List of allowed values for the property.
///
/// The [representation] getter determines the schema representation of the property.
///
/// This class also implements [decode] and [encode] methods for serialization
/// and deserialization of the property data.
class APIProperty extends APIObject {
/// The data type of the property.
///
/// This field represents the primary type of the API property, such as string,
/// number, boolean, array, or object. It is defined as nullable to allow for
/// cases where the type might not be specified.
///
/// The value is of type [APIType], which is likely an enum or similar type
/// defining the possible data types in the API schema.
APIType? type;
/// The format of the property.
///
/// This field provides additional format information for the property.
/// It can specify more precise data types or constraints, such as:
/// - For strings: "date", "date-time", "password", etc.
/// - For numbers: "float", "double", etc.
///
/// The format is optional and can be null if not specified.
String? format;
/// The format used for serializing array parameters.
///
/// This property specifies how collection/array parameters are formatted when sent in
/// API requests. It is applicable only when the [type] is [APIType.array].
///
/// Possible values are defined in the [APICollectionFormat] enum:
/// - [APICollectionFormat.csv]: Comma-separated values
/// - [APICollectionFormat.ssv]: Space-separated values
/// - [APICollectionFormat.tsv]: Tab-separated values
/// - [APICollectionFormat.pipes]: Pipe-separated values
///
/// If not specified (null), the default format is typically comma-separated.
APICollectionFormat? collectionFormat;
/// The default value for the property.
///
/// This field represents the default value that should be used for the property
/// if no value is provided. It can be of any type (hence the 'dynamic' type),
/// allowing it to match the property's data type.
///
/// The default value is optional and can be null if not specified.
dynamic defaultValue;
/// The maximum value for numeric properties.
///
/// This field specifies the maximum allowed value for numeric properties.
/// It is applicable when [type] is a numeric type (e.g., integer or number).
/// The value is inclusive unless [exclusiveMaximum] is set to true.
///
/// This property is optional and can be null if not specified.
num? maximum;
/// Indicates whether the maximum value is exclusive.
///
/// When set to `true`, the [maximum] value is treated as an exclusive upper bound.
/// When `false` or `null`, the [maximum] value is inclusive.
///
/// This property is only applicable when [maximum] is set and [type] is a numeric type.
bool? exclusiveMaximum;
/// The minimum value for numeric properties.
///
/// This field specifies the minimum allowed value for numeric properties.
/// It is applicable when [type] is a numeric type (e.g., integer or number).
/// The value is inclusive unless [exclusiveMinimum] is set to true.
///
/// This property is optional and can be null if not specified.
num? minimum;
/// Indicates whether the minimum value is exclusive.
///
/// When set to `true`, the [minimum] value is treated as an exclusive lower bound.
/// When `false` or `null`, the [minimum] value is inclusive.
///
/// This property is only applicable when [minimum] is set and [type] is a numeric type.
bool? exclusiveMinimum;
/// The maximum length for string properties.
///
/// This field specifies the maximum allowed length for string properties.
/// It is applicable when [type] is [APIType.string].
///
/// The value is an integer representing the maximum number of characters allowed.
/// If not specified (null), there is no upper limit on the string length.
int? maxLength;
/// The minimum length for string properties.
///
/// This field specifies the minimum allowed length for string properties.
/// It is applicable when [type] is [APIType.string].
///
/// The value is an integer representing the minimum number of characters required.
/// If not specified (null), there is no lower limit on the string length.
int? minLength;
/// The regular expression pattern for string properties.
///
/// This field specifies a regular expression that a string property must match.
/// It is applicable when [type] is [APIType.string].
///
/// The pattern is represented as a string containing a valid regular expression.
/// If not specified (null), no pattern matching is enforced on the string value.
String? pattern;
/// The maximum number of items for array properties.
///
/// This field specifies the maximum allowed number of items in an array property.
/// It is applicable when [type] is [APIType.array].
///
/// The value is an integer representing the maximum number of elements allowed in the array.
/// If not specified (null), there is no upper limit on the number of array items.
int? maxItems;
/// The minimum number of items for array properties.
///
/// This field specifies the minimum required number of items in an array property.
/// It is applicable when [type] is [APIType.array].
///
/// The value is an integer representing the minimum number of elements required in the array.
/// If not specified (null), there is no lower limit on the number of array items.
int? minItems;
/// Indicates whether array items must be unique.
///
/// This field specifies whether all items in an array property must be unique.
/// It is applicable when [type] is [APIType.array].
///
/// When set to `true`, all elements in the array must be unique.
/// When `false` or `null`, duplicate elements are allowed in the array.
///
/// This property is optional and can be null if not specified.
bool? uniqueItems;
/// Specifies that numeric properties must be multiples of this value.
///
/// This field is applicable when [type] is a numeric type (e.g., integer or number).
/// If set, the value of the property must be divisible by this number.
///
/// For example, if [multipleOf] is set to 2, valid values could be 0, 2, 4, -2, etc.
///
/// This property is optional and can be null if not specified.
num? multipleOf;
/// A list of allowed values for the property.
///
/// This field specifies a list of valid values that the property can take.
/// It is applicable to properties of various types, including strings, numbers, and more.
///
/// When set, the value of the property must be one of the items in this list.
/// If not specified (null), there are no restrictions on the allowed values.
///
/// The list is of type `dynamic` to accommodate different data types that may be used
/// for the enumeration values, depending on the property's type.
List<dynamic>? enumerated;
/// Determines the schema representation of the property.
///
/// This getter analyzes the [type] of the property and returns the corresponding
/// [APISchemaRepresentation]:
/// - Returns [APISchemaRepresentation.array] if the type is [APIType.array]
/// - Returns [APISchemaRepresentation.object] if the type is [APIType.object]
/// - Returns [APISchemaRepresentation.primitive] for all other types
///
/// This representation helps in categorizing and handling different property types
/// in API schemas.
///
/// Returns: An [APISchemaRepresentation] value indicating the schema representation.
APISchemaRepresentation get representation {
if (type == APIType.array) {
return APISchemaRepresentation.array;
} else if (type == APIType.object) {
return APISchemaRepresentation.object;
}
return APISchemaRepresentation.primitive;
}
/// Decodes the property information from a [KeyedArchive] object.
///
/// This method populates the fields of the [APIProperty] instance with values
/// decoded from the given [KeyedArchive] object. It decodes various properties
/// such as type, format, constraints, and other metadata associated with the API property.
///
/// The method first calls the superclass's decode method, then decodes specific
/// fields for the [APIProperty] class. Each field is extracted from the archive
/// using its corresponding key.
///
/// Parameters:
/// [object] - A [KeyedArchive] containing the encoded property information.
///
/// Note: This method assumes that the [KeyedArchive] contains keys matching
/// the property names of the [APIProperty] class.
@override
void decode(KeyedArchive object) {
super.decode(object);
type = APITypeCodec.decode(object.decode("type"));
format = object.decode("format");
collectionFormat =
APICollectionFormatCodec.decode(object.decode("collectionFormat"));
defaultValue = object.decode("default");
maximum = object.decode("maximum");
exclusiveMaximum = object.decode("exclusiveMaximum");
minimum = object.decode("minimum");
exclusiveMinimum = object.decode("exclusiveMinimum");
maxLength = object.decode("maxLength");
minLength = object.decode("minLength");
pattern = object.decode("pattern");
maxItems = object.decode("maxItems");
minItems = object.decode("minItems");
uniqueItems = object.decode("uniqueItems");
multipleOf = object.decode("multipleOf");
enumerated = object.decode("enum");
}
/// Encodes the property information into a [KeyedArchive] object.
///
/// This method serializes the fields of the [APIProperty] instance into the given
/// [KeyedArchive] object. It encodes various properties such as type, format,
/// constraints, and other metadata associated with the API property.
///
/// The method first calls the superclass's encode method, then encodes specific
/// fields of the [APIProperty] class. Each field is added to the archive
/// using its corresponding key.
///
/// Parameters:
/// [object] - A [KeyedArchive] to store the encoded property information.
///
/// Note: This method encodes all fields of the [APIProperty] class, including
/// null values. The receiving end should handle potential null values appropriately.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("type", APITypeCodec.encode(type));
object.encode("format", format);
object.encode(
"collectionFormat",
APICollectionFormatCodec.encode(collectionFormat),
);
object.encode("default", defaultValue);
object.encode("maximum", maximum);
object.encode("exclusiveMaximum", exclusiveMaximum);
object.encode("minimum", minimum);
object.encode("exclusiveMinimum", exclusiveMinimum);
object.encode("maxLength", maxLength);
object.encode("minLength", minLength);
object.encode("pattern", pattern);
object.encode("maxItems", maxItems);
object.encode("minItems", minItems);
object.encode("uniqueItems", uniqueItems);
object.encode("multipleOf", multipleOf);
object.encode("enum", enumerated);
}
}

View file

@ -1,104 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents an HTTP response in the OpenAPI specification.
///
/// This class extends [APIObject] and provides properties and methods to
/// handle the response description, schema, and headers.
///
/// Properties:
/// - [description]: A string describing the response.
/// - [schema]: An [APISchemaObject] representing the structure of the response body.
/// - [headers]: A map of response headers, where keys are header names and values are [APIHeader] objects.
///
/// The class includes methods for encoding and decoding the response object
/// to and from a [KeyedArchive].
class APIResponse extends APIObject {
/// Creates a new instance of [APIResponse].
///
/// This constructor initializes a new [APIResponse] object with default values.
/// The [description] is set to an empty string, [schema] is null,
/// and [headers] is an empty map.
APIResponse();
/// A string describing the response.
///
/// This property holds a brief description of the API response.
/// It provides context about what the response represents or contains.
/// The description is optional and defaults to an empty string if not specified.
String? description = "";
/// Represents the structure of the response body.
///
/// This property is of type [APISchemaObject] and defines the schema
/// for the response payload. It describes the structure, types, and
/// constraints of the data returned in the response body.
///
/// The schema can be null if the response doesn't have a body or if
/// the schema is not defined in the API specification.
APISchemaObject? schema;
/// A map of response headers.
///
/// This property is a [Map] where the keys are header names (strings) and the values
/// are [APIHeader] objects or null. It represents the headers that are expected
/// to be included in the API response.
///
/// The map is nullable and initialized as an empty map by default. Each header
/// in the map can also be null, allowing for optional headers in the response.
Map<String, APIHeader?>? headers = {};
/// Decodes the [APIResponse] object from a [KeyedArchive].
///
/// This method overrides the [decode] method from the superclass and is responsible
/// for populating the properties of the [APIResponse] object from the given [KeyedArchive].
///
/// It performs the following actions:
/// 1. Calls the superclass's decode method.
/// 2. Decodes the 'description' field into the [description] property.
/// 3. Decodes the 'schema' field into the [schema] property, creating a new [APISchemaObject] if necessary.
/// 4. Decodes the 'headers' field into the [headers] property, creating new [APIHeader] objects as needed.
///
/// Parameters:
/// - [object]: A [KeyedArchive] containing the encoded data for the [APIResponse].
@override
void decode(KeyedArchive object) {
super.decode(object);
description = object.decode("description");
schema = object.decodeObject("schema", () => APISchemaObject());
headers = object.decodeObjectMap("headers", () => APIHeader());
}
/// Encodes the [APIResponse] object into a [KeyedArchive].
///
/// This method overrides the [encode] method from the superclass and is responsible
/// for encoding the properties of the [APIResponse] object into the given [KeyedArchive].
///
/// It performs the following actions:
/// 1. Calls the superclass's encode method.
/// 2. Encodes the [headers] property into the 'headers' field of the archive.
/// 3. Encodes the [schema] property into the 'schema' field of the archive.
/// 4. Encodes the [description] property into the 'description' field of the archive.
///
/// Parameters:
/// - [object]: A [KeyedArchive] to store the encoded data of the [APIResponse].
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encodeObjectMap("headers", headers);
object.encodeObject("schema", schema);
object.encode("description", description);
}
}

View file

@ -1,249 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents a schema object in the OpenAPI specification.
///
/// This class extends [APIProperty] and provides additional properties and methods
/// specific to schema objects in the OpenAPI specification.
///
/// Properties:
/// - [title]: A string representing the title of the schema.
/// - [description]: A string describing the schema.
/// - [example]: An example value for the schema.
/// - [isRequired]: A list of required properties.
/// - [readOnly]: A boolean indicating if the schema is read-only.
/// - [items]: An [APISchemaObject] representing array items (valid when type is array).
/// - [properties]: A map of property names to [APISchemaObject]s (valid when type is null).
/// - [additionalProperties]: An [APISchemaObject] for additional properties (valid when type is object).
///
/// Methods:
/// - [representation]: Returns the [APISchemaRepresentation] of the schema.
/// - [decode]: Decodes the schema from a [KeyedArchive].
/// - [encode]: Encodes the schema to a [KeyedArchive].
///
/// This class also overrides the [castMap] getter to provide custom casting for the 'required' field.
class APISchemaObject extends APIProperty {
/// Default constructor for APISchemaObject.
///
/// Creates a new instance of APISchemaObject with default values.
/// This constructor doesn't take any parameters and initializes
/// the object with its default state.
APISchemaObject();
/// The title of the schema.
///
/// This property represents the title of the schema object.
/// It can be null if no title is specified.
String? title;
/// A description of the schema.
///
/// This property provides a detailed explanation of the schema object.
/// It can be null if no description is specified.
String? description;
/// An example value for the schema.
///
/// This property holds an example value that represents the schema.
/// It can be of any type (String, int, bool, Map, List, etc.) depending on the schema definition.
/// The example is used to provide a clear illustration of what data conforming to the schema might look like.
/// This property can be null if no example is specified.
String? example;
/// A list of required properties for this schema object.
///
/// This list contains the names of properties that are required
/// for this schema to be valid. Each element is a String representing
/// a property name. The list can be empty if no properties are required,
/// or it can be null if the required properties are not specified.
///
/// Note: The list allows null values, though typically all elements
/// should be non-null property names.
List<String?>? isRequired = [];
/// Indicates whether the schema is read-only.
///
/// When set to true, it specifies that the schema should be treated as read-only,
/// meaning it can be retrieved and read, but should not be modified or updated.
/// This is particularly useful for properties that are auto-generated or controlled by the system.
///
/// Defaults to false, indicating that the schema is writable by default.
bool readOnly = false;
/// Represents the schema for array items when the type is 'array'.
///
/// This property is only applicable when the schema's type is set to 'array'.
/// It defines the schema for the items within the array.
///
/// - If [items] is null, it means the array items have no specific schema defined.
/// - If [items] is set, it provides the schema that all items in the array must conform to.
///
/// Example:
/// If this schema represents an array of strings, [items] would be an APISchemaObject
/// with its type set to 'string'.
APISchemaObject? items;
/// A map of property names to their corresponding schema objects.
///
/// This property is valid when the schema's type is null or 'object'.
/// It defines the structure of an object by specifying the schemas of its properties.
///
/// The keys in the map are strings representing property names.
/// The values are [APISchemaObject] instances that define the schema for each property.
///
/// This property can be null if no properties are defined, or if the schema
/// represents a type other than an object.
///
/// Example:
/// ```dart
/// properties = {
/// "name": APISchemaObject()..type = "string",
/// "age": APISchemaObject()..type = "integer"
/// };
/// ```
Map<String, APISchemaObject?>? properties;
/// Represents the schema for additional properties when the type is 'object'.
///
/// This property is applicable when the schema's type is set to 'object'.
/// It defines the schema for any additional properties that are not explicitly defined
/// in the [properties] map.
///
/// - If [additionalProperties] is null, it means additional properties are not allowed.
/// - If [additionalProperties] is set to an [APISchemaObject], it specifies the schema
/// that any additional properties must conform to.
///
/// This property allows for flexible object structures where some properties are
/// explicitly defined, while others can follow a general schema.
///
/// Example:
/// If set to an [APISchemaObject] with type "string", it means any additional
/// properties not listed in [properties] must have string values.
///
/// Valid when type == object
APISchemaObject? additionalProperties;
/// Returns the representation of this schema object.
///
/// This method overrides the base [representation] getter to provide
/// specific behavior for schema objects with properties.
///
/// If the [properties] map is not null, it indicates that this schema
/// represents a structured object, so it returns [APISchemaRepresentation.structure].
///
/// If [properties] is null, it falls back to the superclass implementation
/// to determine the representation based on other attributes of the schema.
///
/// Returns:
/// [APISchemaRepresentation.structure] if [properties] is not null,
/// otherwise returns the result of the superclass [representation] getter.
@override
APISchemaRepresentation get representation {
if (properties != null) {
return APISchemaRepresentation.structure;
}
return super.representation;
}
/// Overrides the [castMap] getter to provide custom casting for the 'required' field.
///
/// This getter returns a Map that defines how certain fields should be cast
/// when encoding or decoding the object. Specifically:
///
/// - The 'required' field is cast to a List of strings.
///
/// This ensures that the 'required' field, which represents the list of required
/// properties for this schema object, is always treated as a list of strings,
/// even if the incoming data might have a different format.
///
/// Returns:
/// A Map where the key is the field name ('required') and the value is a Cast
/// object specifying how to cast the field (List of strings in this case).
@override
Map<String, cast.Cast> get castMap =>
{"required": const cast.List(cast.string)};
/// Decodes the APISchemaObject from a KeyedArchive.
///
/// This method overrides the base [decode] method to provide custom decoding
/// for APISchemaObject properties. It populates the object's fields from the
/// provided [KeyedArchive] object.
///
/// Parameters:
/// - [object]: A [KeyedArchive] containing the encoded data for this schema object.
///
/// The method decodes the following properties:
/// - title: The schema's title (String)
/// - description: The schema's description (String)
/// - isRequired: List of required properties (List<String?>)
/// - example: An example value for the schema (String)
/// - readOnly: Whether the schema is read-only (bool, defaults to false)
/// - items: The schema for array items (APISchemaObject)
/// - additionalProperties: The schema for additional properties (APISchemaObject)
/// - properties: A map of property names to their schemas (Map<String, APISchemaObject?>)
///
/// Note: This method calls the superclass [decode] method first to handle any
/// decoding defined in the parent class.
@override
void decode(KeyedArchive object) {
super.decode(object);
title = object.decode("title");
description = object.decode("description");
isRequired = object.decode("required");
example = object.decode("example");
readOnly = object.decode("readOnly") ?? false;
items = object.decodeObject("items", () => APISchemaObject());
additionalProperties =
object.decodeObject("additionalProperties", () => APISchemaObject());
properties = object.decodeObjectMap("properties", () => APISchemaObject());
}
/// Encodes the APISchemaObject into a KeyedArchive.
///
/// This method overrides the base [encode] method to provide custom encoding
/// for APISchemaObject properties. It serializes the object's fields into the
/// provided [KeyedArchive] object.
///
/// Parameters:
/// - [object]: A [KeyedArchive] where the encoded data for this schema object will be stored.
///
/// The method encodes the following properties:
/// - title: The schema's title (String)
/// - description: The schema's description (String)
/// - isRequired: List of required properties (List<String?>)
/// - example: An example value for the schema (String)
/// - readOnly: Whether the schema is read-only (bool)
/// - items: The schema for array items (APISchemaObject)
/// - additionalProperties: The schema for additional properties (APISchemaObject)
/// - properties: A map of property names to their schemas (Map<String, APISchemaObject?>)
///
/// Note: This method calls the superclass [encode] method first to handle any
/// encoding defined in the parent class.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("title", title);
object.encode("description", description);
object.encode("required", isRequired);
object.encode("example", example);
object.encode("readOnly", readOnly);
object.encodeObject("items", items);
object.encodeObject("additionalProperties", additionalProperties);
object.encodeObjectMap("properties", properties);
}
}

View file

@ -1,366 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v2.dart';
/// Represents a OAuth 2.0 security scheme flow in the OpenAPI specification.
///
/// This enum defines the different types of OAuth 2.0 flows that can be used
/// in an OpenAPI security scheme. The available flows are:
///
/// - [implicit]: The implicit grant flow.
/// - [password]: The resource owner password credentials grant flow.
/// - [application]: The client credentials grant flow.
/// - [authorizationCode]: The authorization code grant flow.
///
/// These flows correspond to the standard OAuth 2.0 grant types as defined
/// in RFC 6749.
enum APISecuritySchemeFlow {
implicit,
password,
application,
authorizationCode
}
/// A utility class for encoding and decoding [APISecuritySchemeFlow] values.
///
/// This class provides static methods to convert between [APISecuritySchemeFlow] enum values
/// and their corresponding string representations used in the OpenAPI specification.
class APISecuritySchemeFlowCodec {
/// Decodes a string representation of an OAuth 2.0 flow into an [APISecuritySchemeFlow] enum value.
///
/// This method takes a [String] parameter [flow] and returns the corresponding
/// [APISecuritySchemeFlow] enum value. If the input string doesn't match any
/// known flow, the method returns null.
///
/// The mapping is as follows:
/// - "accessCode" -> [APISecuritySchemeFlow.authorizationCode]
/// - "password" -> [APISecuritySchemeFlow.password]
/// - "implicit" -> [APISecuritySchemeFlow.implicit]
/// - "application" -> [APISecuritySchemeFlow.application]
///
/// Parameters:
/// [flow]: A string representation of the OAuth 2.0 flow.
///
/// Returns:
/// The corresponding [APISecuritySchemeFlow] enum value, or null if no match is found.
static APISecuritySchemeFlow? decode(String? flow) {
switch (flow) {
case "accessCode":
return APISecuritySchemeFlow.authorizationCode;
case "password":
return APISecuritySchemeFlow.password;
case "implicit":
return APISecuritySchemeFlow.implicit;
case "application":
return APISecuritySchemeFlow.application;
default:
return null;
}
}
/// Encodes an [APISecuritySchemeFlow] enum value into its string representation.
///
/// This method takes an [APISecuritySchemeFlow] enum value as input and returns
/// the corresponding string representation used in the OpenAPI specification.
/// If the input is null or doesn't match any known flow, the method returns null.
///
/// The mapping is as follows:
/// - [APISecuritySchemeFlow.authorizationCode] -> "accessCode"
/// - [APISecuritySchemeFlow.password] -> "password"
/// - [APISecuritySchemeFlow.implicit] -> "implicit"
/// - [APISecuritySchemeFlow.application] -> "application"
///
/// Parameters:
/// [flow]: An [APISecuritySchemeFlow] enum value to be encoded.
///
/// Returns:
/// The string representation of the OAuth 2.0 flow, or null if no match is found.
static String? encode(APISecuritySchemeFlow? flow) {
switch (flow) {
case APISecuritySchemeFlow.authorizationCode:
return "accessCode";
case APISecuritySchemeFlow.password:
return "password";
case APISecuritySchemeFlow.implicit:
return "implicit";
case APISecuritySchemeFlow.application:
return "application";
default:
return null;
}
}
}
/// Represents a security scheme in the OpenAPI specification.
///
/// This class defines various types of security schemes that can be used in an OpenAPI document.
/// It supports three main types of security schemes:
/// 1. Basic Authentication
/// 2. API Key
/// 3. OAuth2
///
/// The class provides constructors for each type of security scheme and methods to encode and decode
/// the security scheme information to and from a KeyedArchive object.
///
/// Properties:
/// - [type]: The type of the security scheme (e.g., "basic", "apiKey", "oauth2").
/// - [description]: An optional description of the security scheme.
/// - [apiKeyName]: The name of the API key (for API Key type).
/// - [apiKeyLocation]: The location of the API key (for API Key type).
/// - [oauthFlow]: The OAuth2 flow type (for OAuth2 type).
/// - [authorizationURL]: The authorization URL (for OAuth2 type).
/// - [tokenURL]: The token URL (for OAuth2 type).
/// - [scopes]: A map of available scopes (for OAuth2 type).
///
/// The class also includes utility methods and properties:
/// - [isOAuth2]: A getter that returns true if the security scheme is OAuth2.
/// - [castMap]: Provides casting information for the 'scopes' property.
/// - [decode]: Decodes the security scheme information from a KeyedArchive object.
/// - [encode]: Encodes the security scheme information into a KeyedArchive object.
class APISecurityScheme extends APIObject {
/// Default constructor for the [APISecurityScheme] class.
///
/// This constructor creates an instance of [APISecurityScheme] without initializing any specific properties.
/// It's typically used when you want to create a security scheme object and set its properties manually later.
APISecurityScheme();
/// Creates a basic authentication security scheme.
///
/// This constructor initializes an [APISecurityScheme] instance
/// with the type set to "basic", representing HTTP Basic Authentication.
/// Basic Authentication allows API clients to authenticate by providing
/// their username and password.
///
/// Example usage:
/// ```dart
/// var basicAuth = APISecurityScheme.basic();
/// ```
APISecurityScheme.basic() {
type = "basic";
}
/// Creates an API Key security scheme.
///
/// This constructor initializes an [APISecurityScheme] instance
/// with the type set to "apiKey". It requires two parameters:
///
/// - [apiKeyName]: The name of the API key to be used for authentication.
/// - [apiKeyLocation]: The location where the API key should be included in the request,
/// typically specified as a value from the [APIParameterLocation] enum.
///
/// API Key authentication allows API clients to authenticate by including a specific
/// key in their requests, either in the header, query parameters, or cookies.
///
/// Example usage:
/// ```dart
/// var apiKeyAuth = APISecurityScheme.apiKey('X-API-Key', APIParameterLocation.header);
/// ```
APISecurityScheme.apiKey(this.apiKeyName, this.apiKeyLocation) {
type = "apiKey";
}
/// Creates an OAuth2 security scheme.
///
/// This constructor initializes an [APISecurityScheme] instance
/// with the type set to "oauth2". It requires one mandatory parameter:
///
/// - [oauthFlow]: The OAuth2 flow type, specified as an [APISecuritySchemeFlow] enum value.
///
/// Optional parameters include:
///
/// - [authorizationURL]: The authorization URL for the OAuth2 flow.
/// - [tokenURL]: The token URL for the OAuth2 flow.
/// - [scopes]: A map of available scopes for the OAuth2 flow. Defaults to an empty map.
///
/// OAuth2 allows API clients to obtain limited access to user accounts on an HTTP service,
/// either on behalf of a user or on behalf of the client itself.
///
/// Example usage:
/// ```dart
/// var oauth2Auth = APISecurityScheme.oauth2(
/// APISecuritySchemeFlow.authorizationCode,
/// authorizationURL: 'https://example.com/oauth/authorize',
/// tokenURL: 'https://example.com/oauth/token',
/// scopes: {'read': 'Read access', 'write': 'Write access'}
/// );
/// ```
APISecurityScheme.oauth2(
this.oauthFlow, {
this.authorizationURL,
this.tokenURL,
this.scopes = const {},
}) {
type = "oauth2";
}
/// The type of the security scheme.
///
/// This property specifies the type of the security scheme. It can be one of:
/// - "basic" for Basic Authentication
/// - "apiKey" for API Key Authentication
/// - "oauth2" for OAuth2 Authentication
///
/// This field is required and must be set for the security scheme to be valid.
late String type;
/// A description of the security scheme.
///
/// This optional property provides additional information about the security scheme.
/// It can be used to explain how the security scheme works, its purpose, or any
/// specific requirements for using it.
///
/// The value is a string that can contain multiple lines of text if needed.
/// If not specified, this property will be null.
String? description;
/// The name of the API key for API Key authentication.
///
/// This property is used when the security scheme type is "apiKey".
/// It specifies the name of the API key that should be used in the request.
/// For example, if set to "X-API-Key", the client would need to include
/// this header in their request: "X-API-Key: <actual-api-key-value>".
///
/// This property is nullable and will be null for non-API Key security schemes.
String? apiKeyName;
/// The location of the API key in the request.
///
/// This property specifies where the API key should be included in the request
/// when using API Key authentication. It can be one of the following:
/// - [APIParameterLocation.query] for including the key in the query parameters
/// - [APIParameterLocation.header] for including the key in the request headers
/// - [APIParameterLocation.cookie] for including the key in a cookie
///
/// This property is nullable and will be null for non-API Key security schemes.
/// It is typically used in conjunction with [apiKeyName] to define how an API key
/// should be sent with requests.
APIParameterLocation? apiKeyLocation;
/// The OAuth2 flow type for this security scheme.
///
/// This property specifies the type of OAuth2 flow used when the security scheme
/// is of type "oauth2". It is represented by the [APISecuritySchemeFlow] enum,
/// which can have one of the following values:
/// - [APISecuritySchemeFlow.implicit]
/// - [APISecuritySchemeFlow.password]
/// - [APISecuritySchemeFlow.application]
/// - [APISecuritySchemeFlow.authorizationCode]
///
/// This property is nullable and will be null for non-OAuth2 security schemes.
/// It is used in conjunction with other OAuth2-specific properties like
/// [authorizationURL], [tokenURL], and [scopes] to fully define the OAuth2 flow.
APISecuritySchemeFlow? oauthFlow;
String? authorizationURL;
String? tokenURL;
Map<String, String>? scopes;
bool get isOAuth2 {
return type == "oauth2";
}
/// Provides a mapping of property names to their respective casting functions.
///
/// This getter overrides the base class implementation to specify custom casting
/// behavior for the 'scopes' property of the [APISecurityScheme] class.
///
/// Returns:
/// A [Map] where the key is the property name ('scopes') and the value is a [cast.Cast]
/// object that defines how to cast the property's value. In this case, it specifies
/// that 'scopes' should be cast as a Map with string keys and string values.
@override
Map<String, cast.Cast> get castMap =>
{"scopes": const cast.Map(cast.string, cast.string)};
/// Decodes the security scheme information from a [KeyedArchive] object.
///
/// This method populates the properties of the [APISecurityScheme] instance
/// based on the data stored in the provided [KeyedArchive] object. It handles
/// different types of security schemes (basic, OAuth2, and API key) and
/// decodes their specific properties accordingly.
///
/// The method performs the following tasks:
/// 1. Calls the superclass's decode method.
/// 2. Decodes the 'type' and 'description' properties.
/// 3. Based on the 'type', decodes additional properties:
/// - For 'basic', no additional properties are decoded.
/// - For 'oauth2', decodes 'flow', 'authorizationUrl', 'tokenUrl', and 'scopes'.
/// - For 'apiKey', decodes 'name' and 'in' (location) properties.
///
/// Parameters:
/// [object]: A [KeyedArchive] containing the encoded security scheme information.
///
/// Note: This method assumes that the 'scopes' property, when present, is
/// a non-null Map<String, String>. It will throw an error if this assumption
/// is not met.
@override
void decode(KeyedArchive object) {
super.decode(object);
type = object.decode("type") ?? "oauth2";
description = object.decode("description");
if (type == "basic") {
} else if (type == "oauth2") {
oauthFlow = APISecuritySchemeFlowCodec.decode(object.decode("flow"));
authorizationURL = object.decode("authorizationUrl");
tokenURL = object.decode("tokenUrl");
final scopeMap = object.decode<Map<String, String>>("scopes")!;
scopes = Map<String, String>.from(scopeMap);
} else if (type == "apiKey") {
apiKeyName = object.decode("name");
apiKeyLocation = APIParameterLocationCodec.decode(object.decode("in"));
}
}
/// Encodes the security scheme information into a [KeyedArchive] object.
///
/// This method serializes the properties of the [APISecurityScheme] instance
/// into the provided [KeyedArchive] object. It handles different types of
/// security schemes (basic, OAuth2, and API key) and encodes their specific
/// properties accordingly.
///
/// The method performs the following tasks:
/// 1. Calls the superclass's encode method.
/// 2. Encodes the 'type' and 'description' properties.
/// 3. Based on the 'type', encodes additional properties:
/// - For 'basic', no additional properties are encoded.
/// - For 'apiKey', encodes 'name' and 'in' (location) properties.
/// - For 'oauth2', encodes 'flow', 'authorizationUrl', 'tokenUrl', and 'scopes'.
///
/// Parameters:
/// [object]: A [KeyedArchive] to store the encoded security scheme information.
///
/// Note: This method assumes that all required properties for each security
/// scheme type are properly set. It's the responsibility of the caller to
/// ensure that the object is in a valid state before encoding.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("type", type);
object.encode("description", description);
if (type == "basic") {
/* nothing to do */
} else if (type == "apiKey") {
object.encode("name", apiKeyName);
object.encode("in", APIParameterLocationCodec.encode(apiKeyLocation));
} else if (type == "oauth2") {
object.encode("flow", APISecuritySchemeFlowCodec.encode(oauthFlow));
object.encode("authorizationUrl", authorizationURL);
object.encode("tokenUrl", tokenURL);
object.encode("scopes", scopes);
}
}
}

View file

@ -1,87 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// Represents the different data types used in API responses and requests.
///
/// This enum defines the following types:
/// - [string]: Represents textual data.
/// - [number]: Represents numeric data, including floating-point numbers.
/// - [integer]: Represents whole number values.
/// - [boolean]: Represents true/false values.
/// - [array]: Represents a collection of values.
/// - [file]: Represents file data.
/// - [object]: Represents complex structured data.
enum APIType { string, number, integer, boolean, array, file, object }
/// A utility class for encoding and decoding [APIType] values.
///
/// This class provides static methods to convert between [APIType] enum values
/// and their corresponding string representations.
class APITypeCodec {
/// Decodes a string representation of an API type into its corresponding [APIType] enum value.
///
/// This method takes a [String] parameter [type] and returns the matching [APIType] enum value.
/// If the input string doesn't match any known API type, the method returns null.
///
/// Parameters:
/// [type]: A string representation of the API type.
///
/// Returns:
/// The corresponding [APIType] enum value, or null if no match is found.
static APIType? decode(String? type) {
switch (type) {
case "string":
return APIType.string;
case "number":
return APIType.number;
case "integer":
return APIType.integer;
case "boolean":
return APIType.boolean;
case "array":
return APIType.array;
case "file":
return APIType.file;
case "object":
return APIType.object;
}
return null;
}
/// Encodes an [APIType] enum value into its corresponding string representation.
///
/// This method takes an [APIType] parameter [type] and returns the matching string representation.
/// If the input [APIType] is null or doesn't match any known API type, the method returns null.
///
/// Parameters:
/// [type]: An [APIType] enum value.
///
/// Returns:
/// The corresponding string representation of the [APIType], or null if no match is found or input is null.
static String? encode(APIType? type) {
switch (type) {
case APIType.string:
return "string";
case APIType.number:
return "number";
case APIType.integer:
return "integer";
case APIType.boolean:
return "boolean";
case APIType.array:
return "array";
case APIType.file:
return "file";
case APIType.object:
return "object";
default:
return null;
}
}
}

View file

@ -1,91 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents a callback object in an OpenAPI specification.
///
/// Each value in the map is a [APIPath] that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.
class APICallback extends APIObject {
/// Creates an [APICallback] instance.
///
/// [paths] is an optional parameter that represents the callback paths.
/// Each key in the [paths] map is a runtime expression that identifies the URL
/// to be used for the callback request, and the corresponding value is an [APIPath]
/// object describing the set of requests and expected responses.
APICallback({this.paths});
/// Creates an empty [APICallback] instance.
///
/// This constructor initializes an [APICallback] with no paths.
/// It can be used when you need to create an empty callback object
/// that will be populated later.
APICallback.empty();
/// 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.
///
/// This map represents the various callback paths available in the API callback.
/// Each entry in the map consists of:
/// - A key (String): A runtime expression that identifies the URL for the callback request.
/// - A value (APIPath): An object describing the set of requests and expected responses for that callback URL.
///
/// The map can be null if no callback paths are defined.
Map<String, APIPath>? paths;
/// Decodes the [APICallback] object from a [KeyedArchive].
///
/// This method overrides the `decode` method from the superclass and performs the following steps:
/// 1. Calls the superclass's `decode` method.
/// 2. Initializes the `paths` map.
/// 3. Iterates through each key-value pair in the `object`.
/// 4. For each pair, it checks if the value is a [KeyedArchive].
/// 5. If it's not a [KeyedArchive], it throws an [ArgumentError].
/// 6. If it is a [KeyedArchive], it decodes the value into an [APIPath] object and adds it to the `paths` map.
///
/// Throws:
/// - [ArgumentError] if any value in the callback is not an object.
///
/// Parameters:
/// - object: The [KeyedArchive] to decode from.
@override
void decode(KeyedArchive object) {
super.decode(object);
paths = {};
object.forEach((key, dynamic value) {
if (value is! KeyedArchive) {
throw ArgumentError(
"Invalid specification. Callback contains non-object value.",
);
}
paths![key] = value.decodeObject(key, () => APIPath())!;
});
}
/// Encodes the [APICallback] object into a [KeyedArchive].
///
/// This method overrides the `encode` method from the superclass.
/// Currently, this method is not implemented and will throw a [StateError]
/// when called.
///
/// Parameters:
/// - object: The [KeyedArchive] to encode into.
///
/// Throws:
/// - [StateError] with the message "APICallback.encode: not yet implemented."
@override
void encode(KeyedArchive object) {
super.encode(object);
throw StateError("APICallback.encode: not yet implemented.");
}
}

View file

@ -1,242 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/util.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents the Components Object as defined in the OpenAPI Specification.
///
/// All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.
class APIComponents extends APIObject {
/// Default constructor for APIComponents.
///
/// Creates a new instance of APIComponents with default (empty) values for all properties.
APIComponents();
/// Creates an empty instance of APIComponents.
///
/// This constructor initializes an APIComponents object with no pre-defined components.
/// All component maps (schemas, responses, parameters, etc.) will be empty.
APIComponents.empty();
/// An object to hold reusable [APISchemaObject?] instances.
///
/// This map stores reusable schema objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the schemas, and the values are the corresponding [APISchemaObject] instances.
/// These schemas can be referenced throughout the API specification using $ref syntax.
Map<String, APISchemaObject> schemas = {};
/// An object to hold reusable [APIResponse?] instances.
///
/// This map stores reusable response objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the responses, and the values are the corresponding [APIResponse] instances.
/// These responses can be referenced throughout the API specification using $ref syntax.
Map<String, APIResponse> responses = {};
/// An object to hold reusable [APIParameter?] instances.
///
/// This map stores reusable parameter objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the parameters, and the values are the corresponding [APIParameter] instances.
/// These parameters can be referenced throughout the API specification using $ref syntax.
Map<String, APIParameter> parameters = {};
/// remove?
///Map<String, APIExample> examples = {};
/// An object to hold reusable [APIRequestBody] instances.
///
/// This map stores reusable request body objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the request bodies, and the values are the corresponding [APIRequestBody] instances.
/// These request bodies can be referenced throughout the API specification using $ref syntax.
Map<String, APIRequestBody> requestBodies = {};
/// An object to hold reusable [APIHeader] instances.
///
/// This map stores reusable header objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the headers, and the values are the corresponding [APIHeader] instances.
/// These headers can be referenced throughout the API specification using $ref syntax.
Map<String, APIHeader> headers = {};
/// An object to hold reusable [APISecurityScheme] instances.
///
/// This map stores reusable security scheme objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the security schemes, and the values are the corresponding [APISecurityScheme] instances.
/// These security schemes can be referenced throughout the API specification using $ref syntax.
/// They define the security mechanisms that can be used across the API.
Map<String, APISecurityScheme> securitySchemes = {};
/// remove?
///Map<String, APILink> links = {};
/// An object to hold reusable [APICallback] instances.
///
/// This map stores reusable callback objects defined in the components section of an OpenAPI document.
/// The keys are unique names for the callbacks, and the values are the corresponding [APICallback] instances.
/// These callbacks can be referenced throughout the API specification using $ref syntax.
/// Callbacks are used to define webhook-like behavior where the API can make calls back to the client.
Map<String, APICallback> callbacks = {};
/// Resolves a component definition based on the provided URI.
///
/// Construct [uri] as a path, e.g. `Uri(path: /components/schemas/name)`.
APIObject? resolveUri(Uri uri) {
final segments = uri.pathSegments;
if (segments.length != 3) {
throw ArgumentError(
"Invalid reference URI. Must be a path URI of the form: '/components/<type>/<name>'",
);
}
if (segments.first != "components") {
throw ArgumentError(
"Invalid reference URI: does not begin with /components/",
);
}
Map<String, APIObject?>? namedMap;
switch (segments[1]) {
case "schemas":
namedMap = schemas;
break;
case "responses":
namedMap = responses;
break;
case "parameters":
namedMap = parameters;
break;
case "requestBodies":
namedMap = requestBodies;
break;
case "headers":
namedMap = headers;
break;
case "securitySchemes":
namedMap = securitySchemes;
break;
case "callbacks":
namedMap = callbacks;
break;
}
if (namedMap == null) {
throw ArgumentError(
"Invalid reference URI: component type '${segments[1]}' does not exist.",
);
}
final result = namedMap[segments.last];
return result;
}
/// Resolves a reference object to its corresponding component in the API specification.
///
/// This method takes a reference object of type [T] (which must extend [APIObject])
/// and resolves it to the actual component it refers to within the API components.
///
/// Parameters:
/// [refObject]: The reference object to resolve. Must have a non-null [referenceURI].
///
/// Returns:
/// The resolved component object of type [T], or null if the reference couldn't be resolved.
///
/// Throws:
/// [ArgumentError] if the provided [refObject] is not a reference (i.e., has a null [referenceURI]).
T? resolve<T extends APIObject>(T refObject) {
if (refObject.referenceURI == null) {
throw ArgumentError("APIObject is not a reference to a component.");
}
return resolveUri(refObject.referenceURI!) as T?;
}
/// Decodes the APIComponents object from a KeyedArchive.
///
/// This method overrides the decode method from the superclass and populates
/// the various component maps of the APIComponents object. It decodes each
/// component type (schemas, responses, parameters, etc.) from the provided
/// KeyedArchive object.
///
/// The method uses removeNullsFromMap to ensure that no null values are
/// present in the decoded maps. Each component type is decoded using a
/// specific factory function to create new instances of the appropriate type.
///
/// Note: The 'examples' and 'links' components are currently commented out
/// and not being decoded.
///
/// Parameters:
/// object: A KeyedArchive containing the encoded APIComponents data.
@override
void decode(KeyedArchive object) {
super.decode(object);
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 = removeNullsFromMap(
object.decodeObjectMap("requestBodies", () => APIRequestBody.empty()),
);
headers = removeNullsFromMap(
object.decodeObjectMap("headers", () => APIHeader()),
);
securitySchemes = removeNullsFromMap(
object.decodeObjectMap("securitySchemes", () => APISecurityScheme()),
);
// links = object.decodeObjectMap("links", () => APILink());
callbacks = removeNullsFromMap(
object.decodeObjectMap("callbacks", () => APICallback()),
);
}
/// Encodes the APIComponents object into a KeyedArchive.
///
/// This method overrides the encode method from the superclass and serializes
/// the various component maps of the APIComponents object. It encodes each
/// non-empty component type (schemas, responses, parameters, etc.) into the
/// provided KeyedArchive object.
///
/// The method only encodes non-empty maps to avoid creating unnecessary empty
/// objects in the resulting OpenAPI specification. Each component type is
/// encoded using the encodeObjectMap method of the KeyedArchive.
///
/// Note: The 'examples' and 'links' components are currently commented out
/// and not being encoded.
///
/// Parameters:
/// object: A KeyedArchive to which the APIComponents data will be encoded.
@override
void encode(KeyedArchive object) {
super.encode(object);
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);
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);
if (callbacks.isNotEmpty) object.encodeObjectMap("callbacks", callbacks);
}
}

View file

@ -1,260 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// This class represents the root document object of the OpenAPI document.
///
/// It contains all the necessary fields to describe an API according to the OpenAPI Specification.
/// The class provides methods to create an empty specification, create from a map,
/// encode to and decode from a KeyedArchive, and convert to a map.
///
/// Required fields:
/// - version: The semantic version number of the OpenAPI Specification.
/// - info: Metadata about the API.
/// - paths: Available paths and operations for the API.
///
/// Optional fields:
/// - servers: Connectivity information to target servers.
/// - components: Reusable schemas for the specification.
/// - security: Declaration of security mechanisms that can be used across the API.
/// - tags: List of tags used by the specification with additional metadata.
///
/// This class extends APIObject and implements encoding and decoding logic
/// to work with the KeyedArchive serialization system.
class APIDocument extends APIObject {
/// Creates an empty APIDocument instance.
///
/// This constructor initializes a new APIDocument with default values for all fields.
/// It can be used as a starting point for building a new OpenAPI specification document.
APIDocument();
/// Creates an APIDocument instance from a decoded JSON or YAML document object.
///
/// This constructor initializes a new APIDocument by decoding the provided [map].
/// The [map] should contain key-value pairs representing the structure of an OpenAPI document.
///
/// It uses [KeyedArchive.unarchive] to convert the map into a KeyedArchive object,
/// which is then decoded to populate the fields of the APIDocument.
///
/// The [allowReferences] parameter is set to true, allowing the decoding process
/// to handle references within the document.
///
/// Example:
/// ```dart
/// var document = APIDocument.fromMap({
/// 'openapi': '3.0.0',
/// 'info': {'title': 'Sample API', 'version': '1.0.0'},
/// 'paths': {}
/// });
/// ```
APIDocument.fromMap(Map<String, dynamic> map) {
decode(KeyedArchive.unarchive(map, allowReferences: true));
}
/// The semantic version number of the OpenAPI Specification that this 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";
/// Provides metadata about the API.
///
/// REQUIRED. The metadata MAY be used by tooling as required.
///
/// This field is of type [APIInfo] and is initialized with an empty instance
/// using [APIInfo.empty()]. It contains essential information about the API,
/// such as its title, version, description, and other relevant metadata.
/// This information is crucial for API documentation and client generation tools.
APIInfo info = APIInfo.empty();
/// An array of [APIServerDescription] objects that provide connectivity information to target servers.
///
/// 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<APIServerDescription?>? servers;
/// The available paths and operations for the API.
///
/// REQUIRED. This field is a map where each key represents a unique path in the API,
/// and the corresponding value is an [APIPath] object describing the operations
/// available on that path.
///
/// The paths field is a crucial part of the OpenAPI specification as it defines
/// the structure and endpoints of the API. Each path may support multiple HTTP
/// methods (GET, POST, PUT, DELETE, etc.), each with its own operation details.
///
/// Example:
/// ```dart
/// {
/// "/users": APIPath(...),
/// "/products": APIPath(...),
/// }
/// ```
///
/// Note: This field is nullable, but it's required for a valid OpenAPI document.
/// An empty map should be used instead of null for APIs with no paths.
Map<String, APIPath?>? paths;
/// An element to hold various schemas for the specification.
///
/// This field allows the definition of various reusable objects for different aspects of the OAS.
/// It can include schemas, responses, parameters, examples, and more.
/// These components can be referenced throughout the specification, promoting reusability and reducing duplication.
/// The field is optional but can significantly improve the organization and maintainability of large API specifications.
///
/// Example usage:
/// ```dart
/// components = APIComponents()
/// ..schemas = {'User': APISchemaObject()}
/// ..responses = {'NotFound': APIResponse()};
/// ```
APIComponents? components;
/// A declaration of which security mechanisms can be used across the API.
///
/// This field is an optional list of [APISecurityRequirement] objects that define
/// the security schemes applicable to the entire API. Each object in the list
/// represents an alternative set of security requirements.
///
/// Key features:
/// - Multiple security requirement objects can be specified.
/// - Only one of the security requirement objects needs to be satisfied to authorize a request.
/// - Individual operations can override this global definition.
/// - If the list is empty, it means that there are no global security requirements.
///
/// The security schemes referenced in this list must be defined in the
/// [components.securitySchemes] section of the OpenAPI document.
///
/// Example usage:
/// ```dart
/// security = [
/// APISecurityRequirement()..addRequirement("api_key", []),
/// APISecurityRequirement()
/// ..addRequirement("oauth2", ["read:api"])
/// ..addRequirement("userPassword", []),
/// ];
/// ```
///
/// In this example, a request can be authorized using either an API key,
/// or a combination of OAuth2 with "read:api" scope and user password.
List<APISecurityRequirement?>? 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.
///
/// This field is optional and can be null. When provided, it contains a list of [APITag] objects.
/// Each [APITag] typically includes a name and description, and can be used to categorize and
/// group related operations across the API.
///
/// Tags defined here can be referenced by [APIOperation] objects throughout the specification,
/// allowing for logical grouping and organization of API endpoints.
///
/// Example usage:
/// ```dart
/// tags = [
/// APITag()
/// ..name = "users"
/// ..description = "Operations about users",
/// APITag()
/// ..name = "products"
/// ..description = "Product-related operations",
/// ];
/// ```
///
/// Note: While this field is optional, using tags can significantly improve the structure
/// and readability of API documentation generated from the OpenAPI specification.
List<APITag?>? tags;
/// Converts this APIDocument instance to a Map<String, dynamic>.
///
/// This method uses KeyedArchive.archive to serialize the APIDocument object
/// into a Map representation. The resulting map can be used for JSON/YAML
/// serialization or other purposes where a dictionary-like structure is needed.
///
/// The allowReferences parameter is set to true, which means that object
/// references within the document will be preserved during the archiving process.
///
/// Returns:
/// A Map<String, dynamic> representation of this APIDocument instance.
Map<String, dynamic> asMap() {
return KeyedArchive.archive(this, allowReferences: true);
}
/// Decodes the APIDocument from a KeyedArchive object.
///
/// This method populates the fields of the APIDocument instance using data
/// from the provided [object]. It decodes various components of the OpenAPI
/// specification, including version, info, servers, paths, components,
/// security requirements, and tags.
///
/// The method uses default values or empty instances for optional fields
/// if they are not present in the archive.
///
/// Parameters:
/// - object: A KeyedArchive containing the encoded APIDocument data.
///
/// Note: This method overrides the decode method from the superclass and
/// calls the superclass implementation before decoding specific fields.
@override
void decode(KeyedArchive object) {
super.decode(object);
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.decodeObjects("security", () => APISecurityRequirement.empty());
tags = object.decodeObjects("tags", () => APITag.empty());
}
/// Encodes the APIDocument into a KeyedArchive object.
///
/// This method serializes the APIDocument instance into the provided [object],
/// which is a KeyedArchive. It encodes all the fields of the APIDocument,
/// including version, info, servers, paths, components, security, and tags.
///
/// Before encoding, it checks if the required fields 'info' and 'paths' are valid.
/// If these are not valid or missing, it throws an ArgumentError.
///
/// Parameters:
/// - object: A KeyedArchive to encode the APIDocument data into.
///
/// Throws:
/// - ArgumentError: If 'info' is not valid or 'paths' is null.
///
/// Note: This method overrides the encode method from the superclass and
/// calls the superclass implementation before encoding specific fields.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (!info.isValid || paths == null) {
throw ArgumentError(
"APIDocument must have values for: 'version', 'info' and 'paths'.",
);
}
object.encode("openapi", version);
object.encodeObject("info", info);
object.encodeObjects("servers", servers);
object.encodeObjectMap("paths", paths);
object.encodeObject("components", components);
object.encodeObjects("security", security);
object.encodeObjects("tags", tags);
}
}

View file

@ -1,180 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// A single encoding definition applied to a single schema property.
///
/// This class represents an encoding definition as specified in the OpenAPI Specification.
/// It provides details about how a specific property should be serialized when sent as
/// part of a request body, particularly for multipart requests or application/x-www-form-urlencoded
/// request bodies.
///
/// Properties:
/// - [contentType]: Specifies the Content-Type for encoding a specific property.
/// - [headers]: Additional headers that may be sent with the request.
/// - [allowReserved]: Determines if reserved characters should be percent-encoded.
/// - [explode]: Controls how arrays and objects are serialized.
/// - [style]: Describes how a specific property value will be serialized.
///
/// This class extends [APIObject] and implements [Codable] for serialization and deserialization.
class APIEncoding extends APIObject {
/// Creates a new [APIEncoding] instance.
///
/// Parameters:
/// - [contentType]: The Content-Type for encoding a specific property.
/// - [headers]: A map of additional headers to be included with the request.
/// - [style]: Describes how a specific property value will be serialized.
/// - [allowReserved]: Determines if reserved characters should be percent-encoded. Defaults to false.
/// - [explode]: Controls how arrays and objects are serialized. Defaults to false.
APIEncoding({
this.contentType,
this.headers,
this.style,
this.allowReserved = false,
this.explode = false,
});
/// Creates an empty [APIEncoding] instance with default values.
///
/// This constructor initializes an [APIEncoding] with [allowReserved] and [explode]
/// set to false. All other properties are left uninitialized.
APIEncoding.empty()
: allowReserved = false,
explode = false;
/// The Content-Type for encoding a specific property.
///
/// Specifies the media type to be used for encoding this property when sending the request body.
/// The 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: 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/*)
/// - A comma-separated list of the above types
///
/// This property is particularly relevant for multipart request bodies and
/// application/x-www-form-urlencoded request bodies.
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.
///
/// This map represents a collection of headers associated with the encoding. Each key in the map
/// is a header name, and the corresponding value is an [APIHeader] object that defines the header's
/// properties. These headers provide supplementary information for the encoded content.
///
/// Note:
/// - The Content-Type header is handled separately and should not be included in this map.
/// - This property is only applicable for multipart request body media types. It will be ignored
/// for other media types.
///
/// Example usage:
/// ```dart
/// headers = {
/// "Content-Disposition": APIHeader(description: "Specifies the filename for the uploaded file"),
/// "X-Custom-Header": APIHeader(description: "A custom header for additional metadata")
/// };
/// ```
Map<String, APIHeader?>? headers;
/// Determines whether the parameter value should allow reserved characters 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? allowReserved;
/// Determines how array and object properties are serialized in form-style parameters.
///
/// 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? explode;
/// Describes how a specific property value will be serialized depending on its type.
///
/// This property specifies the serialization style for the encoded value. It follows the same
/// behavior and values as the style property for query parameters in [APIParameter].
///
/// The style affects how the property is serialized, especially for complex types like arrays
/// and objects. Common values include:
/// - 'form': comma-separated values for arrays (default for application/x-www-form-urlencoded)
/// - 'spaceDelimited': space-separated values for arrays
/// - 'pipeDelimited': pipe-separated values for arrays
/// - 'deepObject': for nested objects
///
/// Note:
/// - This property is only applicable when the request body media type is
/// application/x-www-form-urlencoded. It will be ignored for other media types.
/// - If not specified, the default style depends on the parameter type and the media type
/// of the request body.
///
/// See [APIParameter] for more detailed information on style values and their effects.
String? style;
/// Decodes the [APIEncoding] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIEncoding]
/// instance from the provided [KeyedArchive] object. It decodes each property
/// using the appropriate key and method from the archive.
///
/// The following properties are decoded:
/// - [contentType]: The Content-Type for encoding a specific property.
/// - [headers]: A map of additional headers to be included with the request.
/// - [allowReserved]: Determines if reserved characters should be percent-encoded.
/// - [explode]: Controls how arrays and objects are serialized.
/// - [style]: Describes how a specific property value will be serialized.
///
/// This method also calls the superclass's decode method to handle any inherited properties.
///
/// Parameters:
/// - [object]: The [KeyedArchive] containing the encoded data for this [APIEncoding] instance.
@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");
style = object.decode("style");
}
/// Encodes the [APIEncoding] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APIEncoding]
/// instance into the provided [KeyedArchive] object. It encodes each property
/// using the appropriate key and method for the archive.
///
/// The following properties are encoded:
/// - [contentType]: The Content-Type for encoding a specific property.
/// - [headers]: A map of additional headers to be included with the request.
/// - [allowReserved]: Determines if reserved characters should be percent-encoded.
/// - [explode]: Controls how arrays and objects are serialized.
/// - [style]: Describes how a specific property value will be serialized.
///
/// This method also calls the superclass's encode method to handle any inherited properties.
///
/// Parameters:
/// - [object]: The [KeyedArchive] where the encoded data for this [APIEncoding] instance will be stored.
@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("style", style);
}
}

View file

@ -1,58 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents an API Header in OpenAPI specifications.
///
/// name MUST NOT be specified, it is given in the corresponding headers map.
/// 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 {
/// Creates an [APIHeader] instance.
///
/// This constructor initializes an [APIHeader] with an optional [schema].
/// The [schema] parameter is of type [APISchemaObject] and defines the
/// structure and constraints of the header value.
///
/// The constructor calls the superclass constructor [super.header] with
/// a null name and the provided schema.
APIHeader({APISchemaObject? schema}) : super.header(null, schema: schema);
/// Creates an empty [APIHeader] instance.
///
/// This constructor initializes an [APIHeader] without specifying a schema.
/// It calls the superclass constructor [super.header] with a null name and
/// no schema, resulting in an empty header definition.
APIHeader.empty() : super.header(null);
/// Encodes the [APIHeader] object into a [KeyedArchive].
///
/// This method overrides the superclass's encode method to handle the specific
/// encoding requirements of an API header. It performs the following steps:
/// 1. Temporarily sets the 'name' property to "temporary".
/// 2. Calls the superclass's encode method to perform the base encoding.
/// 3. Removes the "name" and "in" keys from the encoded object, as these
/// are not required for API headers in OpenAPI specifications.
/// 4. Resets the 'name' property to null.
///
/// This approach ensures that the header is correctly encoded while adhering
/// to OpenAPI specifications for headers.
///
/// [object] The [KeyedArchive] to encode the header information into.
@override
void encode(KeyedArchive object) {
name = "temporary";
super.encode(object);
object.remove("name");
object.remove("in");
name = null;
}
}

View file

@ -1,106 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents a media type in an API specification.
///
/// An [APIMediaType] object provides schema and encoding information for a specific media type.
/// It is typically used in OpenAPI specifications to describe the structure and format of request
/// or response bodies for different content types.
///
/// The [schema] property defines the structure of the media type content, while the [encoding]
/// property provides additional information about how to encode specific properties, particularly
/// useful for multipart and application/x-www-form-urlencoded media types.
///
/// This class extends [APIObject] and implements [Codable] for serialization and deserialization.
/// The [decode] and [encode] methods are overridden to handle the specific properties of this class.
/// Each [APIMediaType] provides schema and examples for the media type identified by its key.
class APIMediaType extends APIObject {
/// Creates an [APIMediaType] instance.
///
/// [schema] is an optional [APISchemaObject] that defines the structure of the media type content.
/// [encoding] is an optional [Map] that provides additional information about how to encode specific properties.
///
/// This constructor allows for the creation of an [APIMediaType] with or without a schema and encoding information.
APIMediaType({this.schema, this.encoding});
/// Creates an empty [APIMediaType] instance.
///
/// This constructor initializes an [APIMediaType] with no schema or encoding information.
/// It can be used when you need to create a placeholder or default media type object
/// that will be populated later.
APIMediaType.empty();
/// The schema defining the type used for the request body.
///
/// This property holds an optional [APISchemaObject] that describes the structure
/// and constraints of the data for this media type. It defines the expected format,
/// types, and validation rules for the request body when this media type is used.
/// If not specified, it indicates that the structure of the request body is not strictly defined
/// or is described elsewhere in the API specification.
APISchemaObject? schema;
/// A map between a property name and its encoding information.
///
/// This property holds an optional [Map] where each key is a property name and each value
/// is an [APIEncoding] object providing encoding information for that property.
///
/// 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.
///
/// This map is particularly useful for specifying additional metadata about the encoding
/// of specific properties within the media type, such as content type, headers, or style
/// when dealing with complex data structures in request bodies.
///
/// If this property is null or empty, it indicates that no specific encoding information
/// is provided for the properties of this media type.
Map<String, APIEncoding?>? encoding;
/// Decodes the [APIMediaType] object from a [KeyedArchive].
///
/// This method overrides the [decode] method from the superclass and is responsible for
/// populating the properties of the [APIMediaType] object from the given [KeyedArchive].
///
/// It performs the following operations:
/// 1. Calls the superclass's decode method to handle any inherited properties.
/// 2. Decodes the "schema" field into an [APISchemaObject], if present.
/// 3. Decodes the "encoding" field into a Map of [String] to [APIEncoding], if present.
///
/// [object] is the [KeyedArchive] containing the encoded data for this [APIMediaType].
@override
void decode(KeyedArchive object) {
super.decode(object);
schema = object.decodeObject("schema", () => APISchemaObject());
encoding = object.decodeObjectMap("encoding", () => APIEncoding());
}
/// Encodes the [APIMediaType] object into a [KeyedArchive].
///
/// This method overrides the [encode] method from the superclass and is responsible for
/// serializing the properties of the [APIMediaType] object into the given [KeyedArchive].
///
/// It performs the following operations:
/// 1. Calls the superclass's encode method to handle any inherited properties.
/// 2. Encodes the "schema" field from the [APISchemaObject], if present.
/// 3. Encodes the "encoding" field as a Map of [String] to [APIEncoding], if present.
///
/// [object] is the [KeyedArchive] where the encoded data for this [APIMediaType] will be stored.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encodeObject("schema", schema);
object.encodeObjectMap("encoding", encoding);
}
}

View file

@ -1,605 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.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.
///
/// This class represents the OpenAPI Info Object, which contains basic information about the API.
/// It includes required fields like 'title' and 'version', as well as optional fields such as
/// 'description', 'termsOfServiceURL', 'contact', and 'license'.
///
/// The [APIInfo] class provides methods to encode and decode the information to and from a [KeyedArchive],
/// which is useful for serialization and deserialization of the API metadata.
///
/// Usage:
/// ```dart
/// var info = APIInfo('My API', '1.0.0',
/// description: 'This is a sample API',
/// termsOfServiceURL: Uri.parse('https://example.com/terms'),
/// contact: APIContact(name: 'API Support', email: 'support@example.com'),
/// license: APILicense('Apache 2.0', url: Uri.parse('https://www.apache.org/licenses/LICENSE-2.0.html'))
/// );
/// ```
///
/// The [isValid] getter can be used to check if the required fields are non-null.
class APIInfo extends APIObject {
/// Creates an [APIInfo] instance with the required fields and optional metadata.
///
/// [title] and [version] are required parameters.
///
/// Optional parameters include:
/// - [description]: A short description of the API.
/// - [termsOfServiceURL]: A URL to the Terms of Service for the API.
/// - [license]: The license information for the exposed API.
/// - [contact]: The contact information for the exposed API.
///
/// Example:
/// ```dart
/// var info = APIInfo(
/// 'My API',
/// '1.0.0',
/// description: 'This is a sample API',
/// termsOfServiceURL: Uri.parse('https://example.com/terms'),
/// license: APILicense('Apache 2.0'),
/// contact: APIContact(name: 'API Support', email: 'support@example.com')
/// );
/// ```
APIInfo(
this.title,
this.version, {
this.description,
this.termsOfServiceURL,
this.license,
this.contact,
});
/// Creates an empty [APIInfo] instance.
///
/// This constructor initializes an [APIInfo] object without setting any of its properties.
/// It can be useful when you need to create an instance of [APIInfo] and populate its
/// properties later, or when decoding from a serialized format.
APIInfo.empty();
/// The title of the application.
///
/// This field is REQUIRED according to the OpenAPI Specification.
/// It provides the name of the API or application that this [APIInfo] object describes.
///
/// Example:
/// ```dart
/// var info = APIInfo('My Amazing API', '1.0.0');
/// print(info.title); // Output: My Amazing API
/// ```
///
/// Note: Despite being marked as required in the specification, this field is nullable
/// to allow for deserialization of incomplete data. Always ensure this field is set
/// before using the [APIInfo] object in production.
String? title;
/// A short description of the application.
///
/// This field provides a brief summary of the API or application that this [APIInfo] object describes.
/// It's an optional field that can be used to give users a quick understanding of the API's purpose.
///
/// The OpenAPI Specification allows for CommonMark syntax to be used in this field,
/// enabling rich text representation for more detailed or formatted descriptions.
///
/// Example:
/// ```dart
/// var info = APIInfo('My API', '1.0.0',
/// description: 'This API provides access to our product catalog and order management system.'
/// );
/// ```
///
/// Note: This field is nullable, as it's not a required field in the OpenAPI Specification.
String? description;
/// The version of the OpenAPI document.
///
/// REQUIRED.
String? version;
/// A URL to the Terms of Service for the API.
///
/// This field provides a link to the Terms of Service for the API, if available.
/// It must be in the format of a valid URL.
///
/// According to the OpenAPI Specification, if provided, this field MUST be a URL.
/// It's an optional field, so it can be null if no Terms of Service URL is specified.
///
/// Example:
/// ```dart
/// var info = APIInfo('My API', '1.0.0',
/// termsOfServiceURL: Uri.parse('https://example.com/terms')
/// );
/// ```
///
/// Note: When setting this field, ensure that the provided URI is valid and accessible.
Uri? termsOfServiceURL;
/// The contact information for the exposed API.
///
/// This field contains an [APIContact] object that provides contact information
/// for the API. It can include details such as the name of the contact person or
/// organization, a URL for contact information, and an email address.
///
/// This field is optional according to the OpenAPI Specification, so it can be null
/// if no contact information is provided.
///
/// Example:
/// ```dart
/// var info = APIInfo('My API', '1.0.0',
/// contact: APIContact(
/// name: 'API Support',
/// url: Uri.parse('https://www.example.com/support'),
/// email: 'support@example.com'
/// )
/// );
/// ```
APIContact? contact;
/// The license information for the exposed API.
///
/// This field contains an [APILicense] object that provides license information
/// for the API. It typically includes the name of the license and optionally
/// a URL where the full license text can be found.
///
/// This field is optional according to the OpenAPI Specification, so it can be null
/// if no license information is provided.
///
/// Example:
/// ```dart
/// var info = APIInfo('My API', '1.0.0',
/// license: APILicense('Apache 2.0', url: Uri.parse('https://www.apache.org/licenses/LICENSE-2.0.html'))
/// );
/// ```
APILicense? license;
/// Checks if the [APIInfo] object is valid according to the OpenAPI Specification.
///
/// This getter returns `true` if both the [title] and [version] fields are non-null,
/// as these are required fields in the OpenAPI Specification for the Info Object.
///
/// Returns:
/// A boolean value: `true` if both [title] and [version] are non-null, `false` otherwise.
///
/// Example:
/// ```dart
/// var info = APIInfo('My API', '1.0.0');
/// print(info.isValid); // Output: true
///
/// var incompleteInfo = APIInfo.empty();
/// print(incompleteInfo.isValid); // Output: false
/// ```
bool get isValid => title != null && version != null;
/// Decodes the [APIInfo] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIInfo] object
/// from a [KeyedArchive]. It decodes the following fields:
/// - 'title': The title of the API (String)
/// - 'description': A short description of the API (String)
/// - 'termsOfService': URL to the Terms of Service (Uri)
/// - 'contact': Contact information (APIContact)
/// - 'license': License information (APILicense)
/// - 'version': The version of the API (String)
///
/// The 'contact' and 'license' fields are decoded as objects of their respective types.
///
/// This method overrides the [decode] method from the superclass and calls it before
/// performing its own decoding operations.
///
/// @param object The [KeyedArchive] containing the encoded [APIInfo] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
title = object.decode("title");
description = object.decode("description");
termsOfServiceURL = object.decode("termsOfService");
contact = object.decodeObject("contact", () => APIContact());
license = object.decodeObject("license", () => APILicense.empty());
version = object.decode("version");
}
/// Encodes the [APIInfo] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APIInfo] object
/// into a [KeyedArchive]. It encodes the following fields:
/// - 'title': The title of the API (String)
/// - 'description': A short description of the API (String)
/// - 'version': The version of the API (String)
/// - 'termsOfService': URL to the Terms of Service (Uri)
/// - 'contact': Contact information (APIContact)
/// - 'license': License information (APILicense)
///
/// The method first checks if the required fields 'title' and 'version' are non-null.
/// If either is null, it throws an [ArgumentError].
///
/// This method overrides the [encode] method from the superclass and calls it before
/// performing its own encoding operations.
///
/// @param object The [KeyedArchive] to encode the [APIInfo] data into.
/// @throws ArgumentError if 'title' or 'version' is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (title == null || version == null) {
throw ArgumentError(
"APIInfo must have non-null values for: 'title', 'version'.",
);
}
object.encode("title", title);
object.encode("description", description);
object.encode("version", version);
object.encode("termsOfService", termsOfServiceURL);
object.encodeObject("contact", contact);
object.encodeObject("license", license);
}
}
/// Contact information for the exposed API.
///
/// This class represents the Contact Object as defined in the OpenAPI Specification.
/// It provides optional fields for the name, URL, and email of the contact person or organization
/// responsible for the API.
///
/// The [APIContact] class extends [APIObject] and provides methods to encode and decode
/// the contact information to and from a [KeyedArchive], which is useful for serialization
/// and deserialization of the API metadata.
///
/// Usage:
/// ```dart
/// var contact = APIContact(
/// name: 'API Support',
/// url: Uri.parse('https://www.example.com/support'),
/// email: 'support@example.com'
/// );
/// ```
class APIContact extends APIObject {
/// Creates an [APIContact] instance with optional name, URL, and email.
///
/// This constructor allows you to create an [APIContact] object by providing
/// optional parameters for the contact's name, URL, and email address.
///
/// Parameters:
/// - [name]: The identifying name of the contact person/organization.
/// - [url]: The URL pointing to the contact information. Must be a valid URI.
/// - [email]: The email address of the contact person/organization.
///
/// Example:
/// ```dart
/// var contact = APIContact(
/// name: 'API Support',
/// url: Uri.parse('https://www.example.com/support'),
/// email: 'support@example.com'
/// );
/// ```
APIContact({this.name, this.url, this.email});
/// Creates an empty [APIContact] instance.
///
/// This constructor initializes an [APIContact] object without setting any of its properties.
/// It can be useful when you need to create an instance of [APIContact] and populate its
/// properties later, or when decoding from a serialized format.
APIContact.empty();
/// The identifying name of the contact person/organization.
///
/// This property represents the name of the individual or organization
/// responsible for the API. It's an optional field in the OpenAPI Specification,
/// so it can be null if no contact name is provided.
///
/// Example:
/// ```dart
/// var contact = APIContact(name: 'API Support Team');
/// ```
String? name;
/// The URL pointing to the contact information.
///
/// This property represents a URL that provides additional contact information
/// for the API. According to the OpenAPI Specification, if provided, this field
/// MUST be in the format of a valid URL.
///
/// This field is optional and can be null if no contact URL is specified.
///
/// Example:
/// ```dart
/// var contact = APIContact(
/// url: Uri.parse('https://www.example.com/api-support')
/// );
/// ```
///
/// Note: When setting this field, ensure that the provided URI is valid and accessible.
Uri? url;
/// The email address of the contact person/organization.
///
/// This property represents the email address for contacting the individual or organization
/// responsible for the API. According to the OpenAPI Specification, if provided, this field
/// MUST be in the format of a valid email address.
///
/// This field is optional and can be null if no contact email is specified.
///
/// Example:
/// ```dart
/// var contact = APIContact(email: 'support@example.com');
/// ```
///
/// Note: When setting this field, ensure that the provided email address is valid and follows
/// the standard email format (e.g., username@domain.com).
///
/// MUST be in the format of an email address.
String? email;
/// Decodes the [APIContact] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIContact] object
/// from a [KeyedArchive]. It decodes the following fields:
/// - 'name': The identifying name of the contact person/organization (String)
/// - 'url': The URL pointing to the contact information (Uri)
/// - 'email': The email address of the contact person/organization (String)
///
/// This method overrides the [decode] method from the superclass and calls it before
/// performing its own decoding operations.
///
/// @param object The [KeyedArchive] containing the encoded [APIContact] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
url = object.decode("url");
email = object.decode("email");
}
/// Encodes the [APIContact] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APIContact] object
/// into a [KeyedArchive]. It encodes the following fields:
/// - 'name': The identifying name of the contact person/organization (String)
/// - 'url': The URL pointing to the contact information (Uri)
/// - 'email': The email address of the contact person/organization (String)
///
/// This method overrides the [encode] method from the superclass and calls it before
/// performing its own encoding operations.
///
/// @param object The [KeyedArchive] to encode the [APIContact] data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("name", name);
object.encode("url", url);
object.encode("email", email);
}
}
/// License information for the exposed API.
///
/// This class represents the License Object as defined in the OpenAPI Specification.
/// It provides information about the license under which the API is made available.
///
/// The [APILicense] class extends [APIObject] and provides methods to encode and decode
/// the license information to and from a [KeyedArchive], which is useful for serialization
/// and deserialization of the API metadata.
///
/// Usage:
/// ```dart
/// var license = APILicense('Apache 2.0', url: Uri.parse('https://www.apache.org/licenses/LICENSE-2.0.html'));
/// ```
class APILicense extends APIObject {
/// Creates an [APILicense] instance with a required name and an optional URL.
///
/// This constructor initializes an [APILicense] object with the provided license name
/// and an optional URL to the full license text.
///
/// Parameters:
/// - [name]: The name of the license. This parameter is required.
/// - [url]: An optional URL pointing to the full text of the license.
///
/// Example:
/// ```dart
/// var license = APILicense('Apache 2.0', url: Uri.parse('https://www.apache.org/licenses/LICENSE-2.0.html'));
/// ```
APILicense(this.name, {this.url});
/// Creates an empty [APILicense] instance.
///
/// This constructor initializes an [APILicense] object without setting any of its properties.
/// It can be useful when you need to create an instance of [APILicense] and populate its
/// properties later, or when decoding from a serialized format.
APILicense.empty();
/// The license name used for the API.
///
/// This property represents the name of the license under which the API is made available.
/// According to the OpenAPI Specification, this field is REQUIRED for the License Object.
///
/// Despite being marked as required in the specification, this field is nullable
/// to allow for deserialization of incomplete data. Always ensure this field is set
/// before using the [APILicense] object in production.
///
/// Example:
/// ```dart
/// var license = APILicense('Apache 2.0');
/// print(license.name); // Output: Apache 2.0
/// ```
///
/// REQUIRED.
String? name;
/// A URL to the license used for the API.
///
/// This property represents a URL pointing to the full text of the license
/// under which the API is made available. According to the OpenAPI Specification,
/// if provided, this field MUST be in the format of a valid URL.
///
/// This field is optional and can be null if no license URL is specified.
///
/// Example:
/// ```dart
/// var license = APILicense('Apache 2.0',
/// url: Uri.parse('https://www.apache.org/licenses/LICENSE-2.0.html')
/// );
/// ```
///
/// Note: When setting this field, ensure that the provided URI is valid and accessible.
///
/// MUST be in the format of a URL.
Uri? url;
/// Decodes the [APILicense] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APILicense] object
/// from a [KeyedArchive]. It decodes the following fields:
/// - 'name': The name of the license used for the API (String)
/// - 'url': A URL to the license used for the API (Uri)
///
/// This method overrides the [decode] method from the superclass and calls it before
/// performing its own decoding operations.
///
/// @param object The [KeyedArchive] containing the encoded [APILicense] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
url = object.decode("url");
}
/// Encodes the [APILicense] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APILicense] object
/// into a [KeyedArchive]. It encodes the following fields:
/// - 'name': The name of the license used for the API (String)
/// - 'url': A URL to the license used for the API (Uri)
///
/// This method first calls the superclass's encode method, then checks if the required
/// 'name' field is non-null. If 'name' is null, it throws an [ArgumentError].
///
/// @param object The [KeyedArchive] to encode the [APILicense] data into.
/// @throws ArgumentError if 'name' is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (name == null) {
throw ArgumentError("APILicense must have non-null values for: 'name'.");
}
object.encode("name", name);
object.encode("url", url);
}
}
/// Adds metadata to a single tag that is used by the [APIOperation].
///
/// It is not mandatory to have a [APITag] per tag defined in the [APIOperation] instances.
///
/// The [APITag] class extends [APIObject] and provides methods to encode and decode
/// the tag information to and from a [KeyedArchive], which is useful for serialization
/// and deserialization of the API metadata.
///
/// Usage:
/// ```dart
/// var tag = APITag('user', description: 'User-related operations');
/// ```
class APITag extends APIObject {
/// Creates an [APITag] instance with a required name and an optional description.
///
/// Parameters:
/// - [name]: The name of the tag. This parameter is required.
/// - [description]: An optional short description for the tag.
///
/// Example:
/// ```dart
/// var tag = APITag('user', description: 'User-related operations');
/// ```
APITag(this.name, {this.description});
/// Creates an empty [APITag] instance.
///
/// This constructor initializes an [APITag] object without setting any of its properties.
/// It can be useful when you need to create an instance of [APITag] and populate its
/// properties later, or when decoding from a serialized format.
APITag.empty();
/// The name of the tag.
///
/// This property represents the name of the tag used to group operations.
/// According to the OpenAPI Specification, this field is REQUIRED for the Tag Object.
///
/// Despite being marked as required in the specification, this field is nullable
/// to allow for deserialization of incomplete data. Always ensure this field is set
/// before using the [APITag] object in production.
///
/// REQUIRED.
String? name;
/// A short description for the tag.
///
/// This property provides a brief description of the tag's purpose or the operations it groups.
/// According to the OpenAPI Specification, CommonMark syntax MAY be used for rich text representation.
///
/// This field is optional and can be null if no description is provided.
///
/// CommonMark syntax MAY be used for rich text representation.
String? description;
/// Decodes the [APITag] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APITag] object
/// from a [KeyedArchive]. It decodes the following fields:
/// - 'name': The name of the tag (String)
/// - 'description': A short description for the tag (String)
///
/// This method overrides the [decode] method from the superclass and calls it before
/// performing its own decoding operations.
///
/// @param object The [KeyedArchive] containing the encoded [APITag] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
description = object.decode("description");
}
/// Encodes the [APITag] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APITag] object
/// into a [KeyedArchive]. It encodes the following fields:
/// - 'name': The name of the tag (String)
/// - 'description': A short description for the tag (String)
///
/// This method first calls the superclass's encode method, then checks if the required
/// 'name' field is non-null. If 'name' is null, it throws an [ArgumentError].
///
/// @param object The [KeyedArchive] to encode the [APITag] data into.
/// @throws ArgumentError if 'name' is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (name == null) {
throw ArgumentError("APITag must have non-null values for: 'name'.");
}
object.encode("name", name);
object.encode("description", description);
}
}

View file

@ -1,430 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Describes a single API operation on a path.
///
/// This class represents an operation (HTTP method) in an OpenAPI specification.
/// It contains information about the operation such as tags, summary, description,
/// parameters, security requirements, request body, responses, and more.
///
/// The class provides methods to add parameters, security requirements, and responses,
/// as well as to retrieve specific parameters by name.
///
/// This class extends [APIObject] and implements [Codable] for serialization and deserialization.
class APIOperation extends APIObject {
/// Creates a new [APIOperation] instance.
///
/// [id] is the unique string used to identify the operation.
/// [responses] is a map of possible responses as they are returned from executing this operation.
///
/// Optional parameters:
/// - [tags]: A list of tags for API documentation control.
/// - [summary]: A short summary of what the operation does.
/// - [description]: A verbose explanation of the operation behavior.
/// - [parameters]: A list of parameters that are applicable for this operation.
/// - [security]: A declaration of which security mechanisms can be used for this operation.
/// - [requestBody]: The request body applicable for this operation.
/// - [callbacks]: A map of possible out-of band callbacks related to the parent operation.
/// - [deprecated]: Declares this operation to be deprecated.
APIOperation(
this.id,
this.responses, {
this.tags,
this.summary,
this.description,
this.parameters,
this.security,
this.requestBody,
this.callbacks,
this.deprecated,
});
/// Creates an empty [APIOperation] instance.
///
/// This constructor initializes an [APIOperation] with no properties set,
/// allowing for manual population of fields after creation.
APIOperation.empty();
/// A list of tags for API documentation control.
///
/// Tags can be used for logical grouping of operations by resources or any other qualifier.
/// These tags are used to categorize and organize API operations in documentation tools and clients.
/// Each tag is a string that represents a specific category or group.
/// Tags are optional but can greatly improve the organization and discoverability of API operations.
/// Multiple tags can be applied to a single operation, allowing for flexible categorization.
List<String>? tags;
/// A short summary of what the operation does.
///
/// This property provides a concise description of the operation's purpose.
/// It should be brief, typically a single sentence or short paragraph.
/// The summary is often used in API documentation to give users a quick
/// understanding of what the operation does without needing to read the
/// full description.
String? summary;
/// A verbose explanation of the operation behavior.
///
/// This property provides a detailed description of what the operation does,
/// how it works, and any important information that users or developers should
/// know about its behavior. It can include information about:
/// - The purpose of the operation
/// - Expected input and output
/// - Possible side effects
/// - Usage examples
/// - Any limitations or constraints
///
/// The description can be more extensive than the summary and is meant to provide
/// a comprehensive understanding of the operation.
///
/// CommonMark syntax MAY be used for rich text representation, allowing for
/// formatted text, lists, code blocks, and other Markdown features to enhance
/// readability and organization of the 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.
///
/// This property serves as a unique identifier for the operation within the API specification.
/// It is crucial for:
/// - Distinguishing between different operations
/// - Enabling tools and libraries to reference specific operations
/// - Maintaining consistency and clarity in API documentation
///
/// Best practices for assigning an operationId:
/// - Use camelCase naming convention
/// - Make it descriptive of the operation's purpose
/// - Ensure it's unique across all operations in the API
/// - Keep it concise while still being meaningful
///
/// Example: 'getUserProfile', 'createOrder', 'updateItemInventory'
///
/// Note: While optional in the OpenAPI specification, providing an operationId
/// is strongly recommended for better API organization and tooling support.
String? id;
/// A list of parameters that are applicable for this operation.
///
/// This property defines the parameters that are specific to this operation.
/// These parameters can be in addition to or overriding the parameters defined
/// at the path level.
///
/// Key points:
/// - If a parameter is already defined at the Path Item level, the definition
/// here will override it, but cannot remove it entirely.
/// - 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 in the OpenAPI Object's components/parameters section.
/// - Parameters defined here are specific to this operation and may not apply
/// to other operations, even within the same path.
///
/// This property allows for fine-grained control over the parameters for each
/// individual operation, enabling precise API documentation and client generation.
List<APIParameter>? parameters;
/// A declaration of which security mechanisms can be used for this operation.
///
/// This property defines the security requirements for the specific operation.
/// It is represented as a list of [APISecurityRequirement] objects.
///
/// Key points:
/// - Each element in the list represents an alternative security requirement.
/// - Only one of the security requirement objects needs to be satisfied to authorize a request.
/// - This definition overrides any declared top-level security for the API.
/// - To remove a top-level security declaration, an empty array can be used.
/// - If not specified, the security requirements defined at the API level apply.
///
/// The security requirements can include various authentication schemes such as:
/// - API keys
/// - OAuth2 flows
/// - OpenID Connect Discovery
/// - HTTP authentication schemes (e.g., Basic, Bearer)
///
/// By specifying security at the operation level, you can have fine-grained
/// control over the security requirements for different API endpoints.
List<APISecurityRequirement>? security;
/// The request body applicable for this operation.
///
/// This property specifies the request body content for this operation. It is represented
/// by an [APIRequestBody] object, which describes a single request body.
///
/// Key points:
/// - The requestBody is only supported in HTTP methods where the HTTP 1.1 specification
/// RFC7231 has explicitly defined semantics for request bodies.
/// - In cases where the HTTP spec is vague about request bodies, this property SHALL be
/// ignored by consumers.
/// - It can be used to describe the content, format, and schema of the request body.
/// - This property is particularly useful for POST, PUT, and PATCH operations.
/// - It can specify multiple content types that the API can consume.
/// - If null, it indicates that the operation does not expect a request body.
///
/// Note: The actual support and behavior regarding request bodies may vary depending on
/// the specific HTTP method used and how strictly the implementation follows the HTTP
/// specifications.
APIRequestBody? requestBody;
/// The list of possible responses as they are returned from executing this operation.
///
/// This property is a map where the keys are HTTP status codes (as strings) and the values
/// are [APIResponse] objects describing the response for that status code.
///
/// Key points:
/// - This property is REQUIRED in the OpenAPI specification.
/// - It must contain at least one response object, which may be the 'default' response.
/// - The map can use the wildcard HTTP status code '2XX', '3XX', '4XX', or '5XX' to describe
/// multiple status codes at once.
/// - The 'default' key can be used to describe the response for all undeclared status codes.
///
/// Example:
/// ```
/// "responses": {
/// "200": { ... }, // Successful response
/// "400": { ... }, // Bad request response
/// "default": { ... } // Unexpected error response
/// }
/// ```
///
/// Each [APIResponse] object in this map provides details about the response such as
/// description, headers, content, and links.
Map<String, APIResponse?>? 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<String, APICallback?>? 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.
///
/// This property allows specifying operation-specific servers, which take precedence over servers defined at the path or document level.
/// Each [APIServerDescription] in the list represents a server that can handle requests for this specific operation.
///
/// Key points:
/// - If specified, this array overrides any server configurations defined at higher levels (path, document).
/// - It allows for fine-grained control over which servers can handle specific operations.
/// - Useful for scenarios where certain operations are only available on specific servers.
/// - Can be used to specify different environments (e.g., production, staging) for different operations.
///
/// The list can be null if no operation-specific servers are defined, in which case the servers defined at higher levels will be used.
List<APIServerDescription?>? servers;
/// Declares this operation to be deprecated.
///
/// This property indicates whether the operation is deprecated and should be avoided in future use.
///
/// Key points:
/// - When set to true, it signals that the operation is no longer recommended for use.
/// - Consumers of the API SHOULD refrain from using deprecated operations.
/// - It helps in managing API lifecycle by indicating which operations are being phased out.
/// - Can be used to guide API users towards newer or preferred alternatives.
/// - The default value is false if not explicitly set.
///
/// Note: Even when an operation is marked as deprecated, it may still be functional.
/// However, it's a strong indication that the operation may be removed or altered in future versions of the API.
bool? deprecated;
/// Returns the parameter named [name] or null if it doesn't exist.
///
/// This method searches the [parameters] list for a parameter with the specified [name].
/// If found, it returns the matching [APIParameter] object.
/// If no parameter with the given name is found, or if [parameters] is null, it returns null.
///
/// Parameters:
/// [name]: The name of the parameter to search for.
///
/// Returns:
/// An [APIParameter] object if a parameter with the specified name is found, otherwise null.
APIParameter? parameterNamed(String name) =>
parameters?.firstWhere((p) => p.name == name);
/// Adds a parameter to the list of parameters for this operation.
///
/// If [parameters] is null, invoking this method will set it to a list containing [parameter].
/// Otherwise, [parameter] is added to [parameters].
void addParameter(APIParameter parameter) {
parameters ??= [];
parameters!.add(parameter);
}
/// Adds a security requirement to the operation's security list.
///
/// If [security] is null, invoking this method will set it to a list containing [requirement].
/// Otherwise, [requirement] is added to [security].
void addSecurityRequirement(APISecurityRequirement requirement) {
security ??= [];
security!.add(requirement);
}
/// Adds [response] to [responses], merging schemas if necessary.
///
/// This method adds the given [response] to the [responses] map using the [statusCode] as the key.
/// If a response already exists for the given [statusCode], it merges the new response with the existing one.
///
/// Parameters:
/// [statusCode]: The HTTP status code for the response.
/// [response]: The APIResponse object to be added or merged.
///
/// Behavior:
/// - If [responses] is null, it initializes it as an empty map.
/// - If no response exists for the given [statusCode], it simply adds the new response.
/// - If a response already exists:
/// - The descriptions are concatenated.
/// - Headers from the new response are added to the existing response.
/// - Content from the new response is added to the existing response.
///
/// Note: This method modifies the [responses] property of the current object.
///
/// Example:
/// ```dart
/// var operation = APIOperation.empty();
/// var response = APIResponse(...);
/// operation.addResponse(200, response);
/// ```
///
/// This method is useful for building or updating the responses of an API operation,
/// allowing for the gradual construction of complex response structures or the
/// modification of existing responses without overwriting all information.
void addResponse(int statusCode, APIResponse? response) {
responses ??= {};
final key = "$statusCode";
final existingResponse = responses![key];
if (existingResponse == null) {
responses![key] = response;
return;
}
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);
});
}
/// Defines the casting rules for specific properties of this class.
///
/// This getter provides a map where the keys are property names and the values
/// are [cast.Cast] objects that define how these properties should be cast
/// when being decoded or encoded.
///
/// In this case, it specifies that the 'tags' property should be cast as a
/// List of strings. This ensures that when the 'tags' property is processed,
/// it will be treated as a list of string values.
///
/// Returns:
/// A Map<String, cast.Cast> where:
/// - The key 'tags' is associated with a cast.List(cast.string) value,
/// indicating that 'tags' should be cast as a list of strings.
@override
Map<String, cast.Cast> get castMap => {"tags": const cast.List(cast.string)};
/// Decodes the properties of this [APIOperation] from a [KeyedArchive] object.
///
/// This method is responsible for populating the properties of the [APIOperation]
/// instance from the provided [KeyedArchive] object. It decodes various fields
/// such as tags, summary, description, parameters, responses, and more.
///
/// The method handles different types of properties:
/// - Simple properties like 'tags', 'summary', 'description' are directly decoded.
/// - Complex properties like 'parameters', 'security', 'servers' are decoded as lists of objects.
/// - Map properties like 'responses' and 'callbacks' are decoded as object maps.
/// - Objects like 'requestBody' are decoded as single instances.
///
/// Some decoded properties (like 'parameters' and 'security') are filtered to remove null values.
///
/// This method overrides the 'decode' method from a superclass and calls the superclass
/// implementation before performing its own decoding.
///
/// Parameters:
/// [object]: The [KeyedArchive] containing the encoded data to be decoded.
@override
void decode(KeyedArchive object) {
super.decode(object);
tags = object.decode("tags");
summary = object.decode("summary");
description = object.decode("description");
id = object.decode("operationId");
parameters = object
.decodeObjects("parameters", () => APIParameter.empty())
?.nonNulls
.toList();
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", () => APISecurityRequirement.empty())
?.nonNulls
.toList();
servers =
object.decodeObjects("servers", () => APIServerDescription.empty());
}
/// Encodes the properties of this [APIOperation] into a [KeyedArchive] object.
///
/// This method is responsible for serializing the properties of the [APIOperation]
/// instance into the provided [KeyedArchive] object. It encodes various fields
/// such as tags, summary, description, parameters, responses, and more.
///
/// The method handles different types of properties:
/// - Simple properties like 'tags', 'summary', 'description' are directly encoded.
/// - Complex properties like 'parameters', 'security', 'servers' are encoded as lists of objects.
/// - Map properties like 'responses' and 'callbacks' are encoded as object maps.
/// - Objects like 'requestBody' are encoded as single instances.
///
/// This method throws an [ArgumentError] if the 'responses' property is null,
/// as it is a required field in the OpenAPI specification.
///
/// This method overrides the 'encode' method from a superclass and calls the superclass
/// implementation before performing its own encoding.
///
/// Parameters:
/// [object]: The [KeyedArchive] where the encoded data will be stored.
///
/// Throws:
/// [ArgumentError]: If the 'responses' property is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (responses == null) {
throw ArgumentError(
"Invalid specification. APIOperation must have non-null values for: 'responses'.",
);
}
object.encode("tags", tags);
object.encode("summary", summary);
object.encode("description", description);
object.encode("operationId", id);
object.encodeObjects("parameters", parameters);
object.encodeObject("requestBody", requestBody);
object.encodeObjectMap("responses", responses);
object.encodeObjectMap("callbacks", callbacks);
object.encode("deprecated", deprecated);
object.encodeObjects("security", security);
object.encodeObjects("servers", servers);
}
}

View file

@ -1,606 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Enumerates the possible locations for parameters in an API request.
///
/// - path:
/// - query:
/// - header:
/// - cookie:
enum APIParameterLocation {
/// Parameters that are appended to the URL.
///
/// For example, in /items?id=###, the query parameter is id.
///
/// Query parameters are used to filter, sort, or provide additional
/// information for the requested resource. They appear after the question
/// mark (?) in the URL and are separated by ampersands (&) if there are
/// multiple parameters.
query,
/// Custom headers that are expected as part of the request.
///
/// Headers are additional metadata sent with an HTTP request or response.
/// They provide information about the request or response, such as content type,
/// authentication tokens, or caching directives.
///
/// Examples of common headers include:
/// - Content-Type: Specifies the media type of the resource
/// - Authorization: Contains credentials for authenticating the client
/// - User-Agent: Identifies the client software making the request
///
/// Note that RFC7230 states header names are case insensitive.
header,
/// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
///
/// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
///
/// Path parameters are used to identify a specific resource or resources. They are part of the URL path and are typically
/// used to specify the ID of a resource. Path parameters are always required, as they are necessary to construct the full URL.
///
/// Examples:
/// - /users/{userId}: 'userId' is a path parameter
/// - /posts/{postId}/comments/{commentId}: both 'postId' and 'commentId' are path parameters
///
/// When defining an API, path parameters are denoted by curly braces {} in the URL template.
path,
/// Used to pass a specific cookie value to the API.
///
/// Cookie parameters are used to send data to the server using HTTP cookies.
/// They are typically used for maintaining session state, tracking user preferences,
/// or passing authentication tokens.
///
/// Cookie parameters are sent in the Cookie HTTP header. Unlike query parameters,
/// cookie parameters are not visible in the URL and are sent with every request
/// to the domain that set the cookie.
///
/// Example of a cookie header:
/// Cookie: session_id=abc123; user_preference=dark_mode
///
/// Note: Use of cookies should be done with consideration for security and privacy implications.
cookie
}
/// A utility class for encoding and decoding [APIParameterLocation] values.
///
/// This class provides two static methods:
/// - [decode]: Converts a string representation of a parameter location to an [APIParameterLocation] enum value.
/// - [encode]: Converts an [APIParameterLocation] enum value to its string representation.
///
/// Both methods handle null values gracefully, returning null if the input is null or not recognized.
///
/// Usage:
/// ```dart
/// // Decoding a string to APIParameterLocation
/// APIParameterLocation? location = APIParameterLocationCodec.decode("query");
/// print(location); // Output: APIParameterLocation.query
///
/// // Encoding an APIParameterLocation to a string
/// String? locationString = APIParameterLocationCodec.encode(APIParameterLocation.path);
/// print(locationString); // Output: "path"
/// ```
///
/// This class is particularly useful when working with API specifications or
/// when serializing/deserializing API parameter location data.
class APIParameterLocationCodec {
/// Decodes a string representation of a parameter location to an [APIParameterLocation] enum value.
///
/// Returns:
/// The corresponding [APIParameterLocation] enum value, or null if not recognized.
static APIParameterLocation? decode(String? location) {
switch (location) {
case "query":
return APIParameterLocation.query;
case "header":
return APIParameterLocation.header;
case "path":
return APIParameterLocation.path;
case "cookie":
return APIParameterLocation.cookie;
default:
return null;
}
}
/// Encodes an [APIParameterLocation] enum value to its string representation.
///
/// This method takes an [APIParameterLocation] enum value [location] and returns
/// the corresponding string representation. If the input is null or not recognized,
/// the method returns null.
///
/// Supported [APIParameterLocation] values and their string representations:
/// - [APIParameterLocation.query] returns "query"
/// - [APIParameterLocation.header] returns "header"
/// - [APIParameterLocation.path] returns "path"
/// - [APIParameterLocation.cookie] returns "cookie"
///
/// Parameters:
/// [location] - An [APIParameterLocation] enum value to be encoded.
///
/// Returns:
/// A [String] representation of the [APIParameterLocation], or null if not recognized.
static String? encode(APIParameterLocation? location) {
switch (location) {
case APIParameterLocation.query:
return "query";
case APIParameterLocation.header:
return "header";
case APIParameterLocation.path:
return "path";
case APIParameterLocation.cookie:
return "cookie";
default:
return null;
}
}
}
/// Describes a single operation parameter in an API specification.
///
/// A unique parameter is defined by a combination of a [name] and [location].
class APIParameter extends APIObject {
/// Creates an [APIParameter] instance.
///
/// Parameters:
/// - [name]: The name of the parameter.
/// - [location]: The location of the parameter (query, header, path, or cookie).
/// - [description]: A brief description of the parameter.
/// - [schema]: The schema defining the type used for the parameter.
/// - [content]: A map containing the representations for the parameter.
/// - [style]: Describes how the parameter value will be serialized.
/// - [isRequired]: Determines whether this parameter is mandatory.
/// - [deprecated]: Specifies that a parameter is deprecated.
/// - [allowEmptyValue]: Sets the ability to pass empty-valued parameters.
/// - [explode]: When true, generates separate parameters for each value of array or object.
/// - [allowReserved]: Determines whether the parameter value should allow reserved characters.
APIParameter(
this.name,
this.location, {
this.description,
this.schema,
this.content,
this.style,
bool? isRequired,
this.deprecated,
this.allowEmptyValue,
this.explode,
this.allowReserved,
}) : _required = isRequired;
/// Creates an empty [APIParameter] instance.
///
/// This constructor initializes an [APIParameter] without setting any properties.
/// It can be useful when you need to create a parameter object and set its
/// properties later, or when you want to create a placeholder parameter.
///
/// Example usage:
/// ```dart
/// var emptyParameter = APIParameter.empty();
/// // Properties can be set later
/// emptyParameter.name = 'exampleParam';
/// emptyParameter.location = APIParameterLocation.query;
/// ```
APIParameter.empty();
/// Creates an [APIParameter] instance for a header parameter.
///
/// This constructor initializes an [APIParameter] with the location set to [APIParameterLocation.header].
///
/// Parameters:
/// - [name]: The name of the header parameter.
/// - [description]: Optional. A brief description of the parameter.
/// - [schema]: Optional. The schema defining the type used for the parameter.
/// - [content]: Optional. A map containing the representations for the parameter.
/// - [style]: Optional. Describes how the parameter value will be serialized.
/// - [isRequired]: Optional. Determines whether this parameter is mandatory.
/// - [deprecated]: Optional. Specifies that a parameter is deprecated.
/// - [allowEmptyValue]: Optional. Sets the ability to pass empty-valued parameters.
/// - [explode]: Optional. When true, generates separate parameters for each value of array or object.
/// - [allowReserved]: Optional. Determines whether the parameter value should allow reserved characters.
APIParameter.header(
this.name, {
this.description,
this.schema,
this.content,
this.style,
bool? isRequired,
this.deprecated,
this.allowEmptyValue,
this.explode,
this.allowReserved,
}) : _required = isRequired {
location = APIParameterLocation.header;
}
/// Creates an [APIParameter] instance for a query parameter.
///
/// This constructor initializes an [APIParameter] with the location set to [APIParameterLocation.query].
///
/// Parameters:
/// - [name]: The name of the query parameter.
/// - [description]: Optional. A brief description of the parameter.
/// - [schema]: Optional. The schema defining the type used for the parameter.
/// - [content]: Optional. A map containing the representations for the parameter.
/// - [style]: Optional. Describes how the parameter value will be serialized.
/// - [isRequired]: Optional. Determines whether this parameter is mandatory.
/// - [deprecated]: Optional. Specifies that a parameter is deprecated.
/// - [allowEmptyValue]: Optional. Sets the ability to pass empty-valued parameters.
/// - [explode]: Optional. When true, generates separate parameters for each value of array or object.
/// - [allowReserved]: Optional. Determines whether the parameter value should allow reserved characters.
APIParameter.query(
this.name, {
this.description,
this.schema,
this.content,
this.style,
bool? isRequired,
this.deprecated,
this.allowEmptyValue,
this.explode,
this.allowReserved,
}) : _required = isRequired {
location = APIParameterLocation.query;
}
/// Creates an [APIParameter] instance for a path parameter.
///
/// This constructor initializes an [APIParameter] with the following properties:
/// - [location] is set to [APIParameterLocation.path]
/// - [schema] is set to a string schema using [APISchemaObject.string()]
/// - [_required] is set to true, as path parameters are always required
///
/// Parameters:
/// - [name]: The name of the path parameter.
///
/// Usage:
/// ```dart
/// var pathParam = APIParameter.path('userId');
/// ```
APIParameter.path(this.name)
: location = APIParameterLocation.path,
schema = APISchemaObject.string(),
_required = true;
/// Creates an [APIParameter] instance for a cookie parameter.
///
/// This constructor initializes an [APIParameter] with the location set to [APIParameterLocation.cookie].
///
/// Parameters:
/// - [name]: The name of the cookie parameter.
/// - [description]: Optional. A brief description of the parameter.
/// - [schema]: Optional. The schema defining the type used for the parameter.
/// - [content]: Optional. A map containing the representations for the parameter.
/// - [style]: Optional. Describes how the parameter value will be serialized.
/// - [isRequired]: Optional. Determines whether this parameter is mandatory.
/// - [deprecated]: Optional. Specifies that a parameter is deprecated.
/// - [allowEmptyValue]: Optional. Sets the ability to pass empty-valued parameters.
/// - [explode]: Optional. When true, generates separate parameters for each value of array or object.
/// - [allowReserved]: Optional. Determines whether the parameter value should allow reserved characters.
APIParameter.cookie(
this.name, {
this.description,
this.schema,
this.content,
this.style,
bool? isRequired,
this.deprecated,
this.allowEmptyValue,
this.explode,
this.allowReserved,
}) : _required = isRequired {
location = APIParameterLocation.cookie;
}
/// The name of the parameter.
///
/// This property is REQUIRED for all parameters. The name is case sensitive and must be unique within the parameter list.
///
/// Specific behavior based on the parameter location:
/// - If [location] is "path", the name MUST correspond to a path segment in the [APIDocument.paths] field.
/// - If [location] is "header" and the name is "Accept", "Content-Type", or "Authorization", the parameter definition will be ignored.
/// - For all other cases, the name corresponds to the parameter name used by the [location] property.
///
/// See Path Templating in the OpenAPI Specification for more information on path parameters.
///
/// Note: This field is nullable in the class definition, but should be non-null when used in a valid API specification.
String? name;
/// A brief description of the parameter.
///
/// This property provides a short explanation of the parameter's purpose, usage, or any other relevant information.
/// It can include examples to illustrate how the parameter should be used.
///
/// The description supports CommonMark syntax, allowing for rich text formatting such as bold, italic, lists, and more.
/// This enables clear and structured documentation of the parameter.
///
/// Example:
/// ```
/// description: "The **user's age** in years. Must be a positive integer."
/// ```
///
/// Note: This property is optional but highly recommended for clear API documentation.
String? description;
/// Determines whether this parameter is mandatory.
///
/// This property is implemented as a getter and setter pair.
///
/// The getter:
/// - Returns true if the parameter location is "path", regardless of the value of _required.
/// - Otherwise, returns the value of _required.
///
/// The setter:
/// - Sets the value of _required to the provided boolean value.
///
/// Note: If the parameter location is "path", this property is REQUIRED and its value MUST be true.
/// For other locations, the property MAY be included and its default value is false.
bool? get isRequired =>
location == APIParameterLocation.path ? true : _required;
set isRequired(bool? f) {
_required = f;
}
/// Stores the required status of the parameter.
///
/// This private field is used to back the [isRequired] property.
/// It's nullable to allow for cases where the required status is not explicitly set.
///
/// Note: For path parameters, this value is ignored as they are always required.
/// For other parameter types, if not set, it defaults to false.
bool? _required;
/// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
///
/// When set to true, it indicates that the parameter is deprecated and consumers of the API
/// should refrain from using it. This allows API providers to gradually phase out parameters
/// while maintaining backward compatibility.
///
/// Default value is false (not deprecated) when not specified.
///
/// Example usage:
/// ```dart
/// var parameter = APIParameter('oldParam', APIParameterLocation.query);
/// parameter.deprecated = true;
/// ```
bool? deprecated;
/// The location of the parameter.
///
/// This property specifies where the parameter is expected to be found in the API request.
/// It is a REQUIRED field for all parameter objects.
///
/// The value must be one of the following:
/// - "query": The parameter is part of the query string in the URL.
/// - "header": The parameter is included as an HTTP header.
/// - "path": The parameter is part of the URL path.
/// - "cookie": The parameter is sent as an HTTP cookie.
///
/// This field uses the [APIParameterLocation] enum to ensure type safety and
/// to restrict the possible values to the allowed set.
///
/// Note: Although this field is nullable in the class definition, it should always
/// be set to a non-null value when used in a valid API specification.
APIParameterLocation? location;
/// The schema defining the type used for the parameter.
///
/// This property defines the structure and constraints of the parameter value.
/// It can specify the data type, format, validation rules, and other characteristics
/// of the parameter.
///
/// The schema is represented by an [APISchemaObject], which allows for detailed
/// specification of simple types (like strings or integers) as well as complex
/// structures (like objects or arrays).
///
/// This property is mutually exclusive with the [content] property. When using
/// [schema], the serialization rules for the parameter are defined by the
/// [style] and [explode] fields.
///
/// Example usage:
/// ```dart
/// parameter.schema = APISchemaObject.string(format: 'email');
/// ```
APISchemaObject? schema;
/// Sets the ability to pass empty-valued parameters.
///
/// This property is only applicable for query parameters and allows sending a parameter with an empty value.
/// The default value is false.
///
/// Note: If [style] is used, and if the behavior is not applicable (cannot be serialized),
/// the value of [allowEmptyValue] SHALL be ignored.
///
/// Example usage:
/// ```dart
/// var parameter = APIParameter('queryParam', APIParameterLocation.query);
/// parameter.allowEmptyValue = true;
/// ```
bool? allowEmptyValue = false;
/// Describes how the parameter value will be serialized depending on the type of the parameter value.
///
/// This property determines the format in which the parameter value should be serialized when sent in the request.
/// The appropriate serialization method depends on both the parameter's location and its data type.
///
/// Default values (based on the value of 'in' property):
/// - For query parameters: "form"
/// - For path parameters: "simple"
/// - For header parameters: "simple"
/// - For cookie parameters: "form"
///
/// Possible values include:
/// - "matrix"
/// - "label"
/// - "form"
/// - "simple"
/// - "spaceDelimited"
/// - "pipeDelimited"
/// - "deepObject"
///
/// The exact behavior and applicability of each style depend on the parameter's location and data type.
/// Refer to the OpenAPI Specification for detailed information on how each style affects serialization.
String? style;
/// Specifies whether array or object parameters should be expanded into multiple parameters.
///
/// 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? 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.
///
/// When set to true, reserved characters in the parameter value are allowed to be included without percent-encoding.
/// This can be useful in cases where you want to pass special characters directly in the query string.
///
/// Example:
/// If allowReserved is true, a query parameter like "filter=name:John/age:30" could be sent as-is.
/// If false, it would need to be encoded as "filter=name%3AJohn%2Fage%3A30".
///
/// Note: Use this property with caution, as it may affect how the server interprets the parameter value.
/// It's recommended to keep this false unless you have a specific reason to allow reserved characters.
bool? allowReserved = false;
/// A map containing the representations for the parameter.
///
/// The key is a media type and the value describes it. The map MUST only contain one entry.
///
/// This property is mutually exclusive with the [schema] property. It can be used to describe
/// complex parameter structures or to specify alternative representations of the parameter value.
///
/// The media type (key) should be a string representing the MIME type of the content,
/// such as "application/json" or "text/plain".
///
/// Each [APIMediaType] value provides detailed information about the content,
/// including its schema, examples, and encoding properties.
///
/// Example usage:
/// ```dart
/// parameter.content = {
/// 'application/json': APIMediaType(
/// schema: APISchemaObject.object({
/// 'name': APISchemaObject.string(),
/// 'age': APISchemaObject.integer(),
/// })
/// )
/// };
/// ```
///
/// Note: While the map allows for multiple entries, according to the OpenAPI Specification,
/// it MUST only contain one entry for parameter objects.
Map<String, APIMediaType?>? content;
/// Decodes an [APIParameter] object from a [KeyedArchive].
///
/// This method populates the properties of the [APIParameter] instance
/// by decoding values from the provided [KeyedArchive] object.
///
/// The following properties are decoded:
/// - name: The name of the parameter
/// - description: A brief description of the parameter
/// - location: The location of the parameter (query, header, path, or cookie)
/// - _required: Whether the parameter is required
/// - deprecated: Whether the parameter is deprecated
/// - allowEmptyValue: Whether empty values are allowed for this parameter
/// - schema: The schema defining the type used for the parameter
/// - style: Describes how the parameter value will be serialized
/// - explode: Whether to expand array or object parameters
/// - allowReserved: Whether the parameter value should allow reserved characters
/// - content: A map containing the representations for the parameter
///
/// Note: This method does not currently decode 'example' and 'examples' properties.
///
/// Parameters:
/// object: The [KeyedArchive] containing the encoded [APIParameter] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
description = object.decode("description");
location = APIParameterLocationCodec.decode(object.decode("in"));
_required = object.decode("required");
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");
content = object.decodeObjectMap("content", () => APIMediaType());
}
/// Encodes the [APIParameter] object into a [KeyedArchive].
///
/// This method serializes the properties of the [APIParameter] instance
/// into the provided [KeyedArchive] object for storage or transmission.
///
/// The following properties are encoded:
/// - name: The name of the parameter
/// - description: A brief description of the parameter
/// - location: The location of the parameter (query, header, path, or cookie)
/// - required: Whether the parameter is required (always true for path parameters)
/// - deprecated: Whether the parameter is deprecated
/// - allowEmptyValue: Whether empty values are allowed (only for query parameters)
/// - schema: The schema defining the type used for the parameter
/// - style: Describes how the parameter value will be serialized
/// - explode: Whether to expand array or object parameters
/// - allowReserved: Whether the parameter value should allow reserved characters
/// - content: A map containing the representations for the parameter
///
/// This method also performs a validation check to ensure that both 'name'
/// and 'location' properties are non-null. If either of these properties
/// is null, it throws an [ArgumentError].
///
/// Parameters:
/// object: The [KeyedArchive] where the encoded data will be stored.
///
/// Throws:
/// ArgumentError: If either 'name' or 'location' is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (name == null || location == null) {
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));
if (location == APIParameterLocation.path) {
object.encode("required", true);
} else {
object.encode("required", _required);
}
object.encode("deprecated", deprecated);
if (location == APIParameterLocation.query) {
object.encode("allowEmptyValue", allowEmptyValue);
}
object.encodeObject("schema", schema);
object.encode("style", style);
object.encode("explode", explode);
object.encode("allowReserved", allowReserved);
object.encodeObjectMap("content", content);
}
}

View file

@ -1,187 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents a path in an API specification.
///
/// 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 {
/// Constructs an [APIPath] instance.
///
/// [summary] is an optional string summary, intended to apply to all operations in this path.
/// [description] is an optional string description, intended to apply to all operations in this path.
/// [parameters] is a list of parameters applicable for all operations described under this path. Defaults to an empty list if not provided.
/// [operations] is a map of HTTP methods to their corresponding [APIOperation] objects. Defaults to an empty map if not provided.
APIPath({
this.summary,
this.description,
List<APIParameter?>? parameters,
Map<String, APIOperation?>? operations,
}) {
this.parameters = parameters ?? [];
this.operations = operations ?? {};
}
/// Creates an empty [APIPath] instance.
///
/// This constructor initializes an [APIPath] with an empty list of parameters
/// and an empty map of operations. It's useful when you need to create an
/// [APIPath] instance without any initial data, which can be populated later.
APIPath.empty()
: parameters = <APIParameter?>[],
operations = <String, APIOperation?>{};
/// An optional, string summary, intended to apply to all operations in this path.
///
/// This property provides a brief overview or summary that is applicable to all
/// operations defined within this path. It can be used to quickly convey the
/// general purpose or functionality of the path without going into specific details
/// of individual operations.
String? summary;
/// An optional, string description, intended to apply to all operations in this path.
///
/// This property provides a more detailed explanation that is applicable to all
/// operations defined within this path. It can be used to offer comprehensive
/// information about the path's purpose, usage, or any other relevant details.
///
/// The description supports CommonMark syntax, allowing for rich text representation.
/// This enables the use of formatted text, links, lists, and other markdown elements
/// to create more readable and informative descriptions.
///
/// CommonMark syntax MAY be used for rich text representation.
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.
///
/// This property is marked as 'late' to allow for delayed initialization. It holds a list
/// of [APIParameter] objects, where each object represents a parameter applicable to all
/// operations under this path. The list may contain null values, which should be handled
/// appropriately when processing the parameters.
///
/// The parameters defined here serve as default parameters for all operations in this path,
/// providing a way to specify common parameters without repeating them for each operation.
late List<APIParameter?> parameters;
/// Definitions of operations on this path.
///
/// A map where keys are lowercased HTTP methods (e.g., get, put, delete, post)
/// and values are corresponding [APIOperation] objects.
///
/// This property defines the available operations for this path, associating
/// each HTTP method with its specific operation details. The use of lowercase
/// keys ensures consistency and case-insensitive matching of HTTP methods.
///
/// The map may contain null values, which should be handled appropriately
/// when processing the operations. This property is marked as 'late' to allow
/// for delayed initialization, typically done in the constructor or a dedicated
/// initialization method.
late Map<String, APIOperation?> operations;
/// Checks if this path contains specific path parameters.
///
/// Returns true if [parameters] contains path parameters with names that match [parameterNames] and
/// both lists have the same number of elements.
bool containsPathParameters(List<String> parameterNames) {
final pathParams = parameters
.where((p) => p?.location == APIParameterLocation.path)
.map((p) => p?.name)
.toList();
if (pathParams.length != parameterNames.length) {
return false;
}
return parameterNames.every((check) => pathParams.contains(check));
}
// todo (joeconwaystk): alternative servers not yet implemented
/// Decodes the [APIPath] instance from a [KeyedArchive] object.
///
/// This method populates the properties of the [APIPath] instance using data
/// from the provided [KeyedArchive] object. It decodes the following properties:
///
/// - [summary]: A brief summary of the path.
/// - [description]: A detailed description of the path.
/// - [parameters]: A list of [APIParameter] objects applicable to all operations in this path.
/// - [operations]: A map of HTTP methods to their corresponding [APIOperation] objects.
///
/// The method first calls the superclass's decode method, then decodes each specific
/// property. For [parameters], it uses a factory function to create empty [APIParameter]
/// instances if needed. For [operations], it checks for the presence of each HTTP method
/// in the archive and decodes the corresponding [APIOperation] if present.
///
/// @param object The [KeyedArchive] object containing the encoded data.
@override
void decode(KeyedArchive object) {
super.decode(object);
summary = object.decode("summary");
description = object.decode("description");
parameters =
object.decodeObjects("parameters", () => APIParameter.empty()) ??
<APIParameter?>[];
final methodNames = [
"get",
"put",
"post",
"delete",
"options",
"head",
"patch",
"trace"
];
for (final methodName in methodNames) {
if (object.containsKey(methodName)) {
operations[methodName] =
object.decodeObject(methodName, () => APIOperation.empty());
}
}
}
/// Encodes the [APIPath] instance into a [KeyedArchive] object.
///
/// This method serializes the properties of the [APIPath] instance into the provided
/// [KeyedArchive] object. It encodes the following properties:
///
/// - [summary]: A brief summary of the path.
/// - [description]: A detailed description of the path.
/// - [parameters]: A list of [APIParameter] objects applicable to all operations in this path.
/// - [operations]: A map of HTTP methods to their corresponding [APIOperation] objects.
///
/// The method first calls the superclass's encode method, then encodes each specific
/// property. For [parameters], it only encodes the list if it's not empty. For [operations],
/// it iterates through the map and encodes each operation, ensuring the HTTP method names
/// are in lowercase.
///
/// @param object The [KeyedArchive] object to encode the data into.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("summary", summary);
object.encode("description", description);
if (parameters.isNotEmpty) {
object.encodeObjects("parameters", parameters);
}
operations.forEach((opName, op) {
object.encodeObject(opName.toLowerCase(), op);
});
}
}

View file

@ -1,170 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Describes a single request body in an API specification.
///
/// This class represents a request body as defined in the OpenAPI Specification.
/// It includes information about the content of the request body, an optional
/// description, and whether the request body is required.
///
/// The class provides three constructors:
/// - A default constructor that takes content, description, and isRequired.
/// - An empty constructor.
/// - A schema constructor that creates a request body from a schema object.
///
/// The class also implements encoding and decoding methods for serialization.
class APIRequestBody extends APIObject {
/// Creates a new [APIRequestBody] instance.
///
/// [content] is a required parameter that represents the content of the request body.
/// It's a map where keys are media types and values are [APIMediaType] objects.
///
/// [description] is an optional parameter that provides a brief description of the request body.
///
/// [isRequired] is an optional parameter that determines if the request body is required in the request.
/// It defaults to false if not specified.
APIRequestBody(this.content, {this.description, this.isRequired = false});
/// Creates an empty [APIRequestBody] instance.
///
/// This constructor initializes a new [APIRequestBody] with no content,
/// description, or required status. It can be used as a placeholder or
/// when you need to create an instance that will be populated later.
APIRequestBody.empty();
/// Creates an [APIRequestBody] instance from a schema object.
///
/// This constructor initializes a new [APIRequestBody] using an [APISchemaObject].
///
/// Parameters:
/// - [schema]: An [APISchemaObject] that defines the structure of the request body.
/// - [contentTypes]: An iterable of strings representing the content types for the request body.
/// Defaults to ["application/json"].
/// - [description]: An optional description of the request body.
/// - [isRequired]: A boolean indicating whether the request body is required. Defaults to false.
///
/// The constructor creates a [content] map where each content type in [contentTypes]
/// is associated with an [APIMediaType] object containing the provided [schema].
APIRequestBody.schema(
APISchemaObject schema, {
Iterable<String> contentTypes = const ["application/json"],
this.description,
this.isRequired = false,
}) {
content = contentTypes.fold({}, (prev, elem) {
prev![elem] = APIMediaType(schema: schema);
return prev;
});
}
/// A brief description of the request body.
///
/// This property provides a short explanation of the request body's purpose or content.
/// It can include examples of how to use the request body.
///
/// The description supports CommonMark syntax for rich text formatting,
/// allowing for more detailed and structured explanations.
///
/// This field is optional and can be null if no description is provided.
String? description;
/// The content of the request body.
///
/// This property is a map where the keys are media types or media type ranges,
/// and the values are [APIMediaType] objects describing the content.
///
/// REQUIRED. The content must be provided for the request body to be valid.
///
/// For requests that match multiple keys, only the most specific key is applicable.
/// For example, 'text/plain' would override 'text/*'.
///
/// Example:
/// ```dart
/// content = {
/// 'application/json': APIMediaType(...),
/// 'text/plain': APIMediaType(...),
/// };
/// ```
///
/// Note: Despite being marked as nullable (Map<String, APIMediaType?>?),
/// this property is required for a valid APIRequestBody. The nullability
/// is likely for serialization purposes.
Map<String, APIMediaType?>? content;
/// Determines if the request body is required in the request.
///
/// This boolean property indicates whether the request body is mandatory for the API request.
/// When set to true, the client must include the request body in the API call.
/// When set to false, the request body is optional.
///
/// In the OpenAPI Specification, this corresponds to the 'required' field of the Request Body Object.
///
/// Defaults to false, meaning the request body is optional unless explicitly set to true.
bool isRequired = false;
/// Decodes the [APIRequestBody] object from a [KeyedArchive].
///
/// This method overrides the [decode] method from the superclass and is responsible
/// for populating the properties of the [APIRequestBody] instance from the provided
/// [KeyedArchive] object.
///
/// The method performs the following actions:
/// 1. Calls the superclass's decode method.
/// 2. Decodes the 'description' field from the archive.
/// 3. Decodes the 'required' field, defaulting to false if not present.
/// 4. Decodes the 'content' field as an object map of [APIMediaType] instances.
///
/// Parameters:
/// - [object]: A [KeyedArchive] containing the encoded [APIRequestBody] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
description = object.decode("description");
isRequired = object.decode("required") ?? false;
content = object.decodeObjectMap("content", () => APIMediaType());
}
/// Encodes the [APIRequestBody] object into a [KeyedArchive].
///
/// This method overrides the [encode] method from the superclass and is responsible
/// for encoding the properties of the [APIRequestBody] instance into the provided
/// [KeyedArchive] object.
///
/// The method performs the following actions:
/// 1. Calls the superclass's encode method.
/// 2. Checks if the 'content' property is null, throwing an ArgumentError if it is.
/// 3. Encodes the 'description' field into the archive.
/// 4. Encodes the 'required' field into the archive.
/// 5. Encodes the 'content' field as an object map into the archive.
///
/// Parameters:
/// - [object]: A [KeyedArchive] where the encoded [APIRequestBody] data will be stored.
///
/// Throws:
/// - [ArgumentError]: If the 'content' property is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (content == null) {
throw ArgumentError(
"APIRequestBody must have non-null values for: 'content'.",
);
}
object.encode("description", description);
object.encode("required", isRequired);
object.encodeObjectMap("content", content);
}
}

View file

@ -1,247 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents an API response as defined in the OpenAPI Specification.
///
/// This class models a single response from an API operation, including its description,
/// headers, and content. It provides methods to add headers and content, as well as
/// functionality to encode and decode the response object.
///
/// The [description] field is required and provides a short description of the response.
/// The [headers] field is a map of header names to their definitions.
/// The [content] field is a map of media types to their corresponding media type objects.
///
/// This class also includes utility methods:
/// - [addHeader]: Adds a header to the response
/// - [addContent]: Adds content to the response for a specific content type
///
/// The class implements [Codable] for easy serialization and deserialization.
class APIResponse extends APIObject {
/// Creates a new [APIResponse] instance.
///
/// [description] is a required parameter that provides a short description of the response.
/// [content] is an optional parameter that represents a map of media types to their corresponding media type objects.
/// [headers] is an optional parameter that represents a map of header names to their definitions.
///
/// Example:
/// ```dart
/// var response = APIResponse(
/// 'Successful response',
/// content: {'application/json': APIMediaType(schema: someSchema)},
/// headers: {'X-Rate-Limit': APIHeader()},
/// );
/// ```
APIResponse(this.description, {this.content, this.headers});
/// Creates an empty [APIResponse] instance.
///
/// This constructor initializes an [APIResponse] without any predefined values.
/// It can be useful when you need to create an empty response object that will be
/// populated later or when you want to start with a blank slate.
///
/// Example:
/// ```dart
/// var emptyResponse = APIResponse.empty();
/// // Later, populate the response as needed
/// emptyResponse.description = 'A description';
/// emptyResponse.addContent('application/json', someSchemaObject);
/// ```
APIResponse.empty();
/// Creates an [APIResponse] instance with a schema.
///
/// This constructor initializes an [APIResponse] with a given [description] and [schema].
/// It allows specifying multiple content types for the same schema.
///
/// [description] is a required parameter that provides a short description of the response.
/// [schema] is the [APISchemaObject] that defines the structure of the response body.
/// [contentTypes] is an optional iterable of content type strings. It defaults to ["application/json"].
/// [headers] is an optional parameter that represents a map of header names to their definitions.
///
/// The constructor creates a [content] map where each content type in [contentTypes]
/// is associated with an [APIMediaType] containing the provided [schema].
///
/// Example:
/// ```dart
/// var response = APIResponse.schema(
/// 'Successful response',
/// someSchemaObject,
/// contentTypes: ['application/json', 'application/xml'],
/// headers: {'X-Rate-Limit': APIHeader()},
/// );
/// ```
APIResponse.schema(
this.description,
APISchemaObject schema, {
Iterable<String> contentTypes = const ["application/json"],
this.headers,
}) {
content = contentTypes.fold({}, (prev, elem) {
prev![elem] = APIMediaType(schema: schema);
return prev;
});
}
/// A short description of the response.
///
/// This property is REQUIRED according to the OpenAPI Specification.
/// It provides a brief explanation of the API response.
///
/// The description can use CommonMark syntax for rich text representation,
/// allowing for formatted text, links, and other markup features.
///
/// Example:
/// ```dart
/// var response = APIResponse('Successful response with user data');
/// ```
///
/// Note: While the property is marked as nullable (String?), it is a required
/// field in the OpenAPI Specification and should be provided when creating
/// an APIResponse object.
String? description;
/// Maps a header name to its definition.
///
/// This property represents a map of HTTP headers that may be part of the API response.
/// Each key in the map is a header name (case-insensitive), and the corresponding value
/// is an [APIHeader] object that defines the header's properties.
///
/// According to RFC7230, header names are case-insensitive. It's important to note that
/// if a response header is defined with the name "Content-Type", it SHALL be ignored.
/// This is because the content type is typically handled separately in API specifications.
///
/// The map is nullable, allowing for responses that don't include any custom headers.
///
/// Example:
/// ```dart
/// var response = APIResponse('Success');
/// response.headers = {
/// 'X-Rate-Limit': APIHeader(description: 'Calls per hour allowed by the user'),
/// 'X-Expires-After': APIHeader(description: 'Date in UTC when token expires')
/// };
/// ```
Map<String, APIHeader?>? headers;
/// A map containing descriptions of potential response payloads.
///
/// This property represents the content of the API response for different media types.
/// The key is a media type or media type range (e.g., 'application/json', 'text/*'),
/// and the value is an [APIMediaType] object describing the content for that media type.
///
/// For responses that match multiple keys, only the most specific key is applicable.
/// For example, 'text/plain' would override 'text/*'.
///
/// This property is nullable, allowing for responses that don't include any content.
///
/// Example:
/// ```dart
/// var response = APIResponse('Success');
/// response.content = {
/// 'application/json': APIMediaType(schema: someJsonSchema),
/// 'application/xml': APIMediaType(schema: someXmlSchema)
/// };
/// ```
Map<String, APIMediaType?>? content;
// Currently missing:
// links
/// Adds a header to the [headers] map of the API response.
///
/// If [headers] is null, it is created. If the key does not exist in [headers], [header] is added for the key.
/// If the key exists, [header] is not added. (To replace a header, access [headers] directly.)
void addHeader(String name, APIHeader? header) {
headers ??= {};
if (!headers!.containsKey(name)) {
headers![name] = header;
}
}
/// Adds a [bodyObject] to [content] for a specific content type.
///
/// [contentType] must take the form 'primaryType/subType', e.g. 'application/json'. Do not include charsets.
///
/// If [content] is null, it is created. If [contentType] does not exist in [content], [bodyObject] is added for [contentType].
/// If [contentType] exists, the [bodyObject] is added the list of possible schemas that were previously added.
void addContent(String contentType, APISchemaObject? bodyObject) {
content ??= {};
final key = contentType;
final existingContent = content![key];
if (existingContent == null) {
content![key] = APIMediaType(schema: bodyObject);
return;
}
final schema = existingContent.schema;
if (schema?.oneOf != null) {
schema!.oneOf!.add(bodyObject);
} else {
final container = APISchemaObject()..oneOf = [schema, bodyObject];
existingContent.schema = container;
}
}
/// Decodes the [APIResponse] object from a [KeyedArchive].
///
/// This method is part of the [Codable] interface implementation. It populates
/// the properties of the [APIResponse] object from a [KeyedArchive] object,
/// which typically represents a serialized form of the response.
///
/// The method performs the following actions:
/// 1. Calls the superclass's decode method to handle any inherited properties.
/// 2. Decodes the 'description' field from the archive.
/// 3. Decodes the 'content' field as a map of [APIMediaType] objects.
/// 4. Decodes the 'headers' field as a map of [APIHeader] objects.
///
/// [object] is the [KeyedArchive] containing the encoded [APIResponse] data.
@override
void decode(KeyedArchive object) {
super.decode(object);
description = object.decode("description");
content = object.decodeObjectMap("content", () => APIMediaType());
headers = object.decodeObjectMap("headers", () => APIHeader());
}
/// Encodes the [APIResponse] object into a [KeyedArchive].
///
/// This method is part of the [Codable] interface implementation. It serializes
/// the properties of the [APIResponse] object into a [KeyedArchive] object,
/// which can then be used for storage or transmission.
///
/// The method performs the following actions:
/// 1. Calls the superclass's encode method to handle any inherited properties.
/// 2. Checks if the 'description' field is non-null, throwing an [ArgumentError] if it's null.
/// 3. Encodes the 'description' field into the archive.
/// 4. Encodes the 'headers' field as a map of [APIHeader] objects.
/// 5. Encodes the 'content' field as a map of [APIMediaType] objects.
///
/// [object] is the [KeyedArchive] where the [APIResponse] data will be encoded.
///
/// Throws an [ArgumentError] if the 'description' field is null, as it's a required field.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (description == null) {
throw ArgumentError(
"APIResponse must have non-null values for: 'description'.",
);
}
object.encode("description", description);
object.encodeObjectMap("headers", headers);
object.encodeObjectMap("content", content);
}
}

View file

@ -1,780 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Enum representing the policy for additional properties in an API schema.
///
/// This enum defines three possible policies for handling additional properties
/// in an API schema object:
///
/// - [disallowed]: Prevents properties other than those defined by [APISchemaObject.properties]
/// from being included in the schema. This is the most restrictive policy.
///
/// - [freeForm]: Allows any additional properties to be included in the schema.
/// This is the most permissive policy.
///
/// - [restricted]: Indicates that [APISchemaObject.additionalPropertySchema] contains
/// a schema object that defines the structure for any additional properties.
/// This policy allows additional properties, but they must conform to the
/// specified schema.
///
/// These policies provide different levels of control over the structure and
/// content of API schemas, allowing for flexible schema definitions based on
/// specific requirements.
enum APISchemaAdditionalPropertyPolicy {
/// Indicates that the [APISchemaObject] prevents properties other than those defined by [APISchemaObject.properties] from being included.
///
/// This policy is the most restrictive, disallowing any additional properties beyond those explicitly defined in the schema.
/// It ensures that the object structure strictly adheres to the specified properties.
disallowed,
/// Indicates that the [APISchemaObject] allows any additional properties to be included.
///
/// This policy is the most permissive, allowing any extra properties beyond those explicitly defined in the schema.
/// It provides flexibility for including arbitrary additional data in the object structure.
freeForm,
/// Indicates that [APISchemaObject.additionalPropertySchema] contains a schema object that defines
/// the structure for any additional properties.
///
/// This policy allows additional properties, but they must conform to the schema specified in
/// [APISchemaObject.additionalPropertySchema]. It provides a way to allow extra properties while
/// still maintaining some control over their structure and content.
restricted
}
/// This class extends [APIObject] and provides a comprehensive representation of
/// schema objects as defined in the OpenAPI specification. It includes properties
/// for various schema constraints such as type, format, range limitations,
/// length restrictions, pattern matching, and more.
///
/// The class offers multiple constructors for creating different types of schemas:
/// - Default constructor
/// - Empty constructor
/// - String schema (with optional format)
/// - Number schema
/// - Integer schema
/// - Boolean schema
/// - Map schema (with options for additional properties)
/// - Array schema
/// - Object schema (with properties)
/// - File schema
/// - Free-form schema
///
/// It also includes methods for encoding and decoding schema objects to and from
/// a [KeyedArchive], allowing for serialization and deserialization of schema definitions.
class APISchemaObject extends APIObject {
/// Default constructor for APISchemaObject.
///
/// Creates a new instance of APISchemaObject with default values.
/// This constructor allows for flexible initialization of the schema object,
/// where properties can be set after instantiation.
APISchemaObject();
/// Creates an empty [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] without setting any specific properties.
/// It can be used as a starting point for building a schema object, where properties
/// can be added or modified after instantiation.
APISchemaObject.empty();
/// Creates a string [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.string].
/// It also allows specifying an optional [format] for the string schema.
///
/// Parameters:
/// [format]: An optional string that specifies the format of the string schema.
/// Common formats include 'date-time', 'email', 'hostname', etc.
APISchemaObject.string({this.format}) : type = APIType.string;
/// Creates a number [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.number].
/// It's used to define a schema for numeric values, which can include both integers and floating-point numbers.
APISchemaObject.number() : type = APIType.number;
/// Creates an integer [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.integer].
/// It's used to define a schema for integer values, which are whole numbers without fractional components.
APISchemaObject.integer() : type = APIType.integer;
/// Creates a boolean [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.boolean].
/// It's used to define a schema for boolean values, which can only be true or false.
APISchemaObject.boolean() : type = APIType.boolean;
/// Creates a map [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.object],
/// representing a map-like structure. It allows specifying the type or schema of the
/// additional properties in the map.
///
/// Parameters:
/// [ofType]: An optional [APIType] that specifies the type of values in the map.
/// [ofSchema]: An optional [APISchemaObject] that defines the schema for values in the map.
/// [any]: A boolean flag that, when true, allows any type of additional properties.
///
/// Throws:
/// [ArgumentError] if neither [ofType], [ofSchema], nor [any] is specified.
///
/// The constructor sets [additionalPropertySchema] based on the provided parameters:
/// - If [ofType] is provided, it creates a new [APISchemaObject] with that type.
/// - If [ofSchema] is provided, it uses that schema directly.
/// - If [any] is true, it allows any additional properties without restrictions.
/// - If none of the above are specified, it throws an [ArgumentError].
APISchemaObject.map({
APIType? ofType,
APISchemaObject? ofSchema,
bool any = false,
}) : type = APIType.object {
if (ofType != null) {
additionalPropertySchema = APISchemaObject()..type = ofType;
} else if (ofSchema != null) {
additionalPropertySchema = ofSchema;
} else if (any) {
} else {
throw ArgumentError(
"Invalid 'APISchemaObject.map' with neither 'ofType', 'any' or 'ofSchema' specified.",
);
}
}
/// Creates an array [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.array].
/// It allows specifying the type or schema of the items in the array.
///
/// Parameters:
/// [ofType]: An optional [APIType] that specifies the type of items in the array.
/// [ofSchema]: An optional [APISchemaObject] that defines the schema for items in the array.
///
/// Throws:
/// [ArgumentError] if neither [ofType] nor [ofSchema] is specified.
///
/// The constructor sets [items] based on the provided parameters:
/// - If [ofType] is provided, it creates a new [APISchemaObject] with that type.
/// - If [ofSchema] is provided, it uses that schema directly.
/// - If neither is specified, it throws an [ArgumentError].
APISchemaObject.array({APIType? ofType, APISchemaObject? ofSchema})
: type = APIType.array {
if (ofType != null) {
items = APISchemaObject()..type = ofType;
} else if (ofSchema != null) {
items = ofSchema;
} else {
throw ArgumentError(
"Invalid 'APISchemaObject.array' with neither 'ofType' or 'ofSchema' specified.",
);
}
}
/// Creates an object [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.object].
/// It allows specifying the properties of the object schema.
///
/// Parameters:
/// [properties]: A map of property names to their corresponding [APISchemaObject]s.
/// This defines the structure of the object schema.
APISchemaObject.object(this.properties) : type = APIType.object;
/// Creates a file [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.string]
/// and [format] set based on the [isBase64Encoded] parameter.
///
/// Parameters:
/// [isBase64Encoded]: A boolean flag that determines the format of the file schema.
/// - If true, sets the format to "byte" (for base64-encoded strings).
/// - If false, sets the format to "binary" (for raw binary data).
///
/// The resulting schema object represents either a base64-encoded string (when [isBase64Encoded] is true)
/// or raw binary data (when [isBase64Encoded] is false), both of which are suitable for file content.
APISchemaObject.file({bool isBase64Encoded = false})
: type = APIType.string,
format = isBase64Encoded ? "byte" : "binary";
/// Creates a free-form [APISchemaObject] instance.
///
/// This constructor initializes an [APISchemaObject] with [type] set to [APIType.object]
/// and [additionalPropertyPolicy] set to [APISchemaAdditionalPropertyPolicy.freeForm].
/// It represents a schema that allows any additional properties without restrictions.
///
/// This is useful for scenarios where the object structure is not strictly defined
/// and can contain arbitrary key-value pairs.
APISchemaObject.freeForm()
: type = APIType.object,
additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm;
/// A title for the object.
///
/// This property allows you to specify a human-readable title for the schema object.
/// It can be used to provide a brief, descriptive name for the schema, which can be
/// helpful for documentation and user interfaces. The title is optional and does not
/// affect the validation of data against the schema.
String? title;
/// The maximum value for a numeric instance.
///
/// If the instance is a number, then this keyword validates if
/// "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;
/// Determines whether the maximum value is exclusive.
///
/// An undefined value is the same as false.
///
/// If "exclusiveMaximum" is true, then a numeric instance SHOULD NOT be
/// 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;
/// The minimum value for a numeric instance.
///
/// This property sets the lower limit for a numeric instance in the schema.
/// The value of "minimum" MUST be a number.
///
/// If the instance is a number, this keyword validates in two ways:
/// 1. If "exclusiveMinimum" is true, the instance must be greater than the provided value.
/// 2. If "exclusiveMinimum" is false or not specified, the instance must be greater than or exactly equal to the provided value.
///
/// This property works in conjunction with [exclusiveMinimum] to define the lower bound behavior.
num? minimum;
/// Determines whether the minimum value is exclusive.
///
/// The value of "exclusiveMinimum" MUST be a boolean, representing
/// whether the limit in "minimum" is exclusive or not. An undefined
/// value is the same as false.
///
/// If "exclusiveMinimum" is true, then a numeric instance SHOULD NOT be
/// 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;
/// The maximum length allowed for a string instance.
///
/// The value of this keyword MUST be an integer. This integer MUST be
/// greater than, or equal to, 0.
///
/// A string instance is valid against this keyword if its length is less
/// than, or equal to, the value of this keyword.
///
/// The length of a string instance is defined as the number of its
/// characters as defined by RFC 7159 [RFC7159].
int? maxLength;
/// The minimum length allowed for a string instance.
///
/// The length of a string instance is defined as the number of its
/// characters as defined by RFC 7159 [RFC7159].
///
/// The value of this keyword MUST be an integer. This integer MUST be
/// greater than, or equal to, 0.
///
/// "minLength", if absent, may be considered as being present with
/// integer value 0.
int? minLength;
/// The regular expression pattern that a string instance must match.
///
/// A string instance is considered valid if the regular expression
/// matches the instance successfully. Recall: regular expressions are
/// not implicitly anchored.
String? pattern;
/// The maximum number of items allowed in an array instance.
///
/// An array instance is valid against "maxItems" if its size is less
/// than, or equal to, the value of this keyword.
int? maxItems;
/// The minimum number of items allowed in an array instance.
///
/// An array instance is valid against "minItems" if its size is greater
/// than, or equal to, the value of this keyword.
///
/// If this keyword is not present, it may be considered present with a
/// value of 0.
int? minItems;
/// Specifies whether the items in an array instance must be unique.
///
/// If this keyword has boolean value false, the instance validates
/// successfully. If it has boolean value true, the instance validates
/// successfully if all of its elements are unique.
///
/// If not present, this keyword may be considered present with boolean
/// value false.
bool? uniqueItems;
/// Specifies a value that numeric instances must be divisible by.
///
/// 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;
/// The maximum number of properties allowed in an object instance.
///
/// 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;
/// The minimum number of properties allowed in an object instance.
///
/// An object instance is valid against "minProperties" if its number of
/// properties is greater than, or equal to, the value of this keyword.
///
/// If this keyword is not present, it may be considered present with a
/// value of 0.
int? minProperties;
/// A list of required property names for the schema object.
///
/// An object instance is valid against this keyword if its property set
/// contains all elements in this keyword's array value.
List<String?>? isRequired;
/// Specifies a list of allowed values for the schema instance.
///
/// Elements in the array MAY be of any type, including null.
///
/// An instance validates successfully against this keyword if its value
/// is equal to one of the elements in this keyword's array value.
List<dynamic>? enumerated;
/* Modified JSON Schema for OpenAPI */
/// Represents the type of the API schema object.
///
/// This property defines the data type of the schema. It can be any of the
/// values defined in the [APIType] enum, such as string, number, integer,
/// boolean, array, or object.
///
/// The type is a fundamental property of the schema as it determines the
/// basic structure and validation rules for the data that the schema represents.
///
/// If null, it indicates that the type is not specified, which might be the case
/// for schemas that use composition keywords like allOf, anyOf, or oneOf.
APIType? type;
/// A list of schema objects that this schema object composes.
///
/// This property allows for the composition of schema objects through the 'allOf' keyword.
/// The instance data must be valid against all of the schemas in this list.
/// This is ANDing the constraints specified by each schema in the list.
///
/// If null, it indicates that no composition is specified for this schema object.
List<APISchemaObject?>? allOf;
/// A list of schema objects that this schema object can be any of.
///
/// This property allows for the composition of schema objects through the 'anyOf' keyword.
/// The instance data must be valid against at least one of the schemas in this list.
/// This is ORing the constraints specified by each schema in the list.
///
/// If null, it indicates that no 'anyOf' composition is specified for this schema object.
List<APISchemaObject?>? anyOf;
/// A list of schema objects that this schema object can be one of.
///
/// This property allows for the composition of schema objects through the 'oneOf' keyword.
/// The instance data must be valid against exactly one of the schemas in this list.
/// This represents an exclusive OR (XOR) relationship between the schemas.
///
/// If null, it indicates that no 'oneOf' composition is specified for this schema object.
List<APISchemaObject?>? oneOf;
/// A schema object that this schema object must not match.
///
/// This property allows for the negation of a schema through the 'not' keyword.
/// The instance data must NOT be valid against the schema defined in this property.
/// This is useful for expressing constraints that are the opposite of a given schema.
///
/// If null, it indicates that no 'not' constraint is specified for this schema object.
APISchemaObject? not;
/// Defines the schema for items in an array.
///
/// This property is only applicable when the [type] is set to [APIType.array].
/// It specifies the schema that all items in the array must conform to.
///
/// If null, it indicates that the schema for array items is not specified,
/// allowing items of any type in the array.
APISchemaObject? items;
/// A map of property names to their corresponding schema objects.
///
/// This property defines the structure of an object schema by specifying
/// the schemas for its properties. Each key in the map is the name of a
/// property, and its corresponding value is an [APISchemaObject] that
/// defines the schema for that property.
///
/// If null, it indicates that no properties are defined for this schema object.
/// This could be the case for non-object schemas or for object schemas where
/// properties are not explicitly defined (e.g., when using additionalProperties).
Map<String, APISchemaObject?>? properties;
/// Defines the schema for additional properties in an object.
///
/// This property is used in conjunction with [additionalPropertyPolicy] to specify
/// the schema for additional properties when [additionalPropertyPolicy] is set to
/// [APISchemaAdditionalPropertyPolicy.restricted].
///
/// If null, it indicates that no specific schema is defined for additional properties,
/// which could mean either that additional properties are not allowed or that they
/// can be of any type, depending on the [additionalPropertyPolicy].
APISchemaObject? additionalPropertySchema;
/// Specifies the policy for handling additional properties in the schema object.
///
/// This property determines how the schema should treat properties that are not
/// explicitly defined in the [properties] map. It can have one of three values:
///
/// - [APISchemaAdditionalPropertyPolicy.disallowed]: No additional properties are allowed.
/// - [APISchemaAdditionalPropertyPolicy.freeForm]: Any additional properties are allowed without restrictions.
/// - [APISchemaAdditionalPropertyPolicy.restricted]: Additional properties are allowed, but must conform to the schema defined in [additionalPropertySchema].
///
/// If null, the additional property policy is not explicitly set, and the behavior
/// may depend on the context or default to a specific policy (often 'disallowed' in strict schemas).
APISchemaAdditionalPropertyPolicy? additionalPropertyPolicy;
/// A description of the schema object.
///
/// This property provides a detailed explanation of the purpose, structure,
/// or constraints of the schema. It can be used to give more context to API
/// consumers about what this schema represents or how it should be used.
///
/// If null, it indicates that no description has been provided for this schema.
String? description;
/// Specifies the format of the data.
///
/// This property is a string that further defines the specific format of the data
/// described by the schema. It is often used in conjunction with the [type] property
/// to provide more detailed type information.
///
/// Common formats include:
/// - For strings: 'date-time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', etc.
/// - For numbers: 'float', 'double'
/// - For integers: 'int32', 'int64'
///
/// The interpretation and validation of the format may depend on the data type
/// and the specific OpenAPI tooling being used.
///
/// If null, it indicates that no specific format is defined for this schema property.
String? format;
/// The default value for this schema object.
///
/// This property specifies the default value to be used if the instance value is not supplied.
/// The value SHOULD be valid against the schema.
///
/// The type of this property is dynamic, allowing for default values of any type
/// that matches the schema's type. For example:
/// - For string schemas, it could be a String.
/// - For number schemas, it could be a num.
/// - For boolean schemas, it could be a bool.
/// - For array schemas, it could be a List.
/// - For object schemas, it could be a Map.
///
/// If null, it indicates that no default value is specified for this schema property.
dynamic defaultValue;
/// Determines whether the schema allows null values.
///
/// This getter returns a boolean value indicating if the schema permits null values.
/// It returns true if the schema explicitly allows null values, and false otherwise.
///
/// The getter uses the nullish coalescing operator (??) to provide a default value of false
/// if the internal [_nullable] property is null.
///
/// Returns:
/// A boolean value: true if the schema allows null values, false otherwise.
bool? get isNullable => _nullable ?? false;
/// Sets whether the schema allows null values.
///
/// This setter allows you to specify if the schema should permit null values.
/// Setting it to true means the schema will allow null, while false means it won't.
///
/// Parameters:
/// [n]: A boolean value indicating whether null is allowed (true) or not (false).
/// Can be null, in which case it will clear any previously set value.
set isNullable(bool? n) {
_nullable = n;
}
// APIDiscriminator discriminator;
/// Determines whether the schema is read-only.
///
/// This getter returns a boolean value indicating if the schema is marked as read-only.
/// It returns true if the schema is explicitly set as read-only, and false otherwise.
///
/// The getter uses the nullish coalescing operator (??) to provide a default value of false
/// if the internal [_readOnly] property is null.
///
/// Returns:
/// A boolean value: true if the schema is read-only, false otherwise.
bool? get isReadOnly => _readOnly ?? false;
/// Sets whether the schema is read-only.
///
/// This setter allows you to specify if the schema should be considered read-only.
/// Setting it to true means the schema is read-only, while false means it's not.
///
/// Parameters:
/// [n]: A boolean value indicating whether the schema is read-only (true) or not (false).
/// Can be null, in which case it will clear any previously set value.
set isReadOnly(bool? n) {
_readOnly = n;
}
/// Determines whether the schema is write-only.
///
/// This getter returns a boolean value indicating if the schema is marked as write-only.
/// It returns true if the schema is explicitly set as write-only, and false otherwise.
///
/// The getter uses the nullish coalescing operator (??) to provide a default value of false
/// if the internal [_writeOnly] property is null.
///
/// Returns:
/// A boolean value: true if the schema is write-only, false otherwise.
bool? get isWriteOnly => _writeOnly ?? false;
set isWriteOnly(bool? n) {
_writeOnly = n;
}
/// Internal boolean flag to determine if the schema allows null values.
///
/// This private property is used to store the nullable state of the schema.
/// It's accessed and modified through the public [isNullable] getter and setter.
///
/// - If true, the schema allows null values.
/// - If false, the schema does not allow null values.
/// - If null, the nullable state is not explicitly set, defaulting to false in the getter.
bool? _nullable;
/// Internal boolean flag to determine if the schema is read-only.
///
/// This private property is used to store the read-only state of the schema.
/// It's accessed and modified through the public [isReadOnly] getter and setter.
///
/// - If true, the schema is read-only.
/// - If false, the schema is not read-only.
/// - If null, the read-only state is not explicitly set, defaulting to false in the getter.
bool? _readOnly;
/// Internal boolean flag to determine if the schema is write-only.
///
/// This private property is used to store the write-only state of the schema.
/// It's accessed and modified through the public [isWriteOnly] getter and setter.
///
/// - If true, the schema is write-only.
/// - If false, the schema is not write-only.
/// - If null, the write-only state is not explicitly set, defaulting to false in the getter.
bool? _writeOnly;
/// Indicates whether the schema is deprecated.
///
/// This property specifies if the schema has been deprecated and should be avoided in new implementations.
///
/// - If true, the schema is considered deprecated.
/// - If false, the schema is not deprecated.
/// - If null, the deprecation status is not explicitly set.
///
/// Deprecated schemas may still be used but are typically discouraged in favor of newer alternatives.
bool? deprecated;
/// Provides a map of property names to their corresponding cast functions.
///
/// This getter overrides the base implementation to specify custom casting
/// behavior for certain properties of the APISchemaObject.
///
/// Returns:
/// A Map<String, cast.Cast> where:
/// - The key "required" is associated with a cast.List(cast.string) function,
/// which casts the "required" property to a List of Strings.
///
/// This ensures that when decoding JSON data, the "required" field is properly
/// cast to a List<String>, maintaining type safety and consistency in the object model.
@override
Map<String, cast.Cast> get castMap =>
{"required": const cast.List(cast.string)};
/// Decodes a [KeyedArchive] object into this [APISchemaObject] instance.
///
/// This method populates the properties of the [APISchemaObject] by extracting
/// values from the provided [KeyedArchive] object. It handles both simple properties
/// and complex nested structures.
///
/// The method decodes various schema properties including:
/// - Basic properties like title, maximum, minimum, pattern, etc.
/// - Numeric constraints (maxLength, minLength, maxItems, minItems, etc.)
/// - Enumerated values and required fields
/// - Complex structures like allOf, anyOf, oneOf, and nested schemas
/// - Additional properties policy and schema
/// - Metadata like description, format, and default value
///
/// It also handles special cases like the 'additionalProperties' field, which
/// can be a boolean or an object, affecting the [additionalPropertyPolicy] and
/// [additionalPropertySchema] properties.
///
/// Parameters:
/// [object]: A [KeyedArchive] containing the encoded schema data.
///
/// Note: This method overrides the [decode] method from a superclass and
/// calls the superclass implementation before proceeding with its own decoding.
@override
void decode(KeyedArchive object) {
super.decode(object);
title = object.decode("title");
maximum = object.decode("maximum");
exclusiveMaximum = object.decode("exclusiveMaximum");
minimum = object.decode("minimum");
exclusiveMinimum = object.decode("exclusiveMinimum");
maxLength = object.decode("maxLength");
minLength = object.decode("minLength");
pattern = object.decode("pattern");
maxItems = object.decode("maxItems");
minItems = object.decode("minItems");
uniqueItems = object.decode("uniqueItems");
multipleOf = object.decode("multipleOf");
enumerated = object.decode("enum");
minProperties = object.decode("minProperties");
maxProperties = object.decode("maxProperties");
isRequired = object.decode("required");
//
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", () => APISchemaObject());
properties = object.decodeObjectMap("properties", () => APISchemaObject());
final addlProps = object["additionalProperties"];
if (addlProps is bool) {
if (addlProps) {
additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm;
} else {
additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.disallowed;
}
} else if (addlProps is KeyedArchive && addlProps.isEmpty) {
additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.freeForm;
} else {
additionalPropertyPolicy = APISchemaAdditionalPropertyPolicy.restricted;
additionalPropertySchema =
object.decodeObject("additionalProperties", () => APISchemaObject());
}
description = object.decode("description");
format = object.decode("format");
defaultValue = object.decode("default");
_nullable = object.decode("nullable");
_readOnly = object.decode("readOnly");
_writeOnly = object.decode("writeOnly");
deprecated = object.decode("deprecated");
}
/// Encodes this [APISchemaObject] instance into a [KeyedArchive] object.
///
/// This method serializes all properties of the [APISchemaObject] into the provided
/// [KeyedArchive] object. It handles both simple properties and complex nested structures.
///
/// The method encodes various schema properties including:
/// - Basic properties like title, maximum, minimum, pattern, etc.
/// - Numeric constraints (maxLength, minLength, maxItems, minItems, etc.)
/// - Enumerated values and required fields
/// - Complex structures like allOf, anyOf, oneOf, and nested schemas
/// - Additional properties policy and schema
/// - Metadata like description, format, and default value
///
/// It also handles special cases like the 'additionalProperties' field, which
/// is encoded differently based on the [additionalPropertyPolicy] and
/// [additionalPropertySchema] properties.
///
/// Parameters:
/// [object]: A [KeyedArchive] to store the encoded schema data.
///
/// Note: This method overrides the [encode] method from a superclass and
/// calls the superclass implementation before proceeding with its own encoding.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("title", title);
object.encode("maximum", maximum);
object.encode("exclusiveMaximum", exclusiveMaximum);
object.encode("minimum", minimum);
object.encode("exclusiveMinimum", exclusiveMinimum);
object.encode("maxLength", maxLength);
object.encode("minLength", minLength);
object.encode("pattern", pattern);
object.encode("maxItems", maxItems);
object.encode("minItems", minItems);
object.encode("uniqueItems", uniqueItems);
object.encode("multipleOf", multipleOf);
object.encode("enum", enumerated);
object.encode("minProperties", minProperties);
object.encode("maxProperties", maxProperties);
object.encode("required", isRequired);
//
object.encode("type", APITypeCodec.encode(type));
object.encodeObjects("allOf", allOf);
object.encodeObjects("anyOf", anyOf);
object.encodeObjects("oneOf", oneOf);
object.encodeObject("not", not);
object.encodeObject("items", items);
if (additionalPropertyPolicy != null || additionalPropertySchema != null) {
if (additionalPropertyPolicy ==
APISchemaAdditionalPropertyPolicy.disallowed) {
object.encode("additionalProperties", false);
} else if (additionalPropertyPolicy ==
APISchemaAdditionalPropertyPolicy.freeForm) {
object.encode("additionalProperties", true);
} else {
object.encodeObject("additionalProperties", additionalPropertySchema);
}
}
object.encodeObjectMap("properties", properties);
object.encode("description", description);
object.encode("format", format);
object.encode("default", defaultValue);
object.encode("nullable", _nullable);
object.encode("readOnly", _readOnly);
object.encode("writeOnly", _writeOnly);
object.encode("deprecated", deprecated);
}
}

View file

@ -1,803 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
import 'package:protevus_openapi/v3.dart';
/// Represents the different types of security schemes available in the OpenAPI specification.
///
/// - [apiKey]: API key-based security scheme.
/// - [http]: HTTP authentication scheme.
/// - [oauth2]: OAuth 2.0 authentication scheme.
/// - [openID]: OpenID Connect authentication scheme.
enum APISecuritySchemeType { apiKey, http, oauth2, openID }
/// A utility class for encoding and decoding [APISecuritySchemeType] values.
///
/// This class provides static methods to convert between [APISecuritySchemeType] enum values
/// and their corresponding string representations as defined in the OpenAPI specification.
class APISecuritySchemeTypeCodec {
/// Decodes a string representation of an API security scheme type into an [APISecuritySchemeType] enum value.
///
/// This method takes a [String] parameter `type` and returns the corresponding
/// [APISecuritySchemeType] enum value. If the input string doesn't match any
/// known security scheme type, the method returns `null`.
///
/// Parameters:
/// - type: A [String] representing the security scheme type.
///
/// Returns:
/// The corresponding [APISecuritySchemeType] enum value, or `null` if no match is found.
static APISecuritySchemeType? decode(String? type) {
switch (type) {
case "apiKey":
return APISecuritySchemeType.apiKey;
case "http":
return APISecuritySchemeType.http;
case "oauth2":
return APISecuritySchemeType.oauth2;
case "openID":
return APISecuritySchemeType.openID;
default:
return null;
}
}
/// Encodes an [APISecuritySchemeType] enum value into its corresponding string representation.
///
/// This method takes an [APISecuritySchemeType] parameter `type` and returns the corresponding
/// string representation as defined in the OpenAPI specification. If the input is `null` or
/// doesn't match any known security scheme type, the method returns `null`.
///
/// Parameters:
/// - type: An [APISecuritySchemeType] enum value to be encoded.
///
/// Returns:
/// A [String] representing the security scheme type, or `null` if the input is `null` or invalid.
static String? encode(APISecuritySchemeType? type) {
switch (type) {
case APISecuritySchemeType.apiKey:
return "apiKey";
case APISecuritySchemeType.http:
return "http";
case APISecuritySchemeType.oauth2:
return "oauth2";
case APISecuritySchemeType.openID:
return "openID";
default:
return null;
}
}
}
/// Defines a security scheme that can be used by the operations.
///
/// Supported schemes are HTTP authentication, an API key (either as a header or as a query parameter),
/// OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749,
/// and OpenID Connect Discovery.
///
/// This class represents different types of security schemes:
/// - HTTP authentication (using [APISecurityScheme.http])
/// - API Key (using [APISecurityScheme.apiKey])
/// - OAuth2 (using [APISecurityScheme.oauth2])
/// - OpenID Connect (using [APISecurityScheme.openID])
///
/// The [type] property determines which security scheme is being used, and the corresponding
/// properties should be set based on the chosen type.
///
/// When encoding or decoding, the class ensures that the required properties for each type
/// are present and correctly formatted.
class APISecurityScheme extends APIObject {
/// Default constructor for APISecurityScheme.
///
/// Creates an instance of APISecurityScheme with no initial values set.
/// Use this constructor when you need to create an empty security scheme
/// that will be populated later or when you want to manually set all properties.
APISecurityScheme();
/// Creates an empty instance of APISecurityScheme.
///
/// This named constructor initializes an APISecurityScheme with no pre-set values.
/// It can be used when you need to create a security scheme object that will be
/// populated with data later or when you want to manually set all properties.
APISecurityScheme.empty();
/// Creates an instance of APISecurityScheme for HTTP authentication.
///
/// This constructor initializes a security scheme of type [APISecuritySchemeType.http]
/// with the specified [scheme].
///
/// Parameters:
/// - scheme: A [String] representing the name of the HTTP Authorization scheme
/// to be used in the Authorization header as defined in RFC7235.
///
/// Example:
/// ```dart
/// var httpScheme = APISecurityScheme.http('bearer');
/// ```
APISecurityScheme.http(this.scheme) : type = APISecuritySchemeType.http;
/// Creates an instance of APISecurityScheme for API Key authentication.
///
/// This constructor initializes a security scheme of type [APISecuritySchemeType.apiKey]
/// with the specified [name] and [location].
///
/// Parameters:
/// - name: A [String] representing the name of the API key to be used.
/// - location: An [APIParameterLocation] specifying where the API key is expected
/// (e.g., query parameter, header, or cookie).
///
/// Example:
/// ```dart
/// var apiKeyScheme = APISecurityScheme.apiKey('api_key', APIParameterLocation.header);
/// ```
APISecurityScheme.apiKey(this.name, this.location)
: type = APISecuritySchemeType.apiKey;
/// Creates an instance of APISecurityScheme for OAuth2 authentication.
///
/// This constructor initializes a security scheme of type [APISecuritySchemeType.oauth2]
/// with the specified [flows].
///
/// Parameters:
/// - flows: A [Map<String, APISecuritySchemeOAuth2Flow?>] representing the OAuth2 flows
/// supported by this security scheme. The keys should be flow types
/// (e.g., "implicit", "authorizationCode", "clientCredentials", "password"),
/// and the values should be instances of [APISecuritySchemeOAuth2Flow].
///
/// Example:
/// ```dart
/// var oauth2Scheme = APISecurityScheme.oauth2({
/// 'implicit': APISecuritySchemeOAuth2Flow.implicit(
/// Uri.parse('https://example.com/oauth/authorize'),
/// Uri.parse('https://example.com/oauth/token'),
/// {'read:api': 'Read access to protected resources'},
/// ),
/// });
/// ```
APISecurityScheme.oauth2(this.flows) : type = APISecuritySchemeType.oauth2;
/// Creates an instance of APISecurityScheme for OpenID Connect authentication.
///
/// This constructor initializes a security scheme of type [APISecuritySchemeType.openID]
/// with the specified [connectURL].
///
/// Parameters:
/// - connectURL: A [Uri] representing the OpenID Connect URL used to discover
/// OAuth2 configuration values. This MUST be in the form of a URL.
///
/// Example:
/// ```dart
/// var openIDScheme = APISecurityScheme.openID(
/// Uri.parse('https://example.com/.well-known/openid-configuration')
/// );
/// ```
APISecurityScheme.openID(this.connectURL)
: type = APISecuritySchemeType.openID;
/// The type of the security scheme.
///
/// This property defines the type of security scheme used, which determines
/// how the security is enforced and what additional properties are required.
///
/// REQUIRED. Valid values are:
/// - "apiKey": For API key-based authentication
/// - "http": For HTTP authentication schemes
/// - "oauth2": For OAuth2 authentication
/// - "openIdConnect": For OpenID Connect Discovery-based authentication
///
/// The value of this property affects which other properties of the
/// [APISecurityScheme] are required or applicable.
APISecuritySchemeType? type;
/// A short description for the security scheme.
///
/// This property provides a brief explanation of the security scheme's purpose or usage.
/// It can be used to give additional context or details about how the security mechanism works.
///
/// The description MAY use CommonMark syntax for rich text representation, allowing
/// for formatted text, links, and other markdown features to enhance readability.
///
/// This field is optional but recommended to improve the documentation of the API's security measures.
String? description;
/// The name of the header, query or cookie parameter to be used.
///
/// This property specifies the name of the parameter that will be used to pass the API key.
/// It is applicable only when the security scheme type is [APISecuritySchemeType.apiKey].
///
/// The value of this property depends on the [location] property:
/// - If [location] is [APIParameterLocation.header], this is the name of the header.
/// - If [location] is [APIParameterLocation.query], this is the name of the query parameter.
/// - If [location] is [APIParameterLocation.cookie], this is the name of the cookie.
///
/// This property is REQUIRED when the security scheme type is [APISecuritySchemeType.apiKey].
String? name;
/// The location of the API key.
///
/// This property specifies where the API key should be sent in the request.
/// It is only applicable and REQUIRED when the security scheme type is [APISecuritySchemeType.apiKey].
///
/// For apiKey only. REQUIRED if so.
APIParameterLocation? location;
/// The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235.
///
/// This property specifies the name of the HTTP Authorization scheme that will be used
/// in the Authorization header for requests. It is only applicable and REQUIRED when
/// the security scheme type is [APISecuritySchemeType.http].
///
/// Common values include:
/// - "basic": for Basic Authentication
/// - "bearer": for Bearer Token Authentication
/// - "digest": for Digest Access Authentication
///
/// The value of this property should correspond to the authentication scheme
/// as defined in RFC7235 and should be registered in the IANA Authentication Scheme Registry.
///
/// For http only. REQUIRED if so.
String? scheme;
/// A hint to the client to identify how the bearer token is formatted.
///
/// This property provides additional information about the format of the bearer token
/// when using HTTP authentication with a 'bearer' scheme. It is typically used for
/// documentation purposes to help API consumers understand the expected token format.
///
/// For http only.
String? format;
/// An object containing configuration information for the flow types supported in OAuth2 authentication.
///
/// Fixed keys are implicit, password, clientCredentials and authorizationCode.
///
/// For oauth2 only. REQUIRED if so.
Map<String, APISecuritySchemeOAuth2Flow?>? flows;
/// OpenId Connect URL to discover OAuth2 configuration values.
///
/// This MUST be in the form of a URL.
///
/// For openID only. REQUIRED if so.
///
/// This property specifies the OpenID Connect URL used to discover OAuth2 configuration values.
/// It is only applicable and REQUIRED when the security scheme type is [APISecuritySchemeType.openID].
///
/// The URL should point to the OpenID Connect discovery endpoint, typically ending with
/// '.well-known/openid-configuration'. This endpoint provides information about the OpenID Provider's
/// configuration, including the OAuth 2.0 endpoint locations.
///
/// Example:
/// ```dart
/// connectURL = Uri.parse('https://example.com/.well-known/openid-configuration');
/// ```
///
/// Note: This URL must be a valid URI and should be accessible to clients for retrieving
/// the necessary configuration information to initiate the OpenID Connect authentication flow.
Uri? connectURL;
/// Decodes the security scheme information from a [KeyedArchive] object.
///
/// This method is responsible for populating the properties of the [APISecurityScheme]
/// instance based on the data stored in the provided [KeyedArchive] object. It handles
/// the decoding process for different security scheme types, including API key, OAuth2,
/// HTTP authentication, and OpenID Connect.
///
/// The method performs the following steps:
/// 1. Calls the superclass's decode method.
/// 2. Decodes the 'type' and 'description' fields.
/// 3. Based on the security scheme type, decodes additional fields specific to that type:
/// - For API key: decodes 'name' and 'in' (location) fields.
/// - For OAuth2: decodes the 'flows' object.
/// - For HTTP: decodes 'scheme' and 'bearerFormat' fields.
/// - For OpenID Connect: decodes the 'openIdConnectUrl' field.
///
/// If the security scheme type is not recognized, it throws an [ArgumentError].
///
/// Parameters:
/// - object: A [KeyedArchive] containing the encoded security scheme data.
///
/// Throws:
/// - [ArgumentError] if the security scheme type is null or not recognized.
@override
void decode(KeyedArchive object) {
super.decode(object);
type = APISecuritySchemeTypeCodec.decode(object.decode("type"));
description = object.decode("description");
switch (type) {
case APISecuritySchemeType.apiKey:
{
name = object.decode("name");
location = APIParameterLocationCodec.decode(object.decode("in"));
}
break;
case APISecuritySchemeType.oauth2:
{
flows = object.decodeObjectMap(
"flows",
() => APISecuritySchemeOAuth2Flow.empty(),
);
}
break;
case APISecuritySchemeType.http:
{
scheme = object.decode("scheme");
format = object.decode("bearerFormat");
}
break;
case APISecuritySchemeType.openID:
{
connectURL = object.decode("openIdConnectUrl");
}
break;
default:
throw ArgumentError(
"APISecurityScheme must have non-null values for: 'type'.",
);
}
}
/// Encodes the security scheme information into a [KeyedArchive] object.
///
/// This method is responsible for encoding the properties of the [APISecurityScheme]
/// instance into the provided [KeyedArchive] object. It handles the encoding process
/// for different security scheme types, including API key, OAuth2, HTTP authentication,
/// and OpenID Connect.
///
/// The method performs the following steps:
/// 1. Calls the superclass's encode method.
/// 2. Checks if the 'type' property is set, throwing an [ArgumentError] if it's null.
/// 3. Encodes the 'type' and 'description' fields.
/// 4. Based on the security scheme type, encodes additional fields specific to that type:
/// - For API key: encodes 'name' and 'in' (location) fields.
/// - For OAuth2: encodes the 'flows' object.
/// - For HTTP: encodes 'scheme' and 'bearerFormat' fields.
/// - For OpenID Connect: encodes the 'openIdConnectUrl' field.
///
/// For each type, it checks if the required properties are non-null and throws
/// an [ArgumentError] if any required property is missing.
///
/// Parameters:
/// - object: A [KeyedArchive] to store the encoded security scheme data.
///
/// Throws:
/// - [ArgumentError] if the security scheme type is null or if any required
/// property for a specific type is missing.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (type == null) {
throw ArgumentError(
"APISecurityScheme must have non-null values for: 'type'.",
);
}
object.encode("type", APISecuritySchemeTypeCodec.encode(type));
object.encode("description", description);
switch (type) {
case APISecuritySchemeType.apiKey:
{
if (name == null || location == null) {
throw ArgumentError(
"APISecurityScheme with 'apiKey' type must have non-null values for: 'name', 'location'.",
);
}
object.encode("name", name);
object.encode("in", APIParameterLocationCodec.encode(location));
}
break;
case APISecuritySchemeType.oauth2:
{
if (flows == null) {
throw ArgumentError(
"APISecurityScheme with 'oauth2' type must have non-null values for: 'flows'.",
);
}
object.encodeObjectMap("flows", flows);
}
break;
case APISecuritySchemeType.http:
{
if (scheme == null) {
throw ArgumentError(
"APISecurityScheme with 'http' type must have non-null values for: 'scheme'.",
);
}
object.encode("scheme", scheme);
object.encode("bearerFormat", format);
}
break;
case APISecuritySchemeType.openID:
{
if (connectURL == null) {
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'.",
);
}
}
}
/// This class represents the configuration for different OAuth 2.0 flows as defined in the OpenAPI specification.
/// It supports the following OAuth 2.0 flows:
/// - Authorization Code
/// - Implicit
/// - Resource Owner Password Credentials
/// - Client Credentials
///
/// Each flow type has its own constructor with the required parameters:
/// - [APISecuritySchemeOAuth2Flow.code]: For Authorization Code flow
/// - [APISecuritySchemeOAuth2Flow.implicit]: For Implicit flow
/// - [APISecuritySchemeOAuth2Flow.password]: For Resource Owner Password Credentials flow
/// - [APISecuritySchemeOAuth2Flow.client]: For Client Credentials flow
///
/// The class provides properties for configuring the OAuth 2.0 endpoints and available scopes:
/// - [authorizationURL]: The authorization endpoint URL (required for Authorization Code and Implicit flows)
/// - [tokenURL]: The token endpoint URL (required for Authorization Code, Password, and Client Credentials flows)
/// - [refreshURL]: The refresh token endpoint URL (optional)
/// - [scopes]: A map of available scopes and their descriptions (required for all flows)
///
/// This class extends [APIObject] and provides methods for encoding and decoding its properties
/// to and from a [KeyedArchive] object, which is used for serialization and deserialization.
class APISecuritySchemeOAuth2Flow extends APIObject {
/// Creates an empty instance of APISecuritySchemeOAuth2Flow.
///
/// This constructor initializes an APISecuritySchemeOAuth2Flow with no pre-set values.
/// It can be used when you need to create an OAuth2 flow object that will be
/// populated with data later or when you want to manually set all properties.
APISecuritySchemeOAuth2Flow.empty();
/// Creates an instance of APISecuritySchemeOAuth2Flow for the Authorization Code flow.
///
/// This constructor initializes an OAuth2 flow configuration for the Authorization Code grant type.
///
/// Parameters:
/// - authorizationURL: The authorization endpoint URL. REQUIRED.
/// - tokenURL: The token endpoint URL. REQUIRED.
/// - refreshURL: The refresh token endpoint URL. Optional.
/// - scopes: A map of available scopes and their descriptions. REQUIRED.
///
/// All URL parameters should be in the form of a valid URL.
///
/// Example:
/// ```dart
/// var codeFlow = APISecuritySchemeOAuth2Flow.code(
/// Uri.parse('https://example.com/oauth/authorize'),
/// Uri.parse('https://example.com/oauth/token'),
/// Uri.parse('https://example.com/oauth/refresh'),
/// {'read:api': 'Read access to protected resources'},
/// );
/// ```
APISecuritySchemeOAuth2Flow.code(
this.authorizationURL,
this.tokenURL,
this.refreshURL,
this.scopes,
);
/// Creates an instance of APISecuritySchemeOAuth2Flow for the Implicit flow.
///
/// This constructor initializes an OAuth2 flow configuration for the Implicit grant type.
///
/// Parameters:
/// - authorizationURL: The authorization endpoint URL. REQUIRED.
/// - refreshURL: The refresh token endpoint URL. Optional.
/// - scopes: A map of available scopes and their descriptions. REQUIRED.
///
/// The authorizationURL should be in the form of a valid URL.
///
/// Example:
/// ```dart
/// var implicitFlow = APISecuritySchemeOAuth2Flow.implicit(
/// Uri.parse('https://example.com/oauth/authorize'),
/// Uri.parse('https://example.com/oauth/refresh'),
/// {'read:api': 'Read access to protected resources'},
/// );
/// ```
APISecuritySchemeOAuth2Flow.implicit(
this.authorizationURL,
this.refreshURL,
this.scopes,
);
/// Creates an instance of APISecuritySchemeOAuth2Flow for the Resource Owner Password Credentials flow.
///
/// This constructor initializes an OAuth2 flow configuration for the Password grant type.
///
/// Parameters:
/// - tokenURL: The token endpoint URL. REQUIRED.
/// - refreshURL: The refresh token endpoint URL. Optional.
/// - scopes: A map of available scopes and their descriptions. REQUIRED.
///
/// The tokenURL should be in the form of a valid URL.
///
/// Example:
/// ```dart
/// var passwordFlow = APISecuritySchemeOAuth2Flow.password(
/// Uri.parse('https://example.com/oauth/token'),
/// Uri.parse('https://example.com/oauth/refresh'),
/// {'read:api': 'Read access to protected resources'},
/// );
/// ```
APISecuritySchemeOAuth2Flow.password(
this.tokenURL,
this.refreshURL,
this.scopes,
);
/// Creates an instance of APISecuritySchemeOAuth2Flow for the Client Credentials flow.
///
/// This constructor initializes an OAuth2 flow configuration for the Client Credentials grant type.
///
/// Parameters:
/// - tokenURL: The token endpoint URL. REQUIRED.
/// - refreshURL: The refresh token endpoint URL. Optional.
/// - scopes: A map of available scopes and their descriptions. REQUIRED.
///
/// The tokenURL should be in the form of a valid URL.
///
/// Example:
/// ```dart
/// var clientFlow = APISecuritySchemeOAuth2Flow.client(
/// Uri.parse('https://example.com/oauth/token'),
/// Uri.parse('https://example.com/oauth/refresh'),
/// {'read:api': 'Read access to protected resources'},
/// );
/// ```
APISecuritySchemeOAuth2Flow.client(
this.tokenURL,
this.refreshURL,
this.scopes,
);
/// The authorization URL to be used for this flow.
///
/// This property represents the authorization endpoint URL for OAuth 2.0 flows
/// that require user interaction, such as the Authorization Code flow and the
/// Implicit flow.
///
/// The authorization URL is the endpoint where the resource owner (user) is
/// redirected to grant authorization to the client application. It's typically
/// used to initiate the OAuth 2.0 authorization process.
///
/// This property is REQUIRED for flows that use an authorization endpoint
/// (Authorization Code and Implicit flows). It MUST be in the form of a valid URL.
///
/// Example:
/// ```dart
/// authorizationURL = Uri.parse('https://example.com/oauth/authorize');
/// ```
///
/// Note: This property should not be set for flows that don't use an
/// authorization endpoint, such as the Client Credentials flow or the
/// Resource Owner Password Credentials flow.
Uri? authorizationURL;
/// The token URL to be used for this flow.
///
/// This property represents the token endpoint URL for OAuth 2.0 flows.
/// The token URL is the endpoint where the client application exchanges
/// an authorization grant for an access token.
///
/// This property is REQUIRED for flows that use a token endpoint
/// (Authorization Code, Resource Owner Password Credentials, and Client Credentials flows).
/// It MUST be in the form of a valid URL.
///
/// Example:
/// ```dart
/// tokenURL = Uri.parse('https://example.com/oauth/token');
/// ```
///
/// Note: This property is not used in the Implicit flow, as the access token
/// is returned directly from the authorization endpoint in that flow.
Uri? tokenURL;
/// The URL to be used for obtaining refresh tokens.
///
/// This property represents the refresh token endpoint URL for OAuth 2.0 flows
/// that support token refresh. The refresh URL is used to obtain a new access token
/// using a refresh token, without requiring the user to re-authenticate.
///
/// This property is OPTIONAL for all OAuth 2.0 flows. If provided, it MUST be in the
/// form of a valid URL.
///
/// Example:
/// ```dart
/// refreshURL = Uri.parse('https://example.com/oauth/refresh');
/// ```
///
/// Note: Not all OAuth 2.0 implementations support refresh tokens. When supported,
/// this URL allows clients to refresh their access tokens without user interaction,
/// improving the user experience for long-lived applications.
Uri? refreshURL;
/// The available scopes for the OAuth2 security scheme.
///
/// This property represents a map of OAuth 2.0 scopes available for the security scheme.
/// Each key in the map is a scope name, and its corresponding value is a short description of that scope.
///
/// Scopes are used to define and limit the level of access granted to a client application.
/// They allow for fine-grained control over the permissions given to a client when accessing protected resources.
///
/// This property is REQUIRED for all OAuth 2.0 flows defined in the OpenAPI specification.
///
/// Example:
/// ```dart
/// scopes = {
/// 'read:users': 'Read access to user information',
/// 'write:users': 'Write access to user information',
/// };
/// ```
///
/// Note: The descriptions should be concise yet informative, helping API consumers
/// understand the purpose and implications of each scope.
Map<String, String>? scopes;
/// Encodes the OAuth2 flow configuration into a [KeyedArchive] object.
///
/// This method is responsible for serializing the properties of the [APISecuritySchemeOAuth2Flow]
/// instance into the provided [KeyedArchive] object. It encodes the following properties:
///
/// - authorizationURL: The authorization endpoint URL (if applicable)
/// - tokenURL: The token endpoint URL (if applicable)
/// - refreshURL: The refresh token endpoint URL (if applicable)
/// - scopes: A map of available scopes and their descriptions
///
/// This method first calls the superclass's encode method to handle any inherited properties,
/// then encodes the specific properties of the OAuth2 flow configuration.
///
/// Parameters:
/// - object: A [KeyedArchive] to store the encoded OAuth2 flow configuration data.
@override
void encode(KeyedArchive object) {
super.encode(object);
object.encode("authorizationUrl", authorizationURL);
object.encode("tokenUrl", tokenURL);
object.encode("refreshUrl", refreshURL);
object.encode("scopes", scopes);
}
/// Decodes the OAuth2 flow configuration from a [KeyedArchive] object.
///
/// This method is responsible for deserializing the properties of the [APISecuritySchemeOAuth2Flow]
/// instance from the provided [KeyedArchive] object. It decodes the following properties:
///
/// - authorizationURL: The authorization endpoint URL (if present)
/// - tokenURL: The token endpoint URL (if present)
/// - refreshURL: The refresh token endpoint URL (if present)
/// - scopes: A map of available scopes and their descriptions
///
/// This method first calls the superclass's decode method to handle any inherited properties,
/// then decodes the specific properties of the OAuth2 flow configuration.
///
/// Parameters:
/// - object: A [KeyedArchive] containing the encoded OAuth2 flow configuration data.
@override
void decode(KeyedArchive object) {
super.decode(object);
authorizationURL = object.decode("authorizationUrl");
tokenURL = object.decode("tokenUrl");
refreshURL = object.decode("refreshUrl");
scopes = object.decode<Map<String, String>>("scopes");
}
}
/// Represents a security requirement in the OpenAPI specification.
///
/// The name used for each property MUST correspond to a security scheme declared in [APIComponents.securitySchemes].
/// [APISecurityRequirement] that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information.
/// 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 {
/// Creates an [APISecurityRequirement] instance with the specified security requirements.
///
/// The [requirements] parameter is a map where each key corresponds to a security scheme
/// declared in [APIComponents.securitySchemes], and the value is a list of scope names
/// required for execution (for OAuth2 or OpenID schemes) or an empty list (for other schemes).
///
/// Example:
/// ```dart
/// var securityRequirement = APISecurityRequirement({
/// 'api_key': [],
/// 'oauth2': ['read:api', 'write:api'],
/// });
/// ```
///
/// Note: For security schemes other than OAuth2 or OpenID, the list should be empty.
APISecurityRequirement(this.requirements);
/// Creates an empty instance of APISecurityRequirement.
///
/// This constructor initializes an APISecurityRequirement with no pre-set security requirements.
/// It can be used when you need to create a security requirement object that will be
/// populated with data later or when you want to manually set the requirements.
APISecurityRequirement.empty();
/// A map representing the security requirements for an API operation or the entire API.
///
/// 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<String, List<String>>? requirements;
/// Encodes the security requirements into a [KeyedArchive] object.
///
/// This method is responsible for serializing the [requirements] of the [APISecurityRequirement]
/// instance into the provided [KeyedArchive] object. It performs the following steps:
///
/// 1. Calls the superclass's encode method to handle any inherited properties.
/// 2. If [requirements] is not null, it iterates through each key-value pair in the map.
/// 3. For each pair, it encodes the key (security scheme name) and its corresponding value
/// (list of scope names or an empty list) into the [KeyedArchive] object.
///
/// This encoding process allows the security requirements to be properly serialized
/// for use in the OpenAPI specification.
///
/// Parameters:
/// - object: A [KeyedArchive] to store the encoded security requirement data.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (requirements != null) {
requirements!.forEach((key, value) {
object.encode(key, value);
});
}
}
/// Decodes the security requirements from a [KeyedArchive] object.
///
/// This method is responsible for deserializing the security requirements stored in the
/// provided [KeyedArchive] object into the [requirements] property of this [APISecurityRequirement]
/// instance. It performs the following steps:
///
/// 1. Calls the superclass's decode method to handle any inherited properties.
/// 2. Iterates through all keys in the [KeyedArchive] object.
/// 3. For each key, it attempts to decode the value as an [Iterable<dynamic>].
/// 4. If successful, it converts the decoded value to a [List<String>].
/// 5. If [requirements] is null, it initializes it as an empty map.
/// 6. Adds the key-value pair to the [requirements] map, where the key is the security
/// scheme name and the value is the list of scope names or an empty list.
///
/// This decoding process allows the security requirements to be properly deserialized
/// from the OpenAPI specification format.
///
/// Parameters:
/// - object: A [KeyedArchive] containing the encoded security requirement data.
@override
void decode(KeyedArchive object) {
super.decode(object);
for (final key in object.keys) {
final decoded = object.decode<Iterable<dynamic>>(key);
if (decoded != null) {
final req = List<String>.from(decoded);
requirements ??= <String, List<String>>{};
requirements![key] = req;
}
}
}
}

View file

@ -1,275 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
import 'package:protevus_openapi/object.dart';
/// An object representing a Server.
///
/// This class is used to describe a server in an OpenAPI specification.
/// It includes information about the server's URL, an optional description,
/// and any variables that can be used in the URL template.
///
/// The [url] property is required and represents the URL to the target host.
/// It may include server variables in {brackets} for substitution.
///
/// The [description] property is optional and can provide additional
/// information about the server.
///
/// The [variables] property is a map of server variables that can be used
/// for substitution in the URL template.
class APIServerDescription extends APIObject {
/// Creates a new [APIServerDescription] instance.
///
/// [url] is a required parameter representing the URL to the target host.
/// It may include server variables in {brackets} for substitution.
///
/// [description] is an optional parameter that provides additional
/// information about the server.
///
/// [variables] is an optional parameter representing a map of server
/// variables that can be used for substitution in the URL template.
APIServerDescription(this.url, {this.description, this.variables});
/// Creates an empty [APIServerDescription] instance.
///
/// This constructor initializes an [APIServerDescription] object without setting any of its properties.
/// It can be useful when you need to create an instance that will be populated later.
APIServerDescription.empty();
/// A URL to the target host.
///
/// This URL is required and represents the location of the target host.
/// It supports Server Variables and may be relative, indicating that the host
/// location is relative to where the OpenAPI document is being served.
///
/// Variable substitutions will be made when a variable is named in {brackets}.
/// For example, a URL like "https://{username}.example.com" would allow
/// substitution of the {username} part.
///
/// This field is crucial for specifying where API requests should be sent.
Uri? url;
/// An optional string describing the host designated by the URL.
///
/// This property provides additional information about the server, which can be useful for API consumers.
/// It may include details such as the environment (e.g., production, staging), the purpose of the server,
/// or any specific characteristics.
///
/// The description can use CommonMark syntax for rich text representation, allowing for formatted text,
/// links, and other markup elements.
///
/// Example:
/// ```
/// description: "Production server for the European region"
/// ```
///
/// This field is optional and can be null if no description is provided.
String? description;
/// A map between a variable name and its value.
///
/// This property represents a mapping of server variable names to their corresponding [APIServerVariable] objects.
/// These variables can be used for substitution in the server's URL template.
///
/// Each key in the map is a string representing the variable name, and the corresponding value
/// is an [APIServerVariable] object (or null) that defines the properties of that variable.
///
/// For example, if the server URL is "https://{username}.example.com", the variables map might contain
/// a key "username" with an [APIServerVariable] value that specifies the default username and possible alternatives.
///
/// This field is optional and can be null if no variables are defined for the server.
Map<String, APIServerVariable?>? variables;
/// Decodes the [APIServerDescription] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIServerDescription]
/// instance from the provided [KeyedArchive] object. It performs the following steps:
///
/// 1. Calls the superclass's decode method to handle any inherited properties.
/// 2. Decodes the 'url' field from the archive and assigns it to the [url] property.
/// 3. Decodes the 'description' field and assigns it to the [description] property.
/// 4. Decodes the 'variables' field as an object map, where each value is an [APIServerVariable].
/// It uses [APIServerVariable.empty()] as a factory function to create new instances.
///
/// This method is typically called when deserializing the object from a JSON or similar format.
///
/// [object] is the [KeyedArchive] containing the encoded data for this object.
@override
void decode(KeyedArchive object) {
super.decode(object);
url = object.decode("url");
description = object.decode("description");
variables =
object.decodeObjectMap("variables", () => APIServerVariable.empty());
}
/// Encodes the [APIServerDescription] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APIServerDescription]
/// instance into the provided [KeyedArchive] object. It performs the following steps:
///
/// 1. Calls the superclass's encode method to handle any inherited properties.
/// 2. Checks if the [url] property is null. If it is, an [ArgumentError] is thrown
/// because the 'url' field is required for a valid [APIServerDescription].
/// 3. Encodes the [url] property into the archive with the key "url".
/// 4. Encodes the [description] property into the archive with the key "description".
/// 5. Encodes the [variables] property as an object map into the archive with the key "variables".
///
/// This method is typically called when serializing the object to JSON or a similar format.
///
/// [object] is the [KeyedArchive] where the encoded data for this object will be stored.
///
/// Throws an [ArgumentError] if the [url] property is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (url == null) {
throw ArgumentError(
"APIServerDescription must have non-null values for: 'url'.",
);
}
object.encode("url", url);
object.encode("description", description);
object.encodeObjectMap("variables", variables);
}
}
/// An object representing a Server Variable for server URL template substitution.
///
/// This class extends [APIObject] and represents a variable that can be used
/// in a server URL template. It includes properties for the default value,
/// available values (enum), and an optional description.
///
/// The [defaultValue] is required and represents the value to be used if no
/// other value is provided.
///
/// [availableValues] is an optional list of allowed values for the variable.
///
/// [description] is an optional field providing additional information about
/// the variable.
class APIServerVariable extends APIObject {
/// Creates a new [APIServerVariable] instance.
///
/// [defaultValue] is a required parameter representing the default value to use for substitution.
/// This value MUST be provided by the consumer.
///
/// [availableValues] is an optional parameter that provides a list of allowed values for the variable.
/// If provided, it represents an enumeration of string values to be used if the substitution options
/// are from a limited set.
///
/// [description] is an optional parameter that provides additional information about the server variable.
/// CommonMark syntax MAY be used for rich text representation.
APIServerVariable(
this.defaultValue, {
this.availableValues,
this.description,
});
/// Creates an empty [APIServerVariable] instance.
///
/// This constructor initializes an [APIServerVariable] object without setting any of its properties.
/// It can be useful when you need to create an instance that will be populated later.
APIServerVariable.empty();
/// An enumeration of string values to be used if the substitution options are from a limited set.
///
/// This property represents an optional list of allowed values for the server variable.
/// If provided, it restricts the possible values that can be used for substitution
/// in the server URL template to this specific set of strings.
///
/// When defined, the variable value used for URL substitution must be one of the values
/// listed in this array. This allows for validation and helps ensure that only
/// pre-defined, valid values are used in the server URL.
///
/// The list can be null if there are no restrictions on the possible values for the variable.
List<String>? availableValues;
/// The default value to use for substitution in the server URL template.
///
/// REQUIRED. Unlike the Schema Object's default, this value MUST be provided by the consumer.
String? defaultValue;
/// An optional description for the server variable.
///
/// This property provides additional information about the server variable,
/// which can be helpful for API consumers to understand its purpose or usage.
/// The description can include details such as the expected format of the value,
/// any constraints, or examples of valid inputs.
///
/// CommonMark syntax MAY be used for rich text representation, allowing for
/// formatted text, links, and other markup elements to enhance readability
/// and provide more detailed explanations.
///
/// This field is optional and can be null if no description is provided.
///
/// Example:
/// ```
/// description: "The username for authentication. Use 'demo' for testing."
/// ```
String? description;
/// Decodes the [APIServerVariable] object from a [KeyedArchive].
///
/// This method is responsible for populating the properties of the [APIServerVariable]
/// instance from the provided [KeyedArchive] object. It performs the following steps:
///
/// 1. Calls the superclass's decode method to handle any inherited properties.
/// 2. Decodes the 'enum' field from the archive, casts it to a List<String>, and assigns it to [availableValues].
/// 3. Decodes the 'default' field and assigns it to the [defaultValue] property.
/// 4. Decodes the 'description' field and assigns it to the [description] property.
///
/// This method is typically called when deserializing the object from a JSON or similar format.
///
/// [object] is the [KeyedArchive] containing the encoded data for this object.
@override
void decode(KeyedArchive object) {
super.decode(object);
final enumMap = object.decode("enum") as List<String>;
availableValues = List<String>.from(enumMap);
defaultValue = object.decode("default");
description = object.decode("description");
}
/// Encodes the [APIServerVariable] object into a [KeyedArchive].
///
/// This method is responsible for serializing the properties of the [APIServerVariable]
/// instance into the provided [KeyedArchive] object. It performs the following steps:
///
/// 1. Calls the superclass's encode method to handle any inherited properties.
/// 2. Checks if the [defaultValue] property is null. If it is, an [ArgumentError] is thrown
/// because the 'defaultValue' field is required for a valid [APIServerVariable].
/// 3. Encodes the [availableValues] property into the archive with the key "enum".
/// 4. Encodes the [defaultValue] property into the archive with the key "default".
/// 5. Encodes the [description] property into the archive with the key "description".
///
/// This method is typically called when serializing the object to JSON or a similar format.
///
/// [object] is the [KeyedArchive] where the encoded data for this object will be stored.
///
/// Throws an [ArgumentError] if the [defaultValue] property is null.
@override
void encode(KeyedArchive object) {
super.encode(object);
if (defaultValue == null) {
throw ArgumentError(
"APIServerVariable must have non-null values for: 'defaultValue'.",
);
}
object.encode("enum", availableValues);
object.encode("default", defaultValue);
object.encode("description", description);
}
}

View file

@ -1,85 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// Represents the different data types that can be used in an API.
///
/// This enumeration defines the following types:
/// - [string]: Represents a sequence of characters.
/// - [number]: Represents a numeric value, which can include decimals.
/// - [integer]: Represents a whole number without decimals.
/// - [boolean]: Represents a true or false value.
/// - [array]: Represents a collection of values.
/// - [object]: Represents a complex data structure with key-value pairs.
enum APIType { string, number, integer, boolean, array, object }
/// A utility class for encoding and decoding [APIType] values.
///
/// This class provides static methods to convert between [APIType] enum values
/// and their corresponding string representations.
class APITypeCodec {
/// Decodes a string representation of an [APIType] to its corresponding enum value.
///
/// This method takes a [String] parameter [type] and returns the corresponding
/// [APIType] enum value. If the input string doesn't match any known type,
/// the method returns null.
///
/// Parameters:
/// - type: A [String] representing the API type to be decoded.
///
/// Returns:
/// The corresponding [APIType] enum value, or null if no match is found.
static APIType? decode(String? type) {
switch (type) {
case "string":
return APIType.string;
case "number":
return APIType.number;
case "integer":
return APIType.integer;
case "boolean":
return APIType.boolean;
case "array":
return APIType.array;
case "object":
return APIType.object;
default:
return null;
}
}
/// Encodes an [APIType] enum value to its corresponding string representation.
///
/// This method takes an [APIType] parameter [type] and returns the corresponding
/// string representation. If the input type is null or doesn't match any known type,
/// the method returns null.
///
/// Parameters:
/// - type: An [APIType] enum value to be encoded.
///
/// Returns:
/// The corresponding [String] representation of the [APIType], or null if the input is null or no match is found.
static String? encode(APIType? type) {
switch (type) {
case APIType.string:
return "string";
case APIType.number:
return "number";
case APIType.integer:
return "integer";
case APIType.boolean:
return "boolean";
case APIType.array:
return "array";
case APIType.object:
return "object";
default:
return null;
}
}
}

View file

@ -1,22 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// Utility library for the Protevus OpenAPI package.
///
/// This library exports helper functions for working with lists and maps
/// from the Protevus OpenAPI package. It provides convenient access to
/// utility functions that can be used across the application.
///
/// Exports:
/// - list_helper.dart: Contains functions for list manipulation.
/// - map_helper.dart: Contains functions for map manipulation.
library util;
export 'package:protevus_openapi/src/util/list_helper.dart';
export 'package:protevus_openapi/src/util/map_helper.dart';

View file

@ -1,26 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports various components of the Protevus OpenAPI v2 specification.
/// It provides access to classes and utilities for working with OpenAPI v2 documents,
/// including document structure, headers, metadata, operations, parameters, paths,
/// responses, schemas, security definitions, and common types used throughout the API.
library v2;
export 'package:protevus_openapi/src/v2/document.dart';
export 'package:protevus_openapi/src/v2/header.dart';
export 'package:protevus_openapi/src/v2/metadata.dart';
export 'package:protevus_openapi/src/v2/operation.dart';
export 'package:protevus_openapi/src/v2/parameter.dart';
export 'package:protevus_openapi/src/v2/path.dart';
export 'package:protevus_openapi/src/v2/property.dart';
export 'package:protevus_openapi/src/v2/response.dart';
export 'package:protevus_openapi/src/v2/schema.dart';
export 'package:protevus_openapi/src/v2/security.dart';
export 'package:protevus_openapi/src/v2/types.dart';

View file

@ -1,36 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports various components of the OpenAPI v3 specification.
/// It provides access to essential classes and structures used for defining
/// and working with OpenAPI v3 documents, including callbacks, components,
/// document structure, encodings, headers, media types, metadata, operations,
/// parameters, paths, request bodies, responses, schemas, security definitions,
/// servers, and other related types.
///
/// By importing this library, users can access all the necessary elements
/// to create, parse, and manipulate OpenAPI v3 specifications in their Dart projects.
library v3;
export 'package:protevus_openapi/src/v3/callback.dart';
export 'package:protevus_openapi/src/v3/components.dart';
export 'package:protevus_openapi/src/v3/document.dart';
export 'package:protevus_openapi/src/v3/encoding.dart';
export 'package:protevus_openapi/src/v3/header.dart';
export 'package:protevus_openapi/src/v3/media_type.dart';
export 'package:protevus_openapi/src/v3/metadata.dart';
export 'package:protevus_openapi/src/v3/operation.dart';
export 'package:protevus_openapi/src/v3/parameter.dart';
export 'package:protevus_openapi/src/v3/path.dart';
export 'package:protevus_openapi/src/v3/request_body.dart';
export 'package:protevus_openapi/src/v3/response.dart';
export 'package:protevus_openapi/src/v3/schema.dart';
export 'package:protevus_openapi/src/v3/security.dart';
export 'package:protevus_openapi/src/v3/server.dart';
export 'package:protevus_openapi/src/v3/types.dart';

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,120 +0,0 @@
// 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 'dart:convert';
import 'dart:io';
import 'package:protevus_openapi/v2.dart';
import 'package:test/test.dart';
void main() {
group("kubenrnetes spec", () {
APIDocument? doc;
Map<String, dynamic>? original;
setUpAll(() async {
/// download sample api document if we don't already have it.
final String config = await fetchKubernetesExample();
final file = File(config);
final contents = file.readAsStringSync();
original = json.decode(contents) as Map<String, dynamic>;
doc = APIDocument.fromMap(original!);
});
test("Has all metadata", () {
expect(doc!.version, "2.0");
expect(doc!.info!.title, "Kubernetes");
expect(doc!.info!.version, 'v1.12.0');
expect(doc!.host, isNull);
expect(doc!.basePath, isNull);
expect(doc!.tags, isNull);
expect(doc!.schemes, isNull);
});
test("Confirm top-level objects", () {
expect(original!.containsKey("consumes"), false);
expect(original!.containsKey("produces"), false);
});
test("Has paths", () {
expect(doc!.paths!.length, greaterThan(0));
expect(doc!.paths!.length, original!["paths"].length);
final Map<String, dynamic> originalPaths =
original!["paths"] as Map<String, dynamic>;
doc!.paths!.forEach((k, v) {
expect(originalPaths.keys.contains(k), true);
});
});
test("Sample - Namespace", () {
final namespacePath = doc!.paths!["/api/v1/namespaces"];
final getNamespace = namespacePath!.operations["get"];
expect(getNamespace!.description, contains("of kind Namespace"));
expect(getNamespace.consumes, ["*/*"]);
expect(getNamespace.produces, contains("application/json"));
expect(getNamespace.produces, contains("application/yaml"));
expect(getNamespace.parameters!.length, 8);
expect(
getNamespace.parameters!
.firstWhere((p) => p!.name == "limit")!
.location,
APIParameterLocation.query,
);
expect(
getNamespace.parameters!.firstWhere((p) => p!.name == "limit")!.type,
APIType.integer,
);
expect(getNamespace.responses!.keys, contains("401"));
expect(getNamespace.responses!.keys, contains("200"));
final postNamespace = namespacePath.operations["post"];
expect(postNamespace!.parameters!.length, 1);
expect(postNamespace.parameters!.first!.name, "body");
expect(
postNamespace.parameters!.first!.location,
APIParameterLocation.body,
);
});
test("Sample - Reference", () {
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(
schema.properties!["serverAddressByClientCIDRs"]!.items!
.properties!["clientCIDR"]!.description,
contains("The CIDR"),
);
});
test("Can encode as JSON", () {
expect(json.encode(doc!.asMap()), isA<String>());
});
});
}
Future<String> fetchKubernetesExample() async {
// 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/kubernetes.json";
final configFile = File(config);
if (!configFile.existsSync()) {
if (!configFile.parent.existsSync()) {
Directory(configFile.parent.path).createSync(recursive: true);
}
const url =
'https://raw.githubusercontent.com/kubernetes/kubernetes/f091073b0fb4d3a550e7f182eb5465338c8b7cbf/api/openapi-spec/swagger.json';
final request = await HttpClient().getUrl(Uri.parse(url));
final response = await request.close();
await response.pipe(configFile.openWrite());
}
return config;
}

View file

@ -1,441 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:protevus_openapi/v3.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();
final ref = APISchemaObject()
..referenceURI = Uri.parse("/components/schemas/foo");
final orig = components.resolve(ref);
expect(orig, isNotNull);
expect(orig!.type, APIType.string);
expect(ref.type, isNull);
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", () {
final components = APIComponents();
try {
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(
APISchemaObject()..referenceURI = Uri.parse("#/components/schemas"),
);
expect(true, false);
} on ArgumentError catch (e) {
expect(e.message, contains("Invalid reference URI"));
}
try {
components.resolve(
APISchemaObject()..referenceURI = Uri.parse("/components/foobar/foo"),
);
expect(true, false);
} on ArgumentError catch (e) {
expect(e.message, contains("Invalid reference URI"));
}
});
test("Nonexisting component returns null", () {
final components = APIComponents();
expect(
components.resolve(
APISchemaObject()
..referenceURI = Uri.parse("/components/schemas/foo"),
),
isNull,
);
});
test("URIs are paths internally, but fragments when serialized", () {
final doc = APIDocument.fromMap({
"openapi": "3.0.0",
"info": {"title": "x", "version": "1"},
"paths": <String, dynamic>{},
"components": {
"schemas": {
"string": {
"type": "string",
},
"container": {"\$ref": "#/components/schemas/string"}
}
}
});
expect(
doc.components!.schemas["container"]!.referenceURI!
.toFilePath(windows: Platform.isWindows),
Uri.parse("/components/schemas/string")
.toFilePath(windows: Platform.isWindows),
);
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",
);
});
});
group("Stripe spec", () {
APIDocument? doc;
Map<String, dynamic>? original;
setUpAll(() async {
final String config = await fetchStripExample();
final file = File(config);
final contents = file.readAsStringSync();
original = json.decode(contents) as Map<String, dynamic>;
if (original != null) {
doc = APIDocument.fromMap(original!);
}
});
test("Emits same document in asMap()", () {
expect(doc!.asMap(), original);
});
test("Has openapi version", () {
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);
});
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);
expect(doc!.servers!.first!.variables, isNull);
});
test("Tags", () {
expect(doc!.tags, isNull);
});
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);
final getOp = p.operations["get"];
final getParams = getOp!.parameters;
final getResponses = getOp.responses;
expect(getOp.description, contains("10 most recent reversals"));
expect(getOp.id, "GetTransfersTransferReversalsId");
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", () {});
test("Security requirement", () {
expect(doc!.security, isNotNull);
expect(doc!.security!.length, 2);
});
});
group("Schema", () {
test("Can read/emit schema object with additionalProperties=true", () {
final doc = APIDocument.fromMap({
"openapi": "3.0.0",
"info": {"title": "x", "version": "1"},
"paths": <String, dynamic>{},
"components": {
"schemas": {
"freeform": {"type": "object", "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,
);
});
test("Can read/emit schema object with additionalProperties={}", () {
final doc = APIDocument.fromMap({
"openapi": "3.0.0",
"info": {"title": "x", "version": "1"},
"paths": <String, dynamic>{},
"components": {
"schemas": {
"freeform": {
"type": "object",
"additionalProperties": <String, dynamic>{}
}
}
}
});
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("'add' methods", () {
test("'addHeader'", () {
final resp = APIResponse("Response");
// when null
resp.addHeader(
"x",
APIHeader(schema: APISchemaObject.string(format: "initial")),
);
expect(resp.headers!["x"]!.schema!.format, "initial");
// add more than one
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",
APIHeader(schema: APISchemaObject.string(format: "third")),
);
expect(resp.headers!["x"]!.schema!.format, "initial");
expect(resp.headers!["y"]!.schema!.format, "second");
});
test("'addContent'", () {
final resp = APIResponse("Response");
// when null
resp.addContent("x/a", APISchemaObject.string(format: "initial"));
expect(resp.content!["x/a"]!.schema!.format, "initial");
// add more than one
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", 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");
});
test("'addResponse'", () {
final op = APIOperation("op", null);
// when null
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,
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,
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);
});
test("'addResponse' guards against null value", () {
final 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,
);
});
});
}
Future<String> fetchStripExample() async {
// 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";
final configFile = File(config);
if (!configFile.existsSync()) {
if (!configFile.parent.existsSync()) {
Directory(configFile.parent.path).createSync(recursive: true);
}
const url =
'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json';
final request = await HttpClient().getUrl(Uri.parse(url));
final response = await request.close();
await response.pipe(File(config).openWrite());
}
return config;
}

View file

@ -1,65 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
library runtime;
import 'dart:io';
export 'package:protevus_runtime/src/analyzer.dart';
export 'package:protevus_runtime/src/build.dart';
export 'package:protevus_runtime/src/build_context.dart';
export 'package:protevus_runtime/src/build_manager.dart';
export 'package:protevus_runtime/src/compiler.dart';
export 'package:protevus_runtime/src/context.dart';
export 'package:protevus_runtime/src/exceptions.dart';
export 'package:protevus_runtime/src/generator.dart';
export 'package:protevus_runtime/src/mirror_coerce.dart';
export 'package:protevus_runtime/src/mirror_context.dart';
import 'package:protevus_runtime/src/compiler.dart';
import 'package:protevus_runtime/src/mirror_context.dart';
/// Compiler for the runtime package itself.
///
/// Removes dart:mirror from a replica of this package, and adds
/// a generated runtime to the replica's pubspec.
class RuntimePackageCompiler extends Compiler {
@override
Map<String, Object> compile(MirrorContext context) => {};
@override
void deflectPackage(Directory destinationDirectory) {
final libraryFile = File.fromUri(
destinationDirectory.uri.resolve("lib/").resolve("runtime.dart"),
);
libraryFile.writeAsStringSync(
"library runtime;\nexport 'src/context.dart';\nexport 'src/exceptions.dart';",
);
final contextFile = File.fromUri(
destinationDirectory.uri
.resolve("lib/")
.resolve("src/")
.resolve("context.dart"),
);
final contextFileContents = contextFile.readAsStringSync().replaceFirst(
"import 'package:protevus_runtime/src/mirror_context.dart';",
"import 'package:generated_runtime/generated_runtime.dart';",
);
contextFile.writeAsStringSync(contextFileContents);
final pubspecFile =
File.fromUri(destinationDirectory.uri.resolve("pubspec.yaml"));
final pubspecContents = pubspecFile.readAsStringSync().replaceFirst(
"\ndependencies:",
"\ndependencies:\n generated_runtime:\n path: ../../generated_runtime/",
);
pubspecFile.writeAsStringSync(pubspecContents);
}
}

View file

@ -1,86 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_runtime/runtime.dart';
const String _listPrefix = "List<";
const String _mapPrefix = "Map<String,";
T cast<T>(dynamic input) {
try {
var typeString = T.toString();
if (typeString.endsWith('?')) {
if (input == null) {
return null as T;
} else {
typeString = typeString.substring(0, typeString.length - 1);
}
}
if (typeString.startsWith(_listPrefix)) {
if (input is! List) {
throw TypeError();
}
if (typeString.startsWith("List<int>")) {
return List<int>.from(input) as T;
} else if (typeString.startsWith("List<num>")) {
return List<num>.from(input) as T;
} else if (typeString.startsWith("List<double>")) {
return List<double>.from(input) as T;
} else if (typeString.startsWith("List<String>")) {
return List<String>.from(input) as T;
} else if (typeString.startsWith("List<bool>")) {
return List<bool>.from(input) as T;
} else if (typeString.startsWith("List<int?>")) {
return List<int?>.from(input) as T;
} else if (typeString.startsWith("List<num?>")) {
return List<num?>.from(input) as T;
} else if (typeString.startsWith("List<double?>")) {
return List<double?>.from(input) as T;
} else if (typeString.startsWith("List<String?>")) {
return List<String?>.from(input) as T;
} else if (typeString.startsWith("List<bool?>")) {
return List<bool?>.from(input) as T;
} else if (typeString.startsWith("List<Map<String, dynamic>>")) {
return List<Map<String, dynamic>>.from(input) as T;
}
} else if (typeString.startsWith(_mapPrefix)) {
if (input is! Map) {
throw TypeError();
}
final inputMap = input as Map<String, dynamic>;
if (typeString.startsWith("Map<String, int>")) {
return Map<String, int>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, num>")) {
return Map<String, num>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, double>")) {
return Map<String, double>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, String>")) {
return Map<String, String>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, bool>")) {
return Map<String, bool>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, int?>")) {
return Map<String, int?>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, num?>")) {
return Map<String, num?>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, double?>")) {
return Map<String, double?>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, String?>")) {
return Map<String, String?>.from(inputMap) as T;
} else if (typeString.startsWith("Map<String, bool?>")) {
return Map<String, bool?>.from(inputMap) as T;
}
}
return input as T;
} on TypeError {
throw TypeCoercionException(T, input.runtimeType);
}
}

View file

@ -1,143 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:io';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:path/path.dart';
class CodeAnalyzer {
CodeAnalyzer(this.uri) {
if (!uri.isAbsolute) {
throw ArgumentError("'uri' must be absolute for CodeAnalyzer");
}
contexts = AnalysisContextCollection(includedPaths: [path]);
if (contexts.contexts.isEmpty) {
throw ArgumentError("no analysis context found for path '$path'");
}
}
String get path {
return getPath(uri);
}
late final Uri uri;
late AnalysisContextCollection contexts;
final _resolvedAsts = <String, AnalysisResult>{};
Future<AnalysisResult> resolveUnitOrLibraryAt(Uri uri) async {
if (FileSystemEntity.isFileSync(
uri.toFilePath(windows: Platform.isWindows),
)) {
return resolveUnitAt(uri);
} else {
return resolveLibraryAt(uri);
}
}
Future<ResolvedLibraryResult> resolveLibraryAt(Uri uri) async {
assert(
FileSystemEntity.isDirectorySync(
uri.toFilePath(windows: Platform.isWindows),
),
);
for (final ctx in contexts.contexts) {
final path = getPath(uri);
if (_resolvedAsts.containsKey(path)) {
return _resolvedAsts[path]! as ResolvedLibraryResult;
}
final output = await ctx.currentSession.getResolvedLibrary(path)
as ResolvedLibraryResult;
return _resolvedAsts[path] = output;
}
throw ArgumentError("'uri' could not be resolved (contexts: "
"${contexts.contexts.map((c) => c.contextRoot.root.toUri()).join(", ")})");
}
Future<ResolvedUnitResult> resolveUnitAt(Uri uri) async {
assert(
FileSystemEntity.isFileSync(
uri.toFilePath(windows: Platform.isWindows),
),
);
for (final ctx in contexts.contexts) {
final path = getPath(uri);
if (_resolvedAsts.containsKey(path)) {
return _resolvedAsts[path]! as ResolvedUnitResult;
}
final output =
await ctx.currentSession.getResolvedUnit(path) as ResolvedUnitResult;
return _resolvedAsts[path] = output;
}
throw ArgumentError("'uri' could not be resolved (contexts: "
"${contexts.contexts.map((c) => c.contextRoot.root.toUri()).join(", ")})");
}
ClassDeclaration? getClassFromFile(String className, Uri fileUri) {
try {
return _getFileAstRoot(fileUri)
.declarations
.whereType<ClassDeclaration>()
.firstWhere((c) => c.name.value() == className);
} catch (e) {
if (e is StateError || e is TypeError || e is ArgumentError) {
return null;
}
rethrow;
}
}
List<ClassDeclaration> getSubclassesFromFile(
String superclassName,
Uri fileUri,
) {
return _getFileAstRoot(fileUri)
.declarations
.whereType<ClassDeclaration>()
.where((c) =>
c.extendsClause?.superclass.name2.toString() == superclassName)
.toList();
}
CompilationUnit _getFileAstRoot(Uri fileUri) {
assert(
FileSystemEntity.isFileSync(
fileUri.toFilePath(windows: Platform.isWindows),
),
);
try {
final path = getPath(fileUri);
if (_resolvedAsts.containsKey(path)) {
return (_resolvedAsts[path]! as ResolvedUnitResult).unit;
}
} finally {}
final unit = contexts.contextFor(path).currentSession.getParsedUnit(
normalize(
absolute(fileUri.toFilePath(windows: Platform.isWindows)),
),
) as ParsedUnitResult;
return unit.unit;
}
static String getPath(dynamic inputUri) {
return PhysicalResourceProvider.INSTANCE.pathContext.normalize(
PhysicalResourceProvider.INSTANCE.pathContext.fromUri(inputUri),
);
}
}

View file

@ -1,214 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io';
import 'dart:mirrors';
import 'package:protevus_runtime/runtime.dart';
import 'package:io/io.dart';
import 'package:package_config/package_config.dart';
class Build {
Build(this.context);
final BuildContext context;
Future execute() async {
final compilers = context.context.compilers;
print("Resolving ASTs...");
final astsToResolve = <Uri>{
...compilers.expand((c) => c.getUrisToResolve(context))
};
await Future.forEach<Uri>(
astsToResolve,
(astUri) async {
final package = await context.getPackageFromUri(astUri);
final Uri packageUri =
package?.packageUriRoot.resolve(package.name) ?? astUri;
return context.analyzer.resolveUnitOrLibraryAt(packageUri);
},
);
print("Generating runtime...");
final runtimeGenerator = RuntimeGenerator();
for (final MapEntry<String, dynamic> entry
in context.context.runtimes.map.entries) {
if (entry.value is SourceCompiler) {
await (entry.value as SourceCompiler).compile(context).then(
(source) =>
runtimeGenerator.addRuntime(name: entry.key, source: source),
);
}
}
await runtimeGenerator.writeTo(context.buildRuntimeDirectory.uri);
print("Generated runtime at '${context.buildRuntimeDirectory.uri}'.");
final nameOfPackageBeingCompiled = context.sourceApplicationPubspec.name;
final pubspecMap = <String, Object>{
'name': 'runtime_target',
'version': '1.0.0',
'environment': {'sdk': '>=3.4.0 <4.0.0'},
'dependency_overrides': {}
};
final overrides = pubspecMap['dependency_overrides'] as Map;
var sourcePackageIsCompiled = false;
for (final compiler in compilers) {
final packageInfo = await _getPackageInfoForCompiler(compiler);
final sourceDirUri = packageInfo.root;
final targetDirUri =
context.buildPackagesDirectory.uri.resolve("${packageInfo.name}/");
print("Compiling package '${packageInfo.name}'...");
await copyPackage(sourceDirUri, targetDirUri);
compiler.deflectPackage(Directory.fromUri(targetDirUri));
if (packageInfo.name != nameOfPackageBeingCompiled) {
overrides[packageInfo.name] = {
"path": targetDirUri.toFilePath(windows: Platform.isWindows)
};
} else {
sourcePackageIsCompiled = true;
}
print("Package '${packageInfo.name}' compiled to '$targetDirUri'.");
}
final appDst = context.buildApplicationDirectory.uri;
if (!sourcePackageIsCompiled) {
print(
"Copying application package (from '${context.sourceApplicationDirectory.uri}')...",
);
await copyPackage(context.sourceApplicationDirectory.uri, appDst);
print("Application packaged copied to '$appDst'.");
}
pubspecMap['dependencies'] = {
nameOfPackageBeingCompiled: {
"path": appDst.toFilePath(windows: Platform.isWindows)
}
};
if (context.forTests) {
final devDeps = context.sourceApplicationPubspecMap['dev_dependencies'];
if (devDeps != null) {
pubspecMap['dev_dependencies'] = devDeps;
}
overrides['conduit_core'] = {
'path': appDst.toFilePath(windows: Platform.isWindows)
};
}
File.fromUri(context.buildDirectoryUri.resolve("pubspec.yaml"))
.writeAsStringSync(json.encode(pubspecMap));
context
.getFile(context.targetScriptFileUri)
.writeAsStringSync(context.source);
for (final compiler in context.context.compilers) {
compiler.didFinishPackageGeneration(context);
}
print("Fetching dependencies (--offline --no-precompile)...");
await getDependencies();
print("Finished fetching dependencies.");
if (!context.forTests) {
print("Compiling...");
await compile(context.targetScriptFileUri, context.executableUri);
print("Success. Executable is located at '${context.executableUri}'.");
}
}
Future getDependencies() async {
const String cmd = "dart";
final res = await Process.run(
cmd,
["pub", "get", "--offline", "--no-precompile"],
workingDirectory:
context.buildDirectoryUri.toFilePath(windows: Platform.isWindows),
runInShell: true,
);
if (res.exitCode != 0) {
print("${res.stdout}");
throw StateError(
"'pub get' failed with the following message: ${res.stderr}",
);
}
}
Future compile(Uri srcUri, Uri dstUri) async {
final res = await Process.run(
"dart",
[
"compile",
"exe",
...(context.environment?.entries.map((e) => "-D${e.key}=${e.value}") ??
[]),
"-v",
srcUri.toFilePath(windows: Platform.isWindows),
"-o",
dstUri.toFilePath(windows: Platform.isWindows)
],
workingDirectory: context.buildApplicationDirectory.uri
.toFilePath(windows: Platform.isWindows),
runInShell: true,
);
if (res.exitCode != 0) {
throw StateError(
"'dart2native' failed with the following message: ${res.stderr}",
);
}
print("${res.stdout}");
}
Future copyPackage(Uri srcUri, Uri dstUri) async {
final dstDir = Directory.fromUri(dstUri);
if (!dstDir.existsSync()) {
dstDir.createSync(recursive: true);
}
try {
await copyPath(
srcUri.toFilePath(windows: Platform.isWindows),
dstUri.toFilePath(windows: Platform.isWindows),
);
} on FileSystemException catch (e) {
if (Platform.isWindows) {
final File f = File(e.path!);
if (f.existsSync()) {
f.deleteSync();
}
File(e.path!).writeAsStringSync('dummy');
await copyPath(
srcUri.toFilePath(windows: Platform.isWindows),
dstUri.toFilePath(windows: Platform.isWindows),
);
} else {
rethrow;
}
}
return context.getFile(srcUri.resolve("pubspec.yaml")).copy(
dstUri
.resolve("pubspec.yaml")
.toFilePath(windows: Platform.isWindows),
);
}
Future<Package> _getPackageInfoForCompiler(Compiler compiler) async {
final compilerUri = reflect(compiler).type.location!.sourceUri;
return (await context.packageConfig)[compilerUri.pathSegments.first]!;
}
}

View file

@ -1,252 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:io';
import 'dart:mirrors';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:protevus_runtime/runtime.dart';
import 'package:package_config/package_config.dart';
import 'package:path/path.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:yaml/yaml.dart';
/// Configuration and context values used during [Build.execute].
class BuildContext {
BuildContext(
this.rootLibraryFileUri,
this.buildDirectoryUri,
this.executableUri,
this.source, {
this.environment,
this.forTests = false,
}) {
analyzer = CodeAnalyzer(sourceApplicationDirectory.uri);
}
factory BuildContext.fromMap(Map<String, dynamic> map) {
return BuildContext(
Uri.parse(map['rootLibraryFileUri']),
Uri.parse(map['buildDirectoryUri']),
Uri.parse(map['executableUri']),
map['source'],
environment: map['environment'],
forTests: map['forTests'] ?? false,
);
}
Map<String, dynamic> get safeMap => {
'rootLibraryFileUri': sourceLibraryFile.uri.toString(),
'buildDirectoryUri': buildDirectoryUri.toString(),
'source': source,
'executableUri': executableUri.toString(),
'environment': environment,
'forTests': forTests
};
late final CodeAnalyzer analyzer;
/// A [Uri] to the library file of the application to be compiled.
final Uri rootLibraryFileUri;
/// A [Uri] to the executable build product file.
final Uri executableUri;
/// A [Uri] to directory where build artifacts are stored during the build process.
final Uri buildDirectoryUri;
/// The source script for the executable.
final String source;
/// Whether dev dependencies of the application package are included in the dependencies of the compiled executable.
final bool forTests;
PackageConfig? _packageConfig;
final Map<String, String>? environment;
/// The [RuntimeContext] available during the build process.
MirrorContext get context => RuntimeContext.current as MirrorContext;
Uri get targetScriptFileUri => forTests
? getDirectory(buildDirectoryUri.resolve("test/"))
.uri
.resolve("main_test.dart")
: buildDirectoryUri.resolve("main.dart");
Pubspec get sourceApplicationPubspec => Pubspec.parse(
File.fromUri(sourceApplicationDirectory.uri.resolve("pubspec.yaml"))
.readAsStringSync(),
);
Map<dynamic, dynamic> get sourceApplicationPubspecMap => loadYaml(
File.fromUri(
sourceApplicationDirectory.uri.resolve("pubspec.yaml"),
).readAsStringSync(),
) as Map<dynamic, dynamic>;
/// The directory of the application being compiled.
Directory get sourceApplicationDirectory =>
getDirectory(rootLibraryFileUri.resolve("../"));
/// The library file of the application being compiled.
File get sourceLibraryFile => getFile(rootLibraryFileUri);
/// The directory where build artifacts are stored.
Directory get buildDirectory => getDirectory(buildDirectoryUri);
/// The generated runtime directory
Directory get buildRuntimeDirectory =>
getDirectory(buildDirectoryUri.resolve("generated_runtime/"));
/// Directory for compiled packages
Directory get buildPackagesDirectory =>
getDirectory(buildDirectoryUri.resolve("packages/"));
/// Directory for compiled application
Directory get buildApplicationDirectory => getDirectory(
buildPackagesDirectory.uri.resolve("${sourceApplicationPubspec.name}/"),
);
/// Gets dependency package location relative to [sourceApplicationDirectory].
Future<PackageConfig> get packageConfig async {
return _packageConfig ??=
(await findPackageConfig(sourceApplicationDirectory))!;
}
/// Returns a [Directory] at [uri], creates it recursively if it doesn't exist.
Directory getDirectory(Uri uri) {
final dir = Directory.fromUri(uri);
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
return dir;
}
/// Returns a [File] at [uri], creates all parent directories recursively if necessary.
File getFile(Uri uri) {
final file = File.fromUri(uri);
if (!file.parent.existsSync()) {
file.parent.createSync(recursive: true);
}
return file;
}
Future<Package?> getPackageFromUri(Uri? uri) async {
if (uri == null) {
return null;
}
if (uri.scheme == "package") {
final segments = uri.pathSegments;
return (await packageConfig)[segments.first]!;
} else if (!uri.isAbsolute) {
throw ArgumentError("'uri' must be absolute or a package URI");
}
return null;
}
Future<List<String>> getImportDirectives({
Uri? uri,
String? source,
bool alsoImportOriginalFile = false,
}) async {
if (uri != null && source != null) {
throw ArgumentError(
"either uri or source must be non-null, but not both",
);
}
if (uri == null && source == null) {
throw ArgumentError(
"either uri or source must be non-null, but not both",
);
}
if (alsoImportOriginalFile == true && uri == null) {
throw ArgumentError(
"flag 'alsoImportOriginalFile' may only be set if 'uri' is also set",
);
}
final Package? package = await getPackageFromUri(uri);
final String? trailingSegments = uri?.pathSegments.sublist(1).join('/');
final fileUri =
package?.packageUriRoot.resolve(trailingSegments ?? '') ?? uri;
final text = source ?? File.fromUri(fileUri!).readAsStringSync();
final importRegex = RegExp("import [\\'\\\"]([^\\'\\\"]*)[\\'\\\"];");
final imports = importRegex.allMatches(text).map((m) {
final importedUri = Uri.parse(m.group(1)!);
if (!importedUri.isAbsolute) {
final path = fileUri
?.resolve(importedUri.path)
.toFilePath(windows: Platform.isWindows);
return "import 'file:${absolute(path!)}';";
}
return text.substring(m.start, m.end);
}).toList();
if (alsoImportOriginalFile) {
imports.add("import '$uri';");
}
return imports;
}
Future<ClassDeclaration?> getClassDeclarationFromType(Type type) async {
final classMirror = reflectType(type);
Uri uri = classMirror.location!.sourceUri;
if (!classMirror.location!.sourceUri.isAbsolute) {
final Package? package = await getPackageFromUri(uri);
uri = package!.packageUriRoot;
}
return analyzer.getClassFromFile(
MirrorSystem.getName(classMirror.simpleName),
uri,
);
}
Future<FieldDeclaration?> _getField(ClassMirror type, String propertyName) {
return getClassDeclarationFromType(type.reflectedType).then((cd) {
try {
return cd!.members.firstWhere(
(m) => (m as FieldDeclaration)
.fields
.variables
.any((v) => v.name.value() == propertyName),
) as FieldDeclaration;
} catch (e) {
return null;
}
});
}
Future<List<Annotation>> getAnnotationsFromField(
Type type1,
String propertyName,
) async {
var type = reflectClass(type1);
FieldDeclaration? field = await _getField(type, propertyName);
while (field == null) {
type = type.superclass!;
if (type.reflectedType == Object) {
break;
}
field = await _getField(type, propertyName);
}
if (field == null) {
return [];
}
return field.metadata;
}
}

View file

@ -1,89 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:io';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:protevus_isolate/isolate.dart';
import 'package:protevus_runtime/runtime.dart';
import 'package:io/io.dart';
class BuildExecutable extends Executable {
BuildExecutable(Map<String, dynamic> message) : super(message) {
context = BuildContext.fromMap(message);
}
late final BuildContext context;
@override
Future execute() async {
final build = Build(context);
await build.execute();
}
}
class BuildManager {
/// Creates a new build manager to compile a non-mirrored build.
BuildManager(this.context);
final BuildContext context;
Uri get sourceDirectoryUri => context.sourceApplicationDirectory.uri;
Future build() async {
if (!context.buildDirectory.existsSync()) {
context.buildDirectory.createSync();
}
// Here is where we need to provide a temporary copy of the script file with the main function stripped;
// this is because when the RuntimeGenerator loads, it needs Mirror access to any declarations in this file
var scriptSource = context.source;
final strippedScriptFile = File.fromUri(context.targetScriptFileUri)
..writeAsStringSync(scriptSource);
final analyzer = CodeAnalyzer(strippedScriptFile.absolute.uri);
final analyzerContext = analyzer.contexts.contextFor(analyzer.path);
final parsedUnit = analyzerContext.currentSession
.getParsedUnit(analyzer.path) as ParsedUnitResult;
final mainFunctions = parsedUnit.unit.declarations
.whereType<FunctionDeclaration>()
.where((f) => f.name.value() == "main")
.toList();
for (final f in mainFunctions.reversed) {
scriptSource = scriptSource.replaceRange(f.offset, f.end, "");
}
strippedScriptFile.writeAsStringSync(scriptSource);
try {
await copyPath(
context.sourceApplicationDirectory.uri.resolve('test/not_tests').path,
context.buildDirectoryUri.resolve('not_tests').path);
} catch (_) {}
await IsolateExecutor.run(
BuildExecutable(context.safeMap),
packageConfigURI:
sourceDirectoryUri.resolve('.dart_tool/package_config.json'),
imports: [
"package:conduit_runtime/runtime.dart",
context.targetScriptFileUri.toString()
],
logHandler: (s) => print(s), //ignore: avoid_print
);
}
Future clean() async {
if (context.buildDirectory.existsSync()) {
context.buildDirectory.deleteSync(recursive: true);
}
}
}

View file

@ -1,40 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:io';
import 'package:protevus_runtime/runtime.dart';
abstract class Compiler {
/// Modifies a package on the filesystem in order to remove dart:mirrors from the package.
///
/// A copy of this compiler's package will be written to [destinationDirectory].
/// This method is overridden to modify the contents of that directory
/// to remove all uses of dart:mirrors.
///
/// Packages should export their [Compiler] in their main library file and only
/// import mirrors in files directly or transitively imported by the Compiler file.
/// This method should remove that export statement and therefore remove all transitive mirror imports.
void deflectPackage(Directory destinationDirectory);
/// Returns a map of runtime objects that can be used at runtime while running in mirrored mode.
Map<String, Object> compile(MirrorContext context);
void didFinishPackageGeneration(BuildContext context) {}
List<Uri> getUrisToResolve(BuildContext context) => [];
}
/// Runtimes that generate source code implement this method.
abstract class SourceCompiler {
/// The source code, including directives, that declare a class that is equivalent in behavior to this runtime.
Future<String> compile(BuildContext ctx) async {
throw UnimplementedError();
}
}

View file

@ -1,76 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_runtime/runtime.dart';
/// Contextual values used during runtime.
abstract class RuntimeContext {
/// The current [RuntimeContext] available to the executing application.
///
/// Is either a `MirrorContext` or a `GeneratedContext`,
/// depending on the execution type.
static final RuntimeContext current = instance;
/// The runtimes available to the executing application.
late RuntimeCollection runtimes;
/// Gets a runtime object for [type].
///
/// Callers typically invoke this method, passing their [runtimeType]
/// in order to retrieve their runtime object.
///
/// It is important to note that a runtime object must exist for every
/// class that extends a class that has a runtime. Use `MirrorContext.getSubclassesOf` when compiling.
///
/// In other words, if the type `Base` has a runtime and the type `Subclass` extends `Base`,
/// `Subclass` must also have a runtime. The runtime objects for both `Subclass` and `Base`
/// must be the same type.
dynamic operator [](Type type) => runtimes[type];
T coerce<T>(dynamic input);
}
class RuntimeCollection {
RuntimeCollection(this.map);
final Map<String, Object> map;
Iterable<Object> get iterable => map.values;
Object operator [](Type t) {
//todo: optimize by keeping a cache where keys are of type [Type] to avoid the
// expensive indexOf and substring calls in this method
final typeName = t.toString();
final r = map[typeName];
if (r != null) {
return r;
}
final genericIndex = typeName.indexOf("<");
if (genericIndex == -1) {
throw ArgumentError("Runtime not found for type '$t'.");
}
final genericTypeName = typeName.substring(0, genericIndex);
final out = map[genericTypeName];
if (out == null) {
throw ArgumentError("Runtime not found for type '$t'.");
}
return out;
}
}
/// Prevents a type from being compiled when it otherwise would be.
///
/// Annotate a type with the const instance of this type to prevent its
/// compilation.
class PreventCompilation {
const PreventCompilation();
}

View file

@ -1,20 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class TypeCoercionException implements Exception {
TypeCoercionException(this.expectedType, this.actualType);
final Type expectedType;
final Type actualType;
@override
String toString() {
return "input is not expected type '$expectedType' (input is '$actualType')";
}
}

View file

@ -1,123 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:async';
import 'dart:io';
const String _directiveToken = "___DIRECTIVES___";
const String _assignmentToken = "___ASSIGNMENTS___";
class RuntimeGenerator {
final _elements = <_RuntimeElement>[];
void addRuntime({required String name, required String source}) {
_elements.add(_RuntimeElement(name, source));
}
Future<void> writeTo(Uri directoryUri) async {
final dir = Directory.fromUri(directoryUri);
final libDir = Directory.fromUri(dir.uri.resolve("lib/"));
final srcDir = Directory.fromUri(libDir.uri.resolve("src/"));
if (!libDir.existsSync()) {
libDir.createSync(recursive: true);
}
if (!srcDir.existsSync()) {
srcDir.createSync(recursive: true);
}
final libraryFile =
File.fromUri(libDir.uri.resolve("generated_runtime.dart"));
await libraryFile.writeAsString(loaderSource);
final pubspecFile = File.fromUri(dir.uri.resolve("pubspec.yaml"));
await pubspecFile.writeAsString(pubspecSource);
await Future.forEach(_elements, (_RuntimeElement e) async {
final file = File.fromUri(srcDir.uri.resolveUri(e.relativeUri));
if (!file.parent.existsSync()) {
file.parent.createSync(recursive: true);
}
await file.writeAsString(e.source);
});
}
String get pubspecSource => """
name: generated_runtime
description: A runtime generated by package:conduit_runtime
version: 1.0.0
environment:
sdk: '>=3.4.0 <4.0.0'
""";
String get _loaderShell => """
import 'package:conduit_runtime/runtime.dart';
import 'package:conduit_runtime/slow_coerce.dart' as runtime_cast;
$_directiveToken
RuntimeContext instance = GeneratedContext._();
class GeneratedContext extends RuntimeContext {
GeneratedContext._() {
final map = <String, Object>{};
$_assignmentToken
runtimes = RuntimeCollection(map);
}
@override
T coerce<T>(dynamic input) {
return runtime_cast.cast<T>(input);
}
}
""";
String get loaderSource {
return _loaderShell
.replaceFirst(_directiveToken, _directives)
.replaceFirst(_assignmentToken, _assignments);
}
String get _directives {
final buf = StringBuffer();
for (final e in _elements) {
buf.writeln(
"import 'src/${e.relativeUri.toFilePath(windows: Platform.isWindows)}' as ${e.importAlias};",
);
}
return buf.toString();
}
String get _assignments {
final buf = StringBuffer();
for (final e in _elements) {
buf.writeln("map['${e.typeName}'] = ${e.importAlias}.instance;");
}
return buf.toString();
}
}
class _RuntimeElement {
_RuntimeElement(this.typeName, this.source);
final String typeName;
final String source;
Uri get relativeUri => Uri.file("${typeName.toLowerCase()}.dart");
String get importAlias {
return "g_${typeName.toLowerCase()}";
}
}

View file

@ -1,79 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:mirrors';
import 'package:protevus_runtime/runtime.dart';
Object runtimeCast(Object object, TypeMirror intoType) {
final exceptionToThrow =
TypeCoercionException(intoType.reflectedType, object.runtimeType);
try {
final objectType = reflect(object).type;
if (objectType.isAssignableTo(intoType)) {
return object;
}
if (intoType.isSubtypeOf(reflectType(List))) {
if (object is! List) {
throw exceptionToThrow;
}
final elementType = intoType.typeArguments.first;
final elements = object.map((e) => runtimeCast(e, elementType));
return (intoType as ClassMirror).newInstance(#from, [elements]).reflectee;
} else if (intoType.isSubtypeOf(reflectType(Map, [String, dynamic]))) {
if (object is! Map<String, dynamic>) {
throw exceptionToThrow;
}
final output = (intoType as ClassMirror)
.newInstance(Symbol.empty, []).reflectee as Map<String, dynamic>;
final valueType = intoType.typeArguments.last;
object.forEach((key, val) {
output[key] = runtimeCast(val, valueType);
});
return output;
}
} on TypeError {
throw exceptionToThrow;
} on TypeCoercionException {
throw exceptionToThrow;
}
throw exceptionToThrow;
}
bool isTypeFullyPrimitive(TypeMirror type) {
if (type == reflectType(dynamic)) {
return true;
}
if (type.isSubtypeOf(reflectType(List))) {
return isTypeFullyPrimitive(type.typeArguments.first);
} else if (type.isSubtypeOf(reflectType(Map))) {
return isTypeFullyPrimitive(type.typeArguments.first) &&
isTypeFullyPrimitive(type.typeArguments.last);
}
if (type.isSubtypeOf(reflectType(num))) {
return true;
}
if (type.isSubtypeOf(reflectType(String))) {
return true;
}
if (type.isSubtypeOf(reflectType(bool))) {
return true;
}
return false;
}

View file

@ -1,90 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:mirrors';
import 'package:protevus_runtime/runtime.dart';
RuntimeContext instance = MirrorContext._();
class MirrorContext extends RuntimeContext {
MirrorContext._() {
final m = <String, Object>{};
for (final c in compilers) {
final compiledRuntimes = c.compile(this);
if (m.keys.any((k) => compiledRuntimes.keys.contains(k))) {
final matching = m.keys.where((k) => compiledRuntimes.keys.contains(k));
throw StateError(
'Could not compile. Type conflict for the following types: ${matching.join(", ")}.',
);
}
m.addAll(compiledRuntimes);
}
runtimes = RuntimeCollection(m);
}
final List<ClassMirror> types = currentMirrorSystem()
.libraries
.values
.where((lib) => lib.uri.scheme == "package" || lib.uri.scheme == "file")
.expand((lib) => lib.declarations.values)
.whereType<ClassMirror>()
.where((cm) => firstMetadataOfType<PreventCompilation>(cm) == null)
.toList();
List<Compiler> get compilers {
return types
.where((b) => b.isSubclassOf(reflectClass(Compiler)) && !b.isAbstract)
.map((b) => b.newInstance(Symbol.empty, []).reflectee as Compiler)
.toList();
}
List<ClassMirror> getSubclassesOf(Type type) {
final mirror = reflectClass(type);
return types.where((decl) {
if (decl.isAbstract) {
return false;
}
if (!decl.isSubclassOf(mirror)) {
return false;
}
if (decl.hasReflectedType) {
if (decl.reflectedType == type) {
return false;
}
}
return true;
}).toList();
}
@override
T coerce<T>(dynamic input) {
try {
return input as T;
} catch (_) {
return runtimeCast(input, reflectType(T)) as T;
}
}
}
T? firstMetadataOfType<T>(DeclarationMirror dm, {TypeMirror? dynamicType}) {
final tMirror = dynamicType ?? reflectType(T);
try {
return dm.metadata
.firstWhere((im) => im.type.isSubtypeOf(tMirror))
.reflectee as T;
} on StateError {
return null;
}
}

View file

@ -1,147 +0,0 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
# protevus_typeforge
[![Build Status](https://travis-ci.org/conduit.dart/dart-codable.svg?branch=master)](https://travis-ci.org/conduit.dart/dart-codable)
A library for encoding and decoding dynamic data into Dart objects.
## Basic Usage
Data objects extend `Coding`:
```dart
class Person extends Coding {
String name;
@override
void decode(KeyedArchive object) {
// must call super
super.decode(object);
name = object.decode("name");
}
@override
void encode(KeyedArchive object) {
object.encode("name", name);
}
}
```
An object that extends `Coding` can be read from JSON:
```dart
final json = json.decode(...);
final archive = KeyedArchive.unarchive(json);
final person = Person()..decode(archive);
```
Objects that extend `Coding` may also be written to JSON:
```dart
final person = Person()..name = "Bob";
final archive = KeyedArchive.archive(person);
final json = json.encode(archive);
```
`Coding` objects can encode or decode other `Coding` objects, including lists of `Coding` objects and maps where `Coding` objects are values. You must provide a closure that instantiates the `Coding` object being decoded.
```dart
class Team extends Coding {
List<Person> members;
Person manager;
@override
void decode(KeyedArchive object) {
super.decode(object); // must call super
members = object.decodeObjects("members", () => Person());
manager = object.decodeObject("manager", () => Person());
}
@override
void encode(KeyedArchive object) {
object.encodeObject("manager", manager);
object.encodeObjects("members", members);
}
}
```
## Dynamic Type Casting
Types with primitive type arguments (e.g., `List<String>` or `Map<String, int>`) are a particular pain point when decoding. Override `castMap` in `Coding` to perform type coercion.
You must import `package:protevus_typeforge/cast.dart as cast` and prefix type names with `cast`.
```dart
import 'package:protevus_typeforge/cast.dart' as cast;
class Container extends Coding {
List<String> things;
@override
Map<String, cast.Cast<dynamic>> get castMap => {
"things": cast.List(cast.String)
};
@override
void decode(KeyedArchive object) {
super.decode(object);
things = object.decode("things");
}
@override
void encode(KeyedArchive object) {
object.encode("things", things);
}
}
```
## Document References
`Coding` objects may be referred to multiple times in a document without duplicating their structure. An object is referenced with the `$key` key.
For example, consider the following JSON:
```json
{
"components": {
"thing": {
"name": "The Thing"
}
},
"data": {
"$ref": "#/components/thing"
}
}
```
In the above, the decoded value of `data` inherits all properties from `/components/thing`:
```json
{
"$ref": "#/components/thing",
"name": "The Thing"
}
```
You may create references in your in-memory data structures through the `Coding.referenceURI`.
```dart
final person = Person()..referenceURI = Uri(path: "/teams/engineering/manager");
```
The above person is encoded as:
```json
{
"$ref": "#/teams/engineering/manager"
}
```
You may have cyclical references.
See the specification for [JSON Schema](http://json-schema.org) and the `$ref` keyword for more details.

View file

@ -1,29 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// The `cast` library provides a collection of utilities for type casting and conversion in Dart.
///
/// This library exports several modules that offer different casting functionalities:
/// - `base_cast.dart`: Contains base casting operations.
/// - `primitive_cast.dart`: Provides casting methods for primitive data types.
/// - `collection_cast.dart`: Offers casting utilities for collections.
/// - `special_cast.dart`: Includes casting operations for special data types.
/// - `utility_cast.dart`: Contains additional utility functions for casting.
/// - `constants.dart`: Defines constants used across the casting operations.
///
/// These modules collectively provide a comprehensive set of tools for handling
/// various type conversion scenarios in Dart applications.
library cast;
export 'src/base_cast.dart';
export 'src/primitive_cast.dart';
export 'src/collection_cast.dart';
export 'src/special_cast.dart';
export 'src/utility_cast.dart';
export 'src/constants.dart';

View file

@ -1,27 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// The `codable` library provides functionality for encoding and decoding objects.
///
/// This library exports several core components:
/// - `referenceable.dart`: Defines objects that can be referenced.
/// - `coding.dart`: Contains encoding and decoding interfaces.
/// - `keyed_archive.dart`: Implements a key-value storage for encoded objects.
/// - `list_archive.dart`: Implements a list-based storage for encoded objects.
/// - `reference_resolver.dart`: Handles resolving references within encoded data.
///
/// These components work together to provide a robust system for object serialization
/// and deserialization, supporting both simple and complex data structures.
library codable;
export 'src/referenceable.dart';
export 'src/coding.dart';
export 'src/keyed_archive.dart';
export 'src/list_archive.dart';
export 'src/reference_resolver.dart';

View file

@ -1,118 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:core' as core;
import 'dart:core' hide Map, String, int;
/// Represents an exception thrown when a cast operation fails.
///
/// This class is used to provide detailed information about the context
/// and reason for a failed cast operation.
///
/// [context] represents the location or scope where the cast failed.
/// [key] is an optional identifier for the specific element that failed to cast.
/// [message] provides additional details about the failure.
class FailedCast implements core.Exception {
dynamic context;
dynamic key;
core.String message;
FailedCast(this.context, this.key, this.message);
@override
core.String toString() {
if (key == null) {
return "Failed cast at $context: $message";
}
return "Failed cast at $context $key: $message";
}
}
/// An abstract class representing a type cast operation.
///
/// This class defines the structure for implementing type casting
/// from dynamic types to a specific type T.
///
/// The [cast] method is the public API for performing the cast,
/// while [safeCast] is the internal implementation that can be
/// overridden by subclasses to define specific casting behavior.
///
/// Usage:
/// ```dart
/// class MyCustomCast extends Cast<MyType> {
/// @override
/// MyType _cast(dynamic from, String context, dynamic key) {
/// // Custom casting logic here
/// }
/// }
/// ```
abstract class Cast<T> {
/// Constructs a new [Cast] instance.
///
/// This constructor is declared as `const` to allow for compile-time
/// constant instances of [Cast] subclasses. This can be beneficial for
/// performance and memory usage in certain scenarios.
const Cast();
/// Performs a safe cast operation from a dynamic type to type T.
///
/// This method wraps the [safeCast] method with additional error handling:
/// - If a [FailedCast] exception is thrown, it's rethrown as-is.
/// - For any other exception, it's caught and wrapped in a new [FailedCast] exception.
///
/// Parameters:
/// [from]: The value to be cast.
/// [context]: A string describing the context where the cast is performed.
/// [key]: An optional identifier for the specific element being cast.
///
/// Returns:
/// The cast value of type T.
///
/// Throws:
/// [FailedCast]: If the cast fails, either from [safeCast] or from wrapping another exception.
T _safeCast(dynamic from, core.String context, dynamic key) {
try {
return safeCast(from, context, key);
} on FailedCast {
rethrow;
} catch (e) {
throw FailedCast(context, key, e.toString());
}
}
/// Performs a safe cast operation from a dynamic type to type T.
///
/// This method is a convenience wrapper around [_safeCast] that provides
/// a default context of "toplevel" and a null key.
///
/// Parameters:
/// [from]: The value to be cast.
///
/// Returns:
/// The cast value of type T.
///
/// Throws:
/// [FailedCast]: If the cast operation fails.
T cast(dynamic from) => _safeCast(from, "toplevel", null);
/// Performs a safe cast operation from a dynamic type to type T.
///
/// This method should be implemented by subclasses to define the specific
/// casting behavior for the type T.
///
/// Parameters:
/// [from]: The value to be cast.
/// [context]: A string describing the context where the cast is performed.
/// [key]: An optional identifier for the specific element being cast.
///
/// Returns:
/// The cast value of type T.
///
/// Throws:
/// [FailedCast]: If the cast operation fails.
T safeCast(dynamic from, core.String context, dynamic key);
}

View file

@ -1,80 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:meta/meta.dart';
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
/// Abstract class representing a coding mechanism.
///
/// This class provides a framework for encoding and decoding objects.
/// It includes a [referenceURI] property and a [castMap] getter for type casting.
///
/// The [decode] method is used to populate the object's properties from a [KeyedArchive].
/// It must be called by subclasses, hence the @mustCallSuper annotation.
///
/// The [encode] method is abstract and must be implemented by subclasses to define
/// how the object should be encoded into a [KeyedArchive].
abstract class Coding {
/// The URI reference for this coding object.
///
/// This property holds a [Uri] that can be used as a reference or identifier
/// for the coded object. It may represent the location or source of the data,
/// or serve as a unique identifier within a larger system.
///
/// The [referenceURI] is typically set during decoding and can be accessed
/// or modified as needed. It may be null if no reference is available or required.
Uri? referenceURI;
/// A map of property names to their corresponding cast functions.
///
/// This getter returns a [Map] where the keys are strings representing
/// property names, and the values are [cast.Cast] functions for those properties.
/// The cast functions are used to convert decoded values to their appropriate types.
///
/// By default, this getter returns `null`, indicating that no custom casting
/// is required. Subclasses can override this getter to provide specific
/// casting behavior for their properties.
///
/// Returns `null` if no custom casting is needed, or a [Map] of property
/// names to cast functions if custom casting is required.
Map<String, cast.Cast<dynamic>>? get castMap => null;
/// Decodes the object from a [KeyedArchive].
///
/// This method is responsible for populating the object's properties from the
/// provided [KeyedArchive]. It performs two main actions:
///
/// 1. Sets the [referenceURI] of this object to the [referenceURI] of the
/// provided [KeyedArchive].
/// 2. Applies any necessary type casting to the values in the [KeyedArchive]
/// using the [castMap] defined for this object.
///
/// This method is marked with [@mustCallSuper], indicating that subclasses
/// overriding this method must call the superclass implementation.
///
/// [object] The [KeyedArchive] containing the encoded data to be decoded.
@mustCallSuper
void decode(KeyedArchive object) {
referenceURI = object.referenceURI;
object.castValues(castMap);
}
/// Encodes the object into a [KeyedArchive].
///
/// This abstract method must be implemented by subclasses to define
/// how the object should be encoded into a [KeyedArchive]. The implementation
/// should write all relevant properties of the object to the provided [object].
///
/// [object] The [KeyedArchive] to which the object's data should be encoded.
///
/// Note that the [referenceURI] of the object is not automatically written
/// to the [KeyedArchive]. See note in [KeyedArchive._encodedObject].
void encode(KeyedArchive object);
}

View file

@ -1,129 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:core' as core;
import 'dart:core' hide Map, String, int;
import 'package:protevus_typeforge/cast.dart';
/// A cast operation for converting dynamic values to [core.Map<K, V>].
///
/// This class extends [Cast<core.Map<K, V>>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.Map<K, V>].
///
/// The class uses two separate [Cast] instances:
/// - [_key] for casting the keys of the input map to type K
/// - [_value] for casting the values of the input map to type V
///
/// The [safeCast] method checks if the input [from] is already a [core.Map].
/// If it is, it creates a new map, casting each key-value pair using the
/// respective [_key] and [_value] casts. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final mapCast = Map(StringCast(), IntCast());
/// final result = mapCast.cast({"a": 1, "b": 2}); // Returns Map<String, int>
/// mapCast.cast("not a map"); // Throws FailedCast
/// ```
class Map<K, V> extends Cast<core.Map<K, V>> {
final Cast<K> _key;
final Cast<V> _value;
const Map(Cast<K> key, Cast<V> value)
: _key = key,
_value = value;
@override
core.Map<K, V> safeCast(dynamic from, core.String context, dynamic key) {
if (from is core.Map) {
final result = <K, V>{};
for (final key in from.keys) {
final newKey = _key.safeCast(key, "map entry", key);
result[newKey] = _value.safeCast(from[key], "map entry", key);
}
return result;
}
return throw FailedCast(context, key, "not a map");
}
}
/// A cast operation for converting dynamic values to [core.Map<core.String, V>].
///
/// This class extends [Cast<core.Map<core.String, V>>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.Map<core.String, V>].
///
/// The class uses a [Cast<V>] instance [_value] for casting the values of the input map to type V.
///
/// The [safeCast] method checks if the input [from] is already a [core.Map].
/// If it is, it creates a new map with [core.String] keys and values of type V,
/// casting each value using the [_value] cast. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final stringMapCast = StringMap(IntCast());
/// final result = stringMapCast.cast({"a": 1, "b": 2}); // Returns Map<String, int>
/// stringMapCast.cast("not a map"); // Throws FailedCast
/// ```
class StringMap<V> extends Cast<core.Map<core.String, V>> {
final Cast<V> _value;
const StringMap(Cast<V> value) : _value = value;
@override
core.Map<core.String, V> safeCast(
dynamic from,
core.String context,
dynamic key,
) {
if (from is core.Map) {
final result = <core.String, V>{};
for (final core.String key in from.keys as core.Iterable<core.String>) {
result[key] = _value.safeCast(from[key], "map entry", key);
}
return result;
}
return throw FailedCast(context, key, "not a map");
}
}
/// A cast operation for converting dynamic values to [core.List<E?>].
///
/// This class extends [Cast<core.List<E?>>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.List<E?>].
///
/// The class uses a [Cast<E>] instance [_entry] for casting each element of the input list to type E.
///
/// The [safeCast] method checks if the input [from] is already a [core.List].
/// If it is, it creates a new list of nullable E elements, casting each non-null
/// element using the [_entry] cast and preserving null values. If not, it throws
/// a [FailedCast] exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final listCast = List(IntCast());
/// final result = listCast.cast([1, 2, null, 3]); // Returns List<int?>
/// listCast.cast("not a list"); // Throws FailedCast
/// ```
class List<E> extends Cast<core.List<E?>> {
final Cast<E> _entry;
const List(Cast<E> entry) : _entry = entry;
@override
core.List<E?> safeCast(dynamic from, core.String context, dynamic key) {
if (from is core.List) {
final length = from.length;
final result = core.List<E?>.filled(length, null);
for (core.int i = 0; i < length; ++i) {
if (from[i] != null) {
result[i] = _entry.safeCast(from[i], "list entry", i);
} else {
result[i] = null;
}
}
return result;
}
return throw FailedCast(context, key, "not a list");
}
}

View file

@ -1,74 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/cast.dart';
/// A constant instance of [AnyCast] that can be used for casting any dynamic value.
///
/// This constant provides a convenient way to use the [AnyCast] functionality
/// without needing to create a new instance each time. It can be used in situations
/// where type-checking is not required, and you want to allow any type to pass through.
///
/// Example usage:
/// ```dart
/// final result = any.cast(someValue); // Returns someValue unchanged, regardless of its type
/// ```
const any = AnyCast();
/// A constant instance of [BoolCast] that can be used for casting dynamic values to [core.bool].
///
/// This constant provides a convenient way to use the [BoolCast] functionality
/// without needing to create a new instance each time. It can be used to perform
/// boolean type checking and casting operations.
///
/// Example usage:
/// ```dart
/// final result = bool.cast(true); // Returns true
/// bool.cast("not a bool"); // Throws FailedCast
/// ```
const bool = BoolCast();
/// A constant instance of [IntCast] that can be used for casting dynamic values to [core.int].
///
/// This constant provides a convenient way to use the [IntCast] functionality
/// without needing to create a new instance each time. It can be used to perform
/// integer type checking and casting operations.
///
/// Example usage:
/// ```dart
/// final result = int.cast(42); // Returns 42
/// int.cast("not an int"); // Throws FailedCast
/// ```
const int = IntCast();
/// A constant instance of [DoubleCast] that can be used for casting dynamic values to [core.double].
///
/// This constant provides a convenient way to use the [DoubleCast] functionality
/// without needing to create a new instance each time. It can be used to perform
/// double type checking and casting operations.
///
/// Example usage:
/// ```dart
/// final result = double.cast(3.14); // Returns 3.14
/// double.cast("not a double"); // Throws FailedCast
/// ```
const double = DoubleCast();
/// A constant instance of [StringCast] that can be used for casting dynamic values to [core.String].
///
/// This constant provides a convenient way to use the [StringCast] functionality
/// without needing to create a new instance each time. It can be used to perform
/// string type checking and casting operations.
///
/// Example usage:
/// ```dart
/// final result = string.cast("Hello"); // Returns "Hello"
/// string.cast(42); // Throws FailedCast
/// ```
const string = StringCast();

View file

@ -1,580 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:collection';
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
/// A container for a dynamic data object that can be decoded into [Coding] objects.
///
/// A [KeyedArchive] is a [Map], but it provides additional behavior for decoding [Coding] objects
/// and managing JSON Schema references ($ref) through methods like [decode], [decodeObject], etc.
///
/// You create a [KeyedArchive] by invoking [KeyedArchive.unarchive] and passing data decoded from a
/// serialization format like JSON and YAML. A [KeyedArchive] is then provided as an argument to
/// a [Coding] subclass' [Coding.decode] method.
///
/// final json = json.decode(...);
/// final archive = KeyedArchive.unarchive(json);
/// final person = Person()..decode(archive);
///
/// You may also create [KeyedArchive]s from [Coding] objects so that they can be serialized.
///
/// final person = Person()..name = "Bob";
/// final archive = KeyedArchive.archive(person);
/// final json = json.encode(archive);
///
/// This class extends [Object] and mixes in [MapBase<String, dynamic>], allowing it to be used as a Map.
/// It also implements [Referenceable], providing functionality for handling references within the archive.
///
/// The constructor is not typically used directly; instead, use the [KeyedArchive.unarchive]
/// or [KeyedArchive.archive] methods to create instances of [KeyedArchive].
class KeyedArchive extends Object
with MapBase<String, dynamic>
implements Referenceable {
/// Use [unarchive] instead.
KeyedArchive(this._map) {
_recode();
}
/// Unarchives [data] into a [KeyedArchive] that can be used by [Coding.decode] to deserialize objects.
///
/// Each [Map] in [data] (including [data] itself) is converted to a [KeyedArchive].
/// Each [List] in [data] is converted to a [ListArchive]. These conversions occur for deeply nested maps
/// and lists.
///
/// If [allowReferences] is true, JSON Schema references will be traversed and decoded objects
/// will contain values from the referenced object. This flag defaults to false.
KeyedArchive.unarchive(this._map, {bool allowReferences = false}) {
_recode();
if (allowReferences) {
resolveOrThrow(ReferenceResolver(this));
}
}
/// Archives a [Coding] object into a [Map] that can be serialized into formats like JSON or YAML.
///
/// Note that the return value of this method, as well as all other [Map] and [List] objects
/// embedded in the return value, are instances of [KeyedArchive] and [ListArchive]. These types
/// implement [Map] and [List], respectively.
///
/// If [allowReferences] is true, JSON Schema references in the emitted document will be validated.
/// Defaults to false.
static Map<String, dynamic> archive(
Coding root, {
bool allowReferences = false,
}) {
final archive = KeyedArchive({});
root.encode(archive);
if (allowReferences) {
archive.resolveOrThrow(ReferenceResolver(archive));
}
return archive.toPrimitive();
}
/// Private constructor that creates an empty [KeyedArchive].
///
/// This constructor initializes the internal [_map] with an empty Map<String, dynamic>.
/// It's intended for internal use within the [KeyedArchive] class.
KeyedArchive._empty() : _map = <String, dynamic>{};
/// A reference to another object in the same document.
///
/// This property represents a URI reference to another object within the same document.
/// It is used to establish relationships between objects in a hierarchical structure.
///
/// Assign values to this property using the default [Uri] constructor and its path argument.
/// This property is serialized as a [Uri] fragment, e.g. `#/components/all`.
///
/// Example:
///
/// final object = new MyObject()
/// ..referenceURI = Uri(path: "/other/object");
/// archive.encodeObject("object", object);
///
Uri? referenceURI;
/// The internal map that stores the key-value pairs of this [KeyedArchive].
///
/// This map is used to store the actual data of the archive. It is of type
/// `Map<String, dynamic>` to allow for flexibility in the types of values
/// that can be stored. The keys are always strings, representing the names
/// of the properties, while the values can be of any type.
///
/// This map is manipulated by various methods of the [KeyedArchive] class,
/// such as the [] operator, decode methods, and encode methods. It's also
/// used when converting the archive to primitive types or when resolving
/// references.
Map<String, dynamic> _map;
/// Stores the inflated (decoded) object associated with this archive.
///
/// This property is used to cache the decoded object after it has been
/// inflated from the archive data. It allows for efficient retrieval
/// of the decoded object in subsequent accesses, avoiding repeated
/// decoding operations.
///
/// The type is [Coding?] to accommodate both null values (when no object
/// has been inflated yet) and any object that implements the [Coding] interface.
Coding? _inflated;
/// A reference to another [KeyedArchive] object.
///
/// This property is used to handle JSON Schema references ($ref).
/// When a reference is resolved, this property holds the referenced [KeyedArchive] object.
/// It allows the current archive to access values from the referenced object
/// when a key is not found in the current archive's map.
KeyedArchive? _objectReference;
/// Typecast the values in this archive.
///
/// Prefer to override [Coding.castMap] instead of using this method directly.
///
/// This method will recursively type values in this archive to the desired type
/// for a given key. Use this method (or [Coding.castMap]) for decoding `List` and `Map`
/// types, where the values are not `Coding` objects.
///
/// You must `import 'package:codable/cast.dart' as cast;`.
///
/// Usage:
///
/// final dynamicObject = {
/// "key": <dynamic>["foo", "bar"]
/// };
/// final archive = KeyedArchive.unarchive(dynamicObject);
/// archive.castValues({
/// "key": cast.List(cast.String)
/// });
///
/// // This now becomes a valid assignment
/// List<String> key = archive.decode("key");
///
/// This method takes a [schema] parameter of type `Map<String, cast.Cast>?`, which defines
/// the types to cast for each key in the archive. If [schema] is null, the method returns
/// without performing any casting. The method uses a flag [_casted] to ensure it only
/// performs the casting once. It creates a [cast.Keyed] object with the provided schema
/// and uses it to cast the values in both the main [_map] and the [_objectReference] map
/// (if it exists). This ensures type safety and consistency across the entire archive structure.
void castValues(Map<String, cast.Cast>? schema) {
if (schema == null) {
return;
}
if (_casted) return;
_casted = true;
final caster = cast.Keyed(schema);
_map = caster.cast(_map);
if (_objectReference != null) {
_objectReference!._map = caster.cast(_objectReference!._map);
}
}
/// A flag indicating whether the values in this archive have been cast.
///
/// This boolean is used to ensure that the [castValues] method is only
/// called once on this archive. It is set to true after the first call
/// to [castValues], preventing redundant type casting operations.
bool _casted = false;
/// Sets the value associated with the given [key] in this [KeyedArchive].
///
/// This operator allows you to assign values to keys in the archive as if it were a regular map.
/// The [key] must be a [String], and [value] can be of any type.
///
/// Example:
/// archive['name'] = 'John Doe';
/// archive['age'] = 30;
///
/// Note that this method directly modifies the internal [_map] of the archive.
/// It does not perform any type checking or conversion on the [value].
@override
void operator []=(covariant String key, dynamic value) {
_map[key] = value;
}
/// Retrieves the value associated with the given [key] from this [KeyedArchive].
///
/// This operator allows you to access values in the archive as if it were a regular map.
/// The [key] must be a [String].
///
/// If the key is found in the current archive's map, its value is returned.
/// If not found and this archive has an [_objectReference], it attempts to retrieve
/// the value from the referenced object.
///
/// Example:
/// var name = archive['name'];
/// var age = archive['age'];
///
/// Returns the value associated with [key], or null if the key is not found.
@override
dynamic operator [](covariant Object key) => _getValue(key as String);
/// Returns an [Iterable] of all the keys in the archive.
///
/// This getter provides access to all the keys stored in the internal [_map]
/// of the [KeyedArchive]. It allows iteration over all keys without exposing
/// the underlying map structure.
///
/// Returns: An [Iterable<String>] containing all the keys in the archive.
@override
Iterable<String> get keys => _map.keys;
/// Removes all entries from this [KeyedArchive].
///
/// After this call, the archive will be empty.
/// This method directly calls the [clear] method on the internal [_map].
@override
void clear() => _map.clear();
/// Removes the entry for the given [key] from this [KeyedArchive] and returns its value.
///
/// This method removes the key-value pair associated with [key] from the internal map
/// of this [KeyedArchive]. If [key] was in the archive, its associated value is returned.
/// If [key] was not in the archive, null is returned.
///
/// The [key] should be a [String], as this is a [KeyedArchive]. However, the method
/// accepts [Object?] to comply with the [MapBase] interface it implements.
///
/// Returns the value associated with [key] before it was removed, or null if [key]
/// was not in the archive.
@override
dynamic remove(Object? key) => _map.remove(key);
/// Converts this [KeyedArchive] to a primitive [Map<String, dynamic>].
///
/// This method recursively converts the contents of the archive to primitive types:
/// - [KeyedArchive] instances are converted to [Map<String, dynamic>]
/// - [ListArchive] instances are converted to [List<dynamic>]
/// - Other values are left as-is
///
/// This is useful when you need to serialize the archive to a format like JSON
/// that doesn't support custom object types.
///
/// Returns a new [Map<String, dynamic>] containing the primitive representation
/// of this archive.
Map<String, dynamic> toPrimitive() {
final out = <String, dynamic>{};
_map.forEach((key, val) {
if (val is KeyedArchive) {
out[key] = val.toPrimitive();
} else if (val is ListArchive) {
out[key] = val.toPrimitive();
} else {
out[key] = val;
}
});
return out;
}
/// Retrieves the value associated with the given [key] from this [KeyedArchive].
///
/// This method first checks if the key exists in the current archive's internal map.
/// If found, it returns the associated value.
/// If the key is not found in the current archive, and this archive has an [_objectReference],
/// it attempts to retrieve the value from the referenced object recursively.
///
/// Parameters:
/// [key] - The string key to look up in the archive.
///
/// Returns:
/// The value associated with the [key] if found, or null if the key is not present
/// in either the current archive or any referenced archives.
dynamic _getValue(String key) {
if (_map.containsKey(key)) {
return _map[key];
}
return _objectReference?._getValue(key);
}
/// Recodes the internal map of this [KeyedArchive].
///
/// This method performs the following operations:
/// 1. Creates a [cast.Map] caster for string keys and any values.
/// 2. Iterates through all keys in the internal map.
/// 3. For each key-value pair:
/// - If the value is a [Map], it's converted to a [KeyedArchive].
/// - If the value is a [List], it's converted to a [ListArchive].
/// - If the key is "$ref", it sets the [referenceURI] by parsing the value.
///
/// This method is called during initialization to ensure proper structure
/// and typing of the archive's contents.
void _recode() {
const caster = cast.Map(cast.string, cast.any);
final keys = _map.keys.toList();
for (final key in keys) {
final val = _map[key];
if (val is Map) {
_map[key] = KeyedArchive(caster.cast(val));
} else if (val is List) {
_map[key] = ListArchive.from(val);
} else if (key == r"$ref") {
referenceURI = Uri.parse(Uri.parse(val.toString()).fragment);
}
}
}
/// Validates and resolves references within this [KeyedArchive] and its nested objects.
///
/// This method is automatically invoked by both [KeyedArchive.unarchive] and [KeyedArchive.archive].
@override
void resolveOrThrow(ReferenceResolver coder) {
if (referenceURI != null) {
_objectReference = coder.resolve(referenceURI!);
if (_objectReference == null) {
throw ArgumentError(
"Invalid document. Reference '#${referenceURI!.path}' does not exist in document.",
);
}
}
_map.forEach((key, val) {
if (val is KeyedArchive) {
val.resolveOrThrow(coder);
} else if (val is ListArchive) {
val.resolveOrThrow(coder);
}
});
}
/// Decodes a [KeyedArchive] into an object of type [T] that extends [Coding].
///
/// This method is responsible for inflating (decoding) an object from its archived form.
/// If the [raw] archive is null, the method returns null.
///
/// If the archive has not been inflated before (i.e., [_inflated] is null),
/// it creates a new instance using the [inflate] function, decodes the archive
/// into this new instance, and caches it in [_inflated] for future use.
///
/// Parameters:
/// [raw]: The [KeyedArchive] containing the encoded object data.
/// [inflate]: A function that returns a new instance of [T].
///
/// Returns:
/// The decoded object of type [T], or null if [raw] is null.
T? _decodedObject<T extends Coding?>(
KeyedArchive? raw,
T Function() inflate,
) {
if (raw == null) {
return null;
}
if (raw._inflated == null) {
raw._inflated = inflate();
raw._inflated!.decode(raw);
}
return raw._inflated as T?;
}
/// Returns the object associated with [key] in this [KeyedArchive].
///
/// If [T] is inferred to be a [Uri] or [DateTime],
/// the associated object is assumed to be a [String] and an appropriate value is parsed
/// from that string.
///
/// If this object is a reference to another object (via [referenceURI]), this object's key-value
/// pairs will be searched first. If [key] is not found, the referenced object's key-values pairs are searched.
/// If no match is found, null is returned.
T? decode<T>(String key) {
final v = _getValue(key);
if (v == null) {
return null;
}
if (T == Uri) {
return Uri.parse(v.toString()) as T;
} else if (T == DateTime) {
return DateTime.parse(v.toString()) as T;
}
return v as T?;
}
/// Decodes and returns an instance of [T] associated with the given [key] in this [KeyedArchive].
///
/// [inflate] must create an empty instance of [T]. The value associated with [key]
/// must be a [KeyedArchive] (a [Map]). The values of the associated object are read into
/// the empty instance of [T].
T? decodeObject<T extends Coding>(String key, T Function() inflate) {
final val = _getValue(key);
if (val == null) {
return null;
}
if (val is! KeyedArchive) {
throw ArgumentError(
"Cannot decode key '$key' into '$T', because the value is not a Map. Actual value: '$val'.",
);
}
return _decodedObject(val, inflate);
}
/// Decodes and returns a list of objects of type [T] associated with the given [key] in this [KeyedArchive].
///
/// [inflate] must create an empty instance of [T]. The value associated with [key]
/// must be a [ListArchive] (a [List] of [Map]). For each element of the archived list,
/// [inflate] is invoked and each object in the archived list is decoded into
/// the instance of [T].
List<T?>? decodeObjects<T extends Coding>(String key, T? Function() inflate) {
final val = _getValue(key);
if (val == null) {
return null;
}
if (val is! List) {
throw ArgumentError(
"Cannot decode key '$key' as 'List<$T>', because value is not a List. Actual value: '$val'.",
);
}
return val
.map((v) => _decodedObject(v as KeyedArchive?, inflate))
.toList()
.cast<T?>();
}
/// Decodes and returns a map of objects of type [T] associated with the given [key] in this [KeyedArchive].
///
/// [inflate] must create an empty instance of [T]. The value associated with [key]
/// must be a [KeyedArchive] (a [Map]), where each value is a [T].
/// For each key-value pair of the archived map, [inflate] is invoked and
/// each value is decoded into the instance of [T].
Map<String, T?>? decodeObjectMap<T extends Coding>(
String key,
T Function() inflate,
) {
final v = _getValue(key);
if (v == null) {
return null;
}
if (v is! Map<String, dynamic>) {
throw ArgumentError(
"Cannot decode key '$key' as 'Map<String, $T>', because value is not a Map. Actual value: '$v'.",
);
}
return {
for (var k in v.keys) k: _decodedObject(v[k] as KeyedArchive?, inflate)
};
}
/// Encodes a [Coding] object into a [Map<String, dynamic>] representation.
///
/// This method creates a [KeyedArchive] from the given [object] and returns its
/// internal map representation. If the [object] has a [referenceURI], it is
/// encoded as a '$ref' key in the resulting map.
///
/// If [object] is null, this method returns null.
///
/// Note: There is a known limitation where overridden values from a reference
/// object are not currently being emitted. This is due to the complexity of
/// handling cyclic references between objects.
///
/// Parameters:
/// [object]: The [Coding] object to be encoded.
///
/// Returns:
/// A [Map<String, dynamic>] representation of the [object], or null if [object] is null.
Map<String, dynamic>? _encodedObject(Coding? object) {
if (object == null) {
return null;
}
final json = KeyedArchive._empty()
.._map = {}
..referenceURI = object.referenceURI;
if (json.referenceURI != null) {
json._map[r"$ref"] = Uri(fragment: json.referenceURI!.path).toString();
} else {
object.encode(json);
}
return json;
}
/// Encodes [value] into this object for [key].
///
/// This method adds a key-value pair to the internal map of the [KeyedArchive].
/// The [key] is always a [String], while [value] can be of any type.
///
/// If [value] is null, no value is encoded and the [key] will not be present
/// in the resulting archive.
void encode(String key, dynamic value) {
if (value == null) {
return;
}
if (value is DateTime) {
_map[key] = value.toIso8601String();
} else if (value is Uri) {
_map[key] = value.toString();
} else {
_map[key] = value;
}
}
/// Encodes a [Coding] object into this object for [key].
///
/// This method takes a [Coding] object [value] and encodes it into the archive
/// under the specified [key]. If [value] is null, no action is taken and the method returns early.
///
/// The encoding process involves:
/// 1. Checking if the [value] is null.
/// 2. If not null, it uses the private [_encodedObject] method to convert the [Coding] object
/// into a format suitable for storage in the archive.
/// 3. The encoded object is then stored in the archive's internal map ([_map]) using the provided [key].
///
/// This method is useful for adding complex objects that implement the [Coding] interface
/// to the archive, allowing for structured data storage and later retrieval.
///
/// Parameters:
/// [key]: A [String] that serves as the identifier for the encoded object in the archive.
/// [value]: A [Coding] object to be encoded and stored. Can be null.
///
/// Example:
/// ```dart
/// final person = Person(name: "John", age: 30);
/// archive.encodeObject("person", person);
/// ```
void encodeObject(String key, Coding? value) {
if (value == null) {
return;
}
_map[key] = _encodedObject(value);
}
/// Encodes a list of [Coding] objects into this archive for the given [key].
///
/// This invokes [Coding.encode] on each object in [value] and adds the list of objects
/// to this archive for the key [key].
void encodeObjects(String key, List<Coding?>? value) {
if (value == null) {
return;
}
_map[key] = ListArchive.from(value.map((v) => _encodedObject(v)).toList());
}
/// Encodes a map of [Coding] objects into this archive for the given [key].
///
/// This invokes [Coding.encode] on each value in [value] and adds the map of objects
/// to this archive for the key [key].
void encodeObjectMap<T extends Coding>(String key, Map<String, T?>? value) {
if (value == null) return;
final object = KeyedArchive({});
value.forEach((k, v) {
object[k] = _encodedObject(v);
});
_map[key] = object;
}
}

View file

@ -1,233 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:collection';
import 'package:protevus_typeforge/codable.dart';
/// A list of values in a [KeyedArchive].
///
/// This object is a [List] that has additional behavior for encoding and decoding [Coding] objects.
/// It provides functionality to store and manipulate a list of dynamic values, with special handling
/// for nested maps and lists. The class implements [Referenceable], allowing it to resolve references
/// within its contents when used in conjunction with a [ReferenceResolver].
///
/// The [ListArchive] can be created empty or initialized from an existing list. When initialized
/// from a list, it automatically converts nested maps and lists to [KeyedArchive] and [ListArchive]
/// instances respectively, providing a consistent interface for complex nested structures.
///
/// This class is particularly useful when working with serializable data structures that may
/// contain nested objects or arrays, as it preserves the structure while allowing for easy
/// manipulation and serialization.
class ListArchive extends Object
with ListBase<dynamic>
implements Referenceable {
/// The internal list that stores the dynamic values of this [ListArchive].
///
/// This list can contain various types of elements, including primitive types,
/// [KeyedArchive] instances (for nested maps), and other [ListArchive] instances
/// (for nested lists). It is used to maintain the structure and order of the
/// archived data while providing the necessary functionality for the [ListArchive].
final List<dynamic> _inner;
/// Creates an empty [ListArchive].
///
/// This constructor initializes a new [ListArchive] instance with an empty internal list.
/// The resulting [ListArchive] is ready to accept new elements through its various
/// list manipulation methods inherited from [ListBase].
ListArchive() : _inner = [];
/// Creates a [ListArchive] from an existing [List] of dynamic values.
///
/// This constructor takes a [List] of dynamic values as input and initializes
/// a new [ListArchive] instance. It processes each element of the input list,
/// converting any nested [Map] to [KeyedArchive] and nested [List] to [ListArchive].
/// This conversion is done using the [_toAtchiveType] function.
///
/// The resulting [ListArchive] maintains the structure of the original list
/// but with enhanced functionality for handling nested data structures.
///
/// Parameters:
/// [raw]: The input [List] of dynamic values to be converted into a [ListArchive].
///
/// Returns:
/// A new [ListArchive] instance containing the processed elements from the input list.
ListArchive.from(List<dynamic> raw)
: _inner = raw.map(_toAtchiveType).toList();
/// Returns the element at the specified [index] in the list.
///
/// This operator overrides the default list indexing behavior to access
/// elements in the internal [_inner] list.
///
/// Parameters:
/// [index]: An integer index of the element to retrieve.
///
/// Returns:
/// The element at the specified [index] in the list.
///
/// Throws:
/// [RangeError] if the [index] is out of bounds.
@override
dynamic operator [](int index) => _inner[index];
/// Returns the length of the internal list.
///
/// This getter overrides the [length] property from [ListBase] to provide
/// the correct length of the internal [_inner] list.
///
/// Returns:
/// An integer representing the number of elements in the [ListArchive].
@override
int get length => _inner.length;
/// Sets the length of the internal list.
///
/// This setter overrides the [length] property from [ListBase] to allow
/// modification of the internal [_inner] list's length. Setting the length
/// can be used to truncate the list or extend it with null values.
///
/// Parameters:
/// [length]: The new length to set for the list.
///
/// Throws:
/// [RangeError] if [length] is negative.
/// [UnsupportedError] if the list is fixed-length.
@override
set length(int length) {
_inner.length = length;
}
/// Sets the value at the specified [index] in the list.
///
/// This operator overrides the default list indexing assignment behavior to
/// modify elements in the internal [_inner] list.
///
/// Parameters:
/// [index]: An integer index of the element to set.
/// [val]: The new value to be assigned at the specified [index].
///
/// Throws:
/// [RangeError] if the [index] is out of bounds.
/// [UnsupportedError] if the list is fixed-length.
@override
void operator []=(int index, dynamic val) {
_inner[index] = val;
}
/// Adds a single element to the end of this list.
///
/// This method overrides the [add] method from [ListBase] to add an element
/// to the internal [_inner] list.
///
/// Parameters:
/// [element]: The element to be added to the list. Can be of any type.
///
/// The list grows by one element.
@override
void add(dynamic element) {
_inner.add(element);
}
/// Adds all elements of the given [iterable] to the end of this list.
///
/// This method overrides the [addAll] method from [ListBase] to add multiple
/// elements to the internal [_inner] list.
///
/// Parameters:
/// [iterable]: An [Iterable] of elements to be added to the list. The elements
/// can be of any type.
///
/// The list grows by the length of the [iterable].
@override
void addAll(Iterable<dynamic> iterable) {
_inner.addAll(iterable);
}
/// Converts the [ListArchive] to a list of primitive values.
///
/// This method traverses the [ListArchive] and converts its contents to a list
/// of primitive values. It recursively processes nested [KeyedArchive] and
/// [ListArchive] instances, ensuring that the entire structure is converted
/// to basic Dart types.
///
/// Returns:
/// A [List<dynamic>] containing the primitive representation of the [ListArchive].
/// - [KeyedArchive] instances are converted to [Map]s.
/// - [ListArchive] instances are converted to [List]s.
/// - Other values are left as-is.
///
/// This method is useful for serialization purposes or when you need to
/// convert the [ListArchive] to a format that can be easily serialized
/// or transmitted.
List<dynamic> toPrimitive() {
final out = [];
for (final val in _inner) {
if (val is KeyedArchive) {
out.add(val.toPrimitive());
} else if (val is ListArchive) {
out.add(val.toPrimitive());
} else {
out.add(val);
}
}
return out;
}
/// Resolves references within this [ListArchive] using the provided [ReferenceResolver].
///
/// This method iterates through all elements in the internal list ([_inner]) and
/// resolves references for nested [KeyedArchive] and [ListArchive] instances.
/// It's part of the [Referenceable] interface implementation, allowing for
/// deep resolution of references in complex nested structures.
///
/// Parameters:
/// [coder]: A [ReferenceResolver] used to resolve references within the archive.
///
/// Throws:
/// May throw exceptions if reference resolution fails, as implied by the method name.
///
/// This method is typically called during the decoding process to ensure all
/// references within the archive structure are properly resolved.
@override
void resolveOrThrow(ReferenceResolver coder) {
for (final i in _inner) {
if (i is KeyedArchive) {
i.resolveOrThrow(coder);
} else if (i is ListArchive) {
i.resolveOrThrow(coder);
}
}
}
}
/// Converts a dynamic value to an archive type if necessary.
///
/// This function takes a dynamic value and converts it to an appropriate archive type:
/// - If the input is a [Map<String, dynamic>], it's converted to a [KeyedArchive].
/// - If the input is a [List], it's converted to a [ListArchive].
/// - For all other types, the input is returned as-is.
///
/// This function is used internally by [ListArchive] to ensure that nested structures
/// (maps and lists) are properly converted to their respective archive types when
/// creating a new [ListArchive] instance.
///
/// Parameters:
/// [e]: The dynamic value to be converted.
///
/// Returns:
/// The input value converted to an appropriate archive type, or the original value
/// if no conversion is necessary.
dynamic _toAtchiveType(dynamic e) {
if (e is Map<String, dynamic>) {
return KeyedArchive(e);
} else if (e is List) {
return ListArchive.from(e);
}
return e;
}

View file

@ -1,108 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:core' as core;
import 'dart:core' hide Map, String, int;
import 'package:protevus_typeforge/cast.dart';
/// A cast operation for converting dynamic values to [core.int].
///
/// This class extends [Cast<core.int>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.int].
///
/// The [safeCast] method checks if the input [from] is already a [core.int].
/// If it is, it returns the value unchanged. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final intCast = IntCast();
/// final result = intCast.cast(42); // Returns 42
/// intCast.cast("not an int"); // Throws FailedCast
/// ```
class IntCast extends Cast<core.int> {
const IntCast();
@override
core.int safeCast(dynamic from, core.String context, dynamic key) =>
from is core.int
? from
: throw FailedCast(context, key, "$from is not an int");
}
/// A cast operation for converting dynamic values to [core.double].
///
/// This class extends [Cast<core.double>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.double].
///
/// The [safeCast] method checks if the input [from] is already a [core.double].
/// If it is, it returns the value unchanged. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final doubleCast = DoubleCast();
/// final result = doubleCast.cast(3.14); // Returns 3.14
/// doubleCast.cast("not a double"); // Throws FailedCast
/// ```
class DoubleCast extends Cast<core.double> {
const DoubleCast();
@override
core.double safeCast(dynamic from, core.String context, dynamic key) =>
from is core.double
? from
: throw FailedCast(context, key, "$from is not an double");
}
/// A cast operation for converting dynamic values to [core.String].
///
/// This class extends [Cast<core.String>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.String].
///
/// The [safeCast] method checks if the input [from] is already a [core.String].
/// If it is, it returns the value unchanged. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final stringCast = StringCast();
/// final result = stringCast.cast("Hello"); // Returns "Hello"
/// stringCast.cast(42); // Throws FailedCast
/// ```
class StringCast extends Cast<core.String> {
const StringCast();
@override
core.String safeCast(dynamic from, core.String context, dynamic key) =>
from is core.String
? from
: throw FailedCast(context, key, "$from is not a String");
}
/// A cast operation for converting dynamic values to [core.bool].
///
/// This class extends [Cast<core.bool>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.bool].
///
/// The [safeCast] method checks if the input [from] is already a [core.bool].
/// If it is, it returns the value unchanged. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final boolCast = BoolCast();
/// final result = boolCast.cast(true); // Returns true
/// boolCast.cast("not a bool"); // Throws FailedCast
/// ```
class BoolCast extends Cast<core.bool> {
const BoolCast();
@override
core.bool safeCast(dynamic from, core.String context, dynamic key) =>
from is core.bool
? from
: throw FailedCast(context, key, "$from is not a bool");
}

View file

@ -1,79 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
/// A class for resolving references within a document structure.
///
/// This class provides functionality to resolve references within a document
/// represented by a [KeyedArchive]. It allows for navigation through the
/// document structure using URI-style references.
///
/// The [ReferenceResolver] is particularly useful in scenarios where you need
/// to traverse complex, nested document structures and resolve references
/// to specific parts of the document.
///
/// Usage:
/// ```dart
/// final document = KeyedArchive(...); // Your document structure
/// final resolver = ReferenceResolver(document);
/// final resolved = resolver.resolve(Uri.parse('#/definitions/child'));
/// ```
///
/// The [resolve] method is the primary way to use this class. It takes a [Uri]
/// reference and returns the corresponding [KeyedArchive] from the document,
/// or null if the reference cannot be resolved.
class ReferenceResolver {
/// Creates a new [ReferenceResolver] instance.
///
/// The [ReferenceResolver] is used to resolve references within a document
/// structure represented by a [KeyedArchive].
///
/// Parameters:
/// [document] - The document to resolve references within. This
/// [KeyedArchive] represents the entire document structure that will be
/// used to resolve references.
ReferenceResolver(this.document);
/// The document to resolve references within.
///
/// This [KeyedArchive] represents the entire document structure
/// that will be used to resolve references.
final KeyedArchive document;
/// Resolves a reference URI to a [KeyedArchive] within the document.
///
/// This method takes a [Uri] [ref] and traverses the document structure
/// to find the corresponding [KeyedArchive]. It uses the path segments
/// of the URI to navigate through the nested structure of the document.
///
/// Parameters:
/// [ref] - A [Uri] representing the reference to resolve.
///
/// Returns:
/// A [KeyedArchive] corresponding to the resolved reference, or null
/// if the reference cannot be resolved within the document structure.
///
/// Example:
/// If [ref] is '#/definitions/child', this method will attempt to
/// navigate to document['definitions']['child'] and return the
/// corresponding [KeyedArchive].
KeyedArchive? resolve(Uri ref) {
final folded = ref.pathSegments.fold<KeyedArchive?>(document,
(KeyedArchive? objectPtr, pathSegment) {
if (objectPtr != null) {
return objectPtr[pathSegment] as KeyedArchive?;
} else {
return null;
}
});
return folded;
}
}

View file

@ -1,46 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_typeforge/codable.dart';
/// This abstract class serves as a contract for objects that need to be
/// resolved based on references. It defines a common interface for resolution
/// operations, allowing for consistent handling of referenceable objects
/// throughout the system.
///
/// Implementations of this class should ensure that:
/// - They provide a meaningful implementation of [resolveOrThrow].
/// - They handle potential errors during resolution and throw appropriate exceptions.
/// - They interact correctly with the provided [ReferenceResolver].
///
/// Example usage:
/// ```dart
/// class ConcreteReferenceable implements Referenceable {
/// @override
/// void resolveOrThrow(ReferenceResolver resolver) {
/// // Implementation of reference resolution
/// }
/// }
abstract class Referenceable {
/// Resolves the references within this object using the provided [resolver].
///
/// This method is responsible for resolving any references or dependencies
/// that this object might have. It should use the [resolver] to look up and
/// resolve these references.
///
/// If the resolution process encounters any errors or fails to resolve
/// necessary references, this method should throw an appropriate exception.
///
/// Parameters:
/// [resolver]: The [ReferenceResolver] instance to use for resolving references.
///
/// Throws:
/// An exception if the resolution process fails or encounters errors.
void resolveOrThrow(ReferenceResolver resolver);
}

View file

@ -1,109 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:async' as async;
import 'dart:core' as core;
import 'dart:core' hide Map, String, int;
import 'package:protevus_typeforge/cast.dart';
/// A cast operation that attempts to cast a dynamic value to either type S or type T.
///
/// This class extends [Cast<dynamic>] and provides a mechanism to attempt casting
/// to two different types in sequence. It first tries to cast to type S using the
/// [_left] cast, and if that fails, it attempts to cast to type T using the [_right] cast.
///
/// The [safeCast] method first attempts to use the [_left] cast. If it succeeds, the result
/// is returned. If it fails (by throwing a [FailedCast] exception), the method then
/// attempts to use the [_right] cast and returns its result.
///
/// This class is useful when you have a value that could be one of two different types
/// and you want to handle both cases.
///
/// Usage:
/// ```dart
/// final oneOfCast = OneOf(IntCast(), StringCast());
/// final resultInt = oneOfCast.cast(42); // Returns 42 as int
/// final resultString = oneOfCast.cast("hello"); // Returns "hello" as String
/// oneOfCast.cast(true); // Throws FailedCast
/// ```
class OneOf<S, T> extends Cast<dynamic> {
final Cast<S> _left;
final Cast<T> _right;
const OneOf(Cast<S> left, Cast<T> right)
: _left = left,
_right = right;
@override
dynamic safeCast(dynamic from, core.String context, dynamic key) {
try {
return _left.safeCast(from, context, key);
} on FailedCast {
return _right.safeCast(from, context, key);
}
}
}
/// A cast operation that applies a transformation function after casting to an intermediate type.
///
/// This class extends [Cast<T>] and combines two operations:
/// 1. Casting the input to type S using the [_first] cast operation.
/// 2. Applying a transformation function [_transform] to convert the result from S to T.
///
/// [S] is the intermediate type after the first cast.
/// [T] is the final type after applying the transformation.
///
/// The [safeCast] method first uses [_first] to cast the input to type S,
/// then applies [_transform] to convert the result to type T.
///
/// This class is useful when you need to perform a cast followed by a type conversion
/// or when you want to apply some transformation logic after casting.
///
/// Usage:
/// ```dart
/// final stringLengthCast = Apply<String, int>((s) => s.length, StringCast());
/// final result = stringLengthCast.cast("hello"); // Returns 5
/// ```
class Apply<S, T> extends Cast<T> {
final Cast<S> _first;
final T Function(S) _transform;
const Apply(T Function(S) transform, Cast<S> first)
: _transform = transform,
_first = first;
@override
T safeCast(dynamic from, core.String context, dynamic key) =>
_transform(_first.safeCast(from, context, key));
}
/// A cast operation for converting dynamic values to [async.Future<E>].
///
/// This class extends [Cast<async.Future<E>>] and implements the [safeCast] method
/// to perform type checking and conversion to [async.Future<E>].
///
/// The class uses a [Cast<E>] instance [_value] for casting the value inside the Future to type E.
///
/// The [safeCast] method checks if the input [from] is already an [async.Future].
/// If it is, it returns a new Future that applies the [_value] cast to the result of the original Future.
/// If not, it throws a [FailedCast] exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final futureCast = Future(IntCast());
/// final result = futureCast.cast(Future.value(42)); // Returns Future<int>
/// futureCast.cast("not a future"); // Throws FailedCast
/// ```
class Future<E> extends Cast<async.Future<E>> {
final Cast<E> _value;
const Future(Cast<E> value) : _value = value;
@override
async.Future<E> safeCast(dynamic from, core.String context, dynamic key) {
if (from is async.Future) {
return from.then(_value.cast);
}
return throw FailedCast(context, key, "not a Future");
}
}

View file

@ -1,77 +0,0 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:core' as core;
import 'dart:core' hide Map, String, int;
import 'package:protevus_typeforge/cast.dart';
/// A cast operation that accepts and returns any dynamic value without modification.
///
/// This class extends [Cast<dynamic>] and provides a no-op cast operation.
/// It's useful when you want to allow any type to pass through without
/// performing any type checking or transformation.
///
/// The [safeCast] method simply returns the input value as-is, regardless of its type.
///
/// Example usage:
/// ```dart
/// final anyCast = AnyCast();
/// final result = anyCast.cast(someValue); // Returns someValue unchanged
/// ```
class AnyCast extends Cast<dynamic> {
const AnyCast();
@override
dynamic safeCast(dynamic from, core.String context, dynamic key) => from;
}
/// A cast operation for converting dynamic values to [core.Map<K, V>] with specific key-value casts.
///
/// This class extends [Cast<core.Map<K, V>>] and implements the [safeCast] method
/// to perform type checking and conversion to [core.Map<K, V>] based on a predefined
/// map of key-specific casts.
///
/// The class uses a [core.Map<K, Cast<V>>] to define custom casts for specific keys.
/// Keys not present in this map will be cast as-is.
///
/// The [keys] getter provides access to the keys of the internal cast map.
///
/// The [safeCast] method checks if the input [from] is a [core.Map]. If it is,
/// it creates a new map, applying the specific casts for keys present in [_map]
/// and preserving other key-value pairs as-is. If not, it throws a [FailedCast]
/// exception with appropriate context information.
///
/// Usage:
/// ```dart
/// final keyedCast = Keyed<String, dynamic>({
/// 'age': IntCast(),
/// 'name': StringCast(),
/// });
/// final result = keyedCast.cast({'age': 30, 'name': 'John', 'city': 'New York'});
/// // Returns Map<String, dynamic> with 'age' as int, 'name' as String, and 'city' preserved as-is
/// ```
class Keyed<K, V> extends Cast<core.Map<K, V>> {
Iterable<K> get keys => _map.keys;
final core.Map<K, Cast<V>> _map;
const Keyed(core.Map<K, Cast<V>> map) : _map = map;
@override
core.Map<K, V> safeCast(dynamic from, core.String context, dynamic key) {
final core.Map<K, V> result = {};
if (from is core.Map) {
for (final K key in from.keys as core.Iterable<K>) {
if (_map.containsKey(key)) {
result[key] = _map[key]!.safeCast(from[key], "map entry", key);
} else {
result[key] = from[key] as V;
}
}
return result;
}
throw FailedCast(context, key, "not a map");
}
}

View file

@ -1,464 +0,0 @@
import 'dart:convert';
import 'package:protevus_typeforge/cast.dart' as cast;
import 'package:protevus_typeforge/codable.dart';
import 'package:test/test.dart';
void main() {
group("Primitive decode", () {
test("Can decode primitive type", () {
final archive = getJSONArchive({"key": 2});
final int? val = archive.decode("key");
expect(val, 2);
});
test("Can decode List<dynamic> type", () {
final archive = getJSONArchive({
"key": [1, "2"]
});
final List<dynamic>? l = archive.decode("key");
expect(l, [1, "2"]);
});
test("Can decode Map<String, dynamic>", () {
final archive = getJSONArchive({
"key": {"key": "val"}
});
final KeyedArchive? d = archive.decode("key");
expect(d, {"key": "val"});
});
test("Can decode URI", () {
final archive = getJSONArchive({"key": "https://host.com"});
final Uri? d = archive.decode("key");
expect(d!.host, "host.com");
});
test("Can decode DateTime", () {
final date = DateTime.now();
final archive = getJSONArchive({"key": date.toIso8601String()});
final DateTime? d = archive.decode("key");
expect(d!.isAtSameMomentAs(date), true);
});
test("If value is null, return null from decode", () {
final archive = getJSONArchive({"key": null});
final int? val = archive.decode("key");
expect(val, isNull);
});
test("If archive does not contain key, return null from decode", () {
final archive = getJSONArchive({});
final int? val = archive.decode("key");
expect(val, isNull);
});
});
group("Primitive map decode", () {
test("Can decode Map<String, String> from Map<String, dynamic>", () {
final archive = getJSONArchive({
"key": {"key": "val"}
});
archive.castValues({"key": const cast.Map(cast.string, cast.string)});
final Map<String, String>? d = archive.decode("key");
expect(d, {"key": "val"});
});
test("Can decode Map<String, List<String>>", () {
final archive = getJSONArchive({
"key": {
"key": ["val"]
}
});
archive.castValues(
{"key": const cast.Map(cast.string, cast.List(cast.string))},
);
final Map<String, List<String?>>? d = archive.decode("key");
expect(d, {
"key": ["val"]
});
});
test("Can decode Map<String, List<String?>> where elements are null", () {
final archive = getJSONArchive({
"key": {
"key": [null, null]
}
});
archive.castValues(
{"key": const cast.Map(cast.string, cast.List(cast.string))},
);
final Map<String, List<String?>>? d = archive.decode("key");
expect(d, {
"key": [null, null]
});
});
test("Can decode Map<String, Map<String, List<String?>>>", () {
final archive = getJSONArchive({
"key": {
"key": {
"key": ["val", null]
}
}
});
archive.castValues({
"key": const cast.Map(
cast.string,
cast.Map(cast.string, cast.List(cast.string)),
)
});
final Map<String, Map<String, List<String?>>>? d = archive.decode("key");
expect(d, {
"key": {
"key": ["val", null]
}
});
});
});
group("Primitive list decode", () {
test("Can decode List<String> from List<dynamic>", () {
final archive = getJSONArchive({
"key": ["val", null]
});
archive.castValues({"key": const cast.List(cast.string)});
final List<String?>? d = archive.decode("key");
expect(d, ["val", null]);
});
test("Can decode List<Map<String, List<String>>>", () {
final archive = getJSONArchive({
"key": [
{
"key": ["val", null]
},
null
]
});
archive.castValues({
"key": const cast.List(cast.Map(cast.string, cast.List(cast.string)))
});
final List<Map<String, List<String?>>?>? d = archive.decode("key");
expect(d, [
{
"key": ["val", null]
},
null
]);
});
});
group("Coding objects", () {
test("Can decode Coding object", () {
final archive = getJSONArchive({
"key": {"name": "Bob"}
});
final Parent p = archive.decodeObject("key", () => Parent())!;
expect(p.name, "Bob");
expect(p.child, isNull);
expect(p.children, isNull);
expect(p.childMap, isNull);
});
test("If coding object is paired with non-Map, an exception is thrown", () {
final archive = getJSONArchive({
"key": [
{"name": "Bob"}
]
});
try {
archive.decodeObject("key", () => Parent());
fail('unreachable');
} on ArgumentError {
// no action required
}
});
test("Can decode list of Coding objects", () {
final archive = getJSONArchive({
"key": [
{"name": "Bob"},
null,
{"name": "Sally"}
]
});
final List<Parent?>? p = archive.decodeObjects("key", () => Parent());
expect(p![0]!.name, "Bob");
expect(p[1], isNull);
expect(p[2]!.name, "Sally");
});
test(
"If coding object list is paired with non-List, an exception is thrown",
() {
final archive = getJSONArchive({
"key": {"name": "Bob"}
});
try {
archive.decodeObjects("key", () => Parent());
fail('unreachable');
} on ArgumentError {
// no op
}
});
test(
"If any element of coding list is not a coding object, an exception is thrown",
() {
final archive = getJSONArchive({
"key": [
{"name": "Bob"},
'foo'
]
});
try {
archive.decodeObjects("key", () => Parent());
fail('unreachable');
} on TypeError {
// no op
}
});
test("Can decode map of Coding objects", () {
final archive = getJSONArchive({
"key": {
"1": {"name": "Bob"},
"2": null
}
});
final map = archive.decodeObjectMap("key", () => Parent())!;
expect(map.length, 2);
expect(map["1"]!.name, "Bob");
expect(map["2"], isNull);
});
test("If coding object map is paired with non-Map, an exception is thrown",
() {
final archive = getJSONArchive({"key": []});
try {
archive.decodeObjectMap("key", () => Parent());
fail('unreachable');
} on ArgumentError {
// no op
}
});
test(
"If any element of coding map is not a coding object, an exception is thrown",
() {
final archive = getJSONArchive({
"key": {"1": "2"}
});
try {
archive.decodeObjectMap("key", () => Parent());
fail('unreachable');
} on TypeError {
// no op
}
});
});
group("Deep Coding objects", () {
test("Can decode single nested object", () {
final archive = getJSONArchive({
"key": {
"name": "Bob",
"child": {"name": "Sally"}
}
});
final o = archive.decodeObject("key", () => Parent())!;
expect(o.name, "Bob");
expect(o.child!.name, "Sally");
expect(o.childMap, isNull);
expect(o.children, isNull);
});
test("Can decode list of nested objects", () {
final archive = getJSONArchive({
"key": {
"name": "Bob",
"children": [
{"name": "Sally"}
]
}
});
final o = archive.decodeObject("key", () => Parent())!;
expect(o.name, "Bob");
expect(o.child, isNull);
expect(o.childMap, isNull);
expect(o.children!.length, 1);
expect(o.children?.first?.name, "Sally");
});
test("Can decode map of nested objects", () {
final archive = getJSONArchive({
"key": {
"name": "Bob",
"childMap": {
"sally": {"name": "Sally"}
}
}
});
final o = archive.decodeObject("key", () => Parent())!;
expect(o.name, "Bob");
expect(o.children, isNull);
expect(o.child, isNull);
expect(o.childMap!.length, 1);
expect(o.childMap!["sally"]!.name, "Sally");
});
});
group("Coding object references", () {
test("Parent can contain reference to child in single object decode", () {
final archive = getJSONArchive(
{
"child": {"name": "Sally"},
"parent": {
"name": "Bob",
"child": {"\$ref": "#/child"}
}
},
allowReferences: true,
);
final p = archive.decodeObject("parent", () => Parent())!;
expect(p.name, "Bob");
expect(p.child!.name, "Sally");
expect(p.child!.parent, isNull);
});
test(
"If reference doesn't exist, an error is thrown when creating document",
() {
try {
getJSONArchive(
{
"parent": {
"name": "Bob",
"child": {"\$ref": "#/child"}
}
},
allowReferences: true,
);
fail("unreachable");
} on ArgumentError catch (e) {
expect(e.toString(), contains("/child"));
}
});
test("Parent can contain reference to child in a list of objects", () {
final archive = getJSONArchive(
{
"child": {"name": "Sally"},
"parent": {
"name": "Bob",
"children": [
{"\$ref": "#/child"},
{"name": "fred"}
]
}
},
allowReferences: true,
);
final p = archive.decodeObject("parent", () => Parent())!;
expect(p.name, "Bob");
expect(p.children?.first?.name, "Sally");
expect(p.children?.last?.name, "fred");
});
test("Cyclical references are resolved", () {
final archive = getJSONArchive(
{
"child": {
"name": "Sally",
"parent": {"\$ref": "#/parent"}
},
"parent": {
"name": "Bob",
"children": [
{"\$ref": "#/child"},
{"name": "fred"}
]
}
},
allowReferences: true,
);
final p = archive.decodeObject("parent", () => Parent())!;
expect(p.name, "Bob");
expect(p.children?.first?.name, "Sally");
expect(p.children?.first?.parent!.name, "Bob");
expect(p.children?.last?.name, "fred");
expect(p.hashCode, isNot(p.children?.first?.parent.hashCode));
});
test("Can override castMap to coerce values", () {
final archive = getJSONArchive({
"key": {
"name": "Bob",
"things": ["value"]
}
});
final p = archive.decodeObject("key", () => Parent())!;
expect(p.things, ["value"]);
});
});
}
/// Strips type info from data
KeyedArchive getJSONArchive(dynamic data, {bool allowReferences = false}) {
return KeyedArchive.unarchive(
json.decode(json.encode(data)) as Map<String, dynamic>,
allowReferences: allowReferences,
);
}
class Parent extends Coding {
String? name;
Child? child;
List<Child?>? children;
Map<String, Child?>? childMap;
List<String?>? things;
@override
Map<String, cast.Cast<dynamic>> get castMap {
return {"things": const cast.List(cast.string)};
}
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
child = object.decodeObject("child", () => Child());
children = object.decodeObjects("children", () => Child());
childMap = object.decodeObjectMap("childMap", () => Child());
things = object.decode("things");
}
@override
void encode(KeyedArchive object) {}
}
class Child extends Coding {
String? name;
Parent? parent;
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
parent = object.decodeObject("parent", () => Parent());
}
@override
void encode(KeyedArchive object) {}
}

View file

@ -1,412 +0,0 @@
import 'dart:convert';
import 'package:protevus_typeforge/codable.dart';
import 'package:test/test.dart';
void main() {
group("Primitive encode", () {
test("Can encode primitive type", () {
final out = encode((obj) {
obj.encode("int", 1);
obj.encode("string", "1");
});
expect(out, {"int": 1, "string": "1"});
});
test("Can encode List<dynamic> type", () {
final out = encode((obj) {
obj.encode("key", [1, "2"]);
});
expect(out, {
"key": [1, "2"]
});
});
test("Can encode Map<String, dynamic>", () {
final out = encode((obj) {
obj.encode("key", {"1": 1, "2": "2"});
});
expect(out, {
"key": {"1": 1, "2": "2"}
});
});
test("Can encode URI", () {
final out = encode((obj) {
obj.encode("key", Uri.parse("https://host.com"));
});
expect(out, {"key": "https://host.com"});
});
test("Can encode DateTime", () {
final out = encode((obj) {
obj.encode("key", DateTime(2000));
});
expect(out, {"key": DateTime(2000).toIso8601String()});
});
test("If value is null, do not include key", () {
final out = encode((obj) {
obj.encode("key", null);
});
expect(out, {});
});
});
group("Coding objects", () {
test("Can encode Coding object", () {
final out = encode((object) {
object.encodeObject("key", Parent("Bob"));
});
expect(out, {
"key": {"name": "Bob"}
});
});
test("Can encode list of Coding objects", () {
final out = encode((object) {
object.encodeObject(
"key",
Parent("Bob", children: [Child("Fred"), null, Child("Sally")]),
);
});
expect(out, {
"key": {
"name": "Bob",
"children": [
{"name": "Fred"},
null,
{"name": "Sally"}
]
}
});
});
test("Can encode map of Coding objects", () {
final out = encode((object) {
object.encodeObject(
"key",
Parent(
"Bob",
childMap: {
"fred": Child("Fred"),
"null": null,
"sally": Child("Sally")
},
),
);
});
expect(out, {
"key": {
"name": "Bob",
"childMap": {
"fred": {"name": "Fred"},
"null": null,
"sally": {"name": "Sally"}
}
}
});
});
});
group("Coding object references", () {
test("Parent can contain reference to child in single object encode", () {
final container = Container(
Parent(
"Bob",
child: Child._()..referenceURI = Uri(path: "/definitions/child"),
),
{"child": Child("Sally")},
);
final out = KeyedArchive.archive(container, allowReferences: true);
expect(out, {
"definitions": {
"child": {"name": "Sally"}
},
"root": {
"name": "Bob",
"child": {"\$ref": "#/definitions/child"}
}
});
});
test(
"If reference doesn't exist, an error is thrown when creating document",
() {
final container = Container(
Parent(
"Bob",
child: Child._()..referenceURI = Uri(path: "/definitions/child"),
),
{},
);
try {
KeyedArchive.archive(container, allowReferences: true);
fail('unreachable');
} on ArgumentError catch (e) {
expect(e.toString(), contains("#/definitions/child"));
}
});
test(
"If reference doesn't exist in objectMap, an error is thrown when creating document",
() {
final container = Container(
Parent(
"Bob",
childMap: {
"c": Child._()..referenceURI = Uri(path: "/definitions/child")
},
),
{},
);
try {
KeyedArchive.archive(container, allowReferences: true);
fail('unreachable');
} on ArgumentError catch (e) {
expect(e.toString(), contains("#/definitions/child"));
}
});
test(
"If reference doesn't exist in objectList, an error is thrown when creating document",
() {
final container = Container(
Parent(
"Bob",
children: [Child._()..referenceURI = Uri(path: "/definitions/child")],
),
{},
);
try {
KeyedArchive.archive(container, allowReferences: true);
fail('unreachable');
} on ArgumentError catch (e) {
expect(e.toString(), contains("#/definitions/child"));
}
});
test("Parent can contain reference to child in a list of objects", () {
final container = Container(
Parent(
"Bob",
children: [
Child("Sally"),
Child._()..referenceURI = Uri(path: "/definitions/child")
],
),
{"child": Child("Fred")},
);
final out = KeyedArchive.archive(container, allowReferences: true);
expect(out, {
"definitions": {
"child": {"name": "Fred"}
},
"root": {
"name": "Bob",
"children": [
{"name": "Sally"},
{"\$ref": "#/definitions/child"}
]
}
});
});
test("Parent can contain reference to child in a map of objects", () {
final container = Container(
Parent(
"Bob",
childMap: {
"sally": Child("Sally"),
"ref": Child._()..referenceURI = Uri(path: "/definitions/child")
},
),
{"child": Child("Fred")},
);
final out = KeyedArchive.archive(container, allowReferences: true);
expect(out, {
"definitions": {
"child": {"name": "Fred"}
},
"root": {
"name": "Bob",
"childMap": {
"sally": {"name": "Sally"},
"ref": {"\$ref": "#/definitions/child"}
}
}
});
});
test("Cyclical references are resolved", () {
final container = Container(
Parent(
"Bob",
children: [
Child("Sally"),
Child._()..referenceURI = Uri(path: "/definitions/child")
],
),
{
"child": Child(
"Fred",
parent: Parent._()..referenceURI = Uri(path: "/root"),
)
});
final out = KeyedArchive.archive(container, allowReferences: true);
final expected = {
"definitions": {
"child": {
"name": "Fred",
"parent": {"\$ref": "#/root"}
}
},
"root": {
"name": "Bob",
"children": [
{"name": "Sally"},
{"\$ref": "#/definitions/child"},
]
}
};
expect(out, expected);
// we'll also ensure that writing it out and reading it back in
// works, to complete the lifecycle of a document. we are ensuring
// that no state is accumulated in decoding that impacts encoding
// and ensure that our data is valid json
final washedData = json.decode(json.encode(out)) as Map<String, dynamic>;
final doc = KeyedArchive.unarchive(washedData);
final decodedContainer = Container._()..decode(doc);
final reencodedArchive = KeyedArchive.archive(decodedContainer);
expect(reencodedArchive, expected);
});
});
test("toPrimitive does not include keyed archives or lists", () {
final archive = KeyedArchive.unarchive({
"value": "v",
"archive": {"key": "value"},
"list": [
"value",
{"key": "value"},
["value"]
]
});
final encoded = archive.toPrimitive();
expect(encoded["value"], "v");
expect(encoded["archive"] is Map<String, dynamic>, true);
expect(encoded["archive"] is KeyedArchive, false);
expect(encoded["list"] is List<dynamic>, true);
expect(encoded["list"] is ListArchive, false);
expect(encoded["list"][0], "value");
expect(encoded["list"][1] is Map<String, dynamic>, true);
expect(encoded["list"][1] is KeyedArchive, false);
expect(encoded["list"][2] is List<dynamic>, true);
expect(encoded["list"][2] is ListArchive, false);
});
}
Map<String, dynamic>? encode(void Function(KeyedArchive object) encoder) {
final archive = KeyedArchive({});
encoder(archive);
return json.decode(json.encode(archive)) as Map<String, dynamic>?;
}
class Container extends Coding {
Container(this.root, this.definitions);
Container._();
Parent? root;
Map<String, Coding?>? definitions;
@override
void decode(KeyedArchive object) {
super.decode(object);
root = object.decodeObject("root", () => Parent._());
definitions = object.decodeObjectMap("definitions", () => Child._());
}
@override
void encode(KeyedArchive object) {
object.encodeObject("root", root);
object.encodeObjectMap("definitions", definitions);
}
}
class Parent extends Coding {
Parent(this.name, {this.child, this.children, this.childMap, this.things});
Parent._();
String? name;
Child? child;
List<Child?>? children;
Map<String, Child?>? childMap;
List<String>? things;
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
child = object.decodeObject("child", () => Child._());
children = object.decodeObjects("children", () => Child._());
childMap = object.decodeObjectMap("childMap", () => Child._());
}
@override
void encode(KeyedArchive object) {
object.encode("name", name);
object.encodeObject("child", child);
object.encodeObjects("children", children);
object.encodeObjectMap("childMap", childMap);
object.encode("things", things);
}
}
class Child extends Coding {
Child(this.name, {this.parent});
Child._();
String? name;
Parent? parent;
@override
void decode(KeyedArchive object) {
super.decode(object);
name = object.decode("name");
parent = object.decodeObject("parent", () => Parent._());
}
@override
void encode(KeyedArchive object) {
object.encode("name", name);
object.encodeObject("parent", parent);
}
}