1.0.0
This commit is contained in:
parent
012c323e0c
commit
b67ddeb444
11 changed files with 201 additions and 30 deletions
|
@ -1,5 +1,5 @@
|
||||||
# relations
|
# relations
|
||||||
[![version 1.0.0-alpha](https://img.shields.io/badge/pub-v1.0.0--alpha-red.svg)](https://pub.dartlang.org/packages/angel_relations)
|
[![version 1.0.0](https://img.shields.io/badge/pub-v1.0.0-brightgreen.svg)](https://pub.dartlang.org/packages/angel_relations)
|
||||||
[![build status](https://travis-ci.org/angel-dart/relations.svg)](https://travis-ci.org/angel-dart/relations)
|
[![build status](https://travis-ci.org/angel-dart/relations.svg)](https://travis-ci.org/angel-dart/relations)
|
||||||
|
|
||||||
Database-agnostic relations between Angel services.
|
Database-agnostic relations between Angel services.
|
||||||
|
|
|
@ -17,20 +17,19 @@ HookedServiceEventListener belongsTo(Pattern servicePath,
|
||||||
String localKey,
|
String localKey,
|
||||||
getForeignKey(obj),
|
getForeignKey(obj),
|
||||||
assignForeignObject(foreign, obj)}) {
|
assignForeignObject(foreign, obj)}) {
|
||||||
|
String localId = localKey;
|
||||||
|
var foreignName =
|
||||||
|
as?.isNotEmpty == true ? as : pluralize.singular(servicePath.toString());
|
||||||
|
|
||||||
|
if (localId == null) {
|
||||||
|
localId = foreignName + 'Id';
|
||||||
|
// print('No local key provided for belongsTo, defaulting to \'$localId\'.');
|
||||||
|
}
|
||||||
|
|
||||||
return (HookedServiceEvent e) async {
|
return (HookedServiceEvent e) async {
|
||||||
var ref = e.service.app.service(servicePath);
|
var ref = e.service.app.service(servicePath);
|
||||||
var foreignName = as?.isNotEmpty == true
|
|
||||||
? as
|
|
||||||
: pluralize.singular(servicePath.toString());
|
|
||||||
if (ref == null) throw noService(servicePath);
|
if (ref == null) throw noService(servicePath);
|
||||||
|
|
||||||
String localId = localKey;
|
|
||||||
|
|
||||||
if (localId == null) {
|
|
||||||
localId = foreignName + 'Id';
|
|
||||||
print('No local key provided for belongsTo, defaulting to \'$localId\'.');
|
|
||||||
}
|
|
||||||
|
|
||||||
_getForeignKey(obj) {
|
_getForeignKey(obj) {
|
||||||
if (getForeignKey != null)
|
if (getForeignKey != null)
|
||||||
return getForeignKey(obj);
|
return getForeignKey(obj);
|
||||||
|
@ -62,7 +61,7 @@ HookedServiceEventListener belongsTo(Pattern servicePath,
|
||||||
'query': {foreignKey ?? 'id': id}
|
'query': {foreignKey ?? 'id': id}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexed?.isNotEmpty != true) {
|
if (indexed == null || indexed is! List || indexed.isNotEmpty != true) {
|
||||||
await _assignForeignObject(null, obj);
|
await _assignForeignObject(null, obj);
|
||||||
} else {
|
} else {
|
||||||
var child = indexed.first;
|
var child = indexed.first;
|
||||||
|
|
78
lib/src/belongs_to_many.dart
Normal file
78
lib/src/belongs_to_many.dart
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:mirrors';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'plural.dart' as pluralize;
|
||||||
|
import 'no_service.dart';
|
||||||
|
|
||||||
|
/// Represents a relationship in which the current [service] "belongs to"
|
||||||
|
/// multiple members of the service at [servicePath]. Use [as] to set the name
|
||||||
|
/// on the target object.
|
||||||
|
///
|
||||||
|
/// Defaults:
|
||||||
|
/// * [foreignKey]: `userId`
|
||||||
|
/// * [localKey]: `id`
|
||||||
|
HookedServiceEventListener belongsToMany(Pattern servicePath,
|
||||||
|
{String as,
|
||||||
|
String foreignKey,
|
||||||
|
String localKey,
|
||||||
|
getForeignKey(obj),
|
||||||
|
assignForeignObject(List foreign, obj)}) {
|
||||||
|
String localId = localKey;
|
||||||
|
var foreignName =
|
||||||
|
as?.isNotEmpty == true ? as : pluralize.plural(servicePath.toString());
|
||||||
|
|
||||||
|
if (localId == null) {
|
||||||
|
localId = foreignName + 'Id';
|
||||||
|
// print('No local key provided for belongsToMany, defaulting to \'$localId\'.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (HookedServiceEvent e) async {
|
||||||
|
var ref = e.service.app.service(servicePath);
|
||||||
|
if (ref == null) throw noService(servicePath);
|
||||||
|
|
||||||
|
_getForeignKey(obj) {
|
||||||
|
if (getForeignKey != null)
|
||||||
|
return getForeignKey(obj);
|
||||||
|
else if (obj is Map)
|
||||||
|
return obj[localId];
|
||||||
|
else if (obj is Extensible)
|
||||||
|
return obj.properties[localId];
|
||||||
|
else if (localId == null || localId == 'userId')
|
||||||
|
return obj.userId;
|
||||||
|
else
|
||||||
|
return reflect(obj).getField(new Symbol(localId)).reflectee;
|
||||||
|
}
|
||||||
|
|
||||||
|
_assignForeignObject(foreign, obj) {
|
||||||
|
if (assignForeignObject != null)
|
||||||
|
return assignForeignObject(foreign, obj);
|
||||||
|
else if (obj is Map)
|
||||||
|
obj[foreignName] = foreign;
|
||||||
|
else if (obj is Extensible)
|
||||||
|
obj.properties[foreignName] = foreign;
|
||||||
|
else
|
||||||
|
reflect(obj).setField(new Symbol(foreignName), foreign);
|
||||||
|
}
|
||||||
|
|
||||||
|
_normalize(obj) async {
|
||||||
|
if (obj != null) {
|
||||||
|
var id = await _getForeignKey(obj);
|
||||||
|
var indexed = await ref.index({
|
||||||
|
'query': {foreignKey ?? 'id': id}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (indexed == null || indexed is! List || indexed.isNotEmpty != true) {
|
||||||
|
await _assignForeignObject(null, obj);
|
||||||
|
} else {
|
||||||
|
var child = indexed is Iterable ? indexed.toList() : [indexed];
|
||||||
|
await _assignForeignObject(child, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.result is Iterable) {
|
||||||
|
await Future.wait(e.result.map(_normalize));
|
||||||
|
} else
|
||||||
|
await _normalize(e.result);
|
||||||
|
};
|
||||||
|
}
|
|
@ -17,16 +17,13 @@ HookedServiceEventListener hasMany(Pattern servicePath,
|
||||||
String localKey,
|
String localKey,
|
||||||
getLocalKey(obj),
|
getLocalKey(obj),
|
||||||
assignForeignObjects(foreign, obj)}) {
|
assignForeignObjects(foreign, obj)}) {
|
||||||
|
|
||||||
return (HookedServiceEvent e) async {
|
return (HookedServiceEvent e) async {
|
||||||
var ref = e.service.app.service(servicePath);
|
var ref = e.service.app.service(servicePath);
|
||||||
var foreignName =
|
var foreignName =
|
||||||
as?.isNotEmpty == true ? as : pluralize.plural(servicePath.toString());
|
as?.isNotEmpty == true ? as : pluralize.plural(servicePath.toString());
|
||||||
if (ref == null) throw noService(servicePath);
|
if (ref == null) throw noService(servicePath);
|
||||||
|
|
||||||
if (foreignKey == null)
|
|
||||||
print(
|
|
||||||
'WARNING: No foreign key provided for hasMany, defaulting to \'userId\'.');
|
|
||||||
|
|
||||||
_getLocalKey(obj) {
|
_getLocalKey(obj) {
|
||||||
if (getLocalKey != null)
|
if (getLocalKey != null)
|
||||||
return getLocalKey(obj);
|
return getLocalKey(obj);
|
||||||
|
@ -58,7 +55,7 @@ HookedServiceEventListener hasMany(Pattern servicePath,
|
||||||
'query': {foreignKey ?? 'userId': id}
|
'query': {foreignKey ?? 'userId': id}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexed?.isNotEmpty != true) {
|
if (indexed == null || indexed is! List || indexed.isNotEmpty != true) {
|
||||||
await _assignForeignObjects([], obj);
|
await _assignForeignObjects([], obj);
|
||||||
} else {
|
} else {
|
||||||
await _assignForeignObjects(indexed, obj);
|
await _assignForeignObjects(indexed, obj);
|
||||||
|
|
100
lib/src/has_many_through.dart
Normal file
100
lib/src/has_many_through.dart
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:mirrors';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'plural.dart' as pluralize;
|
||||||
|
import 'no_service.dart';
|
||||||
|
|
||||||
|
HookedServiceEventListener hasManyThrough(String servicePath, String pivotPath,
|
||||||
|
{String as,
|
||||||
|
String localKey,
|
||||||
|
String pivotKey,
|
||||||
|
String foreignKey,
|
||||||
|
getLocalKey(obj),
|
||||||
|
getPivotKey(obj),
|
||||||
|
getForeignKey(obj),
|
||||||
|
assignForeignObjects(foreign, obj)}) {
|
||||||
|
var foreignName =
|
||||||
|
as?.isNotEmpty == true ? as : pluralize.plural(servicePath.toString());
|
||||||
|
|
||||||
|
return (HookedServiceEvent e) async {
|
||||||
|
var pivotService = e.getService(pivotPath);
|
||||||
|
var foreignService = e.getService(servicePath);
|
||||||
|
|
||||||
|
if (pivotService == null)
|
||||||
|
throw noService(pivotPath);
|
||||||
|
else if (foreignService == null) throw noService(servicePath);
|
||||||
|
|
||||||
|
_assignForeignObjects(foreign, obj) {
|
||||||
|
if (assignForeignObjects != null)
|
||||||
|
return assignForeignObjects(foreign, obj);
|
||||||
|
else if (obj is Map)
|
||||||
|
obj[foreignName] = foreign;
|
||||||
|
else if (obj is Extensible)
|
||||||
|
obj.properties[foreignName] = foreign;
|
||||||
|
else
|
||||||
|
reflect(obj).setField(new Symbol(foreignName), foreign);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getLocalKey(obj) {
|
||||||
|
if (getLocalKey != null)
|
||||||
|
return getLocalKey(obj);
|
||||||
|
else if (obj is Map)
|
||||||
|
return obj[localKey ?? 'id'];
|
||||||
|
else if (obj is Extensible)
|
||||||
|
return obj.properties[localKey ?? 'id'];
|
||||||
|
else if (localKey == null || localKey == 'id')
|
||||||
|
return obj.id;
|
||||||
|
else
|
||||||
|
return reflect(obj).getField(new Symbol(localKey ?? 'id')).reflectee;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPivotKey(obj) {
|
||||||
|
if (getPivotKey != null)
|
||||||
|
return getPivotKey(obj);
|
||||||
|
else if (obj is Map)
|
||||||
|
return obj[pivotKey ?? 'id'];
|
||||||
|
else if (obj is Extensible)
|
||||||
|
return obj.properties[pivotKey ?? 'id'];
|
||||||
|
else if (pivotKey == null || pivotKey == 'id')
|
||||||
|
return obj.id;
|
||||||
|
else
|
||||||
|
return reflect(obj).getField(new Symbol(pivotKey ?? 'id')).reflectee;
|
||||||
|
}
|
||||||
|
|
||||||
|
_normalize(obj) async {
|
||||||
|
// First, resolve pivot
|
||||||
|
var id = await _getLocalKey(obj);
|
||||||
|
var indexed = await pivotService.index({
|
||||||
|
'query': {pivotKey ?? 'userId': id}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (indexed == null || indexed is! List || indexed.isNotEmpty != true) {
|
||||||
|
await _assignForeignObjects([], obj);
|
||||||
|
} else {
|
||||||
|
// Now, resolve from foreign service
|
||||||
|
var mapped = await Future.wait(indexed.map((pivot) async {
|
||||||
|
var id = await _getPivotKey(obj);
|
||||||
|
var indexed = await foreignService.index({
|
||||||
|
'query': {foreignKey ?? 'postId': id}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (indexed == null ||
|
||||||
|
indexed is! List ||
|
||||||
|
indexed.isNotEmpty != true) {
|
||||||
|
await _assignForeignObjects([], pivot);
|
||||||
|
} else {
|
||||||
|
await _assignForeignObjects(indexed, pivot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pivot;
|
||||||
|
}));
|
||||||
|
await _assignForeignObjects(mapped, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.result is Iterable) {
|
||||||
|
await Future.wait(e.result.map(_normalize));
|
||||||
|
} else
|
||||||
|
await _normalize(e.result);
|
||||||
|
};
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ HookedServiceEventListener hasOne(Pattern servicePath,
|
||||||
String localKey,
|
String localKey,
|
||||||
getLocalKey(obj),
|
getLocalKey(obj),
|
||||||
assignForeignObject(foreign, obj)}) {
|
assignForeignObject(foreign, obj)}) {
|
||||||
|
|
||||||
return (HookedServiceEvent e) async {
|
return (HookedServiceEvent e) async {
|
||||||
var ref = e.service.app.service(servicePath);
|
var ref = e.service.app.service(servicePath);
|
||||||
var foreignName = as?.isNotEmpty == true
|
var foreignName = as?.isNotEmpty == true
|
||||||
|
@ -24,10 +25,6 @@ HookedServiceEventListener hasOne(Pattern servicePath,
|
||||||
: pluralize.singular(servicePath.toString());
|
: pluralize.singular(servicePath.toString());
|
||||||
if (ref == null) throw noService(servicePath);
|
if (ref == null) throw noService(servicePath);
|
||||||
|
|
||||||
if (foreignKey == null)
|
|
||||||
print(
|
|
||||||
'WARNING: No foreign key provided for hasOne, defaulting to \'userId\'.');
|
|
||||||
|
|
||||||
_getLocalKey(obj) {
|
_getLocalKey(obj) {
|
||||||
if (getLocalKey != null)
|
if (getLocalKey != null)
|
||||||
return getLocalKey(obj);
|
return getLocalKey(obj);
|
||||||
|
@ -59,7 +56,7 @@ HookedServiceEventListener hasOne(Pattern servicePath,
|
||||||
'query': {foreignKey ?? 'userId': id}
|
'query': {foreignKey ?? 'userId': id}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexed?.isNotEmpty != true) {
|
if (indexed == null || indexed is! List || indexed.isNotEmpty != true) {
|
||||||
await _assignForeignObject(null, obj);
|
await _assignForeignObject(null, obj);
|
||||||
} else {
|
} else {
|
||||||
var child = indexed.first;
|
var child = indexed.first;
|
||||||
|
|
|
@ -2,7 +2,7 @@ author: Tobe O <thosakwe@gmail.com>
|
||||||
description: Database-agnostic relations between Angel services.
|
description: Database-agnostic relations between Angel services.
|
||||||
homepage: "https://github.com/angel-dart/relations.git"
|
homepage: "https://github.com/angel-dart/relations.git"
|
||||||
name: angel_relations
|
name: angel_relations
|
||||||
version: 1.0.0-alpha
|
version: 1.0.0
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -9,8 +9,8 @@ main() {
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = new Angel()
|
app = new Angel()
|
||||||
..use('/authors', new MapService())
|
..use('/authors', new CustomMapService())
|
||||||
..use('/books', new MapService());
|
..use('/books', new CustomMapService());
|
||||||
|
|
||||||
await app.configure(seed(
|
await app.configure(seed(
|
||||||
'authors',
|
'authors',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:json_god/json_god.dart' as god;
|
import 'package:json_god/json_god.dart' as god;
|
||||||
|
|
||||||
class MapService extends Service {
|
class CustomMapService extends Service {
|
||||||
final List<Map> _items = [];
|
final List<Map> _items = [];
|
||||||
|
|
||||||
Iterable<Map> tailor(Iterable<Map> items, Map params) {
|
Iterable<Map> tailor(Iterable<Map> items, Map params) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ main() {
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = new Angel()
|
app = new Angel()
|
||||||
..use('/authors', new MapService())
|
..use('/authors', new CustomMapService())
|
||||||
..use('/books', new MapService());
|
..use('/books', new CustomMapService());
|
||||||
|
|
||||||
await app.configure(seed(
|
await app.configure(seed(
|
||||||
'authors',
|
'authors',
|
||||||
|
|
|
@ -9,8 +9,8 @@ main() {
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = new Angel()
|
app = new Angel()
|
||||||
..use('/authors', new MapService())
|
..use('/authors', new CustomMapService())
|
||||||
..use('/books', new MapService());
|
..use('/books', new CustomMapService());
|
||||||
|
|
||||||
await app.configure(seed(
|
await app.configure(seed(
|
||||||
'authors',
|
'authors',
|
||||||
|
|
Loading…
Reference in a new issue