Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
2fd8219849 | |||
ea56e2218f | |||
9a07817914 | |||
9bc83b0d1e | |||
98c638e1ab | |||
575ddd36a0 | |||
52b731ce68 | |||
3f6e81b2aa | |||
adad99f6bf | |||
2771c92e85 | |||
440ea9ff3a | |||
51bb8dfa90 | |||
ce3bfa01b4 | |||
265109fca6 | |||
8bfd4d8866 | |||
785f247027 | |||
3f3f488dc4 | |||
0241eda296 | |||
66722759af | |||
e9fad241ee | |||
34face164f | |||
f7bf366962 | |||
046059d228 | |||
78e8171a6a | |||
c97a535035 | |||
dcf198787a | |||
a1e0ebd658 | |||
8bf0a71266 | |||
e499612ecb | |||
e049899599 | |||
37f4d34e7a | |||
ca2e6895ce | |||
ccc5c33656 | |||
546f7f4fc7 | |||
8536060ce4 | |||
e57c332d82 | |||
df87c6c75e | |||
53a4375545 | |||
88022ea14b | |||
2a681644eb | |||
e2708c5bb3 | |||
8c7a71ddce | |||
5acbf420ae | |||
5d673799cc | |||
bb0271e021 | |||
718feb74ae | |||
82aa80d7d9 | |||
67831cd37f | |||
777817b588 | |||
c7dd378eb3 | |||
9d9f67c91a | |||
82cc8c29e1 | |||
625b1c6871 | |||
e149b5a5f1 | |||
2879fb8cee | |||
f98c2cc5e7 | |||
567845bc64 | |||
52677ec036 | |||
8be8ce109e | |||
6788fbf715 | |||
325e568329 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -15,8 +15,6 @@ node_modules/
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_web/
|
||||
dist_serve/
|
||||
dist_ts_web/
|
||||
dist_*/
|
||||
|
||||
# custom
|
@ -19,22 +19,35 @@ mirror:
|
||||
stage: security
|
||||
script:
|
||||
- npmci git mirror
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
snyk:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:snyk
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- docker
|
||||
|
||||
auditDevDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command snyk test
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=dev
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
allow_failure: true
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
@ -49,9 +62,7 @@ testStable:
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- priv
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
@ -62,9 +73,7 @@ testBuild:
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
release:
|
||||
stage: release
|
||||
@ -84,6 +93,8 @@ release:
|
||||
codequality:
|
||||
stage: metadata
|
||||
allow_failure: true
|
||||
only:
|
||||
- tags
|
||||
script:
|
||||
- npmci command npm install -g tslint typescript
|
||||
- npmci npm prepare
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -15,7 +15,7 @@
|
||||
"properties": {
|
||||
"projectType": {
|
||||
"type": "string",
|
||||
"enum": ["website", "element", "service", "npm"]
|
||||
"enum": ["website", "element", "service", "npm", "wcc"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26629
package-lock.json
generated
26629
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@ -1,42 +1,48 @@
|
||||
{
|
||||
"name": "@apiglobal/typedrequest",
|
||||
"version": "1.0.28",
|
||||
"version": "1.0.59",
|
||||
"private": false,
|
||||
"description": "make typed requests towards apis",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "(tstest test/)",
|
||||
"build": "(tsbuild)",
|
||||
"build": "(tsbuild --web && tsbundle npm)",
|
||||
"format": "(gitzone format)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.17",
|
||||
"@gitzone/tstest": "^1.0.28",
|
||||
"@pushrocks/smartexpress": "^3.0.57",
|
||||
"@pushrocks/tapbundle": "^3.2.0",
|
||||
"@types/node": "^13.7.1",
|
||||
"tslint": "^6.0.0",
|
||||
"@gitzone/tsbuild": "^2.1.27",
|
||||
"@gitzone/tsbundle": "^1.0.87",
|
||||
"@gitzone/tstest": "^1.0.57",
|
||||
"@pushrocks/smartexpress": "^3.0.106",
|
||||
"@pushrocks/tapbundle": "^3.2.14",
|
||||
"@types/node": "^16.10.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apiglobal/typedrequest-interfaces": "^1.0.12",
|
||||
"@pushrocks/lik": "^3.0.17",
|
||||
"@pushrocks/smartdelay": "^2.0.6",
|
||||
"@pushrocks/smartjson": "^3.0.10",
|
||||
"@pushrocks/smartrequest": "^1.1.47"
|
||||
"@apiglobal/typedrequest-interfaces": "^1.0.15",
|
||||
"@pushrocks/isounique": "^1.0.4",
|
||||
"@pushrocks/lik": "^5.0.0",
|
||||
"@pushrocks/smartdelay": "^2.0.13",
|
||||
"@pushrocks/smartpromise": "^3.1.6",
|
||||
"@pushrocks/webrequest": "^2.0.13"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
"ts_web/**/*",
|
||||
"dist/**/*",
|
||||
"dist_web/**/*",
|
||||
"dist_*/**/*",
|
||||
"dist_ts/**/*",
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
],
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
]
|
||||
}
|
||||
|
92
readme.md
92
readme.md
@ -8,16 +8,94 @@ make typed requests towards apis
|
||||
* [docs (typedoc)](https://apiglobal.gitlab.io/typedrequest/)
|
||||
|
||||
## Status for master
|
||||
[](https://gitlab.com/apiglobal/typedrequest/commits/master)
|
||||
[](https://gitlab.com/apiglobal/typedrequest/commits/master)
|
||||
[](https://www.npmjs.com/package/@apiglobal/typedrequest)
|
||||
[](https://snyk.io/test/npm/@apiglobal/typedrequest)
|
||||
[](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
[](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
[](https://prettier.io/)
|
||||
|
||||
Status Category | Status Badge
|
||||
-- | --
|
||||
GitLab Pipelines | [](https://lossless.cloud)
|
||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
||||
npm | [](https://lossless.cloud)
|
||||
Snyk | [](https://lossless.cloud)
|
||||
TypeScript Support | [](https://lossless.cloud)
|
||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
Code Style | [](https://lossless.cloud)
|
||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
||||
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
||||
|
||||
## Usage
|
||||
|
||||
Use TypeScript for best in class intellisense.
|
||||
|
||||
```typescript
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
import * as smartexpress from '@pushrocks/smartexpress';
|
||||
|
||||
import * as typedrequest from '../ts/index';
|
||||
|
||||
let testServer: smartexpress.Server;
|
||||
let testTypedHandler: typedrequest.TypedHandler<ITestReqRes>;
|
||||
|
||||
// lets define an interface
|
||||
interface ITestReqRes {
|
||||
method: 'hi';
|
||||
request: {
|
||||
name: string;
|
||||
};
|
||||
response: {
|
||||
surname: string;
|
||||
};
|
||||
}
|
||||
|
||||
tap.test('should create a typedHandler', async () => {
|
||||
// lets use the interface in a TypedHandler
|
||||
testTypedHandler = new typedrequest.TypedHandler<ITestReqRes>('hi', async (reqArg) => {
|
||||
return {
|
||||
surname: 'wow',
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should spawn a server to test with', async () => {
|
||||
testServer = new smartexpress.Server({
|
||||
cors: true,
|
||||
forceSsl: false,
|
||||
port: 3000,
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should define a testHandler', async () => {
|
||||
const testTypedRouter = new typedrequest.TypedRouter(); // typed routers can broker typedrequests between handlers
|
||||
testTypedRouter.addTypedHandler(testTypedHandler);
|
||||
testServer.addRoute(
|
||||
'/testroute',
|
||||
new smartexpress.HandlerTypedRouter(testTypedRouter as any) // the "any" is testspecific, since smartexpress ships with its own version of typedrequest.
|
||||
);
|
||||
});
|
||||
|
||||
tap.test('should start the server', async () => {
|
||||
await testServer.start();
|
||||
});
|
||||
|
||||
tap.test('should fire a request', async () => {
|
||||
const typedRequest = new typedrequest.TypedRequest<ITestReqRes>(
|
||||
'http://localhost:3000/testroute',
|
||||
'hi'
|
||||
);
|
||||
const response = await typedRequest.fire({
|
||||
name: 'really',
|
||||
});
|
||||
console.log('this is the response:');
|
||||
console.log(response);
|
||||
expect(response.surname).to.equal('wow');
|
||||
});
|
||||
|
||||
tap.test('should end the server', async () => {
|
||||
await testServer.stop();
|
||||
});
|
||||
|
||||
tap.start();
|
||||
```
|
||||
|
||||
## Contribution
|
||||
|
||||
|
39
test/test.browser.ts
Normal file
39
test/test.browser.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
|
||||
import * as typedrequest from '../ts/index';
|
||||
|
||||
let testTypedHandler: typedrequest.TypedHandler<ITestReqRes>;
|
||||
|
||||
// lets define an interface
|
||||
interface ITestReqRes {
|
||||
method: 'hi';
|
||||
request: {
|
||||
name: string;
|
||||
};
|
||||
response: {
|
||||
surname: string;
|
||||
};
|
||||
}
|
||||
|
||||
tap.test('should create a typedHandler', async () => {
|
||||
// lets use the interface in a TypedHandler
|
||||
testTypedHandler = new typedrequest.TypedHandler<ITestReqRes>('hi', async (reqArg) => {
|
||||
return {
|
||||
surname: 'wow',
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should define a testHandler', async () => {
|
||||
const testTypedRouter = new typedrequest.TypedRouter(); // typed routers can broker typedrequests between handlers
|
||||
testTypedRouter.addTypedHandler(testTypedHandler);
|
||||
});
|
||||
|
||||
tap.test('should fire a request', async () => {
|
||||
const typedRequest = new typedrequest.TypedRequest<ITestReqRes>(
|
||||
'http://localhost:3000/testroute',
|
||||
'hi'
|
||||
);
|
||||
});
|
||||
|
||||
tap.start();
|
18
test/test.ts
18
test/test.ts
@ -6,6 +6,7 @@ import * as typedrequest from '../ts/index';
|
||||
let testServer: smartexpress.Server;
|
||||
let testTypedHandler: typedrequest.TypedHandler<ITestReqRes>;
|
||||
|
||||
// lets define an interface
|
||||
interface ITestReqRes {
|
||||
method: 'hi';
|
||||
request: {
|
||||
@ -17,9 +18,10 @@ interface ITestReqRes {
|
||||
}
|
||||
|
||||
tap.test('should create a typedHandler', async () => {
|
||||
testTypedHandler = new typedrequest.TypedHandler<ITestReqRes>('hi', async reqArg => {
|
||||
// lets use the interface in a TypedHandler
|
||||
testTypedHandler = new typedrequest.TypedHandler<ITestReqRes>('hi', async (reqArg) => {
|
||||
return {
|
||||
surname: 'wow'
|
||||
surname: 'wow',
|
||||
};
|
||||
});
|
||||
});
|
||||
@ -28,17 +30,16 @@ tap.test('should spawn a server to test with', async () => {
|
||||
testServer = new smartexpress.Server({
|
||||
cors: true,
|
||||
forceSsl: false,
|
||||
port: 3000
|
||||
port: 3000,
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should define a testHandler', async () => {
|
||||
const testTypedRouter = new typedrequest.TypedRouter(); // typed routers can broker typedrequests between handlers
|
||||
testTypedRouter.addTypedHandler(testTypedHandler);
|
||||
testServer.addRoute(
|
||||
'/testroute',
|
||||
new smartexpress.Handler('POST', async (req, res) => {
|
||||
console.log(req.body);
|
||||
res.json(await testTypedHandler.addResponse(req.body));
|
||||
})
|
||||
new smartexpress.HandlerTypedRouter(testTypedRouter as any) // the "any" is testspecific, since smartexpress ships with its own version of typedrequest.
|
||||
);
|
||||
});
|
||||
|
||||
@ -52,8 +53,9 @@ tap.test('should fire a request', async () => {
|
||||
'hi'
|
||||
);
|
||||
const response = await typedRequest.fire({
|
||||
name: 'really'
|
||||
name: 'really',
|
||||
});
|
||||
console.log('this is the response:');
|
||||
console.log(response);
|
||||
expect(response.surname).to.equal('wow');
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
export * from './typedrequest.classes.typedrequest';
|
||||
export * from './typedrequest.classes.typedhandler';
|
||||
export * from './typedrequest.classes.typedrouter';
|
||||
export * from './typedrequest.classes.typedresponseerror';
|
||||
export * from './typedrequest.classes.typedtarget';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as plugins from './typedrequest.plugins';
|
||||
import { TypedResponseError } from './typedrequest.classes.typedresponseerror';
|
||||
|
||||
type THandlerFunction<T extends plugins.typedRequestInterfaces.ITypedRequest> = (
|
||||
export type THandlerFunction<T extends plugins.typedRequestInterfaces.ITypedRequest> = (
|
||||
requestArg: T['request']
|
||||
) => Promise<T['response']>;
|
||||
|
||||
@ -26,8 +27,28 @@ export class TypedHandler<T extends plugins.typedRequestInterfaces.ITypedRequest
|
||||
'this handler has been given a wrong method to answer to. Please use a TypedRouter to filter requests'
|
||||
);
|
||||
}
|
||||
const response = await this.handlerFunction(typedRequestArg.request);
|
||||
typedRequestArg.response = response;
|
||||
let typedResponseError: TypedResponseError;
|
||||
const response = await this.handlerFunction(typedRequestArg.request).catch((e) => {
|
||||
if (e instanceof TypedResponseError) {
|
||||
typedResponseError = e;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
if (typedResponseError) {
|
||||
typedRequestArg.error = {
|
||||
text: typedResponseError.errorText,
|
||||
data: typedResponseError.errorData,
|
||||
};
|
||||
}
|
||||
|
||||
if (response) {
|
||||
typedRequestArg.response = response;
|
||||
}
|
||||
|
||||
typedRequestArg?.correlation?.phase ? (typedRequestArg.correlation.phase = 'response') : null;
|
||||
|
||||
return typedRequestArg;
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,69 @@
|
||||
import * as plugins from './typedrequest.plugins';
|
||||
import { TypedResponseError } from './typedrequest.classes.typedresponseerror';
|
||||
import { TypedRouter } from './typedrequest.classes.typedrouter';
|
||||
import { TypedTarget } from './typedrequest.classes.typedtarget';
|
||||
|
||||
const webrequestInstance = new plugins.webrequest.WebRequest();
|
||||
|
||||
export class TypedRequest<T extends plugins.typedRequestInterfaces.ITypedRequest> {
|
||||
public urlEndPoint: string;
|
||||
|
||||
|
||||
/**
|
||||
* in case we post against a url endpoint
|
||||
*/
|
||||
public urlEndPoint?: string;
|
||||
|
||||
/**
|
||||
* in case we post against a TypedTarget
|
||||
*/
|
||||
typedTarget: TypedTarget;
|
||||
|
||||
public method: string;
|
||||
|
||||
// STATIC
|
||||
constructor(urlEndPointArg: string, methodArg: T['method']) {
|
||||
this.urlEndPoint = urlEndPointArg;
|
||||
/**
|
||||
* note the overloading is thought to deal with promises
|
||||
* @param postEndPointArg
|
||||
* @param methodArg
|
||||
*/
|
||||
constructor(postTarget: string | TypedTarget, methodArg: T['method']) {
|
||||
if (typeof postTarget === 'string') {
|
||||
this.urlEndPoint = postTarget;
|
||||
} else {
|
||||
this.typedTarget = postTarget;
|
||||
}
|
||||
this.method = methodArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* firest the request
|
||||
* fires the request
|
||||
*/
|
||||
public async fire(fireArg: T['request']): Promise<T['response']> {
|
||||
const response = await plugins.smartrequest.postJson(this.urlEndPoint, {
|
||||
requestBody: {
|
||||
method: this.method,
|
||||
request: fireArg,
|
||||
response: null
|
||||
}
|
||||
});
|
||||
const responseBody: T = response.body;
|
||||
const payload: plugins.typedRequestInterfaces.ITypedRequest = {
|
||||
method: this.method,
|
||||
request: fireArg,
|
||||
response: null,
|
||||
correlation: {
|
||||
id: plugins.isounique.uni(),
|
||||
phase: 'request',
|
||||
},
|
||||
};
|
||||
|
||||
let responseBody: plugins.typedRequestInterfaces.ITypedRequest;
|
||||
if (this.urlEndPoint) {
|
||||
const response = await webrequestInstance.postJson(this.urlEndPoint, payload);
|
||||
responseBody = response;
|
||||
} else {
|
||||
responseBody = await this.typedTarget.post(payload);
|
||||
}
|
||||
if (responseBody.error) {
|
||||
console.log(responseBody.error.text);
|
||||
console.log(responseBody.error.data);
|
||||
console.error(
|
||||
`Got an error ${responseBody.error.text} with data ${JSON.stringify(
|
||||
responseBody.error.data
|
||||
)}`
|
||||
);
|
||||
if (!responseBody.retry) {
|
||||
throw new TypedResponseError(responseBody.error.text, responseBody.error.data);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (responseBody.retry) {
|
||||
|
10
ts/typedrequest.classes.typedresponseerror.ts
Normal file
10
ts/typedrequest.classes.typedresponseerror.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import * as plugins from './typedrequest.plugins';
|
||||
|
||||
export class TypedResponseError {
|
||||
public errorText: string;
|
||||
public errorData: any;
|
||||
constructor(errorTextArg: string, errorDataArg?: any) {
|
||||
this.errorText = errorTextArg;
|
||||
this.errorData = errorDataArg;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import * as plugins from './typedrequest.plugins';
|
||||
|
||||
import { TypedHandler } from './typedrequest.classes.typedhandler';
|
||||
import { TypedRequest } from './typedrequest.classes.typedrequest';
|
||||
|
||||
/**
|
||||
* A typed router decides on which typed handler to call based on the method
|
||||
@ -8,41 +9,116 @@ import { TypedHandler } from './typedrequest.classes.typedhandler';
|
||||
* This is thought for reusing the same url endpoint for different methods
|
||||
*/
|
||||
export class TypedRouter {
|
||||
public handlerMap = new plugins.lik.Objectmap<
|
||||
TypedHandler<plugins.typedRequestInterfaces.ITypedRequest>
|
||||
public upstreamTypedRouter: TypedRouter;
|
||||
|
||||
public routerMap = new plugins.lik.ObjectMap<TypedRouter>();
|
||||
|
||||
public handlerMap = new plugins.lik.ObjectMap<
|
||||
TypedHandler<any & plugins.typedRequestInterfaces.ITypedRequest>
|
||||
>();
|
||||
|
||||
public fireEventInterestMap = new plugins.lik.InterestMap<
|
||||
string,
|
||||
plugins.typedRequestInterfaces.ITypedRequest
|
||||
>((correlationId: string) => correlationId);
|
||||
|
||||
/**
|
||||
* adds the handler to the routing map
|
||||
* @param handlerArg
|
||||
* @param typedHandlerArg
|
||||
*/
|
||||
public addTypedHandler<T extends plugins.typedRequestInterfaces.ITypedRequest>(
|
||||
handlerArg: TypedHandler<T>
|
||||
typedHandlerArg: TypedHandler<T>
|
||||
) {
|
||||
this.handlerMap.add(handlerArg);
|
||||
}
|
||||
|
||||
public async addResponse(typedRequest: plugins.typedRequestInterfaces.ITypedRequest) {
|
||||
const typedHandler = this.handlerMap.find(handler => {
|
||||
return handler.method === typedRequest.method;
|
||||
});
|
||||
|
||||
if (!typedHandler) {
|
||||
const availableMethods: string[] = [];
|
||||
await this.handlerMap.forEach(async handler => {
|
||||
availableMethods.push(handler.method);
|
||||
});
|
||||
console.log(`Cannot find method for ${typedHandler}`);
|
||||
console.log(`Available methods are:`);
|
||||
console.log(availableMethods);
|
||||
typedRequest.error = {
|
||||
text: 'There is no available method for this call on the server side',
|
||||
data: {}
|
||||
};
|
||||
return typedRequest;
|
||||
// lets check for deduplication
|
||||
const existingTypedHandler = this.getTypedHandlerForMethod(typedHandlerArg.method);
|
||||
if (existingTypedHandler) {
|
||||
throw new Error(
|
||||
`a TypedHandler for ${typedHandlerArg.method} alredy exists! Can't add another one.`
|
||||
);
|
||||
}
|
||||
|
||||
typedRequest = await typedHandler.addResponse(typedRequest);
|
||||
return typedRequest;
|
||||
this.handlerMap.add(typedHandlerArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds another sub typedRouter
|
||||
* @param typedRequest
|
||||
*/
|
||||
public addTypedRouter(typedRouterArg: TypedRouter) {
|
||||
this.routerMap.add(typedRouterArg);
|
||||
}
|
||||
|
||||
public setUpstreamTypedRouter(typedRouterArg: TypedRouter) {
|
||||
this.upstreamTypedRouter = typedRouterArg;
|
||||
}
|
||||
|
||||
public checkForTypedHandler(methodArg: string): boolean {
|
||||
return !!this.getTypedHandlerForMethod(methodArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a typed Router from the router chain, upstream and downstream
|
||||
* @param methodArg
|
||||
* @param checkUpstreamRouter
|
||||
*/
|
||||
public getTypedHandlerForMethod(
|
||||
methodArg: string,
|
||||
checkUpstreamRouter = true
|
||||
): TypedHandler<any> {
|
||||
let typedHandler: TypedHandler<any>;
|
||||
|
||||
if (this.upstreamTypedRouter && checkUpstreamRouter) {
|
||||
typedHandler = this.upstreamTypedRouter.getTypedHandlerForMethod(methodArg);
|
||||
} else {
|
||||
typedHandler = this.handlerMap.findSync((handler) => {
|
||||
return handler.method === methodArg;
|
||||
});
|
||||
|
||||
if (!typedHandler) {
|
||||
this.routerMap.getArray().forEach((typedRouter) => {
|
||||
if (!typedHandler) {
|
||||
typedHandler = typedRouter.getTypedHandlerForMethod(methodArg, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return typedHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* if typedrequest object has correlation.phase === 'request' -> routes a typed request object to a handler
|
||||
* if typedrequest object has correlation.phase === 'response' -> routes a typed request object to request fire event
|
||||
* @param typedRequestArg
|
||||
*/
|
||||
public async routeAndAddResponse<T extends plugins.typedRequestInterfaces.ITypedRequest = any>(typedRequestArg: T) {
|
||||
if (!typedRequestArg?.correlation?.phase || typedRequestArg.correlation.phase === 'request') {
|
||||
const typedHandler = this.getTypedHandlerForMethod(typedRequestArg.method);
|
||||
|
||||
if (!typedHandler) {
|
||||
console.log(`Cannot find handler for methodname ${typedRequestArg.method}`);
|
||||
typedRequestArg.error = {
|
||||
text: 'There is no available method for this call on the server side',
|
||||
data: {},
|
||||
};
|
||||
return typedRequestArg;
|
||||
}
|
||||
|
||||
typedRequestArg = await typedHandler.addResponse(typedRequestArg);
|
||||
} else if (typedRequestArg.correlation.phase === 'response') {
|
||||
this.fireEventInterestMap
|
||||
.findInterest(typedRequestArg.correlation.id)
|
||||
?.fullfillInterest(typedRequestArg);
|
||||
} else {
|
||||
console.log('received weirdly shaped request');
|
||||
console.log(typedRequestArg);
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
...typedRequestArg,
|
||||
...{
|
||||
method: 'nullPathFromResponse',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
79
ts/typedrequest.classes.typedtarget.ts
Normal file
79
ts/typedrequest.classes.typedtarget.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { TypedRouter } from './typedrequest.classes.typedrouter';
|
||||
import * as plugins from './typedrequest.plugins';
|
||||
|
||||
export type IPostMethod = (
|
||||
typedRequestPostObject: plugins.typedRequestInterfaces.ITypedRequest
|
||||
) => Promise<plugins.typedRequestInterfaces.ITypedRequest>;
|
||||
|
||||
/**
|
||||
* this is an alternative to a post url supplied in `new Typedrequest(new TypedTarget(...), 'someMethodName')`
|
||||
* enables the use of custom post functions
|
||||
* used for things like broadcast channels
|
||||
* e.g. @designestate/dees-comms
|
||||
* the main difference here is, that the response comes back async and is routed by interest through typedrouter
|
||||
*/
|
||||
export type IPostMethodWithTypedRouter = (
|
||||
typedRequestPostObject: plugins.typedRequestInterfaces.ITypedRequest
|
||||
) => Promise<void> | Promise<plugins.typedRequestInterfaces.ITypedRequest>;
|
||||
|
||||
export interface ITypedTargetConstructorOptions {
|
||||
url?: string;
|
||||
postMethod?: IPostMethod;
|
||||
/**
|
||||
* a post method that does not return the answer
|
||||
*/
|
||||
postMethodWithTypedRouter?: IPostMethodWithTypedRouter;
|
||||
/**
|
||||
* this typedrouter allows us to have easy async request response cycles
|
||||
*/
|
||||
typedRouterRef?: TypedRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* a typed target defines a target for requests
|
||||
*/
|
||||
export class TypedTarget {
|
||||
url: string;
|
||||
type: 'rest' | 'socket';
|
||||
options: ITypedTargetConstructorOptions;
|
||||
|
||||
constructor(optionsArg: ITypedTargetConstructorOptions) {
|
||||
if (optionsArg.postMethodWithTypedRouter && !optionsArg.typedRouterRef) {
|
||||
throw new Error('you have to specify a typedrouter when using postmethod with typedrouter');
|
||||
}
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* wether calls to this target are bound to the request/response cycle
|
||||
* if false, always delivers response as result of a call
|
||||
* if true, delivers response in a separate call
|
||||
* can only be async when type is 'socket'
|
||||
*/
|
||||
public isAsync: boolean;
|
||||
|
||||
public async post<T extends plugins.typedRequestInterfaces.ITypedRequest>(payloadArg: T): Promise<T> {
|
||||
let responseInterest: plugins.lik.Interest<
|
||||
string,
|
||||
plugins.typedRequestInterfaces.ITypedRequest
|
||||
>;
|
||||
// having a typedrouter allows us to work with async request response cycles.
|
||||
if (this.options.typedRouterRef) {
|
||||
responseInterest = await this.options.typedRouterRef.fireEventInterestMap.addInterest(
|
||||
payloadArg.correlation.id,
|
||||
payloadArg
|
||||
);
|
||||
}
|
||||
const postMethod = this.options.postMethod || this.options.postMethodWithTypedRouter;
|
||||
const postMethodReturnValue = await postMethod(payloadArg);
|
||||
let responseBody: T;
|
||||
if (responseInterest) {
|
||||
responseBody = (await responseInterest.interestFullfilled) as T;
|
||||
} else if (postMethodReturnValue) {
|
||||
responseBody = postMethodReturnValue as T;
|
||||
} else {
|
||||
responseBody = payloadArg;
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
}
|
@ -4,9 +4,10 @@ import * as typedRequestInterfaces from '@apiglobal/typedrequest-interfaces';
|
||||
export { typedRequestInterfaces };
|
||||
|
||||
// pushrocks scope
|
||||
import * as isounique from '@pushrocks/isounique';
|
||||
import * as lik from '@pushrocks/lik';
|
||||
import * as smartdelay from '@pushrocks/smartdelay';
|
||||
import * as smartrequest from '@pushrocks/smartrequest';
|
||||
import * as smartjson from '@pushrocks/smartjson';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
import * as webrequest from '@pushrocks/webrequest';
|
||||
|
||||
export { lik, smartdelay, smartrequest, smartjson };
|
||||
export { isounique, lik, smartdelay, smartpromise, webrequest };
|
||||
|
Reference in New Issue
Block a user