I found a bug concerning the ComplexType properties. I have an Object which has a property which is a collection of other objects which I defined in the client-side metadata as a Complex property, i.e. "isComplexType: true". However all properties of a complex type are set to 'undefined'. I did a bit of debugging and found this:
// target and source may not be entities that can also be complexTypes.
function updatePropertyFromRawEntity(dp, target, rawSource) {
var val = getPropertyFromRawEntity(rawSource, dp);
if (val === undefined) return;
if (dp.isComplexProperty) {
var coVal = target.getProperty(dp.name);
dp.dataType.dataProperties.forEach(function (cdp) {
// recursive call
updatePropertyFromRawEntity(cdp, coVal, val);
});
} else {
target.setProperty(dp.name, val);
}
}
The rawSource parameter is an array(in case of complexTypes) is an array of objects. So the problem is when invoking the getPropertyFromRawEntity(), it passes an array not an object:
function getPropertyFromRawEntity(rawEntity, dp) {
var propName = dp.nameOnServer || dp.isUnmapped && dp.name;
return parseValueForDp(rawEntity[propName], dp);
}
So rawEntity[propName] will always be undefined because its an array.
A Breeze ComplexType has a well defined structure specified by metadata. Any property, (except) the key can have its datatype be a complexType. However, what you want, I think, is an "untyped" structure. This can be accomplished by defining the property as having a data type of "Undefined", something like:
var newProp = new DataProperty({
name: "Foo"
dataType: DataType.Undefined,
isNullable: true,
isUnmapped: true
});
entityType.addProperty(newProp);
The "Undefined" dataType will accept data of any type and structure.
Related
I have this Python code:
def from_json(cls, data: dict, api: Optional[Overpass] = None) -> "Result":
"""
Create a new instance and load data from json object.
:param data: JSON data returned by the Overpass API
:param api:
:return: New instance of Result object
"""
result = cls(api=api)
elem_cls: Type[Union["Area", "Node", "Relation", "Way"]]
for elem_cls in [Node, Way, Relation, Area]:
for element in data.get("elements", []):
e_type = element.get("type")
if hasattr(e_type, "lower") and e_type.lower() == elem_cls._type_value:
result.append(elem_cls.from_json(element, result=result))
return result
and I want to convert this to Dart code. This is what I've come up with:
factory Result.fromJson(Map<String, dynamic> data, {Overpass? api}) {
Result result = Result(api: api!);
Type elemCls;
for (elemCls in [Node, Way, Relation, Area]) {
for (Map<String, dynamic> element in data["elements"]) {
String eType = element["type"];
if (eType.toLowerCase() == elemCls.typeValue) {
result.append(elemCls.fromJson(element, result: result));
}
}
}
return result;
}
My problems are The getter '_typeValue' isn't defined for the type 'Type'. and The method 'fromJson' isn't defined for the type 'Type'.. This is because _typeValue and fromJson belong to the classes Node, Way, Relation and Area and not to Type which is what I get with this for loop. What do I have to change to get this working like in Python?
You could do eType.toLowerCase() == elemCls.toString().toLowerCase(). However, elem_cls.from_json(element, result=result) cannot be directly done in Dart. There is no way to generically instantiate an object with type elemCls.
What you instead could do is create a lookup table to map type names to constructors:
final _constructionTable = {
"node": Node.fromJson,
"way": Way.fromJson,
"relation": Relation.fromJson,
"area": Area.fromJson,
};
factory Result.fromJson(Map<String, dynamic> data, {Overpass? api}) {
Result result = Result(api: api!);
for (Map<String, dynamic> element in data["elements"]) {
String eType = element["type"]!;
var fromJson = _constructionTable[eType.toLowerCase()];
if (fromJson != null) {
result.append(fromJson(element, result: result));
}
}
return result;
}
Note that this also would be a bit more efficient too, and it avoids relying on the specific strings generated for the Dart types.
I am trying to generate mock data using relay for storybook.
My query is
const QUERY_LIST = graphql`
query modelControllerAllUsersQuery #relay_test_operation {
allUsers {
pageInfo {
hasNextPage
}
edges {
node {
id
firstName
lastName
}
}
}
}
`
and provided RelayEnvironmentProvider as a decorator to the story. I'm trying to return some default values to my query using custom mock resolvers.
const customMockResolvers = {
...mockResolvers,
allUsers:() => ({
pageInfo:{
hasNextPage:false,
},
edges:[
{
node:{
id :'id',
firstName:'fname',
lastName :'lname',
},
},
],
}),
};
and calling it as
(operation) => MockPayloadGenerator.generate(operation, customMockResolvers)
I don't seem to be able to get the default values returned.
Currently, it is returning
{"allUsers":{"pageInfo":{"hasNextPage":false},"edges":[{"node":{"id":"<UserNode-mock-id-1>","firstName":"<mock-value-for-field-\"firstName\">","lastName":"<mock-value-for-field-\"lastName\">"}}]}}
What am I doing wrong?
When using the #relay-test-operation, the keys within your customMockResolvers object must match the type name of the fields, which can be different from the field names themselves.
For example, you could have the following in your schema:
type Foo {
id: ID!
name: String!
}
and the following query:
query FooQuery #relay_test_operation {
foo {
id
name
}
}
Then the customMockResolvers object would look like this:
const customMockResolvers = {
Foo: () => ({
id: "fooId",
name: "fooName"
})
}
Notice that I'm passing in Foo as the key instead of foo.
You can check your schema and see what the the type name of allUsers is. I suspect it would be something like AllUsers or allUsersConnection, or something similar.
Also, if you're interested in creating Storybook stories for Relay components, I created a NPM package just for that: https://www.npmjs.com/package/use-relay-mock-environment
It doesn't require adding the #relay-test-operation directive to your query, and instead relies only on resolving the String type (which is the default for all scalar properties). You can of course still add the #relay-test-operation directive and also extend the resolvers by providing customResolvers in the config.
You can also extend the the String resolver as well, by providing extendStringResolver in the config.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!
Pretty much as the title says: If you have a Type stored in a variable, there's no way to compare your actual object to this type variable, as far as I can tell. I can probably accomplish what I'm trying to do with mirrors, but I'd prefer not to if at all possible.
void example() {
Type myType = String;
String myExample = "Example";
//Syntax error here: The name 'myType' is not a type and cannot be used in an 'is' expression
if (myExample is myType) {
}
}
You can't generally test if a value is of a type using the Type object.
Type objects are reflected types, not real types. They represent the real type, but you can't use them in the code where you need a type: as type assertions, as generic type parameters or with the is/as operators. You must use the name of a type in those places, and not the name of a normal variable that happens to hold a Type object.
Clever stuff using mirrors might get there, but it's likely overkill for most cases (and I understand that you don't want it).
What you might be able to do instead, is to not pass around raw Type objects. You could instead make your own type abstraction, something like:
class MyType<T> {
const MyType();
Type get type => T;
bool isA(Object object) => object is T;
}
Then you can use that to represent types, not a Type object, and do something like:
void main(List<String> args) {
MyType myType = const MyType<String>();
String myExample = "Example";
if(myType.isA(myExample)) {
print('is');
} else {
print('is not');
}
}
That does require that your entire program uses your type objects to pass around types, but it also gives you a lot of control over those objects, so you can implement the functionality that you need.
I tried
library x;
void main(List<String> args) {
Type myType = String;
String myExample = "Example";
if(myExample.runtimeType == myType) {
print('is');
} else {
print('is not');
}
}
and it worked.
I have not much experience with such code in Dart though. Maybe that is not a fail-safe approach.
import 'package:reflection/reflection.dart';
void main() {
var childType = typeInfo(Child);
var baseType = typeInfo(Base);
if(childType.isA(baseType)) {
print("Child is Base");
}
if(baseType.isAssignableFrom(childType)) {
print("Base is assignable from Child");
}
}
class Base {
}
class Child extends Base {
}
Child is Base
Base is assignable for Child
P.S.
The "reflection" package incompatible with dart2js. It work only when used in Dart language.
I am trying to connect to a third party service using Breeze with a custom JsonResultsAdapter.
The third party service has the "metadata" related to an entity in the root node of the array, then the variables are in a "data" property on the "metadata" object.
The format has two ways of defining relationships. One is via a "#ref" field which references the id of another entity. The other is by having the related object defined inline (instead of the "#ref") which does not have an explicit id, but which is only ever referenced by the "parent" object.
The data looks like:
[{
"id" : "abc",
"type" : "foo",
"data": { "relationshipRef" : { "#ref" : "someid" } }
},
{
"id": "someid",
"type" : "bar",
"data" : { "relationshipInline" : { "type" : "baz",
"data" : { "something" : "whatever",
"innerRelation" : { "#ref" : "abc"}
}
}
}]
I'm currently (in JsonResultsAdapter's visitNode function) moving the properties in the "data" object up into the "root" node, and then replacing any object with an "#ref" property with the value of the "#ref" key and appending an ID to the end (so that relationships can use the original name in the EntityType). IE, the first object would become:
{
"id" : "abc",
"type" : "foo",
"relationshipRefID" : "someid"
}
This works for top level entities and relationships, but I'm having problems with the nested ones.
How would you approach solving this problem?
I was going to use ComplexTypes but the documentation mentioned that they cannot have "navigationProperties" (relationships), which as you can see above is required (the "innerRelation" property).
In some cases, the entities can be nested down to 3 levels or so.
Here is my current visitNode function:
visitNode: function(node, parseContext, nodeContext) {
if(node instanceof Object && node.type != null) {
if(node.deleted) {
//TODO: make sure the object is removed from the manager
return {ignore:true};
}
//We need to tweak the data structure to fit what breeze expects.
//It expects properties to be in the same level as the "metadata" for an object ("type" etc),
//So we need to move the properties from the data object into the node, and fix up relationships.
if(parseContext.entityManager.metadataStore.getEntityType(node.type, true) != null) {
var data = node.data;
for(var key in data) {
var prop = data[key];
//Move any foreign key fields to be "relationID":id instead of "relation":{"#ref":id}
if(prop instanceof Object) {
var ref = prop["#ref"];
if(ref != null) {
node[key+"ID"] = ref
data[key] = null;
continue;
}
}
//TODO: Handle inline references <- This is where I need help!
node[key] = data[key];
}
return {
entityType: node.type,
nodeId: node.id
}
}
else {
return {ignore:true};
}
}
}
Well, apparently I should have tested more before asking here.
It turns out that this works automatically based on the navigationProperties defined in the model! Awesome. I did have to generate ids for the inner nodes that did not have them, but that was simple.
the following code will often run out of stack space because the type Entity has a property named EntityAspect, which has a property named Entity of type Entity which points to the owning Entity. This recursive definition causes several tools to fail or run incredibly slow, but most notably, knockout. Can anything be done to address this?
var custType = _this.metadataStore.getEntityType("Customer");
var cust1 = custType.createEntity();
var js = ko.toJS(cust1);
I haven't actually tried this yet but I think you can do this
var js = ko.mapping.toJS(cust1, {
ignore: ['entityAspect']
});
I found I needed to ignore both entityAspect and entityType (snippet from custom datasource kendo datasource):
this.entityManager.executeQuery(query)
.then(function (xhr) {
if (self.autoMapToJS) { // Breeze entities contain recursive properties (ugh!) - eliminate those
payload.data = ko.mapping.toJS(xhr.results, {
ignore: ['entityAspect', 'entityType']
});
} else {
payload.data = xhr.results;
}
if (self.inlineCount) {
payload.total = xhr.inlineCount;
}
options.success(payload); // notify the DataSource that the operation is complete
})
.fail(function (rejected) {
payload.error = rejected;
})
.done(); // terminate chain of promises
}
In particular, trying to use with grids (wijmo & kendo), I was forced to map breeze data or enjoy stackoverflows as those controls would iterate through the properties.