@furystack/rest-service
Advanced tools
Comparing version 9.0.3 to 9.0.4
@@ -10,7 +10,2 @@ # Change Log | ||
### [4.1.9](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.8...@furystack/rest-service@4.1.9) (2022-02-02) | ||
@@ -20,7 +15,2 @@ | ||
### [4.1.8](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.6...@furystack/rest-service@4.1.8) (2022-01-10) | ||
@@ -30,7 +20,2 @@ | ||
### [4.1.7](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.6...@furystack/rest-service@4.1.7) (2022-01-10) | ||
@@ -40,7 +25,2 @@ | ||
### [4.1.6](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.5...@furystack/rest-service@4.1.6) (2021-12-20) | ||
@@ -50,7 +30,2 @@ | ||
### [4.1.5](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.4...@furystack/rest-service@4.1.5) (2021-12-08) | ||
@@ -60,7 +35,2 @@ | ||
### [4.1.4](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.3...@furystack/rest-service@4.1.4) (2021-11-30) | ||
@@ -70,7 +40,2 @@ | ||
### [4.1.3](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.2...@furystack/rest-service@4.1.3) (2021-11-29) | ||
@@ -80,7 +45,2 @@ | ||
### [4.1.2](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.1...@furystack/rest-service@4.1.2) (2021-11-19) | ||
@@ -90,7 +50,2 @@ | ||
### [4.1.1](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.1.0...@furystack/rest-service@4.1.1) (2021-11-17) | ||
@@ -100,17 +55,8 @@ | ||
## [4.1.0](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.21...@furystack/rest-service@4.1.0) (2021-11-09) | ||
### 🚀 What's new | ||
* **@furystack/rest-service:** disabled strict mode for schema validation ([20a9bad](https://github.com/furystack/furystack/commit/20a9bad9368b06b6b3186273b767dbb690e6473a)) | ||
- **@furystack/rest-service:** disabled strict mode for schema validation ([20a9bad](https://github.com/furystack/furystack/commit/20a9bad9368b06b6b3186273b767dbb690e6473a)) | ||
### [4.0.21](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.20...@furystack/rest-service@4.0.21) (2021-10-15) | ||
@@ -120,7 +66,2 @@ | ||
### [4.0.20](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.19...@furystack/rest-service@4.0.20) (2021-10-05) | ||
@@ -130,7 +71,2 @@ | ||
### [4.0.19](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.18...@furystack/rest-service@4.0.19) (2021-09-16) | ||
@@ -140,7 +76,2 @@ | ||
### [4.0.18](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.17...@furystack/rest-service@4.0.18) (2021-08-27) | ||
@@ -150,7 +81,2 @@ | ||
### [4.0.17](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.16...@furystack/rest-service@4.0.17) (2021-08-25) | ||
@@ -160,7 +86,2 @@ | ||
### [4.0.16](https://github.com/furystack/furystack/compare/@furystack/rest-service@4.0.15...@furystack/rest-service@4.0.16) (2021-08-19) | ||
@@ -170,24 +91,14 @@ | ||
### [4.0.15](https://github.com/furystack/furystack/compare/@furystack/rest-service@2.3.2...@furystack/rest-service@4.0.15) (2021-08-19) | ||
### 🐛 Bug Fixes | ||
* **rest-service:** cookie logout ([d1b04c6](https://github.com/furystack/furystack/commit/d1b04c6d976951b74c7880ab57e5676618dd5bb2)) | ||
* **rest-service:** remove session by its id ([14d9729](https://github.com/furystack/furystack/commit/14d9729c1e9581bb86ef0d87b93b3ec65056bc29)) | ||
- **rest-service:** cookie logout ([d1b04c6](https://github.com/furystack/furystack/commit/d1b04c6d976951b74c7880ab57e5676618dd5bb2)) | ||
- **rest-service:** remove session by its id ([14d9729](https://github.com/furystack/furystack/commit/14d9729c1e9581bb86ef0d87b93b3ec65056bc29)) | ||
### [4.0.14](https://github.com/furystack/furystack/compare/@furystack/rest-service@2.3.2...@furystack/rest-service@4.0.14) (2021-07-30) | ||
### 🐛 Bug Fixes | ||
* **rest-service:** cookie logout ([d1b04c6](https://github.com/furystack/furystack/commit/d1b04c6d976951b74c7880ab57e5676618dd5bb2)) | ||
* **rest-service:** remove session by its id ([14d9729](https://github.com/furystack/furystack/commit/14d9729c1e9581bb86ef0d87b93b3ec65056bc29)) | ||
- **rest-service:** cookie logout ([d1b04c6](https://github.com/furystack/furystack/commit/d1b04c6d976951b74c7880ab57e5676618dd5bb2)) | ||
- **rest-service:** remove session by its id ([14d9729](https://github.com/furystack/furystack/commit/14d9729c1e9581bb86ef0d87b93b3ec65056bc29)) |
@@ -1,4 +0,3 @@ | ||
import { isAuthorized } from '@furystack/core'; | ||
import { AuthorizationError, isAuthorized } from '@furystack/core'; | ||
import { sleepAsync } from '@furystack/utils'; | ||
import { JsonResult } from './request-action-implementation.js'; | ||
export const Authorize = (...roles) => (action) => { | ||
@@ -10,7 +9,7 @@ return async (options) => { | ||
await sleepAsync(Math.random() * 1000); | ||
return JsonResult({ error: 'forbidden' }, 403); | ||
throw new AuthorizationError('forbidden'); | ||
} | ||
} | ||
catch (error) { | ||
return JsonResult({ error: 'forbidden' }, 403); | ||
throw new AuthorizationError('forbidden'); | ||
} | ||
@@ -17,0 +16,0 @@ return (await action(options)); |
import { Injector } from '@furystack/inject'; | ||
import { usingAsync } from '@furystack/utils'; | ||
import { IdentityContext } from '@furystack/core'; | ||
import { AuthorizationError, IdentityContext } from '@furystack/core'; | ||
import { Authorize } from './authorize.js'; | ||
@@ -16,5 +16,3 @@ import { EmptyResult } from './request-action-implementation.js'; | ||
const authorized = Authorize('Role1')(exampleAuthorizedAction); | ||
const result = await authorized({ injector: i, request, response }); | ||
expect(result.statusCode).toBe(403); | ||
expect(result.chunk).toEqual({ error: 'forbidden' }); | ||
await expect(() => authorized({ injector: i, request, response })).rejects.toThrow(AuthorizationError); | ||
expect(exampleAuthorizedAction).not.toBeCalled(); | ||
@@ -32,5 +30,3 @@ }); | ||
const authorized = Authorize('Role2')(exampleAuthorizedAction); | ||
const result = await authorized({ injector: i, request, response }); | ||
expect(result.statusCode).toBe(403); | ||
expect(result.chunk).toEqual({ error: 'forbidden' }); | ||
await expect(() => authorized({ injector: i, request, response })).rejects.toThrow(AuthorizationError); | ||
expect(exampleAuthorizedAction).not.toBeCalled(); | ||
@@ -37,0 +33,0 @@ }); |
@@ -33,3 +33,3 @@ import { usingAsync } from '@furystack/utils'; | ||
expect(response.ok).toBe(true); | ||
const json = await response.json(); | ||
const json = (await response.json()); | ||
expect(response.status).toBe(200); | ||
@@ -62,3 +62,3 @@ expect(json.count).toBe(count); | ||
expect(response.ok).toBe(true); | ||
const json = await response.json(); | ||
const json = (await response.json()); | ||
expect(response.status).toBe(200); | ||
@@ -94,3 +94,3 @@ expect(json.count).toBe(count); | ||
expect(response.ok).toBe(true); | ||
const json = await response.json(); | ||
const json = (await response.json()); | ||
expect(response.status).toBe(200); | ||
@@ -126,3 +126,3 @@ expect(json.count).toBe(count); | ||
expect(response.ok).toBe(true); | ||
const json = await response.json(); | ||
const json = (await response.json()); | ||
expect(response.status).toBe(200); | ||
@@ -160,3 +160,3 @@ expect(json.count).toBe(count); | ||
expect(response.status).toBe(200); | ||
const json = await response.json(); | ||
const json = (await response.json()); | ||
expect(json.count).toBe(count); | ||
@@ -163,0 +163,0 @@ expect(json.entries).toEqual(topSkipEntities); |
@@ -8,2 +8,3 @@ import { Injector } from '@furystack/inject'; | ||
import { describe, it, expect } from 'vitest'; | ||
import { getPort } from '@furystack/core/port-generator'; | ||
describe('Injector extensions', () => { | ||
@@ -21,3 +22,4 @@ describe('useHttpAuthentication', () => { | ||
await usingAsync(new Injector(), async (i) => { | ||
await useRestService({ injector: i, api: {}, root: '/', port: 1234 }); | ||
const port = getPort(); | ||
await useRestService({ injector: i, api: {}, root: '/', port }); | ||
expect(i.cachedSingletons.get(ApiManager)).toBeDefined(); | ||
@@ -30,3 +32,4 @@ }); | ||
await usingAsync(new Injector(), async (i) => { | ||
await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port: 1234 }); | ||
const port = getPort(); | ||
await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port }); | ||
expect(i.cachedSingletons.get(StaticServerManager)).toBeDefined(); | ||
@@ -33,0 +36,0 @@ }); |
/// <reference path="server-response-extensions.d.ts" /> | ||
/// <reference path="incoming-message-extensions.d.ts" /> | ||
/// <reference types="node/http.js" /> | ||
import type { IncomingMessage, ServerResponse } from 'http'; | ||
import type { IncomingMessage } from 'http'; | ||
import type { User } from '@furystack/core'; | ||
@@ -45,9 +45,13 @@ import { HttpAuthenticationSettings } from './http-authentication-settings.js'; | ||
*/ | ||
cookieLogin(user: User, serverResponse: Pick<ServerResponse, 'setHeader'>): Promise<User>; | ||
cookieLogout(request: Pick<IncomingMessage, 'headers'>, response: Pick<ServerResponse, 'setHeader'>): Promise<void>; | ||
cookieLogin(user: User, serverResponse: { | ||
setHeader: (header: string, value: string) => void; | ||
}): Promise<User>; | ||
cookieLogout(request: Pick<IncomingMessage, 'headers'>, response: { | ||
setHeader: (header: string, value: string) => void; | ||
}): Promise<void>; | ||
readonly authentication: HttpAuthenticationSettings<User, DefaultSession>; | ||
private readonly storeManager; | ||
private readonly authenticator; | ||
init(): Promise<void>; | ||
init(): void; | ||
} | ||
//# sourceMappingURL=http-user-context.d.ts.map |
@@ -150,3 +150,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
} | ||
async init() { | ||
init() { | ||
this.getUserStore().addListener('onEntityUpdated', ({ id, change }) => { | ||
@@ -153,0 +153,0 @@ if (this.user?.username === id) { |
@@ -6,3 +6,3 @@ import { Injector } from '@furystack/inject'; | ||
import { Validate } from './validate.js'; | ||
import schema from './validate.integration.spec.schema.json' assert { type: 'json' }; | ||
import schema from './validate.integration.spec.schema.json' with { type: 'json' }; | ||
import { useRestService } from './helpers.js'; | ||
@@ -9,0 +9,0 @@ import { describe, it, expect } from 'vitest'; |
@@ -19,12 +19,7 @@ { | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"url", | ||
"result" | ||
], | ||
"required": ["url", "result"], | ||
"type": "object" | ||
@@ -179,13 +174,7 @@ }, | ||
"id": { | ||
"enum": [ | ||
"ASC", | ||
"DESC" | ||
], | ||
"enum": ["ASC", "DESC"], | ||
"type": "string" | ||
}, | ||
"value": { | ||
"enum": [ | ||
"ASC", | ||
"DESC" | ||
], | ||
"enum": ["ASC", "DESC"], | ||
"type": "string" | ||
@@ -199,6 +188,3 @@ } | ||
"items": { | ||
"enum": [ | ||
"id", | ||
"value" | ||
], | ||
"enum": ["id", "value"], | ||
"type": "string" | ||
@@ -236,6 +222,3 @@ }, | ||
}, | ||
"required": [ | ||
"query", | ||
"result" | ||
], | ||
"required": ["query", "result"], | ||
"type": "object" | ||
@@ -259,6 +242,3 @@ }, | ||
}, | ||
"required": [ | ||
"count", | ||
"entries" | ||
], | ||
"required": ["count", "entries"], | ||
"type": "object" | ||
@@ -276,6 +256,3 @@ }, | ||
"items": { | ||
"enum": [ | ||
"id", | ||
"value" | ||
], | ||
"enum": ["id", "value"], | ||
"type": "string" | ||
@@ -299,13 +276,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"query", | ||
"url", | ||
"result" | ||
], | ||
"required": ["query", "url", "result"], | ||
"type": "object" | ||
@@ -323,6 +294,3 @@ }, | ||
}, | ||
"required": [ | ||
"id", | ||
"value" | ||
], | ||
"required": ["id", "value"], | ||
"type": "object" | ||
@@ -357,13 +325,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"body", | ||
"url", | ||
"result" | ||
], | ||
"required": ["body", "url", "result"], | ||
"type": "object" | ||
@@ -382,6 +344,3 @@ }, | ||
}, | ||
"required": [ | ||
"body", | ||
"result" | ||
], | ||
"required": ["body", "result"], | ||
"type": "object" | ||
@@ -402,5 +361,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -420,5 +377,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -438,5 +393,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -456,5 +409,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -474,5 +425,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -492,5 +441,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -510,5 +457,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -528,5 +473,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -546,5 +489,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -573,7 +514,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -594,14 +531,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"body", | ||
"result" | ||
], | ||
"required": ["body", "result"], | ||
"type": "object" | ||
@@ -625,7 +555,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -646,14 +572,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"headers", | ||
"result" | ||
], | ||
"required": ["headers", "result"], | ||
"type": "object" | ||
@@ -677,7 +596,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -698,14 +613,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"query", | ||
"result" | ||
], | ||
"required": ["query", "result"], | ||
"type": "object" | ||
@@ -723,5 +631,3 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
@@ -736,12 +642,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"url", | ||
"result" | ||
], | ||
"required": ["url", "result"], | ||
"type": "object" | ||
@@ -762,5 +663,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -777,5 +676,3 @@ }, | ||
}, | ||
"required": [ | ||
"/mock/:id" | ||
], | ||
"required": ["/mock/:id"], | ||
"type": "object" | ||
@@ -802,9 +699,3 @@ }, | ||
}, | ||
"required": [ | ||
"/validate-query", | ||
"/validate-url/:id", | ||
"/validate-headers", | ||
"/mock", | ||
"/mock/:id" | ||
], | ||
"required": ["/validate-query", "/validate-url/:id", "/validate-headers", "/mock", "/mock/:id"], | ||
"type": "object" | ||
@@ -822,5 +713,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -840,5 +729,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -855,5 +742,3 @@ }, | ||
}, | ||
"required": [ | ||
"/mock/:id" | ||
], | ||
"required": ["/mock/:id"], | ||
"type": "object" | ||
@@ -871,6 +756,3 @@ }, | ||
}, | ||
"required": [ | ||
"/validate-body", | ||
"/mock" | ||
], | ||
"required": ["/validate-body", "/mock"], | ||
"type": "object" | ||
@@ -888,5 +770,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -906,5 +786,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -915,8 +793,3 @@ }, | ||
}, | ||
"required": [ | ||
"GET", | ||
"POST", | ||
"PATCH", | ||
"DELETE" | ||
], | ||
"required": ["GET", "POST", "PATCH", "DELETE"], | ||
"type": "object" | ||
@@ -934,5 +807,3 @@ }, | ||
}, | ||
"required": [ | ||
"value" | ||
], | ||
"required": ["value"], | ||
"type": "object" | ||
@@ -939,0 +810,0 @@ } |
{ | ||
"name": "@furystack/rest-service", | ||
"version": "9.0.3", | ||
"version": "9.0.4", | ||
"description": "Repository implementation for FuryStack", | ||
@@ -40,9 +40,9 @@ "type": "module", | ||
"dependencies": { | ||
"@furystack/core": "^14.0.3", | ||
"@furystack/inject": "^11.0.2", | ||
"@furystack/repository": "^9.0.3", | ||
"@furystack/rest": "^7.0.3", | ||
"@furystack/security": "^5.0.3", | ||
"@furystack/utils": "^7.0.1", | ||
"ajv": "^8.13.0", | ||
"@furystack/core": "^14.0.4", | ||
"@furystack/inject": "^11.0.3", | ||
"@furystack/repository": "^9.0.4", | ||
"@furystack/rest": "^7.0.4", | ||
"@furystack/security": "^5.0.4", | ||
"@furystack/utils": "^7.0.2", | ||
"ajv": "^8.14.0", | ||
"ajv-formats": "^3.0.1", | ||
@@ -53,4 +53,4 @@ "path-to-regexp": "^6.2.2", | ||
"devDependencies": { | ||
"@furystack/rest-client-fetch": "^7.0.3", | ||
"@types/node": "^20.12.11", | ||
"@furystack/rest-client-fetch": "^7.0.4", | ||
"@types/node": "^20.12.13", | ||
"typescript": "^5.4.5", | ||
@@ -57,0 +57,0 @@ "vitest": "^1.6.0" |
104
README.md
@@ -14,3 +14,2 @@ # rest-service | ||
```ts | ||
import { MyApi, MyEntity } from 'my-common-package' | ||
@@ -24,24 +23,23 @@ import { | ||
myInjector.useHttpAuthentication().useRestService<MyApi>({ | ||
port: 8080, // The port to listen | ||
root: '/api', // Routes will be joined on this root path | ||
cors: { // Enable CORS | ||
credentials: true, // Enable cookies for CORS | ||
origins: ['https://my-frontend-1', 'https://my-frontend-2'], // Allowed origins | ||
port: 8080, // The port to listen | ||
root: '/api', // Routes will be joined on this root path | ||
cors: { | ||
// Enable CORS | ||
credentials: true, // Enable cookies for CORS | ||
origins: ['https://my-frontend-1', 'https://my-frontend-2'], // Allowed origins | ||
}, | ||
// This API should implement *all* methods that are defined in `MyApi` | ||
api: { | ||
// Endpoints that can be called with GET Http method | ||
GET: { | ||
'/my-entities': Authenticate()(createGetCollectionEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
'/my-entities/:id': Authenticate()(createGetEntityEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
}, | ||
// This API should implement *all* methods that are defined in `MyApi` | ||
api: { | ||
// Endpoints that can be called with GET Http method | ||
GET: { | ||
'/my-entities': Authenticate()(createGetCollectionEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
'/my-entities/:id': Authenticate()(createGetEntityEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
}, | ||
// Endpoints that can be called with GET Http method | ||
POST: { | ||
'/my-entities': Authenticate()(createPostEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
}, | ||
// Endpoints that can be called with GET Http method | ||
POST: { | ||
'/my-entities': Authenticate()(createPostEndpoint({ model: MyEntity, primaryKey: 'id' })), | ||
}, | ||
}, | ||
}) | ||
``` | ||
@@ -52,9 +50,9 @@ | ||
If you use the underlying layers of FuryStack (`PhysicalStore` -> `Repository`) for an entity type, you can easily create some CRUD endpoints for them. These are the followings: | ||
- createDeleteEndpoint() | ||
- createGetCollectionEndpoint() | ||
- createGetEntityEndpoint() | ||
- createPatchEndpoint() | ||
- createPostEndpoint() | ||
- createDeleteEndpoint() | ||
- createGetCollectionEndpoint() | ||
- createGetEntityEndpoint() | ||
- createPatchEndpoint() | ||
- createPostEndpoint() | ||
The endpoints will use the defined Physical Stores for retrieving entities and the Repository for authorization / event subscriptions. | ||
@@ -67,3 +65,2 @@ | ||
```ts | ||
import { Injector } from '@furystack/inject' | ||
@@ -73,24 +70,24 @@ import { RestApi } from '@furystack/rest' | ||
export type MyCustomRequestAction = { | ||
/** The request should contain this POST Body structure */ | ||
body: { | ||
foo: string | ||
bar: number | ||
} | ||
/** Parameter(s) from the URL */ | ||
url: { | ||
/** This should be also a part of the URL with the `:entityId` syntax */ | ||
entityId: string | ||
} | ||
/** The request should contain this POST Body structure */ | ||
body: { | ||
foo: string | ||
bar: number | ||
} | ||
/** Parameter(s) from the URL */ | ||
url: { | ||
/** This should be also a part of the URL with the `:entityId` syntax */ | ||
entityId: string | ||
} | ||
/** The request should contain this query string parameters in the `?foo=asd&bar=2&baz=false` format */ | ||
query: { foo?: string; bar?: number; baz?: boolean } | ||
/** The request should contain this query string parameters in the `?foo=asd&bar=2&baz=false` format */ | ||
query: { foo?: string; bar?: number; baz?: boolean } | ||
/** The request should contain these header values */ | ||
headers: { foo: string; bar: number; baz: boolean } | ||
/** The request should contain these header values */ | ||
headers: { foo: string; bar: number; baz: boolean } | ||
/** The endpoint will return the following structure in the response */ | ||
result: { | ||
success: boolean | ||
} | ||
} | ||
/** The endpoint will return the following structure in the response */ | ||
result: { | ||
success: boolean | ||
} | ||
} | ||
@@ -177,4 +174,2 @@ /** In a Common module */ | ||
}) | ||
``` | ||
@@ -186,2 +181,3 @@ | ||
The prefferred way is: | ||
1. Create your API interface | ||
@@ -202,3 +198,2 @@ 1. Create JSON Schemas from the API (The `ts-json-schema-generator` package is the best solution nowdays, you can check how it works, [here](https://github.com/furystack/furystack/blob/develop/package.json#L39)) | ||
### Authentication and HttpUserContext | ||
@@ -222,7 +217,8 @@ | ||
The package contains the following built-in actions | ||
- `ErrorAction` - for default error handling and dumping errors in the response | ||
- `GetCurrentUser` - Returns the current user | ||
- `IsAuthenticated` - Returns if a user is logged in | ||
- `Login` - Login with a simple username + password combo | ||
- `Logout` - Destroys the current session | ||
- `NotFoundAction` - The default '404' fallback route | ||
- `ErrorAction` - for default error handling and dumping errors in the response | ||
- `GetCurrentUser` - Returns the current user | ||
- `IsAuthenticated` - Returns if a user is logged in | ||
- `Login` - Login with a simple username + password combo | ||
- `Logout` - Destroys the current session | ||
- `NotFoundAction` - The default '404' fallback route |
@@ -14,3 +14,3 @@ import { Injector } from '@furystack/inject' | ||
i.setExplicitInstance({ isAuthenticated: async () => true }, IdentityContext) | ||
const result = await IsAuthenticated({ injector: i, request, response } as any) | ||
const result = await IsAuthenticated({ injector: i, request, response }) | ||
expect(result.statusCode).toBe(200) | ||
@@ -17,0 +17,0 @@ expect(result.chunk).toEqual({ isAuthenticated: true }) |
@@ -5,3 +5,3 @@ import type { IncomingMessage } from 'http' | ||
import type { User } from '@furystack/core' | ||
import { IdentityContext } from '@furystack/core' | ||
import { AuthorizationError, IdentityContext } from '@furystack/core' | ||
import { Authorize } from './authorize.js' | ||
@@ -26,5 +26,3 @@ import type { ServerResponse } from 'http' | ||
const result = await authorized({ injector: i, request, response }) | ||
expect(result.statusCode).toBe(403) | ||
expect(result.chunk).toEqual({ error: 'forbidden' }) | ||
await expect(() => authorized({ injector: i, request, response })).rejects.toThrow(AuthorizationError) | ||
expect(exampleAuthorizedAction).not.toBeCalled() | ||
@@ -47,5 +45,3 @@ }) | ||
const result = await authorized({ injector: i, request, response }) | ||
expect(result.statusCode).toBe(403) | ||
expect(result.chunk).toEqual({ error: 'forbidden' }) | ||
await expect(() => authorized({ injector: i, request, response })).rejects.toThrow(AuthorizationError) | ||
expect(exampleAuthorizedAction).not.toBeCalled() | ||
@@ -52,0 +48,0 @@ }) |
@@ -1,5 +0,4 @@ | ||
import { isAuthorized } from '@furystack/core' | ||
import { AuthorizationError, isAuthorized } from '@furystack/core' | ||
import { sleepAsync } from '@furystack/utils' | ||
import type { ActionResult, RequestAction, RequestActionOptions } from './request-action-implementation.js' | ||
import { JsonResult } from './request-action-implementation.js' | ||
@@ -14,9 +13,9 @@ export const Authorize = | ||
await sleepAsync(Math.random() * 1000) | ||
return JsonResult({ error: 'forbidden' }, 403) as any | ||
throw new AuthorizationError('forbidden') | ||
} | ||
} catch (error) { | ||
return JsonResult({ error: 'forbidden' }, 403) as any | ||
throw new AuthorizationError('forbidden') | ||
} | ||
return (await action(options)) as any | ||
return (await action(options)) as ActionResult<T> | ||
} | ||
} |
@@ -46,3 +46,3 @@ import { usingAsync } from '@furystack/utils' | ||
expect(response.ok).toBe(true) | ||
const json: GetCollectionResult<MockClass> = await response.json() | ||
const json = (await response.json()) as GetCollectionResult<MockClass> | ||
expect(response.status).toBe(200) | ||
@@ -76,3 +76,3 @@ expect(json.count).toBe(count) | ||
expect(response.ok).toBe(true) | ||
const json: GetCollectionResult<MockClass> = await response.json() | ||
const json = (await response.json()) as GetCollectionResult<MockClass> | ||
expect(response.status).toBe(200) | ||
@@ -112,3 +112,3 @@ expect(json.count).toBe(count) | ||
expect(response.ok).toBe(true) | ||
const json: GetCollectionResult<MockClass> = await response.json() | ||
const json = (await response.json()) as GetCollectionResult<MockClass> | ||
expect(response.status).toBe(200) | ||
@@ -149,3 +149,3 @@ expect(json.count).toBe(count) | ||
expect(response.ok).toBe(true) | ||
const json: GetCollectionResult<MockClass> = await response.json() | ||
const json = (await response.json()) as GetCollectionResult<MockClass> | ||
expect(response.status).toBe(200) | ||
@@ -187,3 +187,3 @@ expect(json.count).toBe(count) | ||
expect(response.status).toBe(200) | ||
const json: GetCollectionResult<MockClass> = await response.json() | ||
const json = (await response.json()) as GetCollectionResult<MockClass> | ||
expect(json.count).toBe(count) | ||
@@ -190,0 +190,0 @@ expect(json.entries).toEqual(topSkipEntities) |
@@ -8,2 +8,3 @@ import { Injector } from '@furystack/inject' | ||
import { describe, it, expect } from 'vitest' | ||
import { getPort } from '@furystack/core/port-generator' | ||
@@ -23,3 +24,5 @@ describe('Injector extensions', () => { | ||
await usingAsync(new Injector(), async (i) => { | ||
await useRestService({ injector: i, api: {}, root: '/', port: 1234 }) | ||
const port = getPort() | ||
await useRestService({ injector: i, api: {}, root: '/', port }) | ||
expect(i.cachedSingletons.get(ApiManager)).toBeDefined() | ||
@@ -33,3 +36,5 @@ }) | ||
await usingAsync(new Injector(), async (i) => { | ||
await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port: 1234 }) | ||
const port = getPort() | ||
await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port }) | ||
expect(i.cachedSingletons.get(StaticServerManager)).toBeDefined() | ||
@@ -36,0 +41,0 @@ }) |
@@ -1,2 +0,2 @@ | ||
import type { IncomingMessage, ServerResponse } from 'http' | ||
import type { IncomingMessage } from 'http' | ||
import type { User } from '@furystack/core' | ||
@@ -142,3 +142,6 @@ import { StoreManager } from '@furystack/core' | ||
*/ | ||
public async cookieLogin(user: User, serverResponse: Pick<ServerResponse, 'setHeader'>): Promise<User> { | ||
public async cookieLogin( | ||
user: User, | ||
serverResponse: { setHeader: (header: string, value: string) => void }, | ||
): Promise<User> { | ||
const sessionId = randomBytes(32).toString('hex') | ||
@@ -151,3 +154,6 @@ await this.getSessionStore().add({ sessionId, username: user.username }) | ||
public async cookieLogout(request: Pick<IncomingMessage, 'headers'>, response: Pick<ServerResponse, 'setHeader'>) { | ||
public async cookieLogout( | ||
request: Pick<IncomingMessage, 'headers'>, | ||
response: { setHeader: (header: string, value: string) => void }, | ||
) { | ||
this.user = undefined | ||
@@ -173,3 +179,3 @@ const sessionId = this.getSessionIdFromRequest(request) | ||
public async init() { | ||
public init() { | ||
this.getUserStore().addListener('onEntityUpdated', ({ id, change }) => { | ||
@@ -176,0 +182,0 @@ if (this.user?.username === id) { |
@@ -19,12 +19,7 @@ { | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"url", | ||
"result" | ||
], | ||
"required": ["url", "result"], | ||
"type": "object" | ||
@@ -179,13 +174,7 @@ }, | ||
"id": { | ||
"enum": [ | ||
"ASC", | ||
"DESC" | ||
], | ||
"enum": ["ASC", "DESC"], | ||
"type": "string" | ||
}, | ||
"value": { | ||
"enum": [ | ||
"ASC", | ||
"DESC" | ||
], | ||
"enum": ["ASC", "DESC"], | ||
"type": "string" | ||
@@ -199,6 +188,3 @@ } | ||
"items": { | ||
"enum": [ | ||
"id", | ||
"value" | ||
], | ||
"enum": ["id", "value"], | ||
"type": "string" | ||
@@ -236,6 +222,3 @@ }, | ||
}, | ||
"required": [ | ||
"query", | ||
"result" | ||
], | ||
"required": ["query", "result"], | ||
"type": "object" | ||
@@ -259,6 +242,3 @@ }, | ||
}, | ||
"required": [ | ||
"count", | ||
"entries" | ||
], | ||
"required": ["count", "entries"], | ||
"type": "object" | ||
@@ -276,6 +256,3 @@ }, | ||
"items": { | ||
"enum": [ | ||
"id", | ||
"value" | ||
], | ||
"enum": ["id", "value"], | ||
"type": "string" | ||
@@ -299,13 +276,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"query", | ||
"url", | ||
"result" | ||
], | ||
"required": ["query", "url", "result"], | ||
"type": "object" | ||
@@ -323,6 +294,3 @@ }, | ||
}, | ||
"required": [ | ||
"id", | ||
"value" | ||
], | ||
"required": ["id", "value"], | ||
"type": "object" | ||
@@ -357,13 +325,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"body", | ||
"url", | ||
"result" | ||
], | ||
"required": ["body", "url", "result"], | ||
"type": "object" | ||
@@ -382,6 +344,3 @@ }, | ||
}, | ||
"required": [ | ||
"body", | ||
"result" | ||
], | ||
"required": ["body", "result"], | ||
"type": "object" | ||
@@ -402,5 +361,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -420,5 +377,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -438,5 +393,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -456,5 +409,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -474,5 +425,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -492,5 +441,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -510,5 +457,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -528,5 +473,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -546,5 +489,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -573,7 +514,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -594,14 +531,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"body", | ||
"result" | ||
], | ||
"required": ["body", "result"], | ||
"type": "object" | ||
@@ -625,7 +555,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -646,14 +572,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"headers", | ||
"result" | ||
], | ||
"required": ["headers", "result"], | ||
"type": "object" | ||
@@ -677,7 +596,3 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
@@ -698,14 +613,7 @@ }, | ||
}, | ||
"required": [ | ||
"foo", | ||
"bar", | ||
"baz" | ||
], | ||
"required": ["foo", "bar", "baz"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"query", | ||
"result" | ||
], | ||
"required": ["query", "result"], | ||
"type": "object" | ||
@@ -723,5 +631,3 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
@@ -736,12 +642,7 @@ }, | ||
}, | ||
"required": [ | ||
"id" | ||
], | ||
"required": ["id"], | ||
"type": "object" | ||
} | ||
}, | ||
"required": [ | ||
"url", | ||
"result" | ||
], | ||
"required": ["url", "result"], | ||
"type": "object" | ||
@@ -762,5 +663,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -777,5 +676,3 @@ }, | ||
}, | ||
"required": [ | ||
"/mock/:id" | ||
], | ||
"required": ["/mock/:id"], | ||
"type": "object" | ||
@@ -802,9 +699,3 @@ }, | ||
}, | ||
"required": [ | ||
"/validate-query", | ||
"/validate-url/:id", | ||
"/validate-headers", | ||
"/mock", | ||
"/mock/:id" | ||
], | ||
"required": ["/validate-query", "/validate-url/:id", "/validate-headers", "/mock", "/mock/:id"], | ||
"type": "object" | ||
@@ -822,5 +713,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -840,5 +729,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -855,5 +742,3 @@ }, | ||
}, | ||
"required": [ | ||
"/mock/:id" | ||
], | ||
"required": ["/mock/:id"], | ||
"type": "object" | ||
@@ -871,6 +756,3 @@ }, | ||
}, | ||
"required": [ | ||
"/validate-body", | ||
"/mock" | ||
], | ||
"required": ["/validate-body", "/mock"], | ||
"type": "object" | ||
@@ -888,5 +770,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -906,5 +786,3 @@ }, | ||
}, | ||
"required": [ | ||
"result" | ||
], | ||
"required": ["result"], | ||
"type": "object" | ||
@@ -915,8 +793,3 @@ }, | ||
}, | ||
"required": [ | ||
"GET", | ||
"POST", | ||
"PATCH", | ||
"DELETE" | ||
], | ||
"required": ["GET", "POST", "PATCH", "DELETE"], | ||
"type": "object" | ||
@@ -934,8 +807,6 @@ }, | ||
}, | ||
"required": [ | ||
"value" | ||
], | ||
"required": ["value"], | ||
"type": "object" | ||
} | ||
} | ||
} | ||
} |
@@ -6,3 +6,3 @@ import { Injector } from '@furystack/inject' | ||
import { Validate } from './validate.js' | ||
import schema from './validate.integration.spec.schema.json' assert { type: 'json' } | ||
import schema from './validate.integration.spec.schema.json' with { type: 'json' } | ||
import type { ValidationApi } from './validate.integration.schema.js' | ||
@@ -9,0 +9,0 @@ import { useRestService } from './helpers.js' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
570782
9627
216
Updated@furystack/core@^14.0.4
Updated@furystack/inject@^11.0.3
Updated@furystack/repository@^9.0.4
Updated@furystack/rest@^7.0.4
Updated@furystack/security@^5.0.4
Updated@furystack/utils@^7.0.2
Updatedajv@^8.14.0