To avoid duplication of data structures I wanted to reuse a type definition on an input type like this
export const DeviceStatus = new ObjectType('DeviceStatus', {
definition: {
time: timestamp,
firmwareVersion: string
},
});
export const DeviceStatusInput = new InputType('DeviceStatusInput', {
definition: {
tenantId: id_required,
deviceId: id_required,
// Reuse of DeviceStatus Field definition
status: DeviceStatus.attribute()
}
});
There is no error since the return type of DeviceStatus.attribute() is fine, and this works for ObjectType inheritance.
From my perspective this should work, but deploying results in a nasty "Internal Error creating Schema" error.
Of course I could move the whole definition into an object and reuse it but that seems weird. Is there any good solution on this for the CodeFirst approach
It seem to be invalid to reference object type in input type.
I recommend to view Can you make a graphql type both an input and output type?
Probably best you can do is to create some convenience method which will create you both object and input type from single definition.
Related
I want that every time I use yup.string(), it will add a specific default value for it
for example:
const schema = yup.object({
text: yup.string()// I want it to also do .default('some string') in the background,
});
or - another option - is there any way to set the default value after creating the scheme? something like setDefault('text', 'some string')
The closest solution I came across to solve your issue is extending your string with a custom method that implements your needs. To do that you need to use addMethod from yup:
import { addMethod, string } from 'yup';
addMethod(string, 'append', function append(appendStr) {
return this.transform((value) => `${value}${appendStr}`);
});
Now, you can use your custom method (append) and apply it to any string you want:
string().append('~~~~').cast('hi'); // 'hi~~~~'
If you want to add the custom method to all your schema types like date, number, etc..., you need to extend the abstract base class Schema:
import { addMethod, Schema } from 'yup';
addMethod(Schema, 'myCustomMethod', ...)
Extra
For Typescript
In your type definition file, you need to declare module yup with your custom method's arguments and return types:
// globals.d.ts
import { StringSchema } from "yup";
declare module 'yup' {
interface StringSchema<TType, TContext, TDefault, TFlags> {
append(appendStr: string): this;
}
}
Unknow behavior for transform method
While I was trying to extend the functionality of the date schema with a custom method that transform the date that user enters from DD-MM-YYY to YYYY-MM-DD, the custom method broke after I used it with other methods like min, max for example.
// `dayMonthYear` should transform "31-12-2022"
// to "2022-12-31" but for some reason it kept
// ignoring the `cast` date and tried to transform
// `1900` instead!
Yup.date().dayMonthYear().min(1900).max(2100).required().cast("31-12-2022") // error
To work around this issue, I appended my custom method at the end of my schema chain:
Yup.date().min(1900).max(2100).required().cast("31-12-2022").dayMonthYear() // works as expected
This issue is mentioned in this GH ticket which I recommend going through it as it's going more in-depth on how to add custom methods with Typescript.
References
addMethod
Extending built-in schema with new methods
Example of addMethod in Typescript (GH ticket)
I have a table defined in Hasura (Postgres) using native uuid as the field datatype. My goal is to create an update mutation from Swift but the uuid type is not mapped implicitly from Postgres through Hasura and Apollo to Swift. The Hasura documentation states that the uuid type supports using String but when I define the variables, in the update mutation, as a String!,
mutation RedeemMagicLinkCode($code: String!, $iosIdentifierForVendor: String!) {
__typename
update_entityname(where: {code: {_eq: $code}, iosIdentifierForVendor: {_is_null: true}}, _set: {iosIdentifierForVendor: $iosIdentifierForVendor}) {
returning {
id
}
}
}
I get an error from the Apollo code gen build step that Variable "$code" of type "String!" used in position expecting type "uuid".. When I change that to uuid!,
mutation RedeemMagicLinkCode($code: uuid!, $iosIdentifierForVendor: uuid!) {
__typename
update_en...
The code gen step completes, producing API.swift, but several instances of the compile error Use of undeclared type 'uuid' raise and are now coming from a failure during compile of the Apollo generated API.swift.
/// API.swift
...
public let operationName = "RedeemMagicLinkCode"
public var code: uuid
public var iosIdentifierForVendor: uuid
...
Can someone point me in the right direction for letting Swift know what a uuid is or defining my query so that I can pass a String as a parameter through the Apollo managed mutation? Thanks so much!
OM(Goodness), I might need to blush at how simple this solution was.
I considered the error Use of undeclared type 'uuid' plus the documentation that Hasura will accept a String for this custom scalar...
/// API+typealias.swift
public typealias uuid = String
I placed the above type alias code in a file all by itself in the folder where my GraphQL files are and voila, it worked great!
I'm not thrilled with this public type alias now hanging out at the global scope but I am thrilled with a working data service
I'd like to store instances of models in a common provider using their classes or interfaces as a keys and then pop them up by class references. I have written some code:
class Provider {
public function new() { }
public function set<T:Any>(instance:T, ?type:Class<T>) {
if (type == null)
type = Type.getClass(instance);
if (type != null && instance != null)
map.set(type, instance);
}
public function get<T:Any>(type:Class<T>):Null<T> {
return cast map.get(type);
}
var map = new Map<Class<Any>, Any>();
}
...alas, it's even doesn't compile.
Probably I have to use qualified class name as a key rather than class/interface reference? But I'd like to keep neat get function design that takes type as argument and returns object just of type taken, without additional type casting.
Is it possible or should I change my approach to this problem?
The issue of using Class<T> as a Map key come up every so often, here is a related discussion. The naive approach of Map<Class<T>, T> fails to compile with something like this:
Abstract haxe.ds.Map has no #:to function that accepts haxe.IMap<Class<Main.T>, Main.T>`
There's several different approaches to this problem:
One can use Type reflection to obtain the fully qualified name of a class instance, and then use that as a key in a Map<String, T>:
var map = new Map<String, Any>();
var name = Type.getClassName(Main);
map[name] = value;
For convenience, you would probably want to have a wrapper that does this for you, such as this ClassMap implementation.
A simpler solution is to simply "trick" Haxe into compiling it by using an empty structure type ({}) as the key type. This causes ObjectMap to be chosen as the underlying map implementation.
var map = new Map<{}, Any>();
map[Main] = value;
However, that allows you to use things as keys that are not of type Class<T>, such as:
map[{foo: "bar"}] = value;
The type safety issues of the previous approach can be remedied by using this ClassKey abstract:
#:coreType abstract ClassKey from Class<Dynamic> to {} {}
This still uses ObjectMap as the underlying map implementation due to the to {} implicit cast. However, using a structure as a key now fails at compile time:
var map = new Map<ClassKey, Any>();
map[{foo: "bar"}] = value; // No #:arrayAccess function accepts arguments [...]
I am trying to implement a code completion macro for JSON files by generating field definitions
var classOfChild = Type.getClass(child); // `child` may look like this array: [[1,2,3],[0.1,0.2,0.3]]
fields.push({
"name": childName,
"pos" : _pos,
"kind": childType,
});
I already watched some videos and read some tutorials on this, but there is no information how to get ComplexType from a Type.typeof(object) result.
I have tried code that doesn't work like:
//"kind": childType,
//"kind": childType.toString(),
//"kind": FVar(macro {childType.toString(); }),
//"kind": FVar(macro Array<$v{arrType}>)
but none of them worked (all of them raise some Error or )
Edit 1: here is my json data:
{"floatVar1":0.1, "str":"some string", "nullValueObject":null, "arrayOfInts":[11,20,9], "matrixLikeArray":[[14, 12, 13, 11, 18]], "floatMatrix":[[14.4, 12.3, 13.7, 11.9, 18.0]], "symbolPayouts":[0.05], "objectInObject":{"prop1":"some str", "prop2": "some str2", "prop3":10.17, "prop4":[[1,2,3],[19.3,20.4]]}}
I would like to create Definitions for prop4, ("prop4":[[1,2,3],[19.3,20.4]])
Edit 2: I have already figured out how to create the "kind" for the simple types ("kind": FVar(macro:Dynamic)) and the object (kind : FVar(TAnonymous( jsonFields ))). But how to do that for arrays, arrays of arrays, etc.
Edit 3: code in gist.github.com
When generating code with macros it is easy to attempt to generate something which isn't efficient and you did get side tracked by trying to define explicit types.
Based on your gist, you just need to define a json field and give it the content of your JSON file as the field's value as if you were defining the value as a Haxe literal object.
Your goal is then to generate something you could have written as:
private var json = { prop1:'hello', prop2:42, prop3:[1,2,3] };
The haxe compiler will strongly type this json field.
To achieve that, your macro just need to add one field with an initial value obtained from the JSON file; and likewise, the Haxe compiler will strictly type it.
Creating a variable with a type to be inferred by the compiler is simply FVar(null, valueExpr), which means your entire macro can be reduced to:
var fields = Context.getBuildFields();
var json = Json.parse(src);
fields.push({
name : "json",
pos : Context.currentPos(),
kind : FVar(null, macro $v{json}),
access: [APrivate],
});
return fields;
For a more elaborated version I can point you on the following gist: ResourceGenerator.hx which will generate recursively "inlinable" and dce-friendly objects.
PS: sadly your "prop4":[[1,2,3],[19.3,20.4]] is impossible because it will be seen by the compiler as an Array of incompatible types ([Array<Int>, Array<Float>]).
I'm trying to serialize data into / from my classes, derived from MonoBehaviour, which cannot be created from client code (e.g., with the new keyword), but rather must be created by a Unity3D-specific method, GameObject.AddComponent<T>(). How can I use the YamlDotNet framework to populate my classes with values without having to create an adapter for each one? Is there some sort of built-in adapter that I can configure, such that YamlDotNet doesn't instantiate the class it's trying to serialize to?
A typical file might contain a mapping of items, e.g.,
%YAML 1.1
%TAG !invt! _PathwaysEngine.Inventory.
%TAG !intf! _PathwaysEngine.Adventure.
---
Backpack_01: !invt!Item+yml
mass: 2
desc:
nouns: /^bag|(back)?pack|sack|container$/
description: |
Your backpack is only slightly worn, and...
rand_descriptions:
- "It's flaps twirl in the breeze."
- "You stare at it. You feel enriched."
MagLite_LR05: !invt!Lamp+yml
cost: 56
mass: 2
time: 5760
desc:
nouns: /^light|flashlight|maglite|lr_05$/
description: |
On the side of this flashlight is a label...
(Type "light" to turn it on and off.)
...
Where the tags are the fully specified class names of my Items, e.g., PathwaysEngine.Inventory.Lamp+yml, PathwaysEngine is the namespace I use for my game engine code, Inventory deals with items & whatnot, and Lamp+yml is how the compiler denotes a nested class, yml inside Lamp. Lamp+yml might look like this:
public partial class Lamp : Item, IWearable {
public new class yml : Item.yml {
public float time {get;set;}
public void Deserialize(Lamp o) {
base.Deserialize((Item) o);
o.time = time;
}
}
}
I call Deserialize() on all objects that derive from Thing from Awake(), i.e., once the MonoBehaviour classes exist in the game. Elsewhere, I've already created a pretty complicated Dictionary filled with objects of type Someclass+yml, and then Deserialize takes an instance of the real, runtime class Someclass and populates it with values. There's got to be a cleaner way to do this, right?
How can I:
Tell the Deserializer what my classes are?
See the second edit for a good solution for the above issue
Get the data without it attempting to create my MonoBehaviour-derived classes?
Edit: I've since worked at the problem, and have found out a good way of dealing with custom data (in my particular case of trying to parse regexes out of my data, and having them not be considered strings & therefore, un-castable to regex) is to use a IYamlTypeConverter for that particular string. Using YamlDotNet with Unity3D MonoBehaviours, however, is still an issue.
Another Edit: The above examples use a pretty ugly way of determining types. In my case, the best thing to do was to register the tags first with the deserializer, e.g.,
var pre = "tag:yaml.org,2002:";
var tags = new Dictionary<string,Type> {
{ "regex", typeof(Regex) },
{ "date", typeof(DateTime) },
{ "item", typeof(Item) }};
foreach (var tag in tags)
deserializer.RegisterTagMapping(
pre+tag.Key, tag.Value);
Then, I use the !!tag notation in the *.yml file, e.g.,
%YAML 1.1
---
Special Item: !!item
nouns: /thing|item|object/
someBoolean: true
Start Date: !!date 2015-12-17
some regex: !!regex /matches\s+whatever/
...
You can pass a custom implementation of IObjectFactory to the constructor of the Deserializer class. Every time the deserializer needs to create an instance of an object, it will use the IObjectFactory to create it.
Notice that your factory will be responsible for creating instances of every type that is deserialized. The easiest way to implement it is to create a decorator around DefaultObjectFactory, such as:
class UnityObjectFactory : IObjectFactory
{
private readonly DefaultObjectFactory DefaultFactory =
new DefaultObjectFactory();
public object Create(Type type)
{
// You can use specific types manually
if (type == typeof(MyCustomType))
{
return GameObject.AddComponent<MyCustomType>();
}
// Or use a marker interface
else if (typeof(IMyMarkerInterface).IsAssignableFrom(type))
{
return typeof(GameObject)
.GetMethod("AddComponent")
.MakeGenericMethod(type)
.Invoke();
}
// Delegate unknown types to the default factory
else
{
return DefaultFactory(type);
}
}
}