Skip to content

Commit 4af4d68

Browse files
Shubhamgreenrobot-team
authored andcommitted
external types: create two separate annotations, @ExternalType and @ExternalName
1 parent 930ae01 commit 4af4d68

File tree

11 files changed

+283
-85
lines changed

11 files changed

+283
-85
lines changed

generator/lib/src/code_builder.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import 'dart:async';
2-
import 'dart:io';
32
import 'dart:convert';
3+
import 'dart:io';
44

55
import 'package:build/build.dart';
66
import 'package:collection/collection.dart';
7+
import 'package:dart_style/dart_style.dart';
78
import 'package:glob/glob.dart';
9+
import 'package:objectbox/internal.dart';
810
import 'package:objectbox_generator/src/analysis/analysis.dart';
911
import 'package:objectbox_generator/src/builder_dirs.dart';
1012
import 'package:path/path.dart' as path;
11-
import 'package:objectbox/internal.dart';
12-
import 'package:dart_style/dart_style.dart';
13-
import 'package:source_gen/source_gen.dart';
1413
import 'package:pubspec_parse/pubspec_parse.dart';
14+
import 'package:source_gen/source_gen.dart';
1515

16+
import 'code_chunks.dart';
1617
import 'config.dart';
1718
import 'entity_resolver.dart';
18-
import 'code_chunks.dart';
1919

2020
/// CodeBuilder collects all '.objectbox.info' files created by EntityResolver and generates objectbox-model.json and
2121
/// objectbox_model.dart
@@ -244,6 +244,8 @@ class CodeBuilder extends Builder {
244244

245245
relInModel.name = rel.name;
246246
relInModel.targetName = rel.targetName;
247+
relInModel.externalPropertyType = rel.externalPropertyType;
248+
relInModel.externalPropertyName = rel.externalPropertyName;
247249
}
248250

249251
IdUid mergeEntity(ModelInfo modelInfo, ModelEntity entity) {

generator/lib/src/code_chunks.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,19 @@ class CodeChunks {
174174
}
175175

176176
static String createModelRelation(ModelRelation relation) {
177+
var additionalArgs = '';
178+
if (relation.externalPropertyType != null) {
179+
additionalArgs += ", externalType: ${relation.externalPropertyType!}";
180+
}
181+
if (relation.externalPropertyName != null) {
182+
additionalArgs += ", externalName: '${relation.externalPropertyName!}'";
183+
}
177184
return '''
178185
$obxInt.ModelRelation(
179186
id: ${createIdUid(relation.id)},
180187
name: '${relation.name}',
181188
targetId: ${createIdUid(relation.targetId)}
189+
$additionalArgs
182190
)
183191
''';
184192
}

generator/lib/src/entity_resolver.dart

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class EntityResolver extends Builder {
2828
final _indexChecker = const TypeChecker.fromRuntime(Index);
2929
final _backlinkChecker = const TypeChecker.fromRuntime(Backlink);
3030
final _hnswChecker = const TypeChecker.fromRuntime(HnswIndex);
31-
final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalProperty);
31+
final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalType);
32+
final _externalNameChecker = const TypeChecker.fromRuntime(ExternalName);
3233

3334
@override
3435
FutureOr<void> build(BuildStep buildStep) async {
@@ -172,6 +173,7 @@ class EntityResolver extends Builder {
172173
final backlinkAnnotations =
173174
_backlinkChecker.annotationsOfExact(annotated);
174175
if (backlinkAnnotations.isNotEmpty) {
176+
// Handles ToMany based on other ToOne or ToMany relation (backlink)
175177
if (!isToManyRel) {
176178
log.severe(
177179
" Skipping property '${f.name}': @Backlink() may only be used with ToMany.");
@@ -184,14 +186,37 @@ class EntityResolver extends Builder {
184186
entity.backlinks.add(backlink);
185187
log.info(' $backlink');
186188
} else if (isToManyRel) {
189+
// Handles standalone (non backlink) ToMany relation
190+
191+
// @ExternalType
192+
int? externalPropertyType;
193+
_externalTypeChecker.runIfMatches(annotated, (annotation) {
194+
final externalTypeId = _readExternalTypeParams(annotation);
195+
externalPropertyType = externalTypeId;
196+
});
197+
198+
// @ExternalName
199+
String? externalPropertyName;
200+
_externalNameChecker.runIfMatches(annotated, (annotation) {
201+
if (externalPropertyType == null) {
202+
throw InvalidGenerationSourceError(
203+
"@ExternalName annotation requires @ExternalType annotation to be present");
204+
}
205+
externalPropertyName = _readExternalNameParams(annotation);
206+
});
207+
187208
// create relation
188209
final rel = ModelRelation.create(IdUid(0, propUid ?? 0), f.name,
189210
targetName: relTargetName,
190-
uidRequest: propUid != null && propUid == 0);
211+
uidRequest: propUid != null && propUid == 0,
212+
externalPropertyName: externalPropertyName,
213+
externalPropertyType: externalPropertyType);
214+
191215
entity.relations.add(rel);
192216

193217
log.info(' $rel');
194218
} else {
219+
// Handles regular properties
195220
// create property (do not use readEntity.createProperty in order to avoid generating new ids)
196221
final prop = ModelProperty.create(
197222
IdUid(0, propUid ?? 0), f.name, fieldType,
@@ -227,8 +252,20 @@ class EntityResolver extends Builder {
227252
_readHnswIndexParams(annotation, prop);
228253
});
229254

255+
// @ExternalType
230256
_externalTypeChecker.runIfMatches(annotated, (annotation) {
231-
_readExternalTypeParams(annotation, prop);
257+
final externalTypeId = _readExternalTypeParams(annotation);
258+
prop.externalPropertyType = externalTypeId;
259+
});
260+
261+
// @ExternalName
262+
_externalNameChecker.runIfMatches(annotated, (annotation) {
263+
if (prop.externalPropertyType == null) {
264+
throw InvalidGenerationSourceError(
265+
"@ExternalName annotation requires @ExternalType annotation to be present");
266+
}
267+
final externalName = _readExternalNameParams(annotation);
268+
prop.externalPropertyName = externalName;
232269
});
233270

234271
// for code generation
@@ -561,17 +598,25 @@ class EntityResolver extends Builder {
561598
property.hnswParams = ModelHnswParams.fromAnnotation(hnswRestored);
562599
}
563600

564-
void _readExternalTypeParams(DartObject annotation, ModelProperty property) {
601+
int _readExternalTypeParams(DartObject annotation) {
565602
final typeIndex =
566-
_enumValueIndex(annotation.getField('type')!, "ExternalProperty.type");
567-
final type = typeIndex != null ? ExternalType.values[typeIndex] : null;
603+
_enumValueIndex(annotation.getField('type')!, "ExternalType.type");
604+
final type =
605+
typeIndex != null ? ExternalPropertyType.values[typeIndex] : null;
568606
if (type == null) {
569607
throw InvalidGenerationSourceError(
570-
"'type' attribute not specified in @ExternalProperty annotation");
608+
"'type' attribute not specified in @ExternalType annotation");
571609
}
610+
return externalTypeToOBXExternalType(type);
611+
}
612+
613+
String _readExternalNameParams(DartObject annotation) {
572614
final name = annotation.getField('name')!.toStringValue();
573-
property.externalPropertyType = externalTypeToOBXExternalType(type);
574-
property.externalPropertyName = name;
615+
if (name == null) {
616+
throw InvalidGenerationSourceError(
617+
"'name' attribute not specified in @ExternalName annotation");
618+
}
619+
return name;
575620
}
576621
}
577622

generator/test/code_builder_test.dart

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,9 @@ void main() {
379379
});
380380
});
381381

382-
test('ExternalType annotation', () async {
383-
final source = r'''
382+
group("ExternalType and ExternalName annotations", () {
383+
test('annotations work on properties', () async {
384+
final source = r'''
384385
library example;
385386
import 'package:objectbox/objectbox.dart';
386387
@@ -390,25 +391,83 @@ void main() {
390391
int id = 0;
391392
392393
@Property(type: PropertyType.byteVector)
393-
@ExternalProperty(type: ExternalType.bson, name: 'my-mongo-bson')
394-
List<int>? mongoBson;
394+
@ExternalType(type: ExternalPropertyType.mongoId)
395+
List<int>? mongoId;
396+
397+
@ExternalType(type: ExternalPropertyType.uuid)
398+
@ExternalName(name: 'my-mongo-uuid')
399+
List<int>? mongoUuid;
400+
}
401+
''';
402+
403+
final testEnv = GeneratorTestEnv();
404+
await testEnv.run(source);
405+
406+
final property1 = testEnv.model.entities[0].properties
407+
.firstWhere((element) => element.name == "mongoId");
408+
expect(property1.externalPropertyType, OBXExternalPropertyType.MongoId);
409+
410+
final property2 = testEnv.model.entities[0].properties
411+
.firstWhere((element) => element.name == "mongoUuid");
412+
expect(property2.externalPropertyType, OBXExternalPropertyType.Uuid);
413+
expect(property2.externalPropertyName, "my-mongo-uuid");
414+
});
415+
416+
test('annotations work on ToMany (standalone) relations', () async {
417+
final source = r'''
418+
library example;
419+
import 'package:objectbox/objectbox.dart';
420+
421+
@Entity()
422+
class Student{
423+
int id;
424+
425+
@ExternalType(type: ExternalPropertyType.mongoId)
426+
final rel1 = ToMany<Student>();
427+
428+
@ExternalType(type: ExternalPropertyType.uuid)
429+
@ExternalName(name: 'my-courses-rel')
430+
final rel2 = ToMany<Student>();
431+
}
432+
''';
433+
434+
final testEnv = GeneratorTestEnv();
435+
await testEnv.run(source);
436+
437+
final relation1 = testEnv.model.entities[0].relations
438+
.firstWhere((element) => element.name == "rel1");
439+
expect(relation1.externalPropertyType, OBXExternalPropertyType.MongoId);
440+
441+
final relation2 = testEnv.model.entities[0].relations
442+
.firstWhere((element) => element.name == "rel2");
443+
expect(relation2.externalPropertyType, OBXExternalPropertyType.Uuid);
444+
expect(relation2.externalPropertyName, "my-courses-rel");
445+
});
446+
});
447+
448+
test('Only ExternalName annotation fails', () async {
449+
final source = r'''
450+
library example;
451+
import 'package:objectbox/objectbox.dart';
452+
453+
@Entity()
454+
class Example {
455+
@Id()
456+
int id = 0;
395457
396-
@ExternalProperty(type: ExternalType.javaScript, name: 'my-mongo-js')
397-
String? mongoJS;
458+
@ExternalName(name: 'my-mongo-uuid')
459+
List<int>? mongoUuid;
398460
}
399461
''';
400462

401463
final testEnv = GeneratorTestEnv();
402-
await testEnv.run(source);
403-
404-
final property1 = testEnv.model.entities[0].properties
405-
.firstWhere((element) => element.name == "mongoBson");
406-
expect(property1.externalPropertyType, OBXExternalPropertyType.Bson);
407-
expect(property1.externalPropertyName, "my-mongo-bson");
408-
final property2 = testEnv.model.entities[0].properties
409-
.firstWhere((element) => element.name == "mongoJS");
410-
expect(property2.externalPropertyType, OBXExternalPropertyType.JavaScript);
411-
expect(property2.externalPropertyName, "my-mongo-js");
464+
await expectLater(
465+
() async => await testEnv.run(source),
466+
throwsA(isA<InvalidGenerationSourceError>().having(
467+
(e) => e.message,
468+
'message',
469+
contains(
470+
"@ExternalName annotation requires @ExternalType annotation to be present"))));
412471
});
413472
}
414473

objectbox/lib/src/annotations.dart

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,8 @@ class HnswIndex {
491491
/// A property type of an external system (e.g. another database) that has no
492492
/// default mapping to an ObjectBox type.
493493
///
494-
/// Use with [ExternalProperty].
495-
enum ExternalType {
494+
/// Use with [ExternalType].
495+
enum ExternalPropertyType {
496496
/// Representing type: ByteVector
497497
///
498498
/// Encoding: 1:1 binary representation, little endian (16 bytes)
@@ -581,15 +581,30 @@ enum ExternalType {
581581
mongoRegex
582582
}
583583

584-
/// Annotates a property to be stored in an external system.
585-
class ExternalProperty {
586-
/// The name assigned to the property in the external system.
587-
final String? name;
588-
584+
/// See the constructor documentation.
585+
class ExternalType {
589586
/// The type of the property in the external system.
590-
/// See [ExternalType] for possible values.
591-
final ExternalType type;
587+
///
588+
/// See [ExternalPropertyType] for possible values.
589+
final ExternalPropertyType type;
590+
591+
/// Sets the type of a property or the type of object IDs of a [ToMany] in an
592+
/// external system (like another database).
593+
///
594+
/// This is useful if there is no default mapping of the ObjectBox type to the
595+
/// type in the external system.
596+
///
597+
/// Carefully look at the documentation of the external type to ensure it is
598+
/// compatible with the ObjectBox type.
599+
const ExternalType({required this.type});
600+
}
601+
602+
/// See the constructor documentation.
603+
class ExternalName {
604+
/// The name assigned to the property in the external system.
605+
final String name;
592606

593-
/// Create an ExternalProperty annotation.
594-
const ExternalProperty({required this.type, this.name});
607+
/// Sets the name of an @Entity, a property or a [ToMany] in an external
608+
/// system (like another database).
609+
const ExternalName({required this.name});
595610
}

objectbox/lib/src/modelinfo/enums.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -240,37 +240,37 @@ abstract class OBXPropertyType {
240240
static const int DateNanoVector = 32;
241241
}
242242

243-
int externalTypeToOBXExternalType(ExternalType type) {
243+
int externalTypeToOBXExternalType(ExternalPropertyType type) {
244244
switch (type) {
245-
case ExternalType.int128:
245+
case ExternalPropertyType.int128:
246246
return OBXExternalPropertyType.Int128;
247-
case ExternalType.uuid:
247+
case ExternalPropertyType.uuid:
248248
return OBXExternalPropertyType.Uuid;
249-
case ExternalType.decimal128:
249+
case ExternalPropertyType.decimal128:
250250
return OBXExternalPropertyType.Decimal128;
251-
case ExternalType.flexMap:
251+
case ExternalPropertyType.flexMap:
252252
return OBXExternalPropertyType.FlexMap;
253-
case ExternalType.flexVector:
253+
case ExternalPropertyType.flexVector:
254254
return OBXExternalPropertyType.FlexVector;
255-
case ExternalType.json:
255+
case ExternalPropertyType.json:
256256
return OBXExternalPropertyType.Json;
257-
case ExternalType.bson:
257+
case ExternalPropertyType.bson:
258258
return OBXExternalPropertyType.Bson;
259-
case ExternalType.javaScript:
259+
case ExternalPropertyType.javaScript:
260260
return OBXExternalPropertyType.JavaScript;
261-
case ExternalType.int128Vector:
261+
case ExternalPropertyType.int128Vector:
262262
return OBXExternalPropertyType.Int128Vector;
263-
case ExternalType.uuidVector:
263+
case ExternalPropertyType.uuidVector:
264264
return OBXExternalPropertyType.UuidVector;
265-
case ExternalType.mongoId:
265+
case ExternalPropertyType.mongoId:
266266
return OBXExternalPropertyType.MongoId;
267-
case ExternalType.mongoIdVector:
267+
case ExternalPropertyType.mongoIdVector:
268268
return OBXExternalPropertyType.MongoIdVector;
269-
case ExternalType.mongoTimestamp:
269+
case ExternalPropertyType.mongoTimestamp:
270270
return OBXExternalPropertyType.MongoTimestamp;
271-
case ExternalType.mongoBinary:
271+
case ExternalPropertyType.mongoBinary:
272272
return OBXExternalPropertyType.MongoBinary;
273-
case ExternalType.mongoRegex:
273+
case ExternalPropertyType.mongoRegex:
274274
return OBXExternalPropertyType.MongoRegex;
275275
default:
276276
throw ArgumentError.value(type, 'type', 'Invalid ExternalType');

0 commit comments

Comments
 (0)