-
-
Notifications
You must be signed in to change notification settings - Fork 472
Description
What version of Effect is running?
3.14.5
What steps can reproduce the bug?
group.add(HttpApiEndpoint.get('listEntity')`/entities`The current HttpApiEndpoint.<method>() API takes a template literal to define the path segment it will be on, and to allow for injection of parameters. Parameters are defined through HttpApiSchema, which appear to be Schema that accept strings. However, it does not accept primitive values (string, number etc), which prevents us from using computed values in path segments.
A lot of APIs include CRUD operations for entities, for example User, Company, etc. The path segments and supported operations can end up being identical save for the 'entity' type.
This means that we have to repeat identical group/endpoint/request/response formats that only vary for the 'entity' name and the schema. Because I can't inject a computed value for the entity into the path segment, I can't optimise my code by creating a helper than does all/most of the boiler plate.
What is the expected behavior?
group.add(HttpApiEndpoint.get(`list${name}`)`/${name}s`...Obviously I'd need to do be more sophisticated on entity name pluralisation, but hopefully you get the point. I could then build parts of my API by using the helper, something like:
HttpApi.make('api')
.add(crudForEntity('user', {
entitySchema: UserSchema,
createSchema: PartialUserSchema,
updateSchema: MutableUserSchema,
})
.add(crudForEntity('company, {
entitySchema: CompanySchema,
createSchema: PartialCompanySchema,
updateSchema: MutableCompanySchema,
})What do you see instead?
const name = 'user'
group.add(HttpApiEndpoint.get(`list${name}`)`/${name}s`...name then produces the error
TS2345: Argument of type string is not assignable to parameter of type AnyString
Which is a bit confusing, but AnyString is a Schema of type Schema.String, not a primitive 'string'.
It would be great if the template literal handling allowed for types are string or perhaps also those that can be converted to string, so that the path segment support computed values.
Additional information
Longer example with two entities (Device and Gateway) where the operations are identical, other than changes to entity names in strings and the input/output schema.
const api = HttpApi.make('api')
// DEVICES
.add(HttpApiGroup.make('devices')
.add(HttpApiEndpoint.get('listDevices')`/devices`
.setUrlParams(ListDevicesQuerySchema)
.addSuccess(DeviceListSchema)
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.post('createDevice')`/devices`
.setPayload(CreateDeviceRequestSchema)
.addSuccess(DeviceEntitySchema, { status: 201 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.Conflict)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.get('getDevice')`/devices/${deviceEuiParam}`
.addSuccess(DeviceEntitySchema)
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.patch('updateDevice')`/devices/${deviceEuiParam}`
.setPayload(UpdateDeviceRequestSchema)
.addSuccess(DeviceEntitySchema, { status: 202 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.del('deleteDevice')`/devices/${deviceEuiParam}`
.addSuccess(Schema.Null, { status: 204 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
)
// GATEWAYS
.add(HttpApiGroup.make('gateways')
.add(HttpApiEndpoint.get('listGateways')`/gateways`
.setUrlParams(ListGatewaysQuerySchema)
.addSuccess(GatewayListSchema)
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.post('createGateway')`/gateways`
.setPayload(CreateGatewayRequestSchema)
.addSuccess(GatewayEntitySchema, { status: 201 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.Conflict)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.get('getGateway')`/gateways/${gatewayEuiParam}`
.addSuccess(GatewayEntitySchema)
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.patch('updateGateway')`/gateways/${gatewayEuiParam}`
.setPayload(UpdateGatewayRequestSchema)
.addSuccess(GatewayEntitySchema, { status: 202 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
.add(HttpApiEndpoint.del('deleteGateway')`/gateways/${gatewayEuiParam}`
.addSuccess(Schema.Null, { status: 204 })
.addError(HttpApiError.BadRequest)
.addError(HttpApiError.Unauthorized)
.addError(HttpApiError.NotFound)
.addError(HttpApiError.InternalServerError)
)
)