Browse Source

Update implemention

Genericise operation implementations.
refactor/new-arch
TheoryOfNekomata 6 months ago
parent
commit
321be25d00
11 changed files with 195 additions and 52 deletions
  1. +2
    -1
      packages/core/src/backend/common.ts
  2. +1
    -0
      packages/core/src/extenders/http/backend/core.ts
  3. +16
    -7
      packages/core/src/extenders/http/client.ts
  4. +35
    -44
      packages/core/src/recipes/resource/core.ts
  5. +15
    -0
      packages/core/src/recipes/resource/implementation/create.ts
  6. +15
    -0
      packages/core/src/recipes/resource/implementation/delete.ts
  7. +15
    -0
      packages/core/src/recipes/resource/implementation/emplace.ts
  8. +51
    -0
      packages/core/src/recipes/resource/implementation/fetch.ts
  9. +15
    -0
      packages/core/src/recipes/resource/implementation/patch-delta.ts
  10. +15
    -0
      packages/core/src/recipes/resource/implementation/patch-merge.ts
  11. +15
    -0
      packages/core/src/recipes/resource/implementation/query.ts

+ 2
- 1
packages/core/src/backend/common.ts View File

@@ -10,9 +10,10 @@ export interface ImplementationContext {
endpoint: Endpoint;
params: Record<string, unknown>;
query?: URLSearchParams;
dataSource?: DataSource;
}

type ImplementationFunction = (params: ImplementationContext) => Promise<Response | void>;
export type ImplementationFunction = (params: ImplementationContext) => Promise<Response | void>;

export interface Backend<App extends BaseApp = BaseApp> {
app: App;


+ 1
- 0
packages/core/src/extenders/http/backend/core.ts View File

@@ -88,6 +88,7 @@ class ServerInstance<Backend extends BaseBackend> implements Server<Backend> {
endpoint,
params: endpointParams ?? {},
query: typeof search !== 'undefined' ? new URLSearchParams(search) : undefined,
dataSource: this.backend.dataSource,
});

if (typeof responseSpec === 'undefined') {


+ 16
- 7
packages/core/src/extenders/http/client.ts View File

@@ -16,6 +16,14 @@ declare module '../../client' {
}
}

const DEFAULT_HOST = '0.0.0.0' as const;

const DEFAULT_PORT = 80 as const;

const DEFAULT_METHOD = 'GET' as const;

const EXTENSION_METHOD_EFFECTIVE_METHOD = 'POST' as const;

class ClientInstance<App extends BaseApp> implements Client<App> {
readonly app: App;
private readonly fetchFn: typeof fetch;
@@ -29,8 +37,8 @@ class ClientInstance<App extends BaseApp> implements Client<App> {

async connect(params: ServiceParams): Promise<ClientConnection> {
const connection = {
host: params.host ?? '0.0.0.0',
port: params.port ?? 80,
host: params.host ?? DEFAULT_HOST,
port: params.port ?? DEFAULT_PORT,
basePath: params.basePath ?? '',
};

@@ -52,14 +60,15 @@ class ClientInstance<App extends BaseApp> implements Client<App> {

makeRequest(operation: Operation) {
const baseUrlFragments = [
this.connection?.host ?? '0.0.0.0'
this.connection?.host ?? DEFAULT_HOST
];

const thePort = (this.connection?.port ?? 80);
if (thePort !== 80) {
const thePort = (this.connection?.port ?? DEFAULT_PORT);
if (thePort !== DEFAULT_PORT) {
baseUrlFragments.push(thePort.toString());
}

// TODO how to set to https?
const scheme = 'http';
// todo need a way to decode url back to endpoint queue
const urlString = serializeEndpointQueue(this.endpointQueue);
@@ -72,9 +81,9 @@ class ClientInstance<App extends BaseApp> implements Client<App> {
if (typeof operation.searchParams !== 'undefined') {
url.search = operation.searchParams.toString();
}
const rawEffectiveMethod = (operation.method ?? 'GET').toUpperCase();
const rawEffectiveMethod = (operation.method ?? DEFAULT_METHOD).toUpperCase();
const finalEffectiveMethod = (AVAILABLE_EXTENSION_METHODS as unknown as string[]).includes(rawEffectiveMethod)
? 'POST' as const
? EXTENSION_METHOD_EFFECTIVE_METHOD
: rawEffectiveMethod;

if (typeof operation.body !== 'undefined') {


+ 35
- 44
packages/core/src/recipes/resource/core.ts View File

@@ -1,12 +1,13 @@
import {Recipe} from '../../common/recipe';
import {endpoint, operation, validation as v} from '../../common';
import {endpoint, validation as v} from '../../common';
import {backend, DataSource} from '../../backend';
import {
DataSourceNotFoundResponseError,
ItemNotFoundReponseError,
ResourceCollectionFetchedResponse,
ResourceItemFetchedResponse,
} from './response';
import * as fetchOperation from './implementation/fetch';
import * as createOperation from './implementation/create';
import * as emplaceOperation from './implementation/emplace';
import * as patchDeltaOperation from './implementation/patch-delta';
import * as patchMergeOperation from './implementation/patch-merge';
import * as queryOperation from './implementation/query';
import * as deleteOperation from './implementation/delete';

interface AddResourceRecipeParams {
endpointName: string;
@@ -15,9 +16,13 @@ interface AddResourceRecipeParams {

export const addResourceRecipe = (params: AddResourceRecipeParams): Recipe => (a) => {
const operations = {
fetch: operation({
name: 'fetch' as const,
}),
fetch: fetchOperation.operation,
create: createOperation.operation,
emplace: emplaceOperation.operation,
patchMerge: patchMergeOperation.operation,
patchDelta: patchDeltaOperation.operation,
query: queryOperation.operation,
delete: deleteOperation.operation,
};

const theEndpoint = endpoint({
@@ -27,10 +32,22 @@ export const addResourceRecipe = (params: AddResourceRecipeParams): Recipe => (a
}),
})
.param('resourceId')
.can('fetch');
.can(fetchOperation.name)
.can(createOperation.name)
.can(emplaceOperation.name)
.can(patchMergeOperation.name)
.can(patchDeltaOperation.name)
.can(queryOperation.name)
.can(deleteOperation.name);

const enhancedApp = a.app
.operation(operations.fetch)
.operation(operations.create)
.operation(operations.emplace)
.operation(operations.patchMerge)
.operation(operations.patchDelta)
.operation(operations.query)
.operation(operations.delete)
.endpoint(theEndpoint);

const theBackend = a.backend ?? backend({
@@ -39,39 +56,13 @@ export const addResourceRecipe = (params: AddResourceRecipeParams): Recipe => (a
});

theBackend
.implementOperation('fetch', async (ctx) => {
const dataSource: DataSource = ctx.endpoint.dataSource ?? theBackend.dataSource ?? {} as DataSource;
// need to genericise the response here so we don't depend on the HTTP responses.
const { resourceId } = ctx.params;
const { getById, getMultiple } = dataSource;

if (typeof resourceId === 'undefined') {
if (typeof getMultiple === 'undefined') {
throw new DataSourceNotFoundResponseError();
}

// TODO add query here
const items = await getMultiple();
return new ResourceCollectionFetchedResponse({
statusMessage: 'Resource Collection Fetched',
body: items,
});
}

if (typeof getById === 'undefined') {
throw new DataSourceNotFoundResponseError();
}

const item = await getById(resourceId);
if (!item) {
throw new ItemNotFoundReponseError();
}

return new ResourceItemFetchedResponse({
statusMessage: 'Resource Item Fetched',
body: item,
});
});
.implementOperation(fetchOperation.name, fetchOperation.implementation)
.implementOperation(createOperation.name, createOperation.implementation)
.implementOperation(deleteOperation.name, deleteOperation.implementation)
.implementOperation(queryOperation.name, queryOperation.implementation)
.implementOperation(patchDeltaOperation.name, patchDeltaOperation.implementation)
.implementOperation(patchMergeOperation.name, patchMergeOperation.implementation)
.implementOperation(emplaceOperation.name, emplaceOperation.implementation);

return {
operations,


+ 15
- 0
packages/core/src/recipes/resource/implementation/create.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'create' as const;

export const method = 'POST' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

+ 15
- 0
packages/core/src/recipes/resource/implementation/delete.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'delete' as const;

export const method = 'DELETE' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

+ 15
- 0
packages/core/src/recipes/resource/implementation/emplace.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'emplace' as const;

export const method = 'PUT' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

+ 51
- 0
packages/core/src/recipes/resource/implementation/fetch.ts View File

@@ -0,0 +1,51 @@
import {DataSource, ImplementationFunction} from '../../../backend';
import {
DataSourceNotFoundResponseError,
ItemNotFoundReponseError,
ResourceCollectionFetchedResponse,
ResourceItemFetchedResponse,
} from '../response';
import {operation as defineOperation} from '../../../common';

export const name = 'fetch' as const;

export const method = 'GET' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {
const dataSource: DataSource = ctx.endpoint.dataSource ?? ctx.dataSource ?? {} as DataSource;
// need to genericise the response here so we don't depend on the HTTP responses.
const { resourceId } = ctx.params;
const { getById, getMultiple } = dataSource;

if (typeof resourceId === 'undefined') {
if (typeof getMultiple === 'undefined') {
throw new DataSourceNotFoundResponseError();
}

// TODO add query here
const items = await getMultiple();
return new ResourceCollectionFetchedResponse({
statusMessage: 'Resource Collection Fetched',
body: items,
});
}

if (typeof getById === 'undefined') {
throw new DataSourceNotFoundResponseError();
}

const item = await getById(resourceId);
if (!item) {
throw new ItemNotFoundReponseError();
}

return new ResourceItemFetchedResponse({
statusMessage: 'Resource Item Fetched',
body: item,
});
};

+ 15
- 0
packages/core/src/recipes/resource/implementation/patch-delta.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'patchDelta' as const;

export const method = 'PATCH' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

+ 15
- 0
packages/core/src/recipes/resource/implementation/patch-merge.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'patchMerge' as const;

export const method = 'PATCH' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

+ 15
- 0
packages/core/src/recipes/resource/implementation/query.ts View File

@@ -0,0 +1,15 @@
import {ImplementationFunction} from '../../../backend';
import {operation as defineOperation} from '../../../common';

export const name = 'query' as const;

export const method = 'QUERY' as const;

export const operation = defineOperation({
name,
method,
});

export const implementation: ImplementationFunction = async (ctx) => {

};

Loading…
Cancel
Save