@dbos-inc/koa-serve 3.0.27-preview → 3.0.35-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/src/dboshttp.d.ts +5 -5
- package/dist/src/dboshttp.d.ts.map +1 -1
- package/dist/src/dboshttp.js.map +1 -1
- package/dist/src/dboskoa.d.ts +1 -0
- package/dist/src/dboskoa.d.ts.map +1 -1
- package/dist/src/dboskoa.js +4 -0
- package/dist/src/dboskoa.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/dboshttp.ts +3 -3
- package/src/dboskoa.ts +5 -0
- package/tests/argsource.test.ts +0 -1
- package/tests/auth.test.ts +0 -1
- package/tests/basic.test.ts +0 -1
- package/tests/cors.test.ts +0 -1
- package/tests/endpoints.test.ts +0 -1
- package/tests/steps.test.ts +79 -0
- package/tests/test-helpers.ts +13 -0
- package/tests/transactions.test.ts +117 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbos-inc/koa-serve",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.35-preview",
|
|
4
4
|
"description": "DBOS HTTP Package for serving workflows with Koa",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"typescript": "^5.3.3"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@dbos-inc/dbos-sdk": "*"
|
|
33
|
+
"@dbos-inc/dbos-sdk": "*",
|
|
34
|
+
"@dbos-inc/knex-datasource": "*"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@koa/bodyparser": "5.0.0",
|
package/src/dboshttp.ts
CHANGED
|
@@ -150,7 +150,7 @@ export class DBOSHTTPBase implements DBOSLifecycleCallback {
|
|
|
150
150
|
|
|
151
151
|
/** Parameter decorator indicating which source to use (URL, BODY, etc) for arg data */
|
|
152
152
|
static argSource(source: ArgSources) {
|
|
153
|
-
return function (target: object, propertyKey:
|
|
153
|
+
return function (target: object, propertyKey: PropertyKey, parameterIndex: number) {
|
|
154
154
|
const curParam = DBOS.associateParamWithInfo(
|
|
155
155
|
DBOSHTTP,
|
|
156
156
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
@@ -190,11 +190,11 @@ export class DBOSHTTPBase implements DBOSLifecycleCallback {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
static argRequired(target: object, propertyKey:
|
|
193
|
+
static argRequired(target: object, propertyKey: PropertyKey, parameterIndex: number) {
|
|
194
194
|
ArgRequired(target, propertyKey, parameterIndex);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
static argOptional(target: object, propertyKey:
|
|
197
|
+
static argOptional(target: object, propertyKey: PropertyKey, parameterIndex: number) {
|
|
198
198
|
ArgOptional(target, propertyKey, parameterIndex);
|
|
199
199
|
}
|
|
200
200
|
|
package/src/dboskoa.ts
CHANGED
|
@@ -72,6 +72,11 @@ function assertCurrentKoaContextStore(): DBOSKoaLocalCtx {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export class DBOSKoa extends DBOSHTTPBase {
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
DBOS.registerLifecycleCallback(this);
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
static get koaContext(): Koa.Context {
|
|
76
81
|
return assertCurrentKoaContextStore().koaCtxt;
|
|
77
82
|
}
|
package/tests/argsource.test.ts
CHANGED
package/tests/auth.test.ts
CHANGED
|
@@ -29,7 +29,6 @@ describe('httpserver-defsec-tests', () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
beforeEach(async () => {
|
|
32
|
-
DBOS.registerLifecycleCallback(dhttp);
|
|
33
32
|
const _classes = [TestEndpointDefSec, SecondClass];
|
|
34
33
|
await DBOS.launch();
|
|
35
34
|
await DBOS.queryUserDB(`DROP TABLE IF EXISTS ${testTableName};`);
|
package/tests/basic.test.ts
CHANGED
|
@@ -50,7 +50,6 @@ describe('decoratorless-api-tests', () => {
|
|
|
50
50
|
|
|
51
51
|
beforeEach(async () => {
|
|
52
52
|
middlewareCounter = middlewareCounter2 = middlewareCounterG = 0;
|
|
53
|
-
DBOS.registerLifecycleCallback(dhttp);
|
|
54
53
|
await DBOS.launch();
|
|
55
54
|
DBOS.logRegisteredEndpoints();
|
|
56
55
|
app = new Koa();
|
package/tests/cors.test.ts
CHANGED
package/tests/endpoints.test.ts
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { DBOS } from '@dbos-inc/dbos-sdk';
|
|
2
|
+
import Koa from 'koa';
|
|
3
|
+
import Router from '@koa/router';
|
|
4
|
+
import { DBOSKoa } from '../src';
|
|
5
|
+
|
|
6
|
+
import request from 'supertest';
|
|
7
|
+
|
|
8
|
+
const dhttp = new DBOSKoa();
|
|
9
|
+
|
|
10
|
+
function customstep() {
|
|
11
|
+
return function decorator<This, Args extends unknown[], Return>(
|
|
12
|
+
target: object,
|
|
13
|
+
propertyKey: PropertyKey,
|
|
14
|
+
descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>,
|
|
15
|
+
) {
|
|
16
|
+
if (!descriptor.value) {
|
|
17
|
+
throw Error('Use of decorator when original method is undefined');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
descriptor.value = DBOS.registerStep(descriptor.value, {
|
|
21
|
+
name: String(propertyKey),
|
|
22
|
+
ctorOrProto: target,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return descriptor;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('registerstep', () => {
|
|
30
|
+
let app: Koa;
|
|
31
|
+
let appRouter: Router;
|
|
32
|
+
|
|
33
|
+
beforeAll(async () => {});
|
|
34
|
+
|
|
35
|
+
afterAll(async () => {});
|
|
36
|
+
|
|
37
|
+
beforeEach(async () => {
|
|
38
|
+
DBOS.setConfig({ name: 'koa-step-test' });
|
|
39
|
+
await DBOS.launch();
|
|
40
|
+
app = new Koa();
|
|
41
|
+
appRouter = new Router();
|
|
42
|
+
dhttp.registerWithApp(app, appRouter);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
await DBOS.shutdown();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('use-customstep-and-koa', async () => {
|
|
50
|
+
const u1 = await KnexKoa.insertOneWay('joe');
|
|
51
|
+
expect(u1.user).toBe('joe');
|
|
52
|
+
const u2 = await KnexKoa.insertTheOtherWay('jack');
|
|
53
|
+
expect(u2.user).toBe('jack');
|
|
54
|
+
|
|
55
|
+
const response1 = await request(app.callback()).get('/api/i1?user=john');
|
|
56
|
+
expect(response1.statusCode).toBe(200);
|
|
57
|
+
const response2 = await request(app.callback()).get('/api/i2?user=jeremy');
|
|
58
|
+
expect(response2.statusCode).toBe(200);
|
|
59
|
+
const response3 = await request(app.callback()).get('/api/i1');
|
|
60
|
+
expect(response3.statusCode).toBe(400);
|
|
61
|
+
const response4 = await request(app.callback()).get('/api/i2');
|
|
62
|
+
expect(response4.statusCode).toBe(400);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
@DBOSKoa.defaultArgValidate
|
|
67
|
+
class KnexKoa {
|
|
68
|
+
@customstep()
|
|
69
|
+
@dhttp.getApi('/api/i2')
|
|
70
|
+
static async insertTheOtherWay(user: string) {
|
|
71
|
+
return Promise.resolve({ user, now: Date.now() });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@dhttp.getApi('/api/i1')
|
|
75
|
+
@customstep()
|
|
76
|
+
static async insertOneWay(user: string) {
|
|
77
|
+
return Promise.resolve({ user, now: Date.now() });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
2
|
+
|
|
3
|
+
export async function ensureDB(client: Client, name: string) {
|
|
4
|
+
const results = await client.query('SELECT 1 FROM pg_database WHERE datname = $1', [name]);
|
|
5
|
+
if (results.rows.length === 0) {
|
|
6
|
+
await client.query(`CREATE DATABASE ${name}`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function dropDB(client: Client, name: string, force: boolean = false) {
|
|
11
|
+
const withForce = force ? ' WITH (FORCE)' : '';
|
|
12
|
+
await client.query(`DROP DATABASE IF EXISTS ${name} ${withForce}`);
|
|
13
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { DBOS } from '@dbos-inc/dbos-sdk';
|
|
2
|
+
import { Client, Pool } from 'pg';
|
|
3
|
+
import { KnexDataSource } from '@dbos-inc/knex-datasource';
|
|
4
|
+
import { dropDB, ensureDB } from './test-helpers';
|
|
5
|
+
import Koa from 'koa';
|
|
6
|
+
import Router from '@koa/router';
|
|
7
|
+
import { DBOSKoa } from '../src';
|
|
8
|
+
|
|
9
|
+
import request from 'supertest';
|
|
10
|
+
|
|
11
|
+
const config = { client: 'pg', connection: { user: 'postgres', database: 'koa_knex_ds_test_userdb' } };
|
|
12
|
+
const knexds = new KnexDataSource('app-db', config);
|
|
13
|
+
|
|
14
|
+
const dhttp = new DBOSKoa();
|
|
15
|
+
|
|
16
|
+
interface greetings {
|
|
17
|
+
name: string;
|
|
18
|
+
greet_count: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('KnexDataSource', () => {
|
|
22
|
+
const userDB = new Pool(config.connection);
|
|
23
|
+
let app: Koa;
|
|
24
|
+
let appRouter: Router;
|
|
25
|
+
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
{
|
|
28
|
+
const client = new Client({ ...config.connection, database: 'postgres' });
|
|
29
|
+
try {
|
|
30
|
+
await client.connect();
|
|
31
|
+
await dropDB(client, 'koa_knex_ds_test', true);
|
|
32
|
+
await dropDB(client, 'koa_knex_ds_test_dbos_sys', true);
|
|
33
|
+
await dropDB(client, config.connection.database, true);
|
|
34
|
+
await ensureDB(client, config.connection.database);
|
|
35
|
+
} finally {
|
|
36
|
+
await client.end();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
const client = await userDB.connect();
|
|
42
|
+
try {
|
|
43
|
+
await client.query(
|
|
44
|
+
'CREATE TABLE greetings(name text NOT NULL, greet_count integer DEFAULT 0, PRIMARY KEY(name))',
|
|
45
|
+
);
|
|
46
|
+
} finally {
|
|
47
|
+
client.release();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await KnexDataSource.initializeDBOSSchema(config);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterAll(async () => {
|
|
55
|
+
await userDB.end();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
beforeEach(async () => {
|
|
59
|
+
DBOS.setConfig({ name: 'koa-knex-ds-test' });
|
|
60
|
+
await DBOS.launch();
|
|
61
|
+
app = new Koa();
|
|
62
|
+
appRouter = new Router();
|
|
63
|
+
dhttp.registerWithApp(app, appRouter);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
afterEach(async () => {
|
|
67
|
+
await DBOS.shutdown();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('use-dstx-and-koa', async () => {
|
|
71
|
+
const u1 = await KnexKoa.insertOneWay('joe');
|
|
72
|
+
expect(u1.user).toBe('joe');
|
|
73
|
+
const u2 = await KnexKoa.insertTheOtherWay('jack');
|
|
74
|
+
expect(u2.user).toBe('jack');
|
|
75
|
+
|
|
76
|
+
const response1 = await request(app.callback()).get('/api/i1?user=john');
|
|
77
|
+
expect(response1.statusCode).toBe(200);
|
|
78
|
+
const response2 = await request(app.callback()).get('/api/i2?user=jeremy');
|
|
79
|
+
expect(response2.statusCode).toBe(200);
|
|
80
|
+
const response3 = await request(app.callback()).get('/api/i1');
|
|
81
|
+
expect(response3.statusCode).toBe(400);
|
|
82
|
+
const response4 = await request(app.callback()).get('/api/i2');
|
|
83
|
+
expect(response4.statusCode).toBe(400);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
@DBOSKoa.defaultArgValidate
|
|
88
|
+
class KnexKoa {
|
|
89
|
+
@knexds.transaction()
|
|
90
|
+
@dhttp.getApi('/api/i2')
|
|
91
|
+
static async insertTheOtherWay(user: string) {
|
|
92
|
+
const rows = await knexds
|
|
93
|
+
.client<greetings>('greetings')
|
|
94
|
+
.insert({ name: user, greet_count: 1 })
|
|
95
|
+
.onConflict('name')
|
|
96
|
+
.merge({ greet_count: knexds.client.raw('greetings.greet_count + 1') })
|
|
97
|
+
.returning('greet_count');
|
|
98
|
+
|
|
99
|
+
const row = rows.length > 0 ? rows[0] : undefined;
|
|
100
|
+
|
|
101
|
+
return { user, greet_count: row?.greet_count, now: Date.now() };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@dhttp.getApi('/api/i1')
|
|
105
|
+
@knexds.transaction()
|
|
106
|
+
static async insertOneWay(user: string) {
|
|
107
|
+
const rows = await knexds
|
|
108
|
+
.client<greetings>('greetings')
|
|
109
|
+
.insert({ name: user, greet_count: 1 })
|
|
110
|
+
.onConflict('name')
|
|
111
|
+
.merge({ greet_count: knexds.client.raw('greetings.greet_count + 1') })
|
|
112
|
+
.returning('greet_count');
|
|
113
|
+
const row = rows.length > 0 ? rows[0] : undefined;
|
|
114
|
+
|
|
115
|
+
return { user, greet_count: row?.greet_count, now: Date.now() };
|
|
116
|
+
}
|
|
117
|
+
}
|