Skip to content

Commit eaa48e6

Browse files
committed
feat(nestjs-json-rpc-sdk): sdk for rpc
- http transport - factory for native js - angular module Closes: #77
1 parent f89f7cf commit eaa48e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1117
-90
lines changed

apps/json-api-front/src/app/app.component.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,23 @@ import { Component, inject, OnInit } from '@angular/core';
22
import { NxWelcomeComponent } from './nx-welcome.component';
33
import { JsonApiSdkService } from 'json-api-nestjs-sdk';
44
import { AtomicFactory } from 'json-api-nestjs-sdk/json-api-nestjs-sdk.module';
5+
import {
6+
JSON_RPC,
7+
RPC_BATCH,
8+
Rpc,
9+
} from '@klerick/nestjs-json-rpc-sdk/json-rpc-sdk.module';
10+
511
import { switchMap } from 'rxjs';
612

13+
interface TestRpc {
14+
test(a: number, b: number): Promise<number>;
15+
test2(firstArg: string, secondArg: number): Promise<string>;
16+
}
17+
18+
type RpcMap = {
19+
TestRpc: TestRpc;
20+
};
21+
722
@Component({
823
standalone: true,
924
imports: [NxWelcomeComponent],
@@ -14,14 +29,19 @@ import { switchMap } from 'rxjs';
1429
export class AppComponent implements OnInit {
1530
private JsonApiSdkService = inject(JsonApiSdkService);
1631
private atomicFactory = inject(AtomicFactory);
32+
private rpc = inject<Rpc<RpcMap>>(JSON_RPC);
33+
private rpcBatch = inject(RPC_BATCH);
1734

1835
ngOnInit(): void {
19-
// this.JsonApiSdkService.getAll(class Users {}, {
20-
// page: {
21-
// size: 2,
22-
// number: 1,
23-
// },
24-
// }).subscribe((r) => console.log(r));
36+
const rpc1 = this.rpc.TestRpc.test(1, 2);
37+
const rpc2 = this.rpc.TestRpc.test2('string', 2);
38+
this.rpcBatch(rpc2, rpc1).subscribe(([r2, r1]) => console.log(r1, r2));
39+
this.JsonApiSdkService.getAll(class Users {}, {
40+
page: {
41+
size: 2,
42+
number: 1,
43+
},
44+
}).subscribe((r) => console.log(r));
2545

2646
class Addresses {
2747
id = 1;

apps/json-api-front/src/app/app.config.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
22
import { JsonApiAngular } from 'json-api-nestjs-sdk/json-api-nestjs-sdk.module';
3+
import {
4+
JsonRpcAngular,
5+
TransportType,
6+
} from '@klerick/nestjs-json-rpc-sdk/json-rpc-sdk.module';
37

48
export const appConfig: ApplicationConfig = {
59
providers: [
@@ -11,5 +15,12 @@ export const appConfig: ApplicationConfig = {
1115
operationUrl: 'operation',
1216
})
1317
),
18+
importProvidersFrom(
19+
JsonRpcAngular.forRoot({
20+
transport: TransportType.HTTP,
21+
rpcPath: 'rpc',
22+
rpcHost: 'http://localhost:4200',
23+
})
24+
),
1425
],
1526
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"extends": ["../../../.eslintrc.base.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
},
17+
{
18+
"files": ["*.json"],
19+
"parser": "jsonc-eslint-parser",
20+
"rules": {
21+
"@nx/dependency-checks": "error"
22+
}
23+
}
24+
]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# nestjs-json-rpc-sdk
2+
3+
This library was generated with [Nx](https://nx.dev).
4+
5+
## Building
6+
7+
Run `nx build nestjs-json-rpc-sdk` to build the library.
8+
9+
## Running unit tests
10+
11+
Run `nx test nestjs-json-rpc-sdk` to execute the unit tests via [Jest](https://jestjs.io).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable */
2+
export default {
3+
displayName: 'nestjs-json-rpc-sdk',
4+
preset: '../../../jest.preset.js',
5+
testEnvironment: 'node',
6+
transform: {
7+
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
8+
},
9+
moduleFileExtensions: ['ts', 'js', 'html'],
10+
coverageDirectory: '../../../coverage/libs/json-rpc/nestjs-json-rpc-sdk',
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "@klerick/nestjs-json-rpc-sdk",
3+
"version": "0.0.1",
4+
"dependencies": {
5+
"tslib": "^2.3.0"
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "nestjs-json-rpc-sdk",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/json-rpc/nestjs-json-rpc-sdk/src",
5+
"projectType": "library",
6+
"targets": {
7+
"build": {
8+
"executor": "@nx/js:tsc",
9+
"outputs": ["{options.outputPath}"],
10+
"options": {
11+
"outputPath": "dist/libs/json-rpc/nestjs-json-rpc-sdk",
12+
"main": "libs/json-rpc/nestjs-json-rpc-sdk/src/index.ts",
13+
"tsConfig": "libs/json-rpc/nestjs-json-rpc-sdk/tsconfig.lib.json",
14+
"assets": ["libs/json-rpc/nestjs-json-rpc-sdk/*.md"]
15+
}
16+
},
17+
"publish": {
18+
"command": "node tools/scripts/publish.mjs nestjs-json-rpc-sdk {args.ver} {args.tag}",
19+
"dependsOn": ["build"]
20+
}
21+
},
22+
"tags": []
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/nestjs-json-rpc-sdk';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/json-rpc-angular';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const JSON_RPC_VERSION = '2.0';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Axios, AxiosResponse } from 'axios';
2+
import { Observable } from 'rxjs';
3+
4+
import {
5+
HttpAgentFactory,
6+
LoopFunc,
7+
PayloadRpc,
8+
ReturnTransportCall,
9+
RpcResult,
10+
} from '../types';
11+
import { map } from 'rxjs/operators';
12+
13+
export function axiosTransportFactory<T extends LoopFunc>(
14+
axios: Axios
15+
): HttpAgentFactory<T> {
16+
return (url: string) => (body: PayloadRpc<T>) => {
17+
const controller = new AbortController();
18+
const signal = controller.signal;
19+
20+
return new Observable<AxiosResponse<RpcResult<T>>>((subscriber) => {
21+
axios
22+
.post<
23+
ReturnTransportCall<T>,
24+
AxiosResponse<RpcResult<T>, PayloadRpc<T>>,
25+
PayloadRpc<T>
26+
>(url, body, { signal })
27+
.then((response) => subscriber.next(response))
28+
.catch((error: unknown) => subscriber.error(error))
29+
.finally(() => subscriber.complete());
30+
31+
return { unsubscribe: () => controller.abort() };
32+
}).pipe(map((r) => r.data));
33+
};
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { idRequest } from './id-request';
2+
3+
describe('id-request', () => {
4+
it('should be increment', () => {
5+
expect(idRequest()).toBe(1);
6+
expect(idRequest()).toBe(2);
7+
expect(idRequest()).toBe(3);
8+
});
9+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let i = 0;
2+
export const idRequest = () => ++i;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './axios-transport.factory';
2+
export * from './id-request';
3+
export * from './rpc.factory';
4+
export * from './transport.factory';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { RpcConfig, RpcReturnList, RpcBatch, RpcBatchPromise } from '../types';
2+
import { transportFactory } from './transport.factory';
3+
import { RpcBatchFactory, rpcProxy, RpcBatchFactoryPromise } from '../utils';
4+
5+
type ResultRpcFactory<T extends object> = {
6+
rpc: RpcReturnList<T, false>;
7+
rpcBatch: RpcBatch;
8+
};
9+
type ResultRpcFactoryPromise<T extends object> = {
10+
rpc: RpcReturnList<T, true>;
11+
rpcForBatch: RpcReturnList<T, false>;
12+
rpcBatch: RpcBatchPromise;
13+
};
14+
15+
export function RpcFactory<T extends object>(
16+
options: RpcConfig,
17+
usePromise: false
18+
): ResultRpcFactory<T>;
19+
export function RpcFactory<T extends object>(
20+
options: RpcConfig,
21+
usePromise: true
22+
): ResultRpcFactoryPromise<T>;
23+
export function RpcFactory<T extends object>(
24+
options: RpcConfig,
25+
usePromise: true | false = false
26+
): ResultRpcFactory<T> | ResultRpcFactoryPromise<T> {
27+
const transport = transportFactory(options);
28+
let rpc: RpcReturnList<T, true> | RpcReturnList<T, false>;
29+
let rpcForBatch: RpcReturnList<T, false>;
30+
31+
if (usePromise) {
32+
rpc = rpcProxy<RpcReturnList<T, true>>(transport, usePromise);
33+
rpcForBatch = rpcProxy<RpcReturnList<T, false>>(transport, usePromise);
34+
return { rpc, rpcForBatch, rpcBatch: RpcBatchFactoryPromise(transport) };
35+
} else {
36+
rpc = rpcProxy<RpcReturnList<T, false>>(transport, usePromise);
37+
return { rpc, rpcBatch: RpcBatchFactory(transport) };
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fromFetch } from 'rxjs/fetch';
2+
import {
3+
RpcConfig,
4+
Transport,
5+
TransportType,
6+
RpcHttpConfig,
7+
LoopFunc,
8+
PayloadRpc,
9+
RpcResult,
10+
} from '../types';
11+
12+
function httpTransport<T extends LoopFunc>(
13+
config: RpcHttpConfig
14+
): Transport<T> {
15+
const url = new URL(config.rpcPath, config.rpcHost).toString();
16+
if (config.httpAgentFactory) {
17+
return config.httpAgentFactory(url);
18+
}
19+
20+
return (body: PayloadRpc<T>) =>
21+
fromFetch<RpcResult<T>>(url, {
22+
method: 'post',
23+
body: JSON.stringify(body),
24+
selector: (r) => r.json(),
25+
});
26+
}
27+
28+
export function transportFactory<T extends LoopFunc>(
29+
rpcConfig: RpcConfig
30+
): Transport<T> {
31+
switch (rpcConfig.transport) {
32+
case TransportType.HTTP:
33+
return httpTransport(rpcConfig);
34+
case TransportType.WS:
35+
throw new Error('Unknown transport');
36+
default:
37+
throw new Error('Unknown transport');
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {
2+
Component,
3+
inject,
4+
InjectionToken,
5+
ModuleWithProviders,
6+
NgModule,
7+
} from '@angular/core';
8+
import { HttpClientModule, HttpClient } from '@angular/common/http';
9+
import {
10+
LoopFunc,
11+
RpcMainHttpConfig,
12+
RpcHttpConfig,
13+
RpcWsConfig,
14+
Transport,
15+
TransportType,
16+
PayloadRpc,
17+
RpcResult,
18+
RpcReturnList,
19+
RpcBatch,
20+
} from './types';
21+
import { transportFactory } from './factory';
22+
import { RpcBatchFactory, rpcProxy } from './utils';
23+
24+
type Rpc<T extends object> = RpcReturnList<T, false>;
25+
26+
export { TransportType, Rpc };
27+
28+
export const JSON_RPC_SDK_CONFIG = new InjectionToken<JsonRpcAngularConfig>(
29+
'Main config object for sdk'
30+
);
31+
32+
export const JSON_RPC_SDK_TRANSPORT = new InjectionToken<Transport<LoopFunc>>(
33+
'Transport for RPC',
34+
{
35+
factory: () => {
36+
const config = inject(JSON_RPC_SDK_CONFIG);
37+
const httpClient = inject(HttpClient);
38+
if (config.transport === TransportType.HTTP) {
39+
(config as unknown as RpcHttpConfig)['httpAgentFactory'] =
40+
(url: string) => (body: PayloadRpc<LoopFunc>) => {
41+
return httpClient.post<RpcResult<LoopFunc>>(url, body);
42+
};
43+
}
44+
return transportFactory(config);
45+
},
46+
}
47+
);
48+
49+
export const JSON_RPC = new InjectionToken<RpcReturnList<object, false>>(
50+
'Rpc client',
51+
{
52+
factory: () =>
53+
rpcProxy<RpcReturnList<any, true>>(inject(JSON_RPC_SDK_TRANSPORT), false),
54+
}
55+
);
56+
57+
export const RPC_BATCH = new InjectionToken<RpcBatch>('Rpc client for batch', {
58+
factory: () => RpcBatchFactory(inject(JSON_RPC_SDK_TRANSPORT)),
59+
});
60+
61+
export type JsonRpcAngularConfig = RpcMainHttpConfig | RpcWsConfig;
62+
63+
@NgModule({
64+
imports: [HttpClientModule],
65+
})
66+
export class JsonRpcAngular {
67+
static forRoot(
68+
config: JsonRpcAngularConfig
69+
): ModuleWithProviders<JsonRpcAngular> {
70+
return {
71+
ngModule: JsonRpcAngular,
72+
providers: [
73+
{
74+
useValue: config,
75+
provide: JSON_RPC_SDK_CONFIG,
76+
},
77+
],
78+
};
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { nestjsJsonRpcSdk } from './nestjs-json-rpc-sdk';
2+
3+
describe('nestjsJsonRpcSdk', () => {
4+
it('should work', () => {
5+
expect(nestjsJsonRpcSdk()).toEqual('nestjs-json-rpc-sdk');
6+
});
7+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { RpcConfig } from './types';
2+
3+
export function nestjsJsonRpcSdk(rpcConfig: RpcConfig): string {
4+
return 'nestjs-json-rpc-sdk';
5+
}

0 commit comments

Comments
 (0)