Skip to content

Commit

Permalink
refactor: move some types around
Browse files Browse the repository at this point in the history
  • Loading branch information
Paulijuz committed Jan 12, 2025
1 parent bada947 commit 8ebfa50
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 44 deletions.
23 changes: 10 additions & 13 deletions src/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,27 @@ import { createZodActionError } from './error'
import { safeServerCall } from './safeServerCall'
import { Session } from '@/auth/Session'
import { Smorekopp } from '@/services/error'
import type { ExtractDetailedType, ValidationTypeUnknown } from '@/services/Validation'
import type { ActionReturn } from './Types'
import type {
ExtractDetailedType,
ServiceMethodReturn,
Validation
} from '@/services/ServiceMethod'
import type { ServiceMethodType } from '@/services/ServiceMethod'
import type { z } from 'zod'

// This function is overloaded to allow for different combinations of parameters and data.

export function action<Return>(
serviceMethod: ServiceMethodReturn<boolean, Return, undefined, undefined>
serviceMethod: ServiceMethodType<boolean, Return, undefined, undefined>
): () => Promise<ActionReturn<Return>>

export function action<Return, ParamsSchema extends z.ZodTypeAny>(
serviceMethod: ServiceMethodReturn<boolean, Return, ParamsSchema, undefined>
serviceMethod: ServiceMethodType<boolean, Return, ParamsSchema, undefined>
): (params: z.infer<ParamsSchema>) => Promise<ActionReturn<Return>>

export function action<Return, DataValidation extends Validation<unknown, unknown>>(
serviceMethod: ServiceMethodReturn<boolean, Return, undefined, DataValidation>
export function action<Return, DataValidation extends ValidationTypeUnknown>(
serviceMethod: ServiceMethodType<boolean, Return, undefined, DataValidation>
): (data: ExtractDetailedType<DataValidation> | FormData) => Promise<ActionReturn<Return>>

export function action<Return, ParamsSchema extends z.ZodTypeAny, DataValidation extends Validation<unknown, unknown>>(
serviceMethod: ServiceMethodReturn<boolean, Return, ParamsSchema, DataValidation>
export function action<Return, ParamsSchema extends z.ZodTypeAny, DataValidation extends ValidationTypeUnknown>(
serviceMethod: ServiceMethodType<boolean, Return, ParamsSchema, DataValidation>
): (params: z.infer<ParamsSchema>, data: ExtractDetailedType<DataValidation> | FormData) => Promise<ActionReturn<Return>>

/**
Expand All @@ -38,9 +35,9 @@ export function action<Return, ParamsSchema extends z.ZodTypeAny, DataValidation
export function action<
Return,
ParamsSchema extends z.ZodTypeAny | undefined = undefined,
DataValidation extends Validation<unknown, unknown> | undefined = undefined,
DataValidation extends ValidationTypeUnknown | undefined = undefined,
>(
serviceMethod: ServiceMethodReturn<boolean, Return, ParamsSchema, DataValidation>
serviceMethod: ServiceMethodType<boolean, Return, ParamsSchema, DataValidation>
) {
// Letting the arguments to the actual function be unknown is safer as anything can be passed to it form the client.
// The action and service method will validate the parameter and data before it is used.
Expand Down
9 changes: 5 additions & 4 deletions src/app/api/apiHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'server-only'
import { Session } from '@/auth/Session'
import { ServerError, Smorekopp } from '@/services/error'
import type { ServiceMethodReturn, Validation } from '@/services/ServiceMethod'
import type { ValidationTypeUnknown } from '@/services/Validation'
import type { ServiceMethodType } from '@/services/ServiceMethod'
import type { ErrorCode } from '@/services/error'
import type { SessionNoUser } from '@/auth/Session'
import type { z } from 'zod'
Expand All @@ -10,9 +11,9 @@ type APIHandler<
RawParams,
Return,
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationTypeUnknown | undefined,
> = {
serviceMethod: ServiceMethodReturn<boolean, Return, ParamsSchema, DataValidation>,
serviceMethod: ServiceMethodType<boolean, Return, ParamsSchema, DataValidation>,
} & (ParamsSchema extends undefined ? {
params?: undefined,
} : {
Expand Down Expand Up @@ -40,7 +41,7 @@ export function apiHandler<
RawParams,
Return,
ParamsSchema extends z.ZodTypeAny | undefined = undefined,
DataValidation extends Validation<unknown, unknown> | undefined = undefined,
DataValidation extends ValidationTypeUnknown| undefined = undefined,
>({ serviceMethod, params }: APIHandler<RawParams, Return, ParamsSchema, DataValidation>) {
return async (req: Request, { params: rawParams }: { params: RawParams }) =>
await apiHandlerGeneric<Return>(req, async session => {
Expand Down
37 changes: 10 additions & 27 deletions src/services/ServiceMethod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,12 @@ import { Smorekopp } from './error'
import { prismaCall } from './prismaCall'
import { default as globalPrisma } from '@/prisma'
import { Session } from '@/auth/Session'
import type { ExtractDetailedType, ValidationType, ValidationTypeUnknown } from './Validation'
import type { Prisma, PrismaClient } from '@prisma/client'
import type { SessionMaybeUser } from '@/auth/Session'
import type { z } from 'zod'
import type { AutherStaticFieldsBound } from '@/auth/auther/Auther'

// TODO: These three types should really be defined in Validation.ts or somewhere else, not here.
export type Validation<GeneralData, DetailedData> = {
detailedValidate: (data: DetailedData | unknown) => DetailedData,
typeValidate: (data: unknown | FormData | GeneralData) => TypeValidateReturn<DetailedData>,
}

export type TypeValidateReturn<
DetailedType
> = {
success: true,
data: DetailedType,
} | {
success: false,
error: z.ZodError,
}

export type ExtractDetailedType<V extends Validation<unknown, unknown>> = ReturnType<V['detailedValidate']>

/**
* This is the type for the prisma client that is passed to the service method.
* It can't simply be PrismaClient because it can be usefull to use a service method
Expand All @@ -47,7 +30,7 @@ export type PrismaPossibleTransaction<
*/
export type ServiceMethodParamsData<
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationType<unknown, unknown> | undefined,
> = (
ParamsSchema extends undefined ? object : { params: z.infer<NonNullable<ParamsSchema>> }
) & (
Expand All @@ -66,7 +49,7 @@ export type ServiceMethodParamsDataUnsafe = {
export type ServiceMethodArguments<
OpensTransaction extends boolean,
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationType<unknown, unknown> | undefined,
> = {
prisma: PrismaPossibleTransaction<OpensTransaction>,
session: SessionMaybeUser,
Expand All @@ -77,7 +60,7 @@ export type ServiceMethodArguments<
*/
export type ServiceMethodExecuteArgs<
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationTypeUnknown | undefined,
> = {
session: SessionMaybeUser | null,
bypassAuth?: boolean,
Expand All @@ -100,7 +83,7 @@ export type ServiceMethodConfig<
OpensTransaction extends boolean,
Return,
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationTypeUnknown | undefined,
> = ({
paramsSchema?: ParamsSchema,
dataValidation?: DataValidation,
Expand All @@ -127,11 +110,11 @@ export type ServiceMethodConfig<
* TypeScript is smart enough to infer the behaviour of the return functons without the need to excplitly
* type the return type of the ServiceMethod function, but it is done so for the sake of clarity.
*/
export type ServiceMethodReturn<
export type ServiceMethodType<
OpensTransaction extends boolean,
Return,
ParamsSchema extends z.ZodTypeAny | undefined,
DataValidation extends Validation<unknown, unknown> | undefined,
DataValidation extends ValidationTypeUnknown | undefined,
> = {
/**
* Pass a specific prisma client to the service method. Usefull when using the service method inside a transaction.
Expand All @@ -147,7 +130,7 @@ export type ServiceMethodReturn<
*/
newClient: () => (
ReturnType<
ServiceMethodReturn<OpensTransaction, Return, ParamsSchema, DataValidation>['client']
ServiceMethodType<OpensTransaction, Return, ParamsSchema, DataValidation>['client']
>
),
paramsSchema?: ParamsSchema,
Expand All @@ -174,10 +157,10 @@ export function ServiceMethod<
OpensTransaction extends boolean,
Return,
ParamsSchema extends z.ZodTypeAny | undefined = undefined,
DataValidation extends Validation<unknown, unknown> | undefined = undefined,
DataValidation extends ValidationTypeUnknown | undefined = undefined,
>(
config: ServiceMethodConfig<DynamicFields, OpensTransaction, Return, ParamsSchema, DataValidation>,
): ServiceMethodReturn<OpensTransaction, Return, ParamsSchema, DataValidation> {
): ServiceMethodType<OpensTransaction, Return, ParamsSchema, DataValidation> {
// Simple utility function to check if the expected arguments are present.
// I.e. if the params/data are present when they should be and vice versa.
// This is needed to help typescript infer the correct types for the arguments.
Expand Down
22 changes: 22 additions & 0 deletions src/services/Validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,25 @@ export type ValidationTypes<V> = V extends Validation<infer Type, infer Detailed
Detailed: PureTsTypeOfSchema<Detailed, true>
} : never;

// The type of the Validation class
export type ValidationType<GeneralData, DetailedData> = {
detailedValidate: (data: DetailedData | unknown) => DetailedData,
typeValidate: (data: unknown | FormData | GeneralData) => TypeValidateReturn<DetailedData>,
}

// The return type of the typeValidate method in Validation
export type TypeValidateReturn<
DetailedType
> = {
success: true,
data: DetailedType,
} | {
success: false,
error: z.ZodError,
}

// Extract the type of the detailed schema from a Validation
export type ExtractDetailedType<V extends ValidationType<unknown, unknown>> = ReturnType<V['detailedValidate']>

// Utility type for a generic Valdiation which can be extended
export type ValidationTypeUnknown = ValidationType<unknown, unknown>

0 comments on commit 8ebfa50

Please sign in to comment.