Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniopresto committed Oct 23, 2023
1 parent b880c9d commit 565b400
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 243 deletions.
108 changes: 108 additions & 0 deletions packages/method/src/App.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
AppOptions,
AppRequestContext,
InferMethodTypes,
MethodLike,
MethodPayload,
MethodRequest,
MethodRequestOptions,
ObjectOfMethods,
} from './types';

export class App<
M extends Readonly<[MethodLike, ...MethodLike[]]>,
Info extends InferMethodTypes<M> = InferMethodTypes<M>,
Context extends AppRequestContext = AppRequestContext
> {
static defaultOptions: Required<AppOptions<any, any>> = {
name: 'unknown',
batchRequests: true,
batchTimeoutMS: 50,
maxBatchCallsCount: 30,
buildRequestContext() {
throw new Error('buildRequestContext was not provided.');
},
onResult() {},
beforeCall() {},
};

private options: Required<AppOptions<M, Context>>;
private methods: ObjectOfMethods<M>;

constructor(methods: M, options: AppOptions<M, Context>) {
let errors: string[] = [];

// @ts-ignore
this.methods = {};

methods.forEach((m) => {
if (this.methods[m.methodName]) {
errors.push(
` ☂︎ found repeated item with methodName "${m.methodName}".`
);
}
// @ts-ignore
this.methods[m.methodName] = m;
});

this.options = { ...App.defaultOptions, ...options };

if (errors.length) {
throw new Error('RootMethod: \n' + errors.join(`\n`));
}
}

private queue: MethodRequest<any, any, any>[] = [];
private timeout: any = null;
private dispatch = async () => {
const requests = [...this.queue];
this.queue = [];

const context = await this.options.buildRequestContext(requests);

const payload: MethodPayload<any, Context> = {
args,
context,
requestInfo: {
app: this,
appOptions: this.options,
requests,
},
};

await this.options.beforeCall([payload]);

return method.call(args, payload);
};

call = <Name extends keyof Info>(
name: Name
): {
with(
args: Info[Name]['args'],
options?: MethodRequestOptions
): Promise<Info[Name]['output']>;
} => {
const method: MethodLike = this.methods[name];

return {
with: async (args, options) => {
const request: Omit<MethodRequest<any, any, any>, 'context'> = {
args,
options: { ...options },
method: name,
};
},
};
};

static create = <
Methods extends Readonly<[MethodLike, ...MethodLike[]]>,
Ctx extends AppRequestContext
>(
methods: Methods,
options: AppOptions<Methods, Ctx>
): App<Methods> => {
return new App(methods, options);
};
}
120 changes: 15 additions & 105 deletions packages/method/src/Method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,101 +7,17 @@ import {
ObjectDefinitionInput,
ObjectType,
} from '@powership/schema';
import { AnyFunction, Compute, IsKnown, MaybePromise } from '@powership/utils';
import { Compute, MaybePromise } from '@powership/utils';

/**
* Enum representing the types of method requests.
* - 'query': For read-only fetch operations.
* - 'mutation': For write operations that change data.
*/
export type MethodRequestType = 'query' | 'mutation';

/**
* Enum representing the types of method responses.
* - 'query_result': The result of a query operation.
* - 'mutation_result': The result of a mutation operation.
*/
export type MethodResponseType = 'query_result' | 'mutation_result';

/**
* Union type for method kinds, either a request or a response.
*/
export type MethodType = MethodRequestType | MethodResponseType;

/**
* Interface for additional context that methods might require.
*/
export interface MethodContext {}

/**
* Interface for additional information that methods might use.
*/
export interface MethodInfo {}

/**
* Type definition for a method.
* @template Args - The type definition for the args fields.
* @template Output - The type definition for the output fields.
*/
export type MethodDefinition<
Args extends ObjectDefinitionInput,
Output extends FieldInput,
Name extends Readonly<string>
> = {
kind: MethodRequestType; // The kind of method, either 'query' or 'mutation'.
name: Name; // The name of the method.
output: Output; // The output type definition.
args: Args; // The args type definition.
throwOnInvalidResultingListItems?: boolean; // Optional flag to throw on invalid list items.
};

/**
* Type definition for the payload of a method.
* @template Args - The type definition for the args fields.
* @template Context - The type definition for the method context.
* @template Parent - The type definition for the parent object.
*/
export type MethodPayload<
Args extends ObjectDefinitionInput,
Context extends MethodContext = MethodContext,
Parent extends any = any
> = {
parent: Parent; // The parent object.
args: Infer<{ object: Args }>; // The actual args data.
rootContextValue: Context; // The root context.
rootExecutionInfo: MethodInfo; // Additional method information.
};

/**
* Type definition for fetch operations.
* @template Args - The type definition for the args fields.
* @template Output - The type definition for the output fields.
*/
export type Fetcher<
Args extends ObjectDefinitionInput,
Output extends FieldInput
> = (args: Infer<{ object: Args }>) => Promise<Infer<Output>>;

/**
* Type definition for handling method operations.
* @template Args - The type definition for the args fields.
* @template Output - The type definition for the output fields.
* @template Context - The type definition for the method context.
* @template Parent - The type definition for the parent object.
*/
export type Handler<
Args extends ObjectDefinitionInput,
Output extends FieldInput,
Context extends MethodContext = MethodContext,
Parent extends any = any
> = [IsKnown<Output>] extends [1]
? [IsKnown<Args>] extends [1]
? (
args: Infer<{ object: Args }>,
payload: Compute<MethodPayload<Args, Context, Parent>>
) => Promise<Infer<Output>>
: never
: never;
import {
AppRequestContext,
Fetcher,
Handler,
MethodDefinition,
MethodKind,
MethodLike,
MethodPayload,
} from './types';

// Regular expression for validating method names.
const NAME_RX = /^[_a-zA-Z][_a-zA-Z0-9]*$/;
Expand All @@ -122,12 +38,6 @@ function validateIdentifier(name: string) {
}
}

export interface MethodLike {
methodName: string;
__isPSMethod: true;
call: AnyFunction;
}

/**
* Class representing a Method.
* @template RequestDefinition - The type definition for the request fields.
Expand All @@ -138,12 +48,12 @@ export class Method<
ArgsDefinition extends ObjectDefinitionInput,
ResponseDefinition extends FieldInput,
Name extends Readonly<string>,
Context extends MethodContext = MethodContext
Context extends AppRequestContext = AppRequestContext
> implements MethodLike
{
__isPSMethod: true; // Internal flag to mark this as a PSMethod.
readonly methodName: Name; // The name of the method.
kind: MethodType; // The kind of method, either 'query' or 'mutation'.
kind: MethodKind; // The kind of method, either 'query' or 'mutation'.
argsType: GraphType<{ object: ArgsDefinition }>; // GraphType for args validation.
outputType: GraphType<ResponseDefinition>; // GraphType for output validation.
private __definition: MethodDefinition<any, any, any>; // Private storage for method definition.
Expand Down Expand Up @@ -258,7 +168,7 @@ export class Method<
args: any,
payload: MethodPayload<ArgsDefinition, Context, Parent>
) {
let { parent, rootContextValue, rootExecutionInfo = {} } = payload;
let { parent, context, requestInfo } = payload;

args = argsType.parse(args, {
customMessage: (_, error) => {
Expand All @@ -269,8 +179,8 @@ export class Method<
const resulting = await handle(args, {
parent,
args,
rootContextValue,
rootExecutionInfo,
context,
requestInfo,
});

return outputType.parse(resulting, {
Expand Down
115 changes: 0 additions & 115 deletions packages/method/src/RootMethod.ts

This file was deleted.

Loading

0 comments on commit 565b400

Please sign in to comment.