blob: 7f3e887930bea173d67ee2e692f7cb53d13920bd [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library discoveryapis_generator.client_schemas;
import '../dart_api_library.dart';
import '../dart_comments.dart';
import '../dart_schemas.dart';
import '../generated_googleapis/discovery/v1.dart';
import '../namer.dart';
import '../utils.dart';
/// Represents a named custom dart class with a number of properties.
class ClientObjectType extends ObjectType {
ClientObjectType(DartApiImports imports, Identifier name,
List<DartClassProperty> properties,
{Comment comment})
: super(imports, name, properties, comment: comment);
@override
String get classDefinition {
var superClassString = '';
if (superVariantType != null) {
superClassString = ' extends ${superVariantType.declaration} ';
}
final fromJsonString = StringBuffer();
fromJsonString
.writeln(' static $className fromJson(${imports.core}.Map _json) {');
fromJsonString.writeln(' var message = $className();');
for (var property in properties) {
// The super variant fromJson() will call this subclass constructor
// and the variant discriminator is final.
if (!isVariantDiscriminator(property)) {
final decodeString = property.type
.jsonDecode("_json['${escapeString(property.jsonName)}']");
fromJsonString.writeln(
" if (_json.containsKey('${escapeString(property.jsonName)}')) {",
);
fromJsonString
.writeln(' message.${property.name} = $decodeString;');
fromJsonString.writeln(' }');
}
}
fromJsonString.writeln(' return message;');
fromJsonString.writeln(' }');
final toJsonString = StringBuffer();
toJsonString
.writeln(' static ${imports.core}.Map toJson($className message) {');
toJsonString.writeln(' var _json = {};');
for (var property in properties) {
toJsonString.writeln(' if (message.${property.name} != null) {');
toJsonString
.writeln(" _json['${escapeString(property.jsonName)}'] = "
'${property.type.jsonEncode('message.${property.name}')};');
toJsonString.writeln(' }');
}
toJsonString.writeln(' return _json;');
toJsonString.write(' }');
return '''
${comment.asDartDoc(0)}class ${className}Factory $superClassString{
$fromJsonString
$toJsonString
}
''';
}
@override
String jsonEncode(String value) => '${className}Factory.toJson($value)';
@override
String jsonDecode(String json) => '${className}Factory.fromJson($json)';
}
/// Parses all schemas in [description] and returns a [DartSchemaTypeDB].
DartSchemaTypeDB parseSchemas(
DartApiImports imports, RestDescription description) {
final namer = imports.namer;
final db = DartSchemaTypeDB(imports);
/*
* Primitive types "integer"/"boolean"/"double"/"number"/"string":
* { "type": "boolean" ... }
*
* Any type:
* { "type" : "any" ... }
*
* Array types:
* { "type": "array", "items" : {"type": ...}, ... }
*
* Map types:
* {
* "type": "object",
* "additionalProperties" : {"type": ...},
* ...
* }
* => key is always String
*
* Forward references:
* { "$ref" : "NamedSchemaType" }
*
* Normal objects:
* {
* "type" : "object",
* "properties": {"prop1" : {"type" : ...}, ... },
* ...
* }
*
* Variant objects:
* {
* "type" : 'object",
* "variant": {
* "discriminant" : "type",
* "map": [
* { "type_value" : "type_a", "$ref" : "NamedSchemaType" },
* { "type_value" : "type_b", "$ref" : "NamedSchemaType" }
* ]
* }
* }
*/
DartSchemaType parse(String className, Scope classScope, JsonSchema schema) {
if (schema.repeated != null) {
throw ArgumentError('Only path/query parameters can be repeated.');
}
if (schema.type == 'object') {
final comment = Comment(schema.description);
if (schema.additionalProperties != null) {
final anonValueClassName = namer.schemaClassName('${className}Value');
final anonClassScope = namer.newClassScope();
final valueType = parse(
anonValueClassName, anonClassScope, schema.additionalProperties);
return db.register(UnnamedMapType(imports, db.stringType, valueType));
} else if (schema.variant != null) {
// This is a variant type, declaring the type discriminant field and all
// subclasses.
// This is currently unsupported for client stub generations.
throw UnimplementedError(
'The \'variant\' schema type is not supported for client stub '
'generation.');
} else {
// This is a normal named schema class, we generate a normal
// [ClientObjectType] for it with the defined properties.
final classId = namer.schemaClass(className);
final properties = <DartClassProperty>[];
if (schema.properties != null) {
orderedForEach(schema.properties,
(String jsonPName, JsonSchema value) {
final propertyName = classScope.newIdentifier(jsonPName);
final propertyClass =
namer.schemaClassName(jsonPName, parent: className);
final propertyClassScope = namer.newClassScope();
final propertyType =
parse(propertyClass, propertyClassScope, value);
var comment = Comment(value.description);
comment = extendEnumComment(comment, propertyType);
comment = extendAnyTypeComment(comment, propertyType);
Identifier byteArrayAccessor;
if (value.format == 'byte' && value.type == 'string') {
byteArrayAccessor =
classScope.newIdentifier('${jsonPName}AsBytes');
}
final property = DartClassProperty(
propertyName, comment, propertyType, jsonPName,
byteArrayAccessor: byteArrayAccessor);
properties.add(property);
});
}
return db.register(
ClientObjectType(imports, classId, properties, comment: comment));
}
} else if (schema.type == 'array') {
return db.register(UnnamedArrayType(
imports, parse(className, namer.newClassScope(), schema.items)));
} else if (schema.type == 'any') {
return db.anyType;
} else if (schema.P_ref != null) {
// This is a forward or backward reference, it will be resolved in
// another pass following the parsing.
return db.register(DartSchemaForwardRef(imports, schema.P_ref));
} else {
return parsePrimitive(imports, db, schema);
}
}
if (description.schemas != null) {
orderedForEach(description.schemas, (String name, JsonSchema schema) {
final className = namer.schemaClassName(name);
final classScope = namer.newClassScope();
db.registerTopLevel(name, parse(className, classScope, schema));
});
// Resolve all forward references and save list in [db.dartTypes].
db.dartTypes = db.dartTypes.map((type) => type.resolve(db)).toList();
// Build map of all top level dart schema classes which will be represented
// as named dart classes.
db.dartClassTypes.addAll(db.dartTypes
.where((type) => type.className != null)
.cast<ComplexDartSchemaType>());
}
return db;
}