Aqueduct ORM ManagedObject's transient property being persisted - dart

I have an Aqueduct project using the ORM, with a data model as follows:
class _Thing {
#primaryKey
int id;
String first;
String second;
}
class Thing extends ManagedObject<_Thing> implements _Thing {
#Serialize()
OtherThing get firstAndSecond() {
// return some value computed from first and second
}
#Serialize()
set firstAndSecond(OtherThing firstAndSecond) {
// set first and second based on some computation
}
}
According to the docs for transient properties, annotating with #Serialize() should enable this model to be serialized/deserialized. It also says that properties in ManagedObjects are not persisted, but when I run the server, I get the error:
Data Model Error: Property 'firstAndSecond' on 'Thing' has an unsupported type.
If I remove the #Serialize(), it doesn't try to persist it, but I can't serialize/deserialize this object.
Any suggestions as to why this is happening or how I can control this behaviour?

This should be in the docs -
A Serializable property must be a primitive type (e.g. String, int, double, bool or a Map or List containing these types). Serializable values are passed directly to the codec that is reading from a request body or writing to a response body (by default, this codec is JSON). In the case of a custom type like OtherThing, the codec doesn't know how to encode or decode that type.
For complex types, you might use a map:
#Serialize()
Map<String, dynanic> get firstAndSecond() {
return {"first": first, "second": second};
}
You might also use CSV-like data:
#Serialize()
String get firstAndSecond() {
return "$first,$second";
}

Related

Dart - How to cast a generic templated class?

In Dart, can one convert a generic templated class of one type to another?
For example, here is a generic templated class that has one member:
class Response<T> {
T data;
}
I want to cast the <dynamic> variant to something specific, like <String>, after I have filled the dynamic member with valid String data.
For example:
final responseAsDynamic = Response(); // Type is Response<dynamic>
responseAsDynamic.data = 'John Smith'; // Type is still Response<dynamic>, but data is String
var responseAsString = Response<String>(); // Type is Response<String>
responseString = responseDynamic; // Error: Response<dynamic> is not a subtype of Response<String>
I understand that Response<dynamic> is not a subtype of Response<String>, but is there a way to tell the compiler that this conversion is ok?
List has cast(), but there doesn't seem to be anything comparable for class<>.
I ran into this while writing a handler for HTTP response data from Dio. A function like Dio.get() returns a generic Response<Map>, which I want to convert to a specifically typed response like Response<List<T>>.
I can get around the issue by simply returning the typed data, instead of trying to to cast the Response class. But this seemed like an interesting generic use-case worth exploring.
No, there is no automatic way to do the conversion. You will need to add a conversion function yourself similar to List's cast method. If you don't own the Response class, you could create an extension method:
extension ResponseCast<T> on Response<T> {
Response<U> cast<U>() {
return Response<U>()..data = this.data as U;
}
}
and then you could do:
responseString = responseDynamic.cast<String>();

Compare Dart #override Metadata Reflectee to Instance of _Override

I am trying to come up with an example of how we could search for the #override metadata annotation using reflection in Dart.
In the examples I used to learn the dart:mirrors library and reflection, they were always searching for custom made annotations.
Here is an example where they search for a custom "Todo" annotation
When searching for custom made annotations, they would simply compare the metadata's reflectee to the class data type to check for a match.
In Dart's documentation linked below, you can see an example implementation of an _Override instance.
Here is Dart documentation on the override constant
This lead to me to try:
if(meta.reflectee is _Override) {
print('Found!);
}
But the "_Override" cannot be resolved and suggest no imports to access such a data instance.
I am able to toString the reflectee for comparison but I feel like it is a dirty solution:
if (meta.reflectee.toString() == "Instance of '_Override'") {
print('Found!');
}
When using the #override annotation, I am struggling to find a way to compare the metadata's reflectee to the instance type of _Override.
Here is the Dog class:
class Dog extends Animal {
Dog(String name) : super(name);
#override
void makeNoise() {
print('Bark, bark!');
}
}
Here is my reflection search code:
Dog dog = Dog('Harper');
InstanceMirror instanceMirror = reflect(dog);
ClassMirror classMirror = instanceMirror.type;
classMirror.instanceMembers.forEach((_, member) {
print(member.owner.simpleName);
print(member.simpleName);
print(member.isRegularMethod);
member.metadata.forEach((meta) {
print(meta.reflectee);
if (meta.reflectee is _Override) {
print('Found!');
}
});
});
Finally, here is my output when the instanceMembers.forEach loop gets to the method I am interested in:
Symbol("Dog")
Symbol("makeNoise")
true
Instance of '_Override'
Use:
if (meta.reflectee == override) {
print('Found!);
}
or
if (identical(meta.reflectee, override)) {
print('Found!);
}
Dart constants are canonicalized, and the override object (an instance of the private _Override class) does not override operator== from Object, so the two expressions do the same thing.
For annotation classes which have data, you don't know the exact instance, so you have to do type checks. For marker annotations like override, which only have one instance, you can compare to the exact constant instance used for annotating.

Source generator: Get actual type for FieldElement

So I've been playing around with code generation in Dart, and I have a problem resolving Element type in "data" classes. I have 2 data classes, and 2nd one has a type of a 1st one defined as a field.
first.dart:
part 'first.g.dart';
#Data(name="First")
class _First {
}
second.dart:
import 'first.dart';
part 'second.g.dart';
#Data(name="Second")
class _Second {
First first;
}
You might have noticed, that _Second has field First instead of _First. That's because I generate two new classes. These two new classes contain additional functionality, like generated hashCode and some other custom methods I need.
In my Generator class, I need to obtain type string of field first. I'm doing this by fieldElement.type?.getDisplayString().
This works fine for all other non-generated classes, like num for example.
But in case of first field, returned type string is "dynamic".
Is there a way to obtain correct type? Should "First" in this case.
data_generator.dart
class Data extends GeneratorForAnnotation<Data> {
#override
generateForAnnotatedElement(Element element, ConstantReader annotation,
BuildStep buildStep) {
if(element is ClassElement) {
FieldElement element = findField();
String type = element.type?.getDisplayString();
assert(type != "dynamic");
//...
}
}
//...
}

How can I change neo4j Id to UUID and get finder methods to work?

Neo4j needs an id field to work which is of type Long. This works well with Spring data neo4j. I would like to have another field of type UUID and have T findOne(T id) to work with my UUID not neo generated Ids.
As I am using Spring Data Rest, I don't want to expose neo's id in the URL.
http://localhost:8080/resource/{neoId}
to
http://localhost:8080/resource/{uuid}
Any ideas if this is possible?
UPDATED
{
name: "Root",
resourceId: "00671e1a-4053-4a68-9c59-f870915e3257",
_links: {
self: {
href: "http://localhost:8080/resource/9750"
},
parents: {
href: "http://localhost:8080/resource/9750/parents"
},
children: {
href: "http://localhost:8080/resource/9750/children"
}
}
}
When it comes to the finder method in your repository you are at liberty to either override the one provided in the CrudRepository in your own interface, or provide an alternative which may be T findByUuid(Long uuid).
Depending on how you are modelling your classes you can rely on the derived query from your method name, or you can annotate with a query, something like:
#Query(value = "MATCH (n:YourNodeType{uuid:{0}) RETURN n")
If you are going to be using a specific UUID class then you will need to tell Neo how to persist the UUID value. If you are will store it as a String (as seems sensible) then I believe there is no need to annotate the field, anything else then you will need a GraphProperty annotation:
#GraphProperty(propertyType = Long.class)
Again depending on the class of UUID you may need to register a conversion class with Spring which implements the org.springframework.core.convert.converter.Converter interface and converts between your domain class type (UUID) and the stored type (String).
Or, just convert the UUID to a string and store it yourself and don't worry about all the conversion.
Whatever you do, make sure that your new uuid is indexed and presumably unique.
You can add a String attribute to your entities, calling it uuid, and simply declare a E findByUuid(String uuid) in your Repository for E, Spring Data will automatically generate code for it.
For example:
#NodeEntity
public class Entity {
...
#Indexed
private String uuid;
...
public String getUuid() {
return uuid;
}
void setUuid(String uuid) {
this.uuid = uuid;
}
...
}
public interface EntityRepository extends GraphRepository<Entity> {
...
Entity findByUuid(String uuid);
...
}

Can non-trivial constructors call Future returning functions (how or alternatives)

Suppose you have:
class Schema {
Schema.fromText(String jsonString) {
...
}
}
In this constructor, assume there is an URL provided in the jsonString to download data and the only API to read an URL is one that returns a Future. Also, assume Schema is only a valid object when that URL data has been read and processed. Is it possible to even implement ...?
What you want to do is not possible with standard constructors.
Instead, try a static method that returns a new instance wrapped in a Future.
Something like:
class Schema {
Schema._fromApi(String apiResults) { ... }
static Future<Schema> build(String jsonString) {
return getContentsOfUrl(jsonString['url'])
.then((contents) => new Schema._fromApi(contents));
}
}

Resources