-
-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Example of proper way to consume types in the callbacks? #45
Comments
In the end, I found this type magic to get me relatively close. Its been harder than consuming types from some libraries, and for a bit I thought it was impossible without modifying the .d.ts to explicitly export some more types, but this seems to be getting close (implementation and product specific details removed for space). import type { Express } from 'express'
import SCIMMY from 'scimmy'
import SCIMMYRouters from 'scimmy-routers'
import { parse as SCIMParse, filter as SCIMFilter } from 'scim2-parse-filter'
type SCIMMYUserSchema = InstanceType<typeof SCIMMY.Schemas.User>
// An arbitrary object. Technically its constrainted at runtime, but there doesn't seem to be a type for that?
type SCIMMYData = Record<string, any>
// The way the constructor of the User object works, it copies all the input data on to extend it
type SCIMMYUserInput = SCIMMYUserSchema | SCIMMYData
type SCIMMYResource = InstanceType<typeof SCIMMY.Types.Resource>
type SCIMMYConstraints = SCIMMYResource['constraints']
type SCIMMYFilter = SCIMMYResource['filter']
// As part of the RFC, the multi-objects have some basic properties
type SCIMMulti = {
type: /*'home' | 'work' | 'other' |*/ string
primary: boolean
display: string
value: string
}
type SCIMEmail = SCIMMulti
type Context = {
// My product context
}
// Manages state, registers routes, contains the controller that performs product specific operations
class SCIMHandler<UserEntity, Controller extends SCIMController<UserEntity>> {
controller: Controller
applyFilter(data: Array<SCIMMYData>, filter: SCIMMYFilter) {
return data.filter(SCIMFilter(SCIMParse(filter.expression)))
}
ingress = async (resource: SCIMMYResource, data: SCIMMYUserInput, ctx: Context): Promise<SCIMMYData> => {
// Implementation removed for space
}
egress = async (resource: SCIMMYResource, ctx: Context) => {
// Implementation removed for space
}
degress = (resource: SCIMMYResource, ctx: Context) => {
// Implementation removed for space
}
// Main entrypoint for adding SCIM functionality to an Express server
registerSCIMRoutes(app: Express) {
const scimConfig = SCIMMY.Config.get() as Record<string, unknown>
console.log(scimConfig)
// Register the User resource
SCIMMY.Resources.declare(SCIMMY.Resources.User, {})
// Add support for the optional EnterpriseUser extension
SCIMMY.Resources.User.extend(SCIMMY.Schemas.EnterpriseUser, false)
// Add handlers for retrieval and submission
SCIMMY.Resources.User.ingress(this.ingress)
SCIMMY.Resources.User.egress(this.egress)
SCIMMY.Resources.User.degress(this.degress)
}
} The types at least seem to line up for the resource.filter, resource.id, resource.constraints. Not so much for the actual |
So I am trying to add SCIM support using SCIMMY and SCIMMYRouters to my express API. So far so good. Everything is hooked up, and my placeholder ingress/egress functions are being hit by my postman tests.
However, I saw that you had type definitions but I can't seem to find a reasonable way to use them in my modules. I was having trouble importing types from the module, possibly because the types aren't reexported at the top level of the module? IDK, I was able to get around that by typing inputs using typeof, but the objects passed to my callbacks don't seem to match these classes members, so its not quite right and would welcome input.
For example, for ingress, it is documented with a js example at https://scimmyjs.github.io/SCIMMY.Types.Resource.html#~IngressHandler (thanks for that) with the arguments types listed as SCIMMY.Types.Resource and SCIMMY.Types.Schema
I tried converting that literally:
But those types' members don't seem to match the members of the object resource/data. I also tried typing using
SCIMMY.Resources.User
and evenSCIMMY.Resources.User.definition
based on following steps through the constructors.Anyway, I get that part of this is probably because the actual attributes on an object are defined by the schema, and can be optionally present, but it seemed like in schemas.js User.#definition did some magic, so I wasn't sure if there was a way for me to type the objects such that typescript would know what the possible (they could be missing on a given call I know) members supplied by the User or Group object would be? I assume that is kinda the point of these resources? To type them?
Maybe I'm missing something obvious, but I've been chasing my tail trying to use SCIMMY.Resources.User, SCIMMY.Schemas.User, SCIMMY.Types.User, etc but I can't seem to figure it out. Maybe its not possible and everyone just types the incoming data as unknown or
Record<string, unknown>
and test everything manually. Let me know if that is true, but it seems like this library validates the input before calling my handler, so it would suck to have to treat the object as an anonymous, untyped object when it has been verified to meet a, well, type.Anyway, I'm liking the library so far, just hoping to add typing into my use case to add auto-complete and transpile-time error checking.
And if I am just missing something, I'd suggest changing the type of the
handler
argument to SCIMMY.Resources.User.ingress/etc. Currently it explicitly seems to takeany
, and it'd at least be cool if it typed its input as a callback that took the three arguments and returns a promise of an object of the requested shape. It'd take some of the guesswork out of making the handler implementation. I think it'd even help non-TS developers since VSCode will show hints based on types even in pure JS these days.The text was updated successfully, but these errors were encountered: