@neuralinnovations/dataisland-sdk 0.0.1-dev1 → 0.0.1-dev2
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/jest.config.ts +5 -5
- package/jest.setup.ts +2 -0
- package/package.json +2 -1
- package/src/appBuilder.ts +20 -1
- package/src/appSdk.ts +28 -9
- package/src/commands/startCommandHandler.ts +14 -0
- package/src/context.ts +31 -0
- package/src/credentials.ts +3 -3
- package/src/disposable.ts +1 -1
- package/src/dto/userInfoResponse.ts +37 -0
- package/src/events.ts +0 -4
- package/src/index.ts +9 -1
- package/src/internal/app.impl.ts +89 -21
- package/src/internal/appBuilder.impl.ts +28 -1
- package/src/middleware.ts +1 -1
- package/src/services/commandService.ts +44 -0
- package/src/services/middlewareService.ts +4 -2
- package/src/services/organizationImpl.ts +51 -0
- package/src/services/organizationService.ts +126 -0
- package/src/services/organizationsImpl.ts +55 -0
- package/src/services/requestBuilder.ts +102 -0
- package/src/services/rpcService.ts +111 -51
- package/src/services/service.ts +1 -1
- package/src/services/userProfileService.ts +86 -0
- package/src/storages/organizations.ts +76 -0
- package/src/storages/userProfile.ts +42 -0
- package/src/types.ts +6 -30
- package/src/unitTest.ts +42 -0
- package/test/commands.test.ts +24 -0
- package/test/index.test.ts +132 -52
- package/test/services.test.ts +56 -0
- package/test/setup.ts +2 -0
- package/test/unitTest.test.ts +21 -0
- package/src/internal/context.ts +0 -13
package/jest.config.ts
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
* https://jestjs.io/docs/configuration
|
4
4
|
*/
|
5
5
|
|
6
|
-
import type {Config} from 'jest'
|
6
|
+
import type { Config } from 'jest'
|
7
7
|
|
8
8
|
const config: Config = {
|
9
9
|
// All imported modules in your tests should be mocked automatically
|
@@ -25,7 +25,7 @@ const config: Config = {
|
|
25
25
|
// collectCoverageFrom: undefined,
|
26
26
|
|
27
27
|
// The directory where Jest should output its coverage files
|
28
|
-
coverageDirectory:
|
28
|
+
coverageDirectory: 'coverage',
|
29
29
|
|
30
30
|
// An array of regexp pattern strings used to skip coverage collection
|
31
31
|
// coveragePathIgnorePatterns: [
|
@@ -134,7 +134,7 @@ const config: Config = {
|
|
134
134
|
// runner: "jest-runner",
|
135
135
|
|
136
136
|
// The paths to modules that run some code to configure or set up the testing environment before each test
|
137
|
-
|
137
|
+
setupFiles: ['./jest.setup.ts']
|
138
138
|
|
139
139
|
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
140
140
|
// setupFilesAfterEnv: [],
|
@@ -194,6 +194,6 @@ const config: Config = {
|
|
194
194
|
|
195
195
|
// Whether to use watchman for file crawling
|
196
196
|
// watchman: true,
|
197
|
-
}
|
197
|
+
}
|
198
198
|
|
199
|
-
export default config
|
199
|
+
export default config
|
package/jest.setup.ts
ADDED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@neuralinnovations/dataisland-sdk",
|
3
|
-
"version": "0.0.1-
|
3
|
+
"version": "0.0.1-dev2",
|
4
4
|
"description": "SDK for DataIsland project",
|
5
5
|
"licenses": [
|
6
6
|
{
|
@@ -55,6 +55,7 @@
|
|
55
55
|
"yargs": "17.7.2"
|
56
56
|
},
|
57
57
|
"dependencies": {
|
58
|
+
"dotenv": "^16.3.2",
|
58
59
|
"jsdom": "^23.2.0"
|
59
60
|
}
|
60
61
|
}
|
package/src/appBuilder.ts
CHANGED
@@ -2,15 +2,22 @@ import type { Middleware } from './middleware'
|
|
2
2
|
import type { CredentialBase } from './credentials'
|
3
3
|
import type { Service, ServiceContext } from './services/service'
|
4
4
|
import type { Constructor } from './internal/registry'
|
5
|
+
import { CommandHandler, Command } from './services/commandService'
|
6
|
+
import { Context } from './context'
|
5
7
|
|
6
8
|
/**
|
7
9
|
* DataIsland App builder.
|
8
10
|
*/
|
9
11
|
export abstract class AppBuilder {
|
12
|
+
/**
|
13
|
+
* Set custom data.
|
14
|
+
*/
|
15
|
+
abstract get env(): Record<string, any>
|
16
|
+
|
10
17
|
/**
|
11
18
|
* Add a middleware to the app.
|
12
19
|
*/
|
13
|
-
abstract
|
20
|
+
abstract registerMiddleware(middleware: Middleware): AppBuilder
|
14
21
|
|
15
22
|
/**
|
16
23
|
* Host of the app.
|
@@ -36,4 +43,16 @@ export abstract class AppBuilder {
|
|
36
43
|
type: Constructor<T>,
|
37
44
|
factory: (context: ServiceContext) => T
|
38
45
|
): AppBuilder
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Register a command to the app.
|
49
|
+
* @param messageType
|
50
|
+
* @param commandFactory
|
51
|
+
*/
|
52
|
+
abstract registerCommand<T extends Command>(
|
53
|
+
messageType: Constructor<T>,
|
54
|
+
commandFactory:
|
55
|
+
| ((context: Context) => CommandHandler<T>)
|
56
|
+
| ((context: Context) => (context: Context) => Promise<void>)
|
57
|
+
): AppBuilder
|
39
58
|
}
|
package/src/appSdk.ts
CHANGED
@@ -1,40 +1,59 @@
|
|
1
1
|
import type { Lifetime } from './disposable'
|
2
2
|
import type { CredentialBase } from './credentials'
|
3
|
+
import { Context } from './context'
|
3
4
|
import type { Constructor } from './internal/registry'
|
5
|
+
import { Organizations } from './storages/organizations'
|
6
|
+
import { UserProfile } from './storages/userProfile'
|
4
7
|
|
5
8
|
/**
|
6
9
|
* DataIsland App instance.
|
7
10
|
*/
|
8
|
-
export
|
11
|
+
export abstract class AppSdk {
|
9
12
|
/**
|
10
13
|
* The name of this app.
|
11
14
|
*/
|
12
|
-
get name(): string
|
15
|
+
abstract get name(): string
|
13
16
|
|
14
17
|
/**
|
15
18
|
* The host of this app.
|
16
19
|
*/
|
17
|
-
get host(): string
|
20
|
+
abstract get host(): string
|
18
21
|
|
19
22
|
/**
|
20
23
|
* The automaticDataCollectionEnabled of this app.
|
21
24
|
*/
|
22
|
-
get automaticDataCollectionEnabled(): boolean
|
25
|
+
abstract get automaticDataCollectionEnabled(): boolean
|
23
26
|
|
24
27
|
/**
|
25
28
|
* The lifetime of this app.
|
26
29
|
*/
|
27
|
-
get lifetime(): Lifetime
|
30
|
+
abstract get lifetime(): Lifetime
|
28
31
|
|
29
32
|
/**
|
30
33
|
* The credential of this app.
|
31
34
|
*/
|
32
|
-
get credential(): CredentialBase | undefined
|
35
|
+
abstract get credential(): CredentialBase | undefined
|
33
36
|
|
34
|
-
set credential(value: CredentialBase)
|
37
|
+
abstract set credential(value: CredentialBase)
|
35
38
|
|
36
39
|
/**
|
37
|
-
*
|
40
|
+
* The context of this app.
|
38
41
|
*/
|
39
|
-
|
42
|
+
abstract get context(): Context
|
43
|
+
|
44
|
+
/**
|
45
|
+
* User's organizations.
|
46
|
+
*/
|
47
|
+
abstract get organizations(): Organizations
|
48
|
+
|
49
|
+
/**
|
50
|
+
* User's profile.
|
51
|
+
*/
|
52
|
+
abstract get userProfile(): UserProfile
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Resolve a service from the app.
|
56
|
+
* @param type
|
57
|
+
*/
|
58
|
+
abstract resolve<T>(type: Constructor<T>): T | undefined
|
40
59
|
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { CommandHandler, Command } from '../services/commandService'
|
2
|
+
import { UserProfileService } from '../services/userProfileService'
|
3
|
+
|
4
|
+
export class StartCommand extends Command {}
|
5
|
+
|
6
|
+
export class StartCommandHandler extends CommandHandler<StartCommand> {
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
8
|
+
async execute(message: StartCommand): Promise<void> {
|
9
|
+
const service = this.context.resolve(
|
10
|
+
UserProfileService
|
11
|
+
) as UserProfileService
|
12
|
+
await service.fetch()
|
13
|
+
}
|
14
|
+
}
|
package/src/context.ts
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
import { type Constructor, type Registry } from './internal/registry'
|
2
|
+
import { type Lifetime } from './disposable'
|
3
|
+
import { Command, CommandService } from './services/commandService'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* DataIsland App context.
|
7
|
+
*/
|
8
|
+
export class Context {
|
9
|
+
constructor(
|
10
|
+
private readonly registry: Registry,
|
11
|
+
public readonly lifetime: Lifetime,
|
12
|
+
public readonly appName: string
|
13
|
+
) {}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Resolve a service from the context.
|
17
|
+
* @param type of the service
|
18
|
+
*/
|
19
|
+
resolve<T>(type: Constructor<T>): T | undefined {
|
20
|
+
return this.registry.get(type)
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Execute a command.
|
25
|
+
* @param command to execute
|
26
|
+
*/
|
27
|
+
async execute<T extends Command>(command: T): Promise<void> {
|
28
|
+
const service = this.resolve(CommandService) as CommandService
|
29
|
+
await service.execute(command)
|
30
|
+
}
|
31
|
+
}
|
package/src/credentials.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { MiddlewareService } from './services/middlewareService'
|
2
2
|
import { type Lifetime } from './disposable'
|
3
|
-
import { type Context } from './
|
3
|
+
import { type Context } from './context'
|
4
4
|
|
5
5
|
/**
|
6
6
|
* DataIsland App credential.
|
@@ -34,7 +34,7 @@ export class BasicCredential extends CredentialBase {
|
|
34
34
|
lifetime.add(
|
35
35
|
service.useMiddleware(async (req, next) => {
|
36
36
|
req.headers.set('Authorization', `Basic ${this.email}:${this.password}`)
|
37
|
-
await next(req)
|
37
|
+
return await next(req)
|
38
38
|
})
|
39
39
|
)
|
40
40
|
}
|
@@ -56,7 +56,7 @@ export class BearerCredential extends CredentialBase {
|
|
56
56
|
lifetime.add(
|
57
57
|
service.useMiddleware(async (req, next) => {
|
58
58
|
req.headers.set('Authorization', `Bearer ${this.token}`)
|
59
|
-
await next(req)
|
59
|
+
return await next(req)
|
60
60
|
})
|
61
61
|
)
|
62
62
|
}
|
package/src/disposable.ts
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
export interface UserInfoResponse {
|
2
|
+
adminInOrganization: string[]
|
3
|
+
organizations: OrganizationDto[]
|
4
|
+
user: UserDto
|
5
|
+
}
|
6
|
+
|
7
|
+
export interface UserDto {
|
8
|
+
id: string
|
9
|
+
isDeleted: boolean
|
10
|
+
created_at: number
|
11
|
+
modified_at: number
|
12
|
+
profile: ProfileDto
|
13
|
+
settings: UserSettings
|
14
|
+
}
|
15
|
+
|
16
|
+
export interface ProfileDto {
|
17
|
+
name: string
|
18
|
+
email: string
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface UserSettings {
|
22
|
+
activeOrganizationId: string
|
23
|
+
activeWorkspaceId: string
|
24
|
+
}
|
25
|
+
|
26
|
+
export interface OrganizationProfileDto {
|
27
|
+
name: string
|
28
|
+
description: string
|
29
|
+
}
|
30
|
+
|
31
|
+
export interface OrganizationDto {
|
32
|
+
id: string
|
33
|
+
createdAt: number
|
34
|
+
modifiedAt: number
|
35
|
+
membersCount: number
|
36
|
+
profile: OrganizationProfileDto
|
37
|
+
}
|
package/src/events.ts
CHANGED
package/src/index.ts
CHANGED
@@ -8,6 +8,7 @@ export * from './types'
|
|
8
8
|
export * from './disposable'
|
9
9
|
export * from './types'
|
10
10
|
export * from './credentials'
|
11
|
+
export * from './appSdk'
|
11
12
|
|
12
13
|
const _appsNotReady = new Map<string, Promise<AppSdk>>()
|
13
14
|
const _appsReady = new Map<string, AppSdk>()
|
@@ -25,7 +26,7 @@ export const DEFAULT_NAME = '[DEFAULT]'
|
|
25
26
|
/**
|
26
27
|
* Default DataIsland App host.
|
27
28
|
*/
|
28
|
-
export const DEFAULT_HOST = 'https://dataisland.com.ua'
|
29
|
+
export const DEFAULT_HOST = 'https://api.dataisland.com.ua'
|
29
30
|
|
30
31
|
export function sdks(): AppSdk[] {
|
31
32
|
return Array.from(_appsReady.values())
|
@@ -62,8 +63,15 @@ export async function appSdk(
|
|
62
63
|
})
|
63
64
|
.catch(reason => {
|
64
65
|
console.error(`Error: ${reason}`)
|
66
|
+
_appsNotReady.delete(name ?? DEFAULT_NAME)
|
65
67
|
})
|
66
68
|
_appsNotReady.set(name, appPromise)
|
69
|
+
} else {
|
70
|
+
if (setup !== undefined) {
|
71
|
+
throw new Error(
|
72
|
+
`App ${name} is initializing. You can't setup the same again.`
|
73
|
+
)
|
74
|
+
}
|
67
75
|
}
|
68
76
|
return await appPromise
|
69
77
|
}
|
package/src/internal/app.impl.ts
CHANGED
@@ -2,16 +2,26 @@ import { DEFAULT_HOST } from '../index'
|
|
2
2
|
import { type AppBuilder } from '../appBuilder'
|
3
3
|
import { AppBuilderImplementation } from './appBuilder.impl'
|
4
4
|
import { type Constructor, Registry } from './registry'
|
5
|
-
import { Context } from '
|
5
|
+
import { Context } from '../context'
|
6
6
|
import { DisposableContainer, type Lifetime } from '../disposable'
|
7
7
|
import { type Service, ServiceContext } from '../services/service'
|
8
8
|
import { CredentialService } from '../services/credentialService'
|
9
9
|
import { MiddlewareService } from '../services/middlewareService'
|
10
10
|
import { type CredentialBase } from '../credentials'
|
11
|
-
import {
|
12
|
-
import { RpcService
|
13
|
-
|
14
|
-
|
11
|
+
import { AppSdk } from '../appSdk'
|
12
|
+
import { RpcService } from '../services/rpcService'
|
13
|
+
import { CommandService } from '../services/commandService'
|
14
|
+
import {
|
15
|
+
StartCommandHandler,
|
16
|
+
StartCommand
|
17
|
+
} from '../commands/startCommandHandler'
|
18
|
+
import { UserProfileService } from '../services/userProfileService'
|
19
|
+
import { OrganizationService } from '../services/organizationService'
|
20
|
+
import { Organizations } from '../storages/organizations'
|
21
|
+
import { UserProfile } from '../storages/userProfile'
|
22
|
+
import { isUnitTest, UnitTest } from '../unitTest'
|
23
|
+
|
24
|
+
export class AppImplementation extends AppSdk {
|
15
25
|
readonly name: string
|
16
26
|
private _host: string = DEFAULT_HOST
|
17
27
|
private _automaticDataCollectionEnabled: boolean = true
|
@@ -20,10 +30,17 @@ export class AppImplementation implements AppSdk {
|
|
20
30
|
private readonly _disposable: DisposableContainer
|
21
31
|
|
22
32
|
constructor(name: string) {
|
33
|
+
super()
|
23
34
|
this.name = name
|
24
35
|
this._registry = new Registry()
|
25
36
|
this._disposable = new DisposableContainer()
|
26
|
-
this._context = new Context(this._registry, this._disposable.lifetime)
|
37
|
+
this._context = new Context(this._registry, this._disposable.lifetime, name)
|
38
|
+
|
39
|
+
this._registry.map(Context).asValue(this._context)
|
40
|
+
}
|
41
|
+
|
42
|
+
get context(): Context {
|
43
|
+
return this._context
|
27
44
|
}
|
28
45
|
|
29
46
|
get credential(): CredentialBase | undefined {
|
@@ -48,12 +65,52 @@ export class AppImplementation implements AppSdk {
|
|
48
65
|
return this._host
|
49
66
|
}
|
50
67
|
|
68
|
+
get organizations(): Organizations {
|
69
|
+
return this.resolve(OrganizationService)?.organizations as Organizations
|
70
|
+
}
|
71
|
+
|
72
|
+
get userProfile(): UserProfile {
|
73
|
+
return this.resolve(UserProfileService)?.userProfile as UserProfile
|
74
|
+
}
|
75
|
+
|
51
76
|
async initialize(
|
52
77
|
setup: ((builder: AppBuilder) => Promise<void>) | undefined
|
53
78
|
): Promise<void> {
|
54
79
|
// create app builder
|
55
80
|
const builder = new AppBuilderImplementation()
|
56
81
|
|
82
|
+
// register commands
|
83
|
+
builder.registerCommand(StartCommand, (context: Context) => {
|
84
|
+
return new StartCommandHandler(context)
|
85
|
+
})
|
86
|
+
|
87
|
+
// register services
|
88
|
+
builder.registerService(CredentialService, (context: ServiceContext) => {
|
89
|
+
return new CredentialService(context)
|
90
|
+
})
|
91
|
+
builder.registerService(MiddlewareService, (context: ServiceContext) => {
|
92
|
+
return new MiddlewareService(context)
|
93
|
+
})
|
94
|
+
builder.registerService(RpcService, (context: ServiceContext) => {
|
95
|
+
return new RpcService(context, builder.host)
|
96
|
+
})
|
97
|
+
builder.registerService(CommandService, (context: ServiceContext) => {
|
98
|
+
return new CommandService(context)
|
99
|
+
})
|
100
|
+
builder.registerService(UserProfileService, (context: ServiceContext) => {
|
101
|
+
return new UserProfileService(context)
|
102
|
+
})
|
103
|
+
builder.registerService(OrganizationService, (context: ServiceContext) => {
|
104
|
+
return new OrganizationService(context)
|
105
|
+
})
|
106
|
+
|
107
|
+
// register middlewares
|
108
|
+
builder.registerMiddleware(async (req, next) => {
|
109
|
+
req.headers.set('accept', 'text/plain')
|
110
|
+
req.headers.set('content-type', 'application/json')
|
111
|
+
return await next(req)
|
112
|
+
})
|
113
|
+
|
57
114
|
// call customer setup
|
58
115
|
if (setup !== undefined) {
|
59
116
|
await setup(builder)
|
@@ -66,17 +123,6 @@ export class AppImplementation implements AppSdk {
|
|
66
123
|
this._automaticDataCollectionEnabled =
|
67
124
|
builder.automaticDataCollectionEnabled
|
68
125
|
|
69
|
-
// register services
|
70
|
-
builder.registerService(CredentialService, (context: ServiceContext) => {
|
71
|
-
return new CredentialService(context)
|
72
|
-
})
|
73
|
-
builder.registerService(MiddlewareService, (context: ServiceContext) => {
|
74
|
-
return new MiddlewareService(context)
|
75
|
-
})
|
76
|
-
builder.registerService(RpcService, (context: ServiceContext) => {
|
77
|
-
return new RpcServiceImpl(context, builder.host) as RpcService
|
78
|
-
})
|
79
|
-
|
80
126
|
// register services
|
81
127
|
const services: Array<[ServiceContext, Service]> = []
|
82
128
|
builder.services.forEach(serviceFactory => {
|
@@ -89,31 +135,53 @@ export class AppImplementation implements AppSdk {
|
|
89
135
|
}, serviceContext)
|
90
136
|
const serviceInstance = serviceFactory[1](serviceContext)
|
91
137
|
services.push([serviceContext, serviceInstance])
|
92
|
-
this._registry.
|
93
|
-
provide: () => serviceInstance
|
94
|
-
})
|
138
|
+
this._registry.map(serviceFactory[0]).asValue(serviceInstance)
|
95
139
|
})
|
96
140
|
|
97
141
|
builder.middlewares.forEach(middleware => {
|
98
142
|
this.resolve(MiddlewareService)?.useMiddleware(middleware)
|
99
143
|
})
|
100
144
|
|
145
|
+
builder.commands.forEach(command => {
|
146
|
+
this.resolve(CommandService)?.register(command[0], command[1])
|
147
|
+
})
|
148
|
+
|
149
|
+
this.credential = builder.credential
|
150
|
+
|
151
|
+
//-------------------------------------------------------------------------
|
152
|
+
// register services
|
153
|
+
//-------------------------------------------------------------------------
|
101
154
|
const waitList: Array<Promise<void>> = []
|
102
155
|
// call onRegister service's callback
|
103
156
|
services.forEach(([serviceContext]) => {
|
104
157
|
waitList.push(serviceContext.onRegister())
|
105
158
|
})
|
106
159
|
|
160
|
+
// wait for all services to register
|
107
161
|
await Promise.all(waitList)
|
162
|
+
//-------------------------------------------------------------------------
|
108
163
|
|
164
|
+
//-------------------------------------------------------------------------
|
165
|
+
// start services
|
166
|
+
//-------------------------------------------------------------------------
|
109
167
|
waitList.length = 0
|
110
168
|
// call onStart service's callback
|
111
169
|
services.forEach(([serviceContext]) => {
|
112
170
|
waitList.push(serviceContext.onStart())
|
113
171
|
})
|
114
172
|
|
173
|
+
// wait for all services to start
|
115
174
|
await Promise.all(waitList)
|
175
|
+
//-------------------------------------------------------------------------
|
116
176
|
|
117
|
-
|
177
|
+
// start app, execute start command
|
178
|
+
if (!isUnitTest(UnitTest.DO_NOT_START)) {
|
179
|
+
await this.context.execute(new StartCommand())
|
180
|
+
}
|
181
|
+
|
182
|
+
// log app initialized
|
183
|
+
if (!isUnitTest(UnitTest.DO_NOT_PRINT_INITIALIZED_LOG)) {
|
184
|
+
console.log(`AppSDK ${this.name} initialized`)
|
185
|
+
}
|
118
186
|
}
|
119
187
|
}
|
@@ -4,13 +4,26 @@ import { type CredentialBase, DefaultCredential } from '../credentials'
|
|
4
4
|
import type { Middleware } from '../middleware'
|
5
5
|
import { type Service, type ServiceContext } from '../services/service'
|
6
6
|
import { type Constructor } from './registry'
|
7
|
+
import { Command, CommandHandler } from '../services/commandService'
|
8
|
+
import { Context } from '../context'
|
9
|
+
import { UnitTest } from '../unitTest'
|
7
10
|
|
8
11
|
export class AppBuilderImplementation extends AppBuilder {
|
12
|
+
envData: Record<string, any> = {
|
13
|
+
unitTest: UnitTest.DO_NOTHING
|
14
|
+
}
|
9
15
|
host: string = DEFAULT_HOST
|
10
16
|
automaticDataCollectionEnabled: boolean = true
|
11
17
|
credential: CredentialBase = new DefaultCredential()
|
12
18
|
middlewares: Middleware[] = []
|
13
19
|
services: Array<[Constructor<any>, (context: ServiceContext) => Service]> = []
|
20
|
+
commands: Array<
|
21
|
+
[Constructor<any>, (context: Context) => CommandHandler<any>]
|
22
|
+
> = []
|
23
|
+
|
24
|
+
get env(): Record<string, any> {
|
25
|
+
return this.envData
|
26
|
+
}
|
14
27
|
|
15
28
|
useHost(host: string): AppBuilder {
|
16
29
|
this.host = host ?? DEFAULT_HOST
|
@@ -35,7 +48,7 @@ export class AppBuilderImplementation extends AppBuilder {
|
|
35
48
|
return this
|
36
49
|
}
|
37
50
|
|
38
|
-
|
51
|
+
registerMiddleware(middleware: Middleware): AppBuilder {
|
39
52
|
if (middleware === undefined || middleware === null) {
|
40
53
|
throw new Error('addMiddleware, middleware is undefined|null')
|
41
54
|
}
|
@@ -56,4 +69,18 @@ export class AppBuilderImplementation extends AppBuilder {
|
|
56
69
|
this.services.push([type, factory])
|
57
70
|
return this
|
58
71
|
}
|
72
|
+
|
73
|
+
registerCommand<T extends Command>(
|
74
|
+
messageType: Constructor<T>,
|
75
|
+
commandFactory: (context: Context) => CommandHandler<T>
|
76
|
+
): AppBuilder {
|
77
|
+
if (messageType === undefined || messageType === null) {
|
78
|
+
throw new Error('registerCommand, messageType is undefined|null')
|
79
|
+
}
|
80
|
+
if (commandFactory === undefined || commandFactory === null) {
|
81
|
+
throw new Error('registerCommand, commandFactory is undefined|null')
|
82
|
+
}
|
83
|
+
this.commands.push([messageType, commandFactory])
|
84
|
+
return this
|
85
|
+
}
|
59
86
|
}
|
package/src/middleware.ts
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
import { Service } from './service'
|
2
|
+
import { Context } from '../context'
|
3
|
+
import { Constructor } from '../internal/registry'
|
4
|
+
|
5
|
+
export abstract class CommandHandler<T> {
|
6
|
+
constructor(protected readonly context: Context) {}
|
7
|
+
|
8
|
+
resolve<T>(type: Constructor<T>): T | undefined {
|
9
|
+
return this.context.resolve<T>(type)
|
10
|
+
}
|
11
|
+
|
12
|
+
abstract execute(message: T): Promise<void>
|
13
|
+
}
|
14
|
+
|
15
|
+
export abstract class Command {}
|
16
|
+
|
17
|
+
export class CommandService extends Service {
|
18
|
+
private readonly _registry: Map<
|
19
|
+
Constructor<any>,
|
20
|
+
(context: Context) => CommandHandler<any>
|
21
|
+
> = new Map()
|
22
|
+
private _lastPromise: Promise<void> = Promise.resolve()
|
23
|
+
|
24
|
+
register<T extends Command>(
|
25
|
+
messageType: Constructor<T>,
|
26
|
+
commandFactory: (context: Context) => CommandHandler<T>
|
27
|
+
): void {
|
28
|
+
this._registry.set(messageType, commandFactory)
|
29
|
+
}
|
30
|
+
|
31
|
+
async execute<T extends Command>(message: T): Promise<void> {
|
32
|
+
const commandFactory = this._registry.get((message as any).constructor)
|
33
|
+
if (commandFactory) {
|
34
|
+
const command = commandFactory(this.context)
|
35
|
+
await this._lastPromise
|
36
|
+
this._lastPromise = command.execute(message)
|
37
|
+
await this._lastPromise
|
38
|
+
} else {
|
39
|
+
throw new Error(
|
40
|
+
`Command not found for message type ${message.constructor.name}`
|
41
|
+
)
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
@@ -24,10 +24,12 @@ export class MiddlewareService extends Service {
|
|
24
24
|
const processNext = async (request: Request): Promise<Response> => {
|
25
25
|
index++
|
26
26
|
if (index < middlewares.length) {
|
27
|
-
await middlewares[index](request, processNext)
|
27
|
+
return await middlewares[index](request, processNext)
|
28
|
+
} else {
|
29
|
+
return await next(request)
|
28
30
|
}
|
29
|
-
return await next(request)
|
30
31
|
}
|
32
|
+
|
31
33
|
return await processNext(req)
|
32
34
|
}
|
33
35
|
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { Organization, OrganizationId } from '../storages/organizations'
|
2
|
+
import { Disposable } from '../disposable'
|
3
|
+
import { OrganizationDto } from '../dto/userInfoResponse'
|
4
|
+
import { OrganizationService } from './organizationService'
|
5
|
+
import { OrganizationsImpl } from './organizationsImpl'
|
6
|
+
|
7
|
+
export class OrganizationImpl extends Organization implements Disposable {
|
8
|
+
private _isDisposed: boolean = false
|
9
|
+
private _isAdmin: boolean = false
|
10
|
+
private _content?: OrganizationDto
|
11
|
+
|
12
|
+
constructor(
|
13
|
+
private readonly service: OrganizationService,
|
14
|
+
private readonly organizations: OrganizationsImpl
|
15
|
+
) {
|
16
|
+
super()
|
17
|
+
}
|
18
|
+
|
19
|
+
get isAdmin(): boolean {
|
20
|
+
return this._isAdmin
|
21
|
+
}
|
22
|
+
|
23
|
+
get isDisposed(): boolean {
|
24
|
+
return this._isDisposed
|
25
|
+
}
|
26
|
+
|
27
|
+
dispose(): void {
|
28
|
+
this._isDisposed = true
|
29
|
+
}
|
30
|
+
|
31
|
+
public initFrom(
|
32
|
+
content: OrganizationDto,
|
33
|
+
isAdmin: boolean
|
34
|
+
): OrganizationImpl {
|
35
|
+
this._content = content
|
36
|
+
this._isAdmin = isAdmin
|
37
|
+
return this
|
38
|
+
}
|
39
|
+
|
40
|
+
get id(): OrganizationId {
|
41
|
+
return <OrganizationId>this._content?.id
|
42
|
+
}
|
43
|
+
|
44
|
+
get name(): string {
|
45
|
+
return <OrganizationId>this._content?.profile.name
|
46
|
+
}
|
47
|
+
|
48
|
+
get description(): string {
|
49
|
+
return <OrganizationId>this._content?.profile.description
|
50
|
+
}
|
51
|
+
}
|