Belongs to many will be difficult to implement...
This commit is contained in:
parent
0adb48ce95
commit
fa3ce3abc8
29 changed files with 896 additions and 92 deletions
.idea/runConfigurations
angel_orm_generator
lib/src/builder/postgres
test
belongs_to_many_test.dartbelongs_to_test.dartcommon.dart
models
author.dartauthor.g.dartauthor.orm.g.dartbook.dartbook.g.dartbook.orm.g.dartcar.dartcar.g.dartcar.orm.g.dartrole.dartrole.down.g.sqlrole.g.dartrole.orm.g.dartrole.up.g.sqluser.dartuser.down.g.sqluser.g.dartuser.orm.g.dartuser.up.g.sql
standalone_test.darttool
|
@ -1,6 +0,0 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="tests in book_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" folderName="book" singleton="true">
|
|
||||||
<option name="filePath" value="$PROJECT_DIR$/angel_orm_generator/test/book_test.dart" />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
|
@ -51,6 +51,9 @@ PostgresBuildContext buildContext(
|
||||||
case 'BelongsTo':
|
case 'BelongsTo':
|
||||||
type = RelationshipType.BELONGS_TO;
|
type = RelationshipType.BELONGS_TO;
|
||||||
break;
|
break;
|
||||||
|
case 'BelongsToMany':
|
||||||
|
type = RelationshipType.BELONGS_TO_MANY;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedError(
|
throw new UnsupportedError(
|
||||||
'Unsupported relationship type "${relationshipAnnotation.type.name}".');
|
'Unsupported relationship type "${relationshipAnnotation.type.name}".');
|
||||||
|
|
|
@ -9,7 +9,6 @@ import 'package:source_gen/src/utils.dart';
|
||||||
import 'build_context.dart';
|
import 'build_context.dart';
|
||||||
import 'postgres_build_context.dart';
|
import 'postgres_build_context.dart';
|
||||||
|
|
||||||
// TODO: HasOne, HasMany, BelongsTo
|
|
||||||
class SQLMigrationGenerator implements Builder {
|
class SQLMigrationGenerator implements Builder {
|
||||||
/// If `true` (default), then field names will automatically be (de)serialized as snake_case.
|
/// If `true` (default), then field names will automatically be (de)serialized as snake_case.
|
||||||
final bool autoSnakeCaseNames;
|
final bool autoSnakeCaseNames;
|
||||||
|
@ -96,25 +95,14 @@ class SQLMigrationGenerator implements Builder {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
ctx.relationshipFields.forEach((f) {
|
ctx.relationships.forEach((name, r) {
|
||||||
if (i++ > 0) buf.writeln(',');
|
var relationship = ctx.populateRelationship(name);
|
||||||
var typeName =
|
|
||||||
f.type.name.startsWith('_') ? f.type.name.substring(1) : f.type.name;
|
|
||||||
var rc = new ReCase(typeName);
|
|
||||||
var relationship = ctx.relationships[f.name];
|
|
||||||
|
|
||||||
if (relationship.type == RelationshipType.BELONGS_TO) {
|
if (relationship.isBelongsTo) {
|
||||||
var localKey = relationship.localKey ??
|
if (i++ > 0) buf.writeln(',');
|
||||||
(autoSnakeCaseNames != false
|
buf.write(
|
||||||
? '${rc.snakeCase}_id'
|
' "${relationship.localKey}" int REFERENCES ${relationship.foreignTable}(${relationship.foreignKey})');
|
||||||
: '${typeName}Id');
|
if (relationship.cascadeOnDelete != false && relationship.isSingular)
|
||||||
var foreignKey = relationship.foreignKey ?? 'id';
|
|
||||||
var foreignTable = relationship.foreignTable ??
|
|
||||||
(autoSnakeCaseNames != false
|
|
||||||
? pluralize(rc.snakeCase)
|
|
||||||
: pluralize(typeName));
|
|
||||||
buf.write(' "$localKey" int REFERENCES $foreignTable($foreignKey)');
|
|
||||||
if (relationship.cascadeOnDelete != false)
|
|
||||||
buf.write(' ON DELETE CASCADE');
|
buf.write(' ON DELETE CASCADE');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,7 @@ const Map<String, String> SORT_MODES = const {
|
||||||
'Ascending': 'ASC'
|
'Ascending': 'ASC'
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: HasOne, HasMany, BelongsTo
|
// TODO: HasOne, HasMany
|
||||||
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
/// If "true" (default), then field names will automatically be (de)serialized as snake_case.
|
/// If "true" (default), then field names will automatically be (de)serialized as snake_case.
|
||||||
final bool autoSnakeCaseNames;
|
final bool autoSnakeCaseNames;
|
||||||
|
@ -67,7 +67,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
resolver,
|
resolver,
|
||||||
autoSnakeCaseNames != false,
|
autoSnakeCaseNames != false,
|
||||||
autoIdAndDateFields != false);
|
autoIdAndDateFields != false);
|
||||||
ctx.relationships.forEach((name, relationship) {
|
ctx.relationships.forEach((name, r) {
|
||||||
|
var relationship = ctx.populateRelationship(name);
|
||||||
var field = ctx.resolveRelationshipField(name);
|
var field = ctx.resolveRelationshipField(name);
|
||||||
var uri = field.type.element.source.uri;
|
var uri = field.type.element.source.uri;
|
||||||
var pathName = p
|
var pathName = p
|
||||||
|
@ -75,7 +76,11 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
var source =
|
var source =
|
||||||
'$pathName.orm.g.dart'; //uri.resolve('$pathName.orm.g.dart').toString();
|
'$pathName.orm.g.dart'; //uri.resolve('$pathName.orm.g.dart').toString();
|
||||||
// TODO: Find good way to source url...
|
// TODO: Find good way to source url...
|
||||||
source = new ReCase(field.type.name).snakeCase + '.orm.g.dart';
|
source = new ReCase(relationship.isList
|
||||||
|
? relationship.modelType.name
|
||||||
|
: field.type.name)
|
||||||
|
.snakeCase +
|
||||||
|
'.orm.g.dart';
|
||||||
|
|
||||||
if (!imported.contains(source)) {
|
if (!imported.contains(source)) {
|
||||||
lib.addDirective(new ImportBuilder(source));
|
lib.addDirective(new ImportBuilder(source));
|
||||||
|
@ -247,8 +252,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
ctx.relationships.forEach((name, r) {
|
ctx.relationships.forEach((name, r) {
|
||||||
var relationship = ctx.populateRelationship(name);
|
var relationship = ctx.populateRelationship(name);
|
||||||
|
|
||||||
// TODO: Has one, has many, belongs to many
|
// TODO: Has one, has many
|
||||||
if (relationship.type == RelationshipType.BELONGS_TO) {
|
if (relationship.isBelongsTo) {
|
||||||
var b = new StringBuffer(
|
var b = new StringBuffer(
|
||||||
' INNER JOIN ${relationship.foreignTable} ON ${ctx.tableName}.${relationship.localKey} = ${relationship.foreignTable}.${relationship.foreignKey}');
|
' INNER JOIN ${relationship.foreignTable} ON ${ctx.tableName}.${relationship.localKey} = ${relationship.foreignTable}.${relationship.foreignKey}');
|
||||||
relationsIfThen
|
relationsIfThen
|
||||||
|
@ -362,7 +367,9 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
int minIndex = i;
|
int minIndex = i;
|
||||||
|
|
||||||
var relationship = ctx.populateRelationship(name);
|
var relationship = ctx.populateRelationship(name);
|
||||||
var rc = new ReCase(relationship.dartType.name);
|
var rc = new ReCase(relationship.isList
|
||||||
|
? relationship.modelType.name
|
||||||
|
: relationship.dartType.name);
|
||||||
var relationshipQuery = new TypeBuilder('${rc.pascalCase}Query');
|
var relationshipQuery = new TypeBuilder('${rc.pascalCase}Query');
|
||||||
List<ExpressionBuilder> relationshipRow = [];
|
List<ExpressionBuilder> relationshipRow = [];
|
||||||
|
|
||||||
|
@ -601,30 +608,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
|
|
||||||
MethodBuilder buildDeleteOneMethod(PostgresBuildContext ctx) {
|
MethodBuilder buildDeleteOneMethod(PostgresBuildContext ctx) {
|
||||||
var meth = new MethodBuilder('deleteOne',
|
var meth = new MethodBuilder('deleteOne',
|
||||||
modifier: MethodModifier.asAsync,
|
|
||||||
returnType:
|
returnType:
|
||||||
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]))
|
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]))
|
||||||
..addPositional(parameter('id', [lib$core.int]))
|
..addPositional(parameter('id', [lib$core.int]))
|
||||||
..addPositional(
|
..addPositional(
|
||||||
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
|
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
|
||||||
|
|
||||||
var id = reference('id');
|
var id = reference('id'),
|
||||||
var connection = reference('connection');
|
connection = reference('connection'),
|
||||||
var result = reference('result');
|
query = reference('query');
|
||||||
|
|
||||||
var buf = new StringBuffer('DELETE FROM "${ctx.tableName}" WHERE id = @id');
|
|
||||||
_addReturning(buf, ctx);
|
|
||||||
|
|
||||||
// await connection.execute('...');
|
|
||||||
meth.addStatement(varField('result',
|
|
||||||
value: connection.invoke('query', [
|
|
||||||
literal(buf.toString())
|
|
||||||
], namedArguments: {
|
|
||||||
'substitutionValues': map({'id': id})
|
|
||||||
}).asAwait()));
|
|
||||||
|
|
||||||
meth.addStatement(
|
meth.addStatement(
|
||||||
reference('parseRow').call([result[literal(0)]]).asReturn());
|
varField('query', value: ctx.queryClassBuilder.newInstance([])));
|
||||||
|
meth.addStatement(
|
||||||
|
query.property('where').property('id').invoke('equals', [id]));
|
||||||
|
meth.addStatement(
|
||||||
|
query.invoke('delete', [connection]).property('first').asReturn());
|
||||||
return meth;
|
return meth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,8 +694,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
ctx.relationships.forEach((name, r) {
|
ctx.relationships.forEach((name, r) {
|
||||||
var relationship = ctx.populateRelationship(name);
|
var relationship = ctx.populateRelationship(name);
|
||||||
|
|
||||||
if (relationship.type == RelationshipType.BELONGS_TO) {
|
if (relationship.isBelongsTo) {
|
||||||
var rc = new ReCase(relationship.dartType.name);
|
var rc = new ReCase(relationship.isList
|
||||||
|
? relationship.modelType.name
|
||||||
|
: relationship.dartType.name);
|
||||||
var type = new TypeBuilder('${rc.pascalCase}Query');
|
var type = new TypeBuilder('${rc.pascalCase}Query');
|
||||||
|
|
||||||
// Resolve index within row...
|
// Resolve index within row...
|
||||||
|
@ -712,14 +712,38 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
col++;
|
col++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
matched = ctx.resolveRelationshipField(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!matched)
|
if (!matched)
|
||||||
throw 'Couldn\'t resolve row index for relationship "${name}".';
|
throw 'Couldn\'t resolve row index for relationship "${name}".';
|
||||||
|
|
||||||
var idAsInt = row[literal(col)];
|
var idAsInt = row[literal(col)];
|
||||||
meth.addStatement(type
|
|
||||||
.invoke('getOne', [idAsInt, reference('connection')])
|
if (relationship.isSingular) {
|
||||||
.asAwait()
|
meth.addStatement(type
|
||||||
.asAssign(output.property(name)));
|
.invoke('getOne', [idAsInt, reference('connection')])
|
||||||
|
.asAwait()
|
||||||
|
.asAssign(output.property(name)));
|
||||||
|
} else {
|
||||||
|
var query = reference('${rc.camelCase}Query');
|
||||||
|
meth.addStatement(
|
||||||
|
varField('${rc.camelCase}Query', value: type.newInstance([])));
|
||||||
|
ExpressionBuilder fetched;
|
||||||
|
|
||||||
|
// TODO: HasMany
|
||||||
|
if (relationship.isBelongsTo) {
|
||||||
|
meth.addStatement(query
|
||||||
|
.property('where')
|
||||||
|
.property('id')
|
||||||
|
.invoke('equals', [idAsInt]));
|
||||||
|
fetched = query.invoke('get', [reference('connection')]).invoke(
|
||||||
|
'toList', []).asAwait();
|
||||||
|
}
|
||||||
|
|
||||||
|
meth.addStatement(output.property(name).invoke('addAll', [fetched]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -729,8 +753,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
ctx.relationships.forEach((name, r) {
|
ctx.relationships.forEach((name, r) {
|
||||||
var relationship = ctx.populateRelationship(name);
|
var relationship = ctx.populateRelationship(name);
|
||||||
|
|
||||||
// TODO: Belongs to many
|
if (relationship.isBelongsTo) {
|
||||||
if (relationship.type == RelationshipType.BELONGS_TO) {
|
|
||||||
var rc = new ReCase(relationship.localKey);
|
var rc = new ReCase(relationship.localKey);
|
||||||
m.addNamed(parameter(rc.camelCase, [lib$core.int]));
|
m.addNamed(parameter(rc.camelCase, [lib$core.int]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,11 +90,21 @@ class PostgresBuildContext extends BuildContext {
|
||||||
|
|
||||||
PopulatedRelationship populateRelationship(String name) {
|
PopulatedRelationship populateRelationship(String name) {
|
||||||
return _populatedRelationships.putIfAbsent(name, () {
|
return _populatedRelationships.putIfAbsent(name, () {
|
||||||
// TODO: Belongs to many
|
|
||||||
var f = raw.fields.firstWhere((f) => f.name == name);
|
var f = raw.fields.firstWhere((f) => f.name == name);
|
||||||
var relationship = relationships[name];
|
var relationship = relationships[name];
|
||||||
|
DartType refType = f.type;
|
||||||
|
|
||||||
|
if (refType.isAssignableTo(typeProvider.listType) || refType.name == 'List') {
|
||||||
|
var iType = refType as InterfaceType;
|
||||||
|
|
||||||
|
if (iType.typeArguments.isEmpty)
|
||||||
|
throw 'Relationship "${f.name}" cannot be modeled as a generic List.';
|
||||||
|
|
||||||
|
refType = iType.typeArguments.first;
|
||||||
|
}
|
||||||
|
|
||||||
var typeName =
|
var typeName =
|
||||||
f.type.name.startsWith('_') ? f.type.name.substring(1) : f.type.name;
|
refType.name.startsWith('_') ? refType.name.substring(1) : refType.name;
|
||||||
var rc = new ReCase(typeName);
|
var rc = new ReCase(typeName);
|
||||||
|
|
||||||
if (relationship.type == RelationshipType.HAS_ONE ||
|
if (relationship.type == RelationshipType.HAS_ONE ||
|
||||||
|
@ -108,13 +118,22 @@ class PostgresBuildContext extends BuildContext {
|
||||||
(autoSnakeCaseNames != false
|
(autoSnakeCaseNames != false
|
||||||
? pluralize(rc.snakeCase)
|
? pluralize(rc.snakeCase)
|
||||||
: pluralize(typeName));
|
: pluralize(typeName));
|
||||||
return new PopulatedRelationship(relationship.type, f.type, buildStep,
|
return new PopulatedRelationship(
|
||||||
resolver, autoSnakeCaseNames, autoIdAndDateFields,
|
relationship.type,
|
||||||
|
f.name,
|
||||||
|
f.type,
|
||||||
|
buildStep,
|
||||||
|
resolver,
|
||||||
|
autoSnakeCaseNames,
|
||||||
|
autoIdAndDateFields,
|
||||||
|
relationship.type == RelationshipType.HAS_ONE,
|
||||||
|
typeProvider,
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
foreignKey: foreignKey,
|
foreignKey: foreignKey,
|
||||||
foreignTable: foreignTable,
|
foreignTable: foreignTable,
|
||||||
cascadeOnDelete: relationship.cascadeOnDelete);
|
cascadeOnDelete: relationship.cascadeOnDelete);
|
||||||
} else if (relationship.type == RelationshipType.BELONGS_TO) {
|
} else if (relationship.type == RelationshipType.BELONGS_TO ||
|
||||||
|
relationship.type == RelationshipType.BELONGS_TO_MANY) {
|
||||||
var localKey = relationship.localKey ??
|
var localKey = relationship.localKey ??
|
||||||
(autoSnakeCaseNames != false
|
(autoSnakeCaseNames != false
|
||||||
? '${rc.snakeCase}_id'
|
? '${rc.snakeCase}_id'
|
||||||
|
@ -124,8 +143,16 @@ class PostgresBuildContext extends BuildContext {
|
||||||
(autoSnakeCaseNames != false
|
(autoSnakeCaseNames != false
|
||||||
? pluralize(rc.snakeCase)
|
? pluralize(rc.snakeCase)
|
||||||
: pluralize(typeName));
|
: pluralize(typeName));
|
||||||
return new PopulatedRelationship(relationship.type, f.type, buildStep,
|
return new PopulatedRelationship(
|
||||||
resolver, autoSnakeCaseNames, autoIdAndDateFields,
|
relationship.type,
|
||||||
|
f.name,
|
||||||
|
f.type,
|
||||||
|
buildStep,
|
||||||
|
resolver,
|
||||||
|
autoSnakeCaseNames,
|
||||||
|
autoIdAndDateFields,
|
||||||
|
relationship.type == RelationshipType.BELONGS_TO,
|
||||||
|
typeProvider,
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
foreignKey: foreignKey,
|
foreignKey: foreignKey,
|
||||||
foreignTable: foreignTable,
|
foreignTable: foreignTable,
|
||||||
|
@ -138,16 +165,28 @@ class PostgresBuildContext extends BuildContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PopulatedRelationship extends Relationship {
|
class PopulatedRelationship extends Relationship {
|
||||||
|
bool _isList;
|
||||||
DartType _modelType;
|
DartType _modelType;
|
||||||
PostgresBuildContext _modelTypeContext;
|
PostgresBuildContext _modelTypeContext;
|
||||||
DartObject _modelTypeORM;
|
DartObject _modelTypeORM;
|
||||||
|
final String originalName;
|
||||||
final DartType dartType;
|
final DartType dartType;
|
||||||
final BuildStep buildStep;
|
final BuildStep buildStep;
|
||||||
final Resolver resolver;
|
final Resolver resolver;
|
||||||
final bool autoSnakeCaseNames, autoIdAndDateFields;
|
final bool autoSnakeCaseNames, autoIdAndDateFields;
|
||||||
|
final bool isSingular;
|
||||||
|
final TypeProvider typeProvider;
|
||||||
|
|
||||||
PopulatedRelationship(int type, this.dartType, this.buildStep, this.resolver,
|
PopulatedRelationship(
|
||||||
this.autoSnakeCaseNames, this.autoIdAndDateFields,
|
int type,
|
||||||
|
this.originalName,
|
||||||
|
this.dartType,
|
||||||
|
this.buildStep,
|
||||||
|
this.resolver,
|
||||||
|
this.autoSnakeCaseNames,
|
||||||
|
this.autoIdAndDateFields,
|
||||||
|
this.isSingular,
|
||||||
|
this.typeProvider,
|
||||||
{String localKey,
|
{String localKey,
|
||||||
String foreignKey,
|
String foreignKey,
|
||||||
String foreignTable,
|
String foreignTable,
|
||||||
|
@ -158,11 +197,34 @@ class PopulatedRelationship extends Relationship {
|
||||||
foreignTable: foreignTable,
|
foreignTable: foreignTable,
|
||||||
cascadeOnDelete: cascadeOnDelete);
|
cascadeOnDelete: cascadeOnDelete);
|
||||||
|
|
||||||
|
bool get isBelongsTo =>
|
||||||
|
type == RelationshipType.BELONGS_TO ||
|
||||||
|
type == RelationshipType.BELONGS_TO_MANY;
|
||||||
|
|
||||||
|
bool get isHas =>
|
||||||
|
type == RelationshipType.HAS_ONE || type == RelationshipType.HAS_MANY;
|
||||||
|
|
||||||
|
bool get isList => _isList ??=
|
||||||
|
dartType.isAssignableTo(typeProvider.listType) || dartType.name == 'List';
|
||||||
|
|
||||||
DartType get modelType {
|
DartType get modelType {
|
||||||
if (_modelType != null) return _modelType;
|
if (_modelType != null) return _modelType;
|
||||||
DartType searchType = dartType;
|
DartType searchType = dartType;
|
||||||
var ormChecker = new TypeChecker.fromRuntime(ORM);
|
var ormChecker = new TypeChecker.fromRuntime(ORM);
|
||||||
|
|
||||||
|
// Get inner type from List if any...
|
||||||
|
if (!isSingular) {
|
||||||
|
if (!isList)
|
||||||
|
throw '"$originalName" is a many-to-one relationship, and thus it should be represented as a List within your Dart class. You have it represented as ${dartType.name}.';
|
||||||
|
else {
|
||||||
|
var iType = dartType as InterfaceType;
|
||||||
|
if (iType.typeArguments.isEmpty)
|
||||||
|
throw '"$originalName" is a many-to-one relationship, and should be modeled as a List that references another model type. Example: `List<T>`, where T is a model type.';
|
||||||
|
else
|
||||||
|
searchType = iType.typeArguments.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (searchType != null) {
|
while (searchType != null) {
|
||||||
var classElement = searchType.element as ClassElement;
|
var classElement = searchType.element as ClassElement;
|
||||||
var ormAnnotation = ormChecker.firstAnnotationOf(classElement);
|
var ormAnnotation = ormChecker.firstAnnotationOf(classElement);
|
||||||
|
|
21
angel_orm_generator/test/belongs_to_many_test.dart
Normal file
21
angel_orm_generator/test/belongs_to_many_test.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:postgres/postgres.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'models/role.dart';
|
||||||
|
import 'models/role.orm.g.dart';
|
||||||
|
import 'models/user.dart';
|
||||||
|
import 'models/user.orm.g.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
PostgreSQLConnection connection;
|
||||||
|
Role manager, clerk;
|
||||||
|
User john;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
connection = await connectToPostgres();
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() => connection.close());
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
/// Tests for @belongsTo...
|
||||||
|
library angel_orm_generator.test.book_test;
|
||||||
|
|
||||||
import 'package:postgres/postgres.dart';
|
import 'package:postgres/postgres.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'models/author.dart';
|
import 'models/author.dart';
|
|
@ -14,6 +14,10 @@ Future<PostgreSQLConnection> connectToPostgres() async {
|
||||||
await conn.execute(query);
|
await conn.execute(query);
|
||||||
query = await new File('test/models/book.up.g.sql').readAsString();
|
query = await new File('test/models/book.up.g.sql').readAsString();
|
||||||
await conn.execute(query);
|
await conn.execute(query);
|
||||||
|
query = await new File('test/models/role.up.g.sql').readAsString();
|
||||||
|
await conn.execute(query);
|
||||||
|
query = await new File('test/models/user.up.g.sql').readAsString();
|
||||||
|
await conn.execute(query);
|
||||||
|
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
library angel_orm.test.models.author;
|
library angel_orm.generator.models.author;
|
||||||
|
|
||||||
import 'package:angel_framework/common.dart';
|
import 'package:angel_framework/common.dart';
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of angel_orm.test.models.author;
|
part of angel_orm.generator.models.author;
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// Generator: JsonModelGenerator
|
// Generator: JsonModelGenerator
|
||||||
|
|
|
@ -167,12 +167,10 @@ class AuthorQuery {
|
||||||
return ctrl.stream;
|
return ctrl.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Author> deleteOne(
|
static Future<Author> deleteOne(int id, PostgreSQLConnection connection) {
|
||||||
int id, PostgreSQLConnection connection) async {
|
var query = new AuthorQuery();
|
||||||
var result = await connection.query(
|
query.where.id.equals(id);
|
||||||
'DELETE FROM "authors" WHERE id = @id RETURNING "id", "name", "created_at", "updated_at";',
|
return query.delete(connection).first;
|
||||||
substitutionValues: {'id': id});
|
|
||||||
return parseRow(result[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Author> insert(PostgreSQLConnection connection,
|
static Future<Author> insert(PostgreSQLConnection connection,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
library angel_orm.test.models.book;
|
library angel_orm.generator.models.book;
|
||||||
|
|
||||||
import 'package:angel_framework/common.dart';
|
import 'package:angel_framework/common.dart';
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of angel_orm.test.models.book;
|
part of angel_orm.generator.models.book;
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// Generator: JsonModelGenerator
|
// Generator: JsonModelGenerator
|
||||||
|
|
|
@ -179,11 +179,10 @@ class BookQuery {
|
||||||
return ctrl.stream;
|
return ctrl.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Book> deleteOne(int id, PostgreSQLConnection connection) async {
|
static Future<Book> deleteOne(int id, PostgreSQLConnection connection) {
|
||||||
var result = await connection.query(
|
var query = new BookQuery();
|
||||||
'DELETE FROM "books" WHERE id = @id RETURNING "id", "name", "created_at", "updated_at", "author_id";',
|
query.where.id.equals(id);
|
||||||
substitutionValues: {'id': id});
|
return query.delete(connection).first;
|
||||||
return parseRow(result[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Book> insert(PostgreSQLConnection connection,
|
static Future<Book> insert(PostgreSQLConnection connection,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
library angel_orm.test.models.car;
|
library angel_orm.generator.models.car;
|
||||||
|
|
||||||
import 'package:angel_framework/common.dart';
|
import 'package:angel_framework/common.dart';
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of angel_orm.test.models.car;
|
part of angel_orm.generator.models.car;
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// Generator: JsonModelGenerator
|
// Generator: JsonModelGenerator
|
||||||
|
|
|
@ -179,11 +179,10 @@ class CarQuery {
|
||||||
return ctrl.stream;
|
return ctrl.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Car> deleteOne(int id, PostgreSQLConnection connection) async {
|
static Future<Car> deleteOne(int id, PostgreSQLConnection connection) {
|
||||||
var result = await connection.query(
|
var query = new CarQuery();
|
||||||
'DELETE FROM "cars" WHERE id = @id RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";',
|
query.where.id.equals(id);
|
||||||
substitutionValues: {'id': id});
|
return query.delete(connection).first;
|
||||||
return parseRow(result[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Car> insert(PostgreSQLConnection connection,
|
static Future<Car> insert(PostgreSQLConnection connection,
|
||||||
|
|
12
angel_orm_generator/test/models/role.dart
Normal file
12
angel_orm_generator/test/models/role.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
library angel_orm_generator.test.models.role;
|
||||||
|
|
||||||
|
import 'package:angel_model/angel_model.dart';
|
||||||
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
|
part 'role.g.dart';
|
||||||
|
|
||||||
|
@serializable
|
||||||
|
@orm
|
||||||
|
class _Role extends Model {
|
||||||
|
String name;
|
||||||
|
}
|
1
angel_orm_generator/test/models/role.down.g.sql
Normal file
1
angel_orm_generator/test/models/role.down.g.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE "roles";
|
53
angel_orm_generator/test/models/role.g.dart
Normal file
53
angel_orm_generator/test/models/role.g.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of angel_orm_generator.test.models.role;
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: JsonModelGenerator
|
||||||
|
// Target: class _Role
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class Role extends _Role {
|
||||||
|
@override
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime createdAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime updatedAt;
|
||||||
|
|
||||||
|
Role({this.id, this.name, this.createdAt, this.updatedAt});
|
||||||
|
|
||||||
|
factory Role.fromJson(Map data) {
|
||||||
|
return new Role(
|
||||||
|
id: data['id'],
|
||||||
|
name: data['name'],
|
||||||
|
createdAt: data['created_at'] is DateTime
|
||||||
|
? data['created_at']
|
||||||
|
: (data['created_at'] is String
|
||||||
|
? DateTime.parse(data['created_at'])
|
||||||
|
: null),
|
||||||
|
updatedAt: data['updated_at'] is DateTime
|
||||||
|
? data['updated_at']
|
||||||
|
: (data['updated_at'] is String
|
||||||
|
? DateTime.parse(data['updated_at'])
|
||||||
|
: null));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'created_at': createdAt == null ? null : createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt == null ? null : updatedAt.toIso8601String()
|
||||||
|
};
|
||||||
|
|
||||||
|
static Role parse(Map map) => new Role.fromJson(map);
|
||||||
|
|
||||||
|
Role clone() {
|
||||||
|
return new Role.fromJson(toJson());
|
||||||
|
}
|
||||||
|
}
|
240
angel_orm_generator/test/models/role.orm.g.dart
Normal file
240
angel_orm_generator/test/models/role.orm.g.dart
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: PostgresORMGenerator
|
||||||
|
// Target: class _Role
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
import 'package:postgres/postgres.dart';
|
||||||
|
import 'role.dart';
|
||||||
|
|
||||||
|
class RoleQuery {
|
||||||
|
final Map<RoleQuery, bool> _unions = {};
|
||||||
|
|
||||||
|
String _sortKey;
|
||||||
|
|
||||||
|
String _sortMode;
|
||||||
|
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
final List<RoleQueryWhere> _or = [];
|
||||||
|
|
||||||
|
final RoleQueryWhere where = new RoleQueryWhere();
|
||||||
|
|
||||||
|
void union(RoleQuery query) {
|
||||||
|
_unions[query] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unionAll(RoleQuery query) {
|
||||||
|
_unions[query] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortDescending(String key) {
|
||||||
|
_sortMode = 'Descending';
|
||||||
|
_sortKey = ('' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortAscending(String key) {
|
||||||
|
_sortMode = 'Ascending';
|
||||||
|
_sortKey = ('' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void or(RoleQueryWhere selector) {
|
||||||
|
_or.add(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toSql([String prefix]) {
|
||||||
|
var buf = new StringBuffer();
|
||||||
|
buf.write(prefix != null
|
||||||
|
? prefix
|
||||||
|
: 'SELECT id, name, created_at, updated_at FROM "roles"');
|
||||||
|
if (prefix == null) {}
|
||||||
|
var whereClause = where.toWhereClause();
|
||||||
|
if (whereClause != null) {
|
||||||
|
buf.write(' ' + whereClause);
|
||||||
|
}
|
||||||
|
_or.forEach((x) {
|
||||||
|
var whereClause = x.toWhereClause(keyword: false);
|
||||||
|
if (whereClause != null) {
|
||||||
|
buf.write(' OR (' + whereClause + ')');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (prefix == null) {
|
||||||
|
if (limit != null) {
|
||||||
|
buf.write(' LIMIT ' + limit.toString());
|
||||||
|
}
|
||||||
|
if (offset != null) {
|
||||||
|
buf.write(' OFFSET ' + offset.toString());
|
||||||
|
}
|
||||||
|
if (_sortMode == 'Descending') {
|
||||||
|
buf.write(' ORDER BY "' + _sortKey + '" DESC');
|
||||||
|
}
|
||||||
|
if (_sortMode == 'Ascending') {
|
||||||
|
buf.write(' ORDER BY "' + _sortKey + '" ASC');
|
||||||
|
}
|
||||||
|
_unions.forEach((query, all) {
|
||||||
|
buf.write(' UNION');
|
||||||
|
if (all) {
|
||||||
|
buf.write(' ALL');
|
||||||
|
}
|
||||||
|
buf.write(' (');
|
||||||
|
var sql = query.toSql().replaceAll(';', '');
|
||||||
|
buf.write(sql + ')');
|
||||||
|
});
|
||||||
|
buf.write(';');
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Role parseRow(List row) {
|
||||||
|
var result = new Role.fromJson({
|
||||||
|
'id': row[0].toString(),
|
||||||
|
'name': row[1],
|
||||||
|
'created_at': row[2],
|
||||||
|
'updated_at': row[3]
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Role> get(PostgreSQLConnection connection) {
|
||||||
|
StreamController<Role> ctrl = new StreamController<Role>();
|
||||||
|
connection.query(toSql()).then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Role> getOne(int id, PostgreSQLConnection connection) {
|
||||||
|
var query = new RoleQuery();
|
||||||
|
query.where.id.equals(id);
|
||||||
|
return query.get(connection).first.catchError((_) => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Role> update(PostgreSQLConnection connection,
|
||||||
|
{String name, DateTime createdAt, DateTime updatedAt}) {
|
||||||
|
var buf = new StringBuffer(
|
||||||
|
'UPDATE "roles" SET ("name", "created_at", "updated_at") = (@name, @createdAt, @updatedAt) ');
|
||||||
|
var whereClause = where.toWhereClause();
|
||||||
|
if (whereClause == null) {
|
||||||
|
buf.write('WHERE "id" = @id');
|
||||||
|
} else {
|
||||||
|
buf.write(whereClause);
|
||||||
|
}
|
||||||
|
var __ormNow__ = new DateTime.now();
|
||||||
|
var ctrl = new StreamController<Role>();
|
||||||
|
connection.query(
|
||||||
|
buf.toString() + ' RETURNING "id", "name", "created_at", "updated_at";',
|
||||||
|
substitutionValues: {
|
||||||
|
'name': name,
|
||||||
|
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||||
|
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||||
|
}).then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Role> delete(PostgreSQLConnection connection) {
|
||||||
|
StreamController<Role> ctrl = new StreamController<Role>();
|
||||||
|
connection
|
||||||
|
.query(toSql('DELETE FROM "roles"') +
|
||||||
|
' RETURNING "id", "name", "created_at", "updated_at";')
|
||||||
|
.then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Role> deleteOne(int id, PostgreSQLConnection connection) {
|
||||||
|
var query = new RoleQuery();
|
||||||
|
query.where.id.equals(id);
|
||||||
|
return query.delete(connection).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Role> insert(PostgreSQLConnection connection,
|
||||||
|
{String name, DateTime createdAt, DateTime updatedAt}) async {
|
||||||
|
var __ormNow__ = new DateTime.now();
|
||||||
|
var result = await connection.query(
|
||||||
|
'INSERT INTO "roles" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt) RETURNING "id", "name", "created_at", "updated_at";',
|
||||||
|
substitutionValues: {
|
||||||
|
'name': name,
|
||||||
|
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||||
|
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||||
|
});
|
||||||
|
var output = parseRow(result[0]);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Role> insertRole(PostgreSQLConnection connection, Role role) {
|
||||||
|
return RoleQuery.insert(connection,
|
||||||
|
name: role.name, createdAt: role.createdAt, updatedAt: role.updatedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Role> updateRole(PostgreSQLConnection connection, Role role) {
|
||||||
|
var query = new RoleQuery();
|
||||||
|
query.where.id.equals(int.parse(role.id));
|
||||||
|
return query
|
||||||
|
.update(connection,
|
||||||
|
name: role.name,
|
||||||
|
createdAt: role.createdAt,
|
||||||
|
updatedAt: role.updatedAt)
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Role> getAll(PostgreSQLConnection connection) =>
|
||||||
|
new RoleQuery().get(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoleQueryWhere {
|
||||||
|
final NumericSqlExpressionBuilder<int> id =
|
||||||
|
new NumericSqlExpressionBuilder<int>();
|
||||||
|
|
||||||
|
final StringSqlExpressionBuilder name = new StringSqlExpressionBuilder();
|
||||||
|
|
||||||
|
final DateTimeSqlExpressionBuilder createdAt =
|
||||||
|
new DateTimeSqlExpressionBuilder('roles.created_at');
|
||||||
|
|
||||||
|
final DateTimeSqlExpressionBuilder updatedAt =
|
||||||
|
new DateTimeSqlExpressionBuilder('roles.updated_at');
|
||||||
|
|
||||||
|
String toWhereClause({bool keyword}) {
|
||||||
|
final List<String> expressions = [];
|
||||||
|
if (id.hasValue) {
|
||||||
|
expressions.add('roles.id ' + id.compile());
|
||||||
|
}
|
||||||
|
if (name.hasValue) {
|
||||||
|
expressions.add('roles.name ' + name.compile());
|
||||||
|
}
|
||||||
|
if (createdAt.hasValue) {
|
||||||
|
expressions.add(createdAt.compile());
|
||||||
|
}
|
||||||
|
if (updatedAt.hasValue) {
|
||||||
|
expressions.add(updatedAt.compile());
|
||||||
|
}
|
||||||
|
return expressions.isEmpty
|
||||||
|
? null
|
||||||
|
: ((keyword != false ? 'WHERE ' : '') + expressions.join(' AND '));
|
||||||
|
}
|
||||||
|
}
|
7
angel_orm_generator/test/models/role.up.g.sql
Normal file
7
angel_orm_generator/test/models/role.up.g.sql
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TEMPORARY TABLE "roles" (
|
||||||
|
"id" serial,
|
||||||
|
"name" varchar,
|
||||||
|
"created_at" timestamp,
|
||||||
|
"updated_at" timestamp,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);
|
16
angel_orm_generator/test/models/user.dart
Normal file
16
angel_orm_generator/test/models/user.dart
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
library angel_orm_generator.test.models.user;
|
||||||
|
|
||||||
|
import 'package:angel_model/angel_model.dart';
|
||||||
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
|
import 'role.dart';
|
||||||
|
part 'user.g.dart';
|
||||||
|
|
||||||
|
@serializable
|
||||||
|
@orm
|
||||||
|
class _User extends Model {
|
||||||
|
String username, password, email;
|
||||||
|
|
||||||
|
@belongsToMany
|
||||||
|
List<Role> roles;
|
||||||
|
}
|
1
angel_orm_generator/test/models/user.down.g.sql
Normal file
1
angel_orm_generator/test/models/user.down.g.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE "users";
|
75
angel_orm_generator/test/models/user.g.dart
Normal file
75
angel_orm_generator/test/models/user.g.dart
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of angel_orm_generator.test.models.user;
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: JsonModelGenerator
|
||||||
|
// Target: class _User
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class User extends _User {
|
||||||
|
@override
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String username;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String password;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String email;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List roles;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime createdAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime updatedAt;
|
||||||
|
|
||||||
|
User(
|
||||||
|
{this.id,
|
||||||
|
this.username,
|
||||||
|
this.password,
|
||||||
|
this.email,
|
||||||
|
this.roles,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt});
|
||||||
|
|
||||||
|
factory User.fromJson(Map data) {
|
||||||
|
return new User(
|
||||||
|
id: data['id'],
|
||||||
|
username: data['username'],
|
||||||
|
password: data['password'],
|
||||||
|
email: data['email'],
|
||||||
|
roles: data['roles'],
|
||||||
|
createdAt: data['created_at'] is DateTime
|
||||||
|
? data['created_at']
|
||||||
|
: (data['created_at'] is String
|
||||||
|
? DateTime.parse(data['created_at'])
|
||||||
|
: null),
|
||||||
|
updatedAt: data['updated_at'] is DateTime
|
||||||
|
? data['updated_at']
|
||||||
|
: (data['updated_at'] is String
|
||||||
|
? DateTime.parse(data['updated_at'])
|
||||||
|
: null));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'email': email,
|
||||||
|
'roles': roles,
|
||||||
|
'created_at': createdAt == null ? null : createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt == null ? null : updatedAt.toIso8601String()
|
||||||
|
};
|
||||||
|
|
||||||
|
static User parse(Map map) => new User.fromJson(map);
|
||||||
|
|
||||||
|
User clone() {
|
||||||
|
return new User.fromJson(toJson());
|
||||||
|
}
|
||||||
|
}
|
290
angel_orm_generator/test/models/user.orm.g.dart
Normal file
290
angel_orm_generator/test/models/user.orm.g.dart
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: PostgresORMGenerator
|
||||||
|
// Target: class _User
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
|
import 'package:postgres/postgres.dart';
|
||||||
|
import 'user.dart';
|
||||||
|
import 'role.orm.g.dart';
|
||||||
|
|
||||||
|
class UserQuery {
|
||||||
|
final Map<UserQuery, bool> _unions = {};
|
||||||
|
|
||||||
|
String _sortKey;
|
||||||
|
|
||||||
|
String _sortMode;
|
||||||
|
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
final List<UserQueryWhere> _or = [];
|
||||||
|
|
||||||
|
final UserQueryWhere where = new UserQueryWhere();
|
||||||
|
|
||||||
|
void union(UserQuery query) {
|
||||||
|
_unions[query] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unionAll(UserQuery query) {
|
||||||
|
_unions[query] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortDescending(String key) {
|
||||||
|
_sortMode = 'Descending';
|
||||||
|
_sortKey = ('users.' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortAscending(String key) {
|
||||||
|
_sortMode = 'Ascending';
|
||||||
|
_sortKey = ('users.' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void or(UserQueryWhere selector) {
|
||||||
|
_or.add(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toSql([String prefix]) {
|
||||||
|
var buf = new StringBuffer();
|
||||||
|
buf.write(prefix != null
|
||||||
|
? prefix
|
||||||
|
: 'SELECT users.id, users.username, users.password, users.email, users.created_at, users.updated_at, roles.id, roles.name, roles.created_at, roles.updated_at FROM "users"');
|
||||||
|
if (prefix == null) {
|
||||||
|
buf.write(' INNER JOIN roles ON users.role_id = roles.id');
|
||||||
|
}
|
||||||
|
var whereClause = where.toWhereClause();
|
||||||
|
if (whereClause != null) {
|
||||||
|
buf.write(' ' + whereClause);
|
||||||
|
}
|
||||||
|
_or.forEach((x) {
|
||||||
|
var whereClause = x.toWhereClause(keyword: false);
|
||||||
|
if (whereClause != null) {
|
||||||
|
buf.write(' OR (' + whereClause + ')');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (prefix == null) {
|
||||||
|
if (limit != null) {
|
||||||
|
buf.write(' LIMIT ' + limit.toString());
|
||||||
|
}
|
||||||
|
if (offset != null) {
|
||||||
|
buf.write(' OFFSET ' + offset.toString());
|
||||||
|
}
|
||||||
|
if (_sortMode == 'Descending') {
|
||||||
|
buf.write(' ORDER BY "' + _sortKey + '" DESC');
|
||||||
|
}
|
||||||
|
if (_sortMode == 'Ascending') {
|
||||||
|
buf.write(' ORDER BY "' + _sortKey + '" ASC');
|
||||||
|
}
|
||||||
|
_unions.forEach((query, all) {
|
||||||
|
buf.write(' UNION');
|
||||||
|
if (all) {
|
||||||
|
buf.write(' ALL');
|
||||||
|
}
|
||||||
|
buf.write(' (');
|
||||||
|
var sql = query.toSql().replaceAll(';', '');
|
||||||
|
buf.write(sql + ')');
|
||||||
|
});
|
||||||
|
buf.write(';');
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static User parseRow(List row) {
|
||||||
|
var result = new User.fromJson({
|
||||||
|
'id': row[0].toString(),
|
||||||
|
'username': row[1],
|
||||||
|
'password': row[2],
|
||||||
|
'email': row[3],
|
||||||
|
'created_at': row[4],
|
||||||
|
'updated_at': row[5]
|
||||||
|
});
|
||||||
|
if (row.length > 6) {
|
||||||
|
result.roles = RoleQuery.parseRow([row[6], row[7], row[8], row[9]]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<User> get(PostgreSQLConnection connection) {
|
||||||
|
StreamController<User> ctrl = new StreamController<User>();
|
||||||
|
connection.query(toSql()).then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
var roleQuery = new RoleQuery();
|
||||||
|
roleQuery.where.id.equals(row[6]);
|
||||||
|
parsed.roles.addAll(await roleQuery.get(connection).toList());
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> getOne(int id, PostgreSQLConnection connection) {
|
||||||
|
var query = new UserQuery();
|
||||||
|
query.where.id.equals(id);
|
||||||
|
return query.get(connection).first.catchError((_) => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<User> update(PostgreSQLConnection connection,
|
||||||
|
{String username,
|
||||||
|
String password,
|
||||||
|
String email,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt}) {
|
||||||
|
var buf = new StringBuffer(
|
||||||
|
'UPDATE "users" SET ("username", "password", "email", "created_at", "updated_at") = (@username, @password, @email, @createdAt, @updatedAt) ');
|
||||||
|
var whereClause = where.toWhereClause();
|
||||||
|
if (whereClause == null) {
|
||||||
|
buf.write('WHERE "id" = @id');
|
||||||
|
} else {
|
||||||
|
buf.write(whereClause);
|
||||||
|
}
|
||||||
|
var __ormNow__ = new DateTime.now();
|
||||||
|
var ctrl = new StreamController<User>();
|
||||||
|
connection.query(
|
||||||
|
buf.toString() +
|
||||||
|
' RETURNING "id", "username", "password", "email", "created_at", "updated_at";',
|
||||||
|
substitutionValues: {
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'email': email,
|
||||||
|
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||||
|
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||||
|
}).then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
var roleQuery = new RoleQuery();
|
||||||
|
roleQuery.where.id.equals(row[6]);
|
||||||
|
parsed.roles.addAll(await roleQuery.get(connection).toList());
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<User> delete(PostgreSQLConnection connection) {
|
||||||
|
StreamController<User> ctrl = new StreamController<User>();
|
||||||
|
connection
|
||||||
|
.query(toSql('DELETE FROM "users"') +
|
||||||
|
' RETURNING "id", "username", "password", "email", "created_at", "updated_at";')
|
||||||
|
.then((rows) async {
|
||||||
|
var futures = rows.map((row) async {
|
||||||
|
var parsed = parseRow(row);
|
||||||
|
var roleQuery = new RoleQuery();
|
||||||
|
roleQuery.where.id.equals(row[6]);
|
||||||
|
parsed.roles.addAll(await roleQuery.get(connection).toList());
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
var output = await Future.wait(futures);
|
||||||
|
output.forEach(ctrl.add);
|
||||||
|
ctrl.close();
|
||||||
|
}).catchError(ctrl.addError);
|
||||||
|
return ctrl.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> deleteOne(int id, PostgreSQLConnection connection) {
|
||||||
|
var query = new UserQuery();
|
||||||
|
query.where.id.equals(id);
|
||||||
|
return query.delete(connection).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> insert(PostgreSQLConnection connection,
|
||||||
|
{String username,
|
||||||
|
String password,
|
||||||
|
String email,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt}) async {
|
||||||
|
var __ormNow__ = new DateTime.now();
|
||||||
|
var result = await connection.query(
|
||||||
|
'INSERT INTO "users" ("username", "password", "email", "created_at", "updated_at") VALUES (@username, @password, @email, @createdAt, @updatedAt) RETURNING "id", "username", "password", "email", "created_at", "updated_at";',
|
||||||
|
substitutionValues: {
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'email': email,
|
||||||
|
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||||
|
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||||
|
});
|
||||||
|
var output = parseRow(result[0]);
|
||||||
|
var roleQuery = new RoleQuery();
|
||||||
|
roleQuery.where.id.equals(result[0][6]);
|
||||||
|
output.roles.addAll(await roleQuery.get(connection).toList());
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> insertUser(PostgreSQLConnection connection, User user,
|
||||||
|
{int roleId}) {
|
||||||
|
return UserQuery.insert(connection,
|
||||||
|
username: user.username,
|
||||||
|
password: user.password,
|
||||||
|
email: user.email,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
updatedAt: user.updatedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> updateUser(PostgreSQLConnection connection, User user) {
|
||||||
|
var query = new UserQuery();
|
||||||
|
query.where.id.equals(int.parse(user.id));
|
||||||
|
return query
|
||||||
|
.update(connection,
|
||||||
|
username: user.username,
|
||||||
|
password: user.password,
|
||||||
|
email: user.email,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
updatedAt: user.updatedAt)
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<User> getAll(PostgreSQLConnection connection) =>
|
||||||
|
new UserQuery().get(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserQueryWhere {
|
||||||
|
final NumericSqlExpressionBuilder<int> id =
|
||||||
|
new NumericSqlExpressionBuilder<int>();
|
||||||
|
|
||||||
|
final StringSqlExpressionBuilder username = new StringSqlExpressionBuilder();
|
||||||
|
|
||||||
|
final StringSqlExpressionBuilder password = new StringSqlExpressionBuilder();
|
||||||
|
|
||||||
|
final StringSqlExpressionBuilder email = new StringSqlExpressionBuilder();
|
||||||
|
|
||||||
|
final DateTimeSqlExpressionBuilder createdAt =
|
||||||
|
new DateTimeSqlExpressionBuilder('users.created_at');
|
||||||
|
|
||||||
|
final DateTimeSqlExpressionBuilder updatedAt =
|
||||||
|
new DateTimeSqlExpressionBuilder('users.updated_at');
|
||||||
|
|
||||||
|
String toWhereClause({bool keyword}) {
|
||||||
|
final List<String> expressions = [];
|
||||||
|
if (id.hasValue) {
|
||||||
|
expressions.add('users.id ' + id.compile());
|
||||||
|
}
|
||||||
|
if (username.hasValue) {
|
||||||
|
expressions.add('users.username ' + username.compile());
|
||||||
|
}
|
||||||
|
if (password.hasValue) {
|
||||||
|
expressions.add('users.password ' + password.compile());
|
||||||
|
}
|
||||||
|
if (email.hasValue) {
|
||||||
|
expressions.add('users.email ' + email.compile());
|
||||||
|
}
|
||||||
|
if (createdAt.hasValue) {
|
||||||
|
expressions.add(createdAt.compile());
|
||||||
|
}
|
||||||
|
if (updatedAt.hasValue) {
|
||||||
|
expressions.add(updatedAt.compile());
|
||||||
|
}
|
||||||
|
return expressions.isEmpty
|
||||||
|
? null
|
||||||
|
: ((keyword != false ? 'WHERE ' : '') + expressions.join(' AND '));
|
||||||
|
}
|
||||||
|
}
|
10
angel_orm_generator/test/models/user.up.g.sql
Normal file
10
angel_orm_generator/test/models/user.up.g.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
CREATE TEMPORARY TABLE "users" (
|
||||||
|
"id" serial,
|
||||||
|
"username" varchar,
|
||||||
|
"password" varchar,
|
||||||
|
"email" varchar,
|
||||||
|
"created_at" timestamp,
|
||||||
|
"updated_at" timestamp,
|
||||||
|
"role_id" int REFERENCES roles(id),
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);
|
|
@ -1,3 +1,6 @@
|
||||||
|
/// Test for queries without relationships.
|
||||||
|
library angel_orm_generator.test.car_test;
|
||||||
|
|
||||||
import 'package:angel_orm/angel_orm.dart';
|
import 'package:angel_orm/angel_orm.dart';
|
||||||
import 'package:postgres/postgres.dart';
|
import 'package:postgres/postgres.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
|
@ -5,10 +5,13 @@ import 'package:angel_serialize_generator/angel_serialize_generator.dart';
|
||||||
|
|
||||||
final InputSet ALL_MODELS =
|
final InputSet ALL_MODELS =
|
||||||
new InputSet('angel_orm_generator', const ['test/models/*.dart']);
|
new InputSet('angel_orm_generator', const ['test/models/*.dart']);
|
||||||
final InputSet STANDALONE_MODELS = new InputSet('angel_orm_generator',
|
final InputSet STANDALONE_MODELS = new InputSet('angel_orm_generator', const [
|
||||||
const ['test/models/car.dart', 'test/models/author.dart']);
|
'test/models/car.dart',
|
||||||
final InputSet DEPENDENT_MODELS =
|
'test/models/author.dart',
|
||||||
new InputSet('angel_orm_generator', const ['test/models/book.dart']);
|
'test/models/role.dart'
|
||||||
|
]);
|
||||||
|
final InputSet DEPENDENT_MODELS = new InputSet('angel_orm_generator',
|
||||||
|
const ['test/models/book.dart', 'test/models/user.dart']);
|
||||||
|
|
||||||
final PhaseGroup PHASES = new PhaseGroup()
|
final PhaseGroup PHASES = new PhaseGroup()
|
||||||
..addPhase(new Phase()
|
..addPhase(new Phase()
|
||||||
|
|
Loading…
Reference in a new issue