ftrackhq / ftrack-ts-schema-generator

Gets the schema from an ftrack instance and generates typescript interfaces for all the entity types in the instance.
Apache License 2.0
2 stars 1 forks source link

feat: strong-type type and object_type names #48

Open ffMathy opened 1 month ago

ffMathy commented 1 month ago

Changes

This PR makes type and object type properties of entities strong-typed. This makes it very nice when building up queries that involve checking the type name.

It also refactors some of the code to be (in my opinion) cleaner. It is now easier to see in one place how various overrides to the types are being made. This increases the cohesion of the code quite heavily.

Test

N/A

gismya commented 1 month ago

Could you give some examples on how this would be used?

ffMathy commented 1 month ago

Sure thing!

For instance, we've built a query builder abstraction on top of our generated schema that's fully strong-typed and protects all queries from SQL injection:

const shot = await new QueryBuilder('Shot')
      .select(x => x
          .property('custom_attributes')
          .property('id'))
      .where(x => x
          .property('id').is("something")
          .expandedProperty(x => x.type.name).is("some-type-name"))
      .withCacheDuration(60)
      .getAll();

This does a few things:

  1. Ensures that the type names are right.
  2. Ensures that the property names are correct.
  3. Automatically provides the operators based on types. For instance, in the above example we're querying for id, so it knows that .is is an operator that's available. Same for the type's name. VS Code will suggest this automatically. For number properties for instance, other operators are valid.
  4. Automatically strong-typing selected properties. In the above example, there's a select clause for the "custom_attributes". and "id" It will ensure that what's returned will have the custom_attributes and id properties set as non-optional, so we don't have to null-check it.
  5. Breaks the query up automatically. The above example (although it fetches everything), breaks it up in smaller batches of 100 results at a time. Also supports ES Iterators to incrementally yield results as you go along.
  6. Supports caching. The results of the above query is automatically cached in Redis for 60 seconds.

This is some of the stuff I've been wanting to demo for you for some time, but haven't had the time yet.

Anyway, the problem is that in the above example, I am also looking at an "expanded property" (that's what we call properties that would implicitly trigger a join in Ftrack's end, and are more expensive). In this particular case, we'd love if "is" was strong-typed. "some-type-name" is just a string, but it's actually not a valid type here and will not work at runtime.

This becomes especially useful when querying against "TypedContext", where it will then also suggest valid types.

Another example is if you (for instance) have a Shot, and you'd like to check the type's name somehow.

const someShot: Shot = null as any as Shot;

if(someShot.type.name === "foo-shot") {
    //in the above example, VS Code suggests valid type names for shot.
}

Could also imagine a case where it would be a TypedContext instead, and it would be nice to get suggestions here too of all the types that could be valid.

However, now that I am thinking about it, what is the difference between types and object types?

The types that are valid for an entity, is that decided by the schema? And can one entity have multiple types, but only one object type? What is the cardinality between these type thingies?

Because if the above is right, then I modelled it wrong in the code, and I'll change that. The above is what I want to achieve at least.