@furystack/rest-service 4.0.19
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/LICENSE +339 -0
- package/README.md +219 -0
- package/dist/actions/error-action.d.ts +14 -0
- package/dist/actions/error-action.d.ts.map +1 -0
- package/dist/actions/error-action.js +29 -0
- package/dist/actions/error-action.js.map +1 -0
- package/dist/actions/error-action.spec.d.ts +2 -0
- package/dist/actions/error-action.spec.d.ts.map +1 -0
- package/dist/actions/error-action.spec.js +51 -0
- package/dist/actions/error-action.spec.js.map +1 -0
- package/dist/actions/get-current-user.d.ts +11 -0
- package/dist/actions/get-current-user.d.ts.map +1 -0
- package/dist/actions/get-current-user.js +15 -0
- package/dist/actions/get-current-user.js.map +1 -0
- package/dist/actions/get-current-user.spec.d.ts +2 -0
- package/dist/actions/get-current-user.spec.d.ts.map +1 -0
- package/dist/actions/get-current-user.spec.js +20 -0
- package/dist/actions/get-current-user.spec.js.map +1 -0
- package/dist/actions/index.d.ts +7 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +10 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/is-authenticated.d.ts +14 -0
- package/dist/actions/is-authenticated.d.ts.map +1 -0
- package/dist/actions/is-authenticated.js +17 -0
- package/dist/actions/is-authenticated.js.map +1 -0
- package/dist/actions/is-authenticated.spec.d.ts +2 -0
- package/dist/actions/is-authenticated.spec.d.ts.map +1 -0
- package/dist/actions/is-authenticated.spec.js +19 -0
- package/dist/actions/is-authenticated.spec.js.map +1 -0
- package/dist/actions/login-action.spec.d.ts +2 -0
- package/dist/actions/login-action.spec.d.ts.map +1 -0
- package/dist/actions/login-action.spec.js +35 -0
- package/dist/actions/login-action.spec.js.map +1 -0
- package/dist/actions/login.d.ts +16 -0
- package/dist/actions/login.d.ts.map +1 -0
- package/dist/actions/login.js +26 -0
- package/dist/actions/login.js.map +1 -0
- package/dist/actions/logout-action.spec.d.ts +2 -0
- package/dist/actions/logout-action.spec.d.ts.map +1 -0
- package/dist/actions/logout-action.spec.js +23 -0
- package/dist/actions/logout-action.spec.js.map +1 -0
- package/dist/actions/logout.d.ts +14 -0
- package/dist/actions/logout.d.ts.map +1 -0
- package/dist/actions/logout.js +20 -0
- package/dist/actions/logout.js.map +1 -0
- package/dist/actions/not-found-action.d.ts +10 -0
- package/dist/actions/not-found-action.d.ts.map +1 -0
- package/dist/actions/not-found-action.js +14 -0
- package/dist/actions/not-found-action.js.map +1 -0
- package/dist/actions/not-found-action.spec.d.ts +2 -0
- package/dist/actions/not-found-action.spec.d.ts.map +1 -0
- package/dist/actions/not-found-action.spec.js +17 -0
- package/dist/actions/not-found-action.spec.js.map +1 -0
- package/dist/add-cors-header.spec.d.ts +2 -0
- package/dist/add-cors-header.spec.d.ts.map +1 -0
- package/dist/add-cors-header.spec.js +99 -0
- package/dist/add-cors-header.spec.js.map +1 -0
- package/dist/api-manager.d.ts +61 -0
- package/dist/api-manager.d.ts.map +1 -0
- package/dist/api-manager.js +144 -0
- package/dist/api-manager.js.map +1 -0
- package/dist/authenticate.d.ts +5 -0
- package/dist/authenticate.d.ts.map +1 -0
- package/dist/authenticate.js +20 -0
- package/dist/authenticate.js.map +1 -0
- package/dist/authenticate.spec.d.ts +2 -0
- package/dist/authenticate.spec.d.ts.map +1 -0
- package/dist/authenticate.spec.js +59 -0
- package/dist/authenticate.spec.js.map +1 -0
- package/dist/authorize.d.ts +5 -0
- package/dist/authorize.d.ts.map +1 -0
- package/dist/authorize.js +22 -0
- package/dist/authorize.js.map +1 -0
- package/dist/authorize.spec.d.ts +2 -0
- package/dist/authorize.spec.d.ts.map +1 -0
- package/dist/authorize.spec.js +55 -0
- package/dist/authorize.spec.js.map +1 -0
- package/dist/endpoint-generators/create-delete-endpoint.d.ts +17 -0
- package/dist/endpoint-generators/create-delete-endpoint.d.ts.map +1 -0
- package/dist/endpoint-generators/create-delete-endpoint.js +24 -0
- package/dist/endpoint-generators/create-delete-endpoint.js.map +1 -0
- package/dist/endpoint-generators/create-delete-endpoint.spec.d.ts +2 -0
- package/dist/endpoint-generators/create-delete-endpoint.spec.d.ts.map +1 -0
- package/dist/endpoint-generators/create-delete-endpoint.spec.js +33 -0
- package/dist/endpoint-generators/create-delete-endpoint.spec.js.map +1 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.d.ts +17 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.js +26 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.js.map +1 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.spec.d.ts +2 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.spec.d.ts.map +1 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.spec.js +143 -0
- package/dist/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.d.ts +17 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.js +29 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.js.map +1 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.spec.d.ts +2 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.spec.d.ts.map +1 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.spec.js +74 -0
- package/dist/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -0
- package/dist/endpoint-generators/create-patch-endpoint.d.ts +18 -0
- package/dist/endpoint-generators/create-patch-endpoint.d.ts.map +1 -0
- package/dist/endpoint-generators/create-patch-endpoint.js +26 -0
- package/dist/endpoint-generators/create-patch-endpoint.js.map +1 -0
- package/dist/endpoint-generators/create-patch-endpoint.spec.d.ts +2 -0
- package/dist/endpoint-generators/create-patch-endpoint.spec.d.ts.map +1 -0
- package/dist/endpoint-generators/create-patch-endpoint.spec.js +36 -0
- package/dist/endpoint-generators/create-patch-endpoint.spec.js.map +1 -0
- package/dist/endpoint-generators/create-post-endpoint.d.ts +18 -0
- package/dist/endpoint-generators/create-post-endpoint.d.ts.map +1 -0
- package/dist/endpoint-generators/create-post-endpoint.js +29 -0
- package/dist/endpoint-generators/create-post-endpoint.js.map +1 -0
- package/dist/endpoint-generators/create-post-endpoint.spec.d.ts +2 -0
- package/dist/endpoint-generators/create-post-endpoint.spec.d.ts.map +1 -0
- package/dist/endpoint-generators/create-post-endpoint.spec.js +34 -0
- package/dist/endpoint-generators/create-post-endpoint.spec.js.map +1 -0
- package/dist/endpoint-generators/index.d.ts +6 -0
- package/dist/endpoint-generators/index.d.ts.map +1 -0
- package/dist/endpoint-generators/index.js +9 -0
- package/dist/endpoint-generators/index.js.map +1 -0
- package/dist/endpoint-generators/utils.d.ts +9 -0
- package/dist/endpoint-generators/utils.d.ts.map +1 -0
- package/dist/endpoint-generators/utils.js +27 -0
- package/dist/endpoint-generators/utils.js.map +1 -0
- package/dist/http-authentication-settings.d.ts +17 -0
- package/dist/http-authentication-settings.d.ts.map +1 -0
- package/dist/http-authentication-settings.js +26 -0
- package/dist/http-authentication-settings.js.map +1 -0
- package/dist/http-user-context.d.ts +54 -0
- package/dist/http-user-context.d.ts.map +1 -0
- package/dist/http-user-context.js +153 -0
- package/dist/http-user-context.js.map +1 -0
- package/dist/http-user-context.spec.d.ts +4 -0
- package/dist/http-user-context.spec.d.ts.map +1 -0
- package/dist/http-user-context.spec.js +267 -0
- package/dist/http-user-context.spec.js.map +1 -0
- package/dist/incoming-message-extensions.d.ts +8 -0
- package/dist/incoming-message-extensions.d.ts.map +1 -0
- package/dist/incoming-message-extensions.js +14 -0
- package/dist/incoming-message-extensions.js.map +1 -0
- package/dist/incoming-message-extensions.spec.d.ts +2 -0
- package/dist/incoming-message-extensions.spec.d.ts.map +1 -0
- package/dist/incoming-message-extensions.spec.js +39 -0
- package/dist/incoming-message-extensions.spec.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/injector-extensions.d.ts +21 -0
- package/dist/injector-extensions.d.ts.map +1 -0
- package/dist/injector-extensions.js +14 -0
- package/dist/injector-extensions.js.map +1 -0
- package/dist/injector-extensions.spec.d.ts +2 -0
- package/dist/injector-extensions.spec.d.ts.map +1 -0
- package/dist/injector-extensions.spec.js +19 -0
- package/dist/injector-extensions.spec.js.map +1 -0
- package/dist/models/cors-options.d.ts +22 -0
- package/dist/models/cors-options.d.ts.map +1 -0
- package/dist/models/cors-options.js +3 -0
- package/dist/models/cors-options.js.map +1 -0
- package/dist/models/default-session.d.ts +14 -0
- package/dist/models/default-session.d.ts.map +1 -0
- package/dist/models/default-session.js +10 -0
- package/dist/models/default-session.js.map +1 -0
- package/dist/request-action-implementation.d.ts +54 -0
- package/dist/request-action-implementation.d.ts.map +1 -0
- package/dist/request-action-implementation.js +42 -0
- package/dist/request-action-implementation.js.map +1 -0
- package/dist/rest-service.integration.spec.d.ts +2 -0
- package/dist/rest-service.integration.spec.d.ts.map +1 -0
- package/dist/rest-service.integration.spec.js +129 -0
- package/dist/rest-service.integration.spec.js.map +1 -0
- package/dist/rest.integration.test.d.ts +58 -0
- package/dist/rest.integration.test.d.ts.map +1 -0
- package/dist/rest.integration.test.js +94 -0
- package/dist/rest.integration.test.js.map +1 -0
- package/dist/schema-validator/index.d.ts +3 -0
- package/dist/schema-validator/index.d.ts.map +1 -0
- package/dist/schema-validator/index.js +6 -0
- package/dist/schema-validator/index.js.map +1 -0
- package/dist/schema-validator/schema-validation-error.d.ts +10 -0
- package/dist/schema-validator/schema-validation-error.d.ts.map +1 -0
- package/dist/schema-validator/schema-validation-error.js +15 -0
- package/dist/schema-validator/schema-validation-error.js.map +1 -0
- package/dist/schema-validator/schema-validator.d.ts +20 -0
- package/dist/schema-validator/schema-validator.d.ts.map +1 -0
- package/dist/schema-validator/schema-validator.js +36 -0
- package/dist/schema-validator/schema-validator.js.map +1 -0
- package/dist/schema-validator/schema-validator.test.d.ts +2 -0
- package/dist/schema-validator/schema-validator.test.d.ts.map +1 -0
- package/dist/schema-validator/schema-validator.test.js +62 -0
- package/dist/schema-validator/schema-validator.test.js.map +1 -0
- package/dist/schema-validator/validate-examples.d.ts +37 -0
- package/dist/schema-validator/validate-examples.d.ts.map +1 -0
- package/dist/schema-validator/validate-examples.js +29 -0
- package/dist/schema-validator/validate-examples.js.map +1 -0
- package/dist/server-manager.d.ts +30 -0
- package/dist/server-manager.d.ts.map +1 -0
- package/dist/server-manager.js +71 -0
- package/dist/server-manager.js.map +1 -0
- package/dist/server-response-extensions.d.ts +21 -0
- package/dist/server-response-extensions.d.ts.map +1 -0
- package/dist/server-response-extensions.js +15 -0
- package/dist/server-response-extensions.js.map +1 -0
- package/dist/server-response-extensions.spec.d.ts +2 -0
- package/dist/server-response-extensions.spec.d.ts.map +1 -0
- package/dist/server-response-extensions.spec.js +49 -0
- package/dist/server-response-extensions.spec.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +66 -0
- package/dist/utils.js.map +1 -0
- package/dist/validate.d.ts +18 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.integration.schema.d.ts +69 -0
- package/dist/validate.integration.schema.d.ts.map +1 -0
- package/dist/validate.integration.schema.js +3 -0
- package/dist/validate.integration.schema.js.map +1 -0
- package/dist/validate.integration.spec.d.ts +13 -0
- package/dist/validate.integration.spec.d.ts.map +1 -0
- package/dist/validate.integration.spec.js +223 -0
- package/dist/validate.integration.spec.js.map +1 -0
- package/dist/validate.integration.spec.schema.json +749 -0
- package/dist/validate.js +49 -0
- package/dist/validate.js.map +1 -0
- package/package.json +56 -0
- package/src/actions/error-action.spec.ts +54 -0
- package/src/actions/error-action.ts +34 -0
- package/src/actions/get-current-user.spec.ts +23 -0
- package/src/actions/get-current-user.ts +15 -0
- package/src/actions/index.ts +6 -0
- package/src/actions/is-authenticated.spec.ts +18 -0
- package/src/actions/is-authenticated.ts +13 -0
- package/src/actions/login-action.spec.ts +41 -0
- package/src/actions/login.ts +26 -0
- package/src/actions/logout-action.spec.ts +27 -0
- package/src/actions/logout.ts +16 -0
- package/src/actions/not-found-action.spec.ts +17 -0
- package/src/actions/not-found-action.ts +13 -0
- package/src/add-cors-header.spec.ts +133 -0
- package/src/api-manager.ts +222 -0
- package/src/authenticate.spec.ts +78 -0
- package/src/authenticate.ts +22 -0
- package/src/authorize.spec.ts +69 -0
- package/src/authorize.ts +19 -0
- package/src/endpoint-generators/create-delete-endpoint.spec.ts +34 -0
- package/src/endpoint-generators/create-delete-endpoint.ts +25 -0
- package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +164 -0
- package/src/endpoint-generators/create-get-collection-endpoint.ts +28 -0
- package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +75 -0
- package/src/endpoint-generators/create-get-entity-endpoint.ts +29 -0
- package/src/endpoint-generators/create-patch-endpoint.spec.ts +36 -0
- package/src/endpoint-generators/create-patch-endpoint.ts +27 -0
- package/src/endpoint-generators/create-post-endpoint.spec.ts +32 -0
- package/src/endpoint-generators/create-post-endpoint.ts +30 -0
- package/src/endpoint-generators/index.ts +5 -0
- package/src/endpoint-generators/utils.ts +34 -0
- package/src/http-authentication-settings.ts +23 -0
- package/src/http-user-context.spec.ts +299 -0
- package/src/http-user-context.ts +160 -0
- package/src/incoming-message-extensions.spec.ts +41 -0
- package/src/incoming-message-extensions.ts +19 -0
- package/src/index.ts +16 -0
- package/src/injector-extensions.spec.ts +19 -0
- package/src/injector-extensions.ts +35 -0
- package/src/models/cors-options.ts +21 -0
- package/src/models/default-session.ts +14 -0
- package/src/request-action-implementation.ts +70 -0
- package/src/rest-service.integration.spec.ts +166 -0
- package/src/rest.integration.test.ts +112 -0
- package/src/schema-validator/index.ts +2 -0
- package/src/schema-validator/schema-validation-error.ts +11 -0
- package/src/schema-validator/schema-validator.test.ts +72 -0
- package/src/schema-validator/schema-validator.ts +31 -0
- package/src/schema-validator/validate-examples.ts +38 -0
- package/src/server-manager.ts +88 -0
- package/src/server-response-extensions.spec.ts +53 -0
- package/src/server-response-extensions.ts +30 -0
- package/src/utils.ts +65 -0
- package/src/validate.integration.schema.ts +50 -0
- package/src/validate.integration.spec.schema.json +779 -0
- package/src/validate.integration.spec.ts +218 -0
- package/src/validate.ts +60 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Constructable } from '@furystack/inject'
|
|
2
|
+
import { DeleteEndpoint } from '@furystack/rest'
|
|
3
|
+
import '@furystack/repository'
|
|
4
|
+
import { JsonResult, RequestAction } from '../request-action-implementation'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a DELETE endpoint for removing entities
|
|
8
|
+
*
|
|
9
|
+
* @param options The options for endpoint creation
|
|
10
|
+
* @param options.model The Model class
|
|
11
|
+
* @param options.primaryKey The field used as primary key on the model
|
|
12
|
+
* @returns a boolean that indicates the success
|
|
13
|
+
*/
|
|
14
|
+
export const createDeleteEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
|
|
15
|
+
model: Constructable<T>
|
|
16
|
+
primaryKey: TPrimaryKey
|
|
17
|
+
}) => {
|
|
18
|
+
const endpoint: RequestAction<DeleteEndpoint<T, TPrimaryKey>> = async ({ injector, getUrlParams }) => {
|
|
19
|
+
const { id } = getUrlParams()
|
|
20
|
+
const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
|
|
21
|
+
await dataSet.remove(injector, id)
|
|
22
|
+
return JsonResult({}, 204)
|
|
23
|
+
}
|
|
24
|
+
return endpoint
|
|
25
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { usingAsync } from '@furystack/utils'
|
|
2
|
+
import { Injector } from '@furystack/inject'
|
|
3
|
+
import got from 'got'
|
|
4
|
+
import { MockClass, setupContext } from './utils'
|
|
5
|
+
import { createGetCollectionEndpoint } from './create-get-collection-endpoint'
|
|
6
|
+
import { GetCollectionEndpoint, GetCollectionResult, serializeToQueryString } from '@furystack/rest'
|
|
7
|
+
import { FindOptions } from '@furystack/core'
|
|
8
|
+
|
|
9
|
+
const addMockEntities = async (i: Injector) =>
|
|
10
|
+
await i
|
|
11
|
+
.getDataSetFor(MockClass, 'id')
|
|
12
|
+
.add(
|
|
13
|
+
i,
|
|
14
|
+
{ id: 'mock1', value: '4' },
|
|
15
|
+
{ id: 'mock2', value: '3' },
|
|
16
|
+
{ id: 'mock3', value: '2' },
|
|
17
|
+
{ id: 'mock4', value: '1' },
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
describe('createGetCollectionEndpoint', () => {
|
|
21
|
+
it('Should return the collection without filter / order', async () => {
|
|
22
|
+
await usingAsync(new Injector(), async (i) => {
|
|
23
|
+
setupContext(i)
|
|
24
|
+
await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
|
|
25
|
+
root: '/api',
|
|
26
|
+
port: 1112,
|
|
27
|
+
api: {
|
|
28
|
+
GET: {
|
|
29
|
+
'/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
await addMockEntities(i)
|
|
34
|
+
|
|
35
|
+
const count = await i.getDataSetFor(MockClass, 'id').count(i)
|
|
36
|
+
const allEntities = await i.getDataSetFor(MockClass, 'id').find(i, {})
|
|
37
|
+
|
|
38
|
+
const response = await got('http://127.0.0.1:1112/api/entities', { method: 'GET' })
|
|
39
|
+
const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
|
|
40
|
+
expect(json.count).toBe(count)
|
|
41
|
+
expect(json.entries).toEqual(allEntities)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('Should return entities in order', async () => {
|
|
46
|
+
await usingAsync(new Injector(), async (i) => {
|
|
47
|
+
setupContext(i)
|
|
48
|
+
await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
|
|
49
|
+
root: '/api',
|
|
50
|
+
port: 1113,
|
|
51
|
+
api: {
|
|
52
|
+
GET: {
|
|
53
|
+
'/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
await addMockEntities(i)
|
|
58
|
+
const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { order: { value: 'ASC' } }
|
|
59
|
+
const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
|
|
60
|
+
const orderedEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
|
|
61
|
+
const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
})
|
|
64
|
+
const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
|
|
65
|
+
expect(json.count).toBe(count)
|
|
66
|
+
expect(json.entries).toEqual(orderedEntities)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('Should return entities with filtering', async () => {
|
|
71
|
+
await usingAsync(new Injector(), async (i) => {
|
|
72
|
+
setupContext(i)
|
|
73
|
+
await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
|
|
74
|
+
root: '/api',
|
|
75
|
+
port: 1113,
|
|
76
|
+
api: {
|
|
77
|
+
GET: {
|
|
78
|
+
'/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
await addMockEntities(i)
|
|
83
|
+
const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
|
|
84
|
+
filter: { id: { $ne: 'mock2' } },
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
|
|
88
|
+
const filteredEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
|
|
89
|
+
|
|
90
|
+
expect(filteredEntities).not.toContainEqual({ id: 'mock2', value: '3' })
|
|
91
|
+
|
|
92
|
+
const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
|
|
93
|
+
method: 'GET',
|
|
94
|
+
})
|
|
95
|
+
const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
|
|
96
|
+
expect(json.count).toBe(count)
|
|
97
|
+
expect(json.entries).toEqual(filteredEntities)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('Should return entities with selecting specific fields', async () => {
|
|
102
|
+
await usingAsync(new Injector(), async (i) => {
|
|
103
|
+
setupContext(i)
|
|
104
|
+
await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
|
|
105
|
+
root: '/api',
|
|
106
|
+
port: 1113,
|
|
107
|
+
api: {
|
|
108
|
+
GET: {
|
|
109
|
+
'/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
await addMockEntities(i)
|
|
114
|
+
const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
|
|
115
|
+
select: ['id'],
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
|
|
119
|
+
const selectedEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
|
|
120
|
+
|
|
121
|
+
selectedEntities.forEach((e) => expect(e.value).toBeUndefined())
|
|
122
|
+
|
|
123
|
+
const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
|
|
124
|
+
method: 'GET',
|
|
125
|
+
})
|
|
126
|
+
const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
|
|
127
|
+
expect(json.count).toBe(count)
|
|
128
|
+
expect(json.entries).toEqual(selectedEntities)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('Should return entities with top/skip', async () => {
|
|
133
|
+
await usingAsync(new Injector(), async (i) => {
|
|
134
|
+
setupContext(i)
|
|
135
|
+
await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
|
|
136
|
+
root: '/api',
|
|
137
|
+
port: 1113,
|
|
138
|
+
api: {
|
|
139
|
+
GET: {
|
|
140
|
+
'/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
await addMockEntities(i)
|
|
145
|
+
const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
|
|
146
|
+
skip: 1,
|
|
147
|
+
top: 2,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
|
|
151
|
+
const topSkipEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
|
|
152
|
+
|
|
153
|
+
expect(topSkipEntities).not.toContainEqual({ id: 'mock1', value: '4' })
|
|
154
|
+
expect(topSkipEntities).not.toContainEqual({ id: 'mock4', value: '1' })
|
|
155
|
+
|
|
156
|
+
const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
|
|
157
|
+
method: 'GET',
|
|
158
|
+
})
|
|
159
|
+
const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
|
|
160
|
+
expect(json.count).toBe(count)
|
|
161
|
+
expect(json.entries).toEqual(topSkipEntities)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Constructable } from '@furystack/inject'
|
|
2
|
+
import { GetCollectionEndpoint } from '@furystack/rest'
|
|
3
|
+
import '@furystack/repository'
|
|
4
|
+
import { JsonResult, RequestAction } from '../request-action-implementation'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a GetCollection endpoint for the given model. The model should have a Repository DataSet
|
|
8
|
+
*
|
|
9
|
+
* @param options The options for endpoint creation
|
|
10
|
+
* @param options.model The Model class
|
|
11
|
+
* @param options.primaryKey The field used as primary key on the model
|
|
12
|
+
* @returns The created endpoint
|
|
13
|
+
*/
|
|
14
|
+
export const createGetCollectionEndpoint = <T, TPrimaryKey extends keyof T>(options: {
|
|
15
|
+
model: Constructable<T>
|
|
16
|
+
primaryKey: TPrimaryKey
|
|
17
|
+
}) => {
|
|
18
|
+
const endpoint: RequestAction<GetCollectionEndpoint<T>> = async ({ injector, getQuery }) => {
|
|
19
|
+
const { findOptions } = getQuery()
|
|
20
|
+
const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
|
|
21
|
+
const entriesPromise = dataSet.find<any>(injector, findOptions || {})
|
|
22
|
+
const countPromise = dataSet.count(injector, findOptions?.filter)
|
|
23
|
+
const [entries, count] = await Promise.all([entriesPromise, countPromise])
|
|
24
|
+
|
|
25
|
+
return JsonResult({ entries, count })
|
|
26
|
+
}
|
|
27
|
+
return endpoint
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { usingAsync } from '@furystack/utils'
|
|
2
|
+
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { GetEntityEndpoint, serializeToQueryString } from '@furystack/rest'
|
|
4
|
+
import got, { HTTPError } from 'got'
|
|
5
|
+
import { MockClass, setupContext } from './utils'
|
|
6
|
+
import { createGetEntityEndpoint } from './create-get-entity-endpoint'
|
|
7
|
+
|
|
8
|
+
describe('createGetEntityEndpoint', () => {
|
|
9
|
+
it('Should return the entity', async () => {
|
|
10
|
+
await usingAsync(new Injector(), async (i) => {
|
|
11
|
+
setupContext(i)
|
|
12
|
+
await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
|
|
13
|
+
root: '/api',
|
|
14
|
+
port: 1113,
|
|
15
|
+
api: {
|
|
16
|
+
GET: {
|
|
17
|
+
'/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
const mockEntity: MockClass = { id: 'mock', value: 'mock' }
|
|
22
|
+
await i.getDataSetFor(MockClass, 'id').add(i, mockEntity)
|
|
23
|
+
|
|
24
|
+
const response = await got('http://127.0.0.1:1113/api/mock', { method: 'GET' })
|
|
25
|
+
expect(JSON.parse(response.body)).toStrictEqual(mockEntity)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('Should return the entity with the selected fields', async () => {
|
|
30
|
+
await usingAsync(new Injector(), async (i) => {
|
|
31
|
+
setupContext(i)
|
|
32
|
+
await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
|
|
33
|
+
root: '/api',
|
|
34
|
+
port: 1114,
|
|
35
|
+
api: {
|
|
36
|
+
GET: {
|
|
37
|
+
'/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
const mockEntity: MockClass = { id: 'mock', value: 'mock' }
|
|
42
|
+
await i.getDataSetFor(MockClass, 'id').add(i, mockEntity)
|
|
43
|
+
|
|
44
|
+
const response = await got(`http://127.0.0.1:1114/api/mock?${serializeToQueryString({ select: ['id'] })}`, {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
})
|
|
47
|
+
expect(JSON.parse(response.body)).toStrictEqual({ id: mockEntity.id })
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('Should return 404 if no entity has been found', async () => {
|
|
52
|
+
await usingAsync(new Injector(), async (i) => {
|
|
53
|
+
setupContext(i)
|
|
54
|
+
await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
|
|
55
|
+
root: '/api',
|
|
56
|
+
port: 1115,
|
|
57
|
+
api: {
|
|
58
|
+
GET: {
|
|
59
|
+
'/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
await new Promise<void>((resolve, reject) => {
|
|
64
|
+
got(`http://127.0.0.1:1115/api/mock`, { method: 'GET' })
|
|
65
|
+
.then(() => reject('Should throw'))
|
|
66
|
+
.catch((err) => {
|
|
67
|
+
const e: HTTPError = err
|
|
68
|
+
expect(e.response.statusCode).toBe(404)
|
|
69
|
+
expect(JSON.parse(e.response.body as string).message).toBe('Entity not found')
|
|
70
|
+
resolve()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Constructable } from '@furystack/inject'
|
|
2
|
+
import { RequestError, GetEntityEndpoint } from '@furystack/rest'
|
|
3
|
+
import '@furystack/repository'
|
|
4
|
+
import { JsonResult, RequestAction } from '../request-action-implementation'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a simple Get Entity endpoint for a specified model.
|
|
8
|
+
*
|
|
9
|
+
* @param options The options for endpoint creation
|
|
10
|
+
* @param options.model The entity model, should have a Repository DataSet
|
|
11
|
+
* @param options.primaryKey The field name used as primary key on the model
|
|
12
|
+
* @returns The generated endpoint
|
|
13
|
+
*/
|
|
14
|
+
export const createGetEntityEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
|
|
15
|
+
model: Constructable<T>
|
|
16
|
+
primaryKey: TPrimaryKey
|
|
17
|
+
}) => {
|
|
18
|
+
const endpoint: RequestAction<GetEntityEndpoint<T, TPrimaryKey>> = async ({ injector, getUrlParams, getQuery }) => {
|
|
19
|
+
const { id } = getUrlParams()
|
|
20
|
+
const { select } = getQuery()
|
|
21
|
+
const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
|
|
22
|
+
const entry = await dataSet.get(injector, id, select)
|
|
23
|
+
if (!entry) {
|
|
24
|
+
throw new RequestError('Entity not found', 404)
|
|
25
|
+
}
|
|
26
|
+
return JsonResult(entry)
|
|
27
|
+
}
|
|
28
|
+
return endpoint
|
|
29
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { usingAsync } from '@furystack/utils'
|
|
2
|
+
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { PatchEndpoint } from '@furystack/rest'
|
|
4
|
+
import { createPatchEndpoint } from './create-patch-endpoint'
|
|
5
|
+
import got from 'got'
|
|
6
|
+
import { MockClass, setupContext } from './utils'
|
|
7
|
+
|
|
8
|
+
describe('createPatchEndpoint', () => {
|
|
9
|
+
it('Should update the entity and report the success', async () => {
|
|
10
|
+
await usingAsync(new Injector(), async (i) => {
|
|
11
|
+
setupContext(i)
|
|
12
|
+
await i.useRestService<{ PATCH: { '/:id': PatchEndpoint<MockClass, 'id'> } }>({
|
|
13
|
+
root: '/api',
|
|
14
|
+
port: 1116,
|
|
15
|
+
api: {
|
|
16
|
+
PATCH: {
|
|
17
|
+
'/:id': createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
await i.getDataSetFor(MockClass, 'id').add(i, { id: 'mock', value: 'mock' })
|
|
22
|
+
|
|
23
|
+
const countBeforeDelete = await i.getDataSetFor(MockClass, 'id').count(i)
|
|
24
|
+
expect(countBeforeDelete).toBe(1)
|
|
25
|
+
|
|
26
|
+
const response = await got('http://127.0.0.1:1116/api/mock', {
|
|
27
|
+
method: 'PATCH',
|
|
28
|
+
body: JSON.stringify({ value: 'updated' }),
|
|
29
|
+
})
|
|
30
|
+
expect(response.statusCode).toBe(200)
|
|
31
|
+
expect(response.body).toBe('{}')
|
|
32
|
+
const updated = await i.getDataSetFor(MockClass, 'id').get(i, 'mock')
|
|
33
|
+
expect(updated?.value).toBe('updated')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Constructable } from '@furystack/inject'
|
|
2
|
+
import { PatchEndpoint } from '@furystack/rest'
|
|
3
|
+
import '@furystack/repository'
|
|
4
|
+
import '../incoming-message-extensions'
|
|
5
|
+
import { JsonResult, RequestAction } from '../request-action-implementation'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a PATCH endpoint for updating entities
|
|
9
|
+
*
|
|
10
|
+
* @param options The options for endpoint creation
|
|
11
|
+
* @param options.model The Model class
|
|
12
|
+
* @param options.primaryKey The field name that is used as primary key on the model
|
|
13
|
+
* @returns a boolean that indicates the success
|
|
14
|
+
*/
|
|
15
|
+
export const createPatchEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
|
|
16
|
+
model: Constructable<T>
|
|
17
|
+
primaryKey: TPrimaryKey
|
|
18
|
+
}) => {
|
|
19
|
+
const endpoint: RequestAction<PatchEndpoint<T, TPrimaryKey>> = async ({ injector, request, getUrlParams }) => {
|
|
20
|
+
const { id } = getUrlParams()
|
|
21
|
+
const patchData = await request.readPostBody<T>()
|
|
22
|
+
const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
|
|
23
|
+
await dataSet.update(injector, id, patchData)
|
|
24
|
+
return JsonResult({})
|
|
25
|
+
}
|
|
26
|
+
return endpoint
|
|
27
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { usingAsync } from '@furystack/utils'
|
|
2
|
+
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { PostEndpoint } from '@furystack/rest'
|
|
4
|
+
import { createPostEndpoint } from './create-post-endpoint'
|
|
5
|
+
import got from 'got'
|
|
6
|
+
import { MockClass, setupContext } from './utils'
|
|
7
|
+
|
|
8
|
+
describe('createPostEndpoint', () => {
|
|
9
|
+
it('Should create the entity and report the success', async () => {
|
|
10
|
+
await usingAsync(new Injector(), async (i) => {
|
|
11
|
+
setupContext(i)
|
|
12
|
+
await i.useRestService<{ POST: { '/:id': PostEndpoint<MockClass, 'id'> } }>({
|
|
13
|
+
root: '/api',
|
|
14
|
+
port: 1117,
|
|
15
|
+
api: {
|
|
16
|
+
POST: {
|
|
17
|
+
'/:id': createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
const entityToPost = { id: 'mock', value: 'posted' }
|
|
22
|
+
const response = await got('http://127.0.0.1:1117/api/mock', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify(entityToPost),
|
|
25
|
+
})
|
|
26
|
+
expect(response.statusCode).toBe(201)
|
|
27
|
+
expect(JSON.parse(response.body)).toStrictEqual(entityToPost)
|
|
28
|
+
const posted = await i.getDataSetFor(MockClass, 'id').get(i, entityToPost.id)
|
|
29
|
+
expect(posted?.value).toBe('posted')
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Constructable } from '@furystack/inject'
|
|
2
|
+
import { RequestError, PostEndpoint } from '@furystack/rest'
|
|
3
|
+
import '@furystack/repository'
|
|
4
|
+
import '../incoming-message-extensions'
|
|
5
|
+
import { JsonResult, RequestAction } from '../request-action-implementation'
|
|
6
|
+
import { WithOptionalId } from '@furystack/core'
|
|
7
|
+
/**
|
|
8
|
+
* Creates a POST endpoint for updating entities
|
|
9
|
+
*
|
|
10
|
+
* @param options The options for endpoint creation
|
|
11
|
+
* @param options.model The Model class
|
|
12
|
+
* @param options.primaryKey The field name used as primary key
|
|
13
|
+
* @returns a boolean that indicates the success
|
|
14
|
+
*/
|
|
15
|
+
export const createPostEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
|
|
16
|
+
model: Constructable<T>
|
|
17
|
+
primaryKey: TPrimaryKey
|
|
18
|
+
}) => {
|
|
19
|
+
const endpoint: RequestAction<PostEndpoint<T, TPrimaryKey>> = async ({ injector, request }) => {
|
|
20
|
+
const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
|
|
21
|
+
|
|
22
|
+
const entityToCreate = await request.readPostBody<WithOptionalId<T, typeof dataSet['primaryKey']>>()
|
|
23
|
+
const { created } = await dataSet.add(injector, entityToCreate)
|
|
24
|
+
if (!created || !created.length) {
|
|
25
|
+
throw new RequestError('Entity not found', 404)
|
|
26
|
+
}
|
|
27
|
+
return JsonResult(created[0], 201)
|
|
28
|
+
}
|
|
29
|
+
return endpoint
|
|
30
|
+
}
|