@neuralinnovations/dataisland-sdk 0.0.1-dev2 → 0.0.1-dev3

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.
Files changed (56) hide show
  1. package/.editorconfig +4 -1
  2. package/.eslintrc.json +1 -1
  3. package/jest.config.ts +3 -3
  4. package/jest.setup.ts +2 -2
  5. package/package.json +3 -2
  6. package/src/appBuilder.ts +6 -6
  7. package/src/appSdk.ts +6 -6
  8. package/src/commands/startCommandHandler.ts +2 -2
  9. package/src/context.ts +3 -3
  10. package/src/credentials.ts +29 -7
  11. package/src/disposable.ts +1 -1
  12. package/src/dto/accessGroupResponse.ts +35 -0
  13. package/src/dto/chatResponse.ts +104 -0
  14. package/src/dto/userInfoResponse.ts +11 -1
  15. package/src/dto/workspacesResponse.ts +49 -0
  16. package/src/events.ts +1 -1
  17. package/src/index.ts +10 -12
  18. package/src/internal/app.impl.ts +21 -21
  19. package/src/internal/appBuilder.impl.ts +16 -16
  20. package/src/internal/createApp.impl.ts +3 -3
  21. package/src/services/commandService.ts +3 -3
  22. package/src/services/credentialService.ts +3 -3
  23. package/src/services/middlewareService.ts +3 -3
  24. package/src/services/organizationService.ts +18 -116
  25. package/src/services/requestBuilder.ts +6 -6
  26. package/src/services/responseUtils.ts +32 -0
  27. package/src/services/rpcService.ts +5 -5
  28. package/src/services/service.ts +3 -3
  29. package/src/services/userProfileService.ts +18 -66
  30. package/src/storages/chat.ts +37 -0
  31. package/src/storages/file.impl.ts +68 -0
  32. package/src/storages/files.impl.ts +192 -0
  33. package/src/storages/files.ts +67 -0
  34. package/src/storages/groups.impl.ts +337 -0
  35. package/src/storages/groups.ts +43 -0
  36. package/src/storages/organization.impl.ts +68 -0
  37. package/src/storages/organization.ts +33 -0
  38. package/src/storages/organizations.impl.ts +191 -0
  39. package/src/storages/organizations.ts +8 -28
  40. package/src/storages/userProfile.impl.ts +56 -0
  41. package/src/storages/userProfile.ts +2 -2
  42. package/src/storages/workspace.impl.ts +109 -0
  43. package/src/storages/workspace.ts +43 -0
  44. package/src/storages/workspaces.impl.ts +212 -0
  45. package/src/storages/workspaces.ts +53 -0
  46. package/test/commands.test.ts +8 -8
  47. package/test/disposable.test.ts +3 -3
  48. package/test/events.test.ts +4 -4
  49. package/test/index.test.ts +102 -40
  50. package/test/registry.test.ts +8 -8
  51. package/test/services.test.ts +15 -15
  52. package/test/unitTest.test.ts +2 -2
  53. package/test_file.pdf +0 -0
  54. package/src/services/organizationImpl.ts +0 -51
  55. package/src/services/organizationsImpl.ts +0 -55
  56. package/src/types.ts +0 -86
@@ -0,0 +1,191 @@
1
+ import {
2
+ OrganizationsEvent,
3
+ OrganizationId,
4
+ Organizations
5
+ } from "./organizations"
6
+ import { OrganizationImpl } from "./organization.impl"
7
+ import { RpcService } from "../services/rpcService"
8
+ import { OrganizationDto, UserSettings } from "../dto/userInfoResponse"
9
+ import { Context } from "../context"
10
+ import { Organization } from "./organization"
11
+ import { ResponseUtils } from "../services/responseUtils"
12
+
13
+ export class OrganizationsImpl extends Organizations {
14
+ constructor(public readonly context: Context) {
15
+ super()
16
+ }
17
+
18
+ public organizations: OrganizationImpl[] = []
19
+ public currentOrganizationId?: OrganizationId
20
+
21
+ get collection(): readonly Organization[] {
22
+ return this.organizations
23
+ }
24
+
25
+ get current(): OrganizationId {
26
+ return <OrganizationId>this.currentOrganizationId
27
+ }
28
+
29
+ set current(value: OrganizationId) {
30
+ if (this.currentOrganizationId !== value) {
31
+ const org = this.tryGet(value)
32
+ if (org) {
33
+ this.currentOrganizationId = value
34
+ this.dispatch({
35
+ type: OrganizationsEvent.CURRENT_CHANGED,
36
+ data: org
37
+ })
38
+ } else {
39
+ throw new Error(`Organization ${value} is not found`)
40
+ }
41
+ }
42
+ }
43
+
44
+ get(id: OrganizationId): Organization {
45
+ return <Organization>this.tryGet(id)
46
+ }
47
+
48
+ tryGet(id: OrganizationId): Organization | undefined {
49
+ return this.organizations.find(organization => organization.id === id)
50
+ }
51
+
52
+ contains(id: OrganizationId): boolean {
53
+ return this.organizations.some(organization => organization.id === id)
54
+ }
55
+
56
+ async create(name: string, description: string): Promise<Organization> {
57
+ return this.internalCreateOrganization(name, description)
58
+ }
59
+
60
+ delete(id: string): Promise<void> {
61
+ return this.internalDeleteOrganization(id)
62
+ }
63
+
64
+ //----------------------------------------------------------------------------
65
+ // INTERNALS
66
+ //----------------------------------------------------------------------------
67
+
68
+ /**
69
+ * Delete organization.
70
+ * @param id
71
+ */
72
+ async internalDeleteOrganization(id: OrganizationId): Promise<void> {
73
+ if (id === undefined || id === null) {
74
+ throw new Error("Organization delete, id is undefined or null")
75
+ }
76
+ if (id.length === 0 || id.trim().length === 0) {
77
+ throw new Error("Organization delete, id is empty")
78
+ }
79
+ if (!this.contains(id)) {
80
+ throw new Error(`Organization delete, id: ${id} is not found`)
81
+ }
82
+ const response = await this.context
83
+ .resolve(RpcService)
84
+ ?.requestBuilder("/api/v1/Organizations")
85
+ .searchParam("id", id)
86
+ .sendDelete()
87
+ if (ResponseUtils.isFail(response)) {
88
+ await ResponseUtils.throwError(
89
+ `Organization ${id} delete, failed`,
90
+ response
91
+ )
92
+ }
93
+ const org = <OrganizationImpl>this.get(id)
94
+ const index = this.organizations.indexOf(org)
95
+ if (index < 0) {
96
+ throw new Error("Organization delete, index is not found")
97
+ }
98
+
99
+ // remove organization from collection
100
+ this.organizations.splice(index, 1)
101
+
102
+ // dispatch event, organization removed
103
+ this.dispatch({
104
+ type: OrganizationsEvent.REMOVED,
105
+ data: org
106
+ })
107
+
108
+ // dispose organization
109
+ org.dispose()
110
+ }
111
+
112
+ /**
113
+ * Create organization.
114
+ * @param name
115
+ * @param description
116
+ */
117
+ async internalCreateOrganization(
118
+ name: string,
119
+ description: string
120
+ ): Promise<OrganizationImpl> {
121
+ if (name === undefined || name === null) {
122
+ throw new Error("Organization create, name is undefined or null")
123
+ }
124
+ if (description === undefined || description === null) {
125
+ throw new Error("Organization create, description is undefined or null")
126
+ }
127
+ if (name.length === 0 || name.trim().length === 0) {
128
+ throw new Error("Organization create, name is empty")
129
+ }
130
+ const response = await this.context
131
+ .resolve(RpcService)
132
+ ?.requestBuilder("api/v1/Organizations")
133
+ .sendPost({
134
+ profile: {
135
+ name: name,
136
+ description: description
137
+ }
138
+ })
139
+ if (ResponseUtils.isFail(response)) {
140
+ await ResponseUtils.throwError(
141
+ `Organization create failed, name: ${name}, description: ${description}`,
142
+ response
143
+ )
144
+ }
145
+ const content = (await response!.json()).organization as OrganizationDto
146
+
147
+ // create organization and init from content
148
+ const org = await new OrganizationImpl(this.context).initFrom(content, true)
149
+
150
+ // add organization to collection
151
+ this.organizations.push(org)
152
+
153
+ // dispatch event, organization added
154
+ this.dispatch({
155
+ type: OrganizationsEvent.ADDED,
156
+ data: org
157
+ })
158
+
159
+ return org
160
+ }
161
+
162
+ /**
163
+ * Init organizations from user profile.
164
+ * @param adminInOrganization
165
+ * @param organizations
166
+ * @param settings
167
+ */
168
+ async internalInitFrom(
169
+ adminInOrganization: string[],
170
+ organizations: OrganizationDto[],
171
+ settings: UserSettings | null | undefined
172
+ ): Promise<void> {
173
+ this.currentOrganizationId = settings?.activeOrganizationId
174
+ for (const organization of organizations) {
175
+ // create organization and init from content
176
+ const org = await new OrganizationImpl(this.context).initFrom(
177
+ organization,
178
+ adminInOrganization.includes(organization.id)
179
+ )
180
+
181
+ // add organization to collection
182
+ this.organizations.push(org)
183
+
184
+ // dispatch event, organization added
185
+ this.dispatch({
186
+ type: OrganizationsEvent.ADDED,
187
+ data: org
188
+ })
189
+ }
190
+ }
191
+ }
@@ -1,46 +1,26 @@
1
- import { EventDispatcher } from '../events'
1
+ import { EventDispatcher } from "../events"
2
+ import { Organization } from "./organization"
2
3
 
3
4
  /**
4
5
  * Organization id.
5
6
  */
6
7
  export type OrganizationId = string
7
8
 
8
- /**
9
- * Organization.
10
- */
11
- export abstract class Organization {
12
- /**
13
- * Organization id.
14
- */
15
- abstract get id(): OrganizationId
16
-
17
- /**
18
- * Organization name.
19
- */
20
- abstract get name(): string
21
-
22
- /**
23
- * Organization description.
24
- */
25
- abstract get description(): string
26
- }
27
-
28
9
  /**
29
10
  * Organization event.
30
11
  */
31
- export enum OrganizationEvent {
32
- ADDED = 'added',
33
- REMOVED = 'removed',
34
- CHANGED = 'changed',
35
- CURRENT_CHANGED = 'currentChanged'
12
+ export enum OrganizationsEvent {
13
+ ADDED = "added",
14
+ REMOVED = "removed",
15
+ CURRENT_CHANGED = "currentChanged"
36
16
  }
37
17
 
38
18
  /**
39
19
  * Organizations storage.
40
20
  */
41
21
  export abstract class Organizations extends EventDispatcher<
42
- OrganizationEvent,
43
- Organization | Organizations
22
+ OrganizationsEvent,
23
+ Organization
44
24
  > {
45
25
  /**
46
26
  * User's organizations.
@@ -0,0 +1,56 @@
1
+ import { UserEvent, UserProfile } from "./userProfile"
2
+ import { UserInfoResponse } from "../dto/userInfoResponse"
3
+
4
+ export class UserProfileImpl extends UserProfile {
5
+ private content?: UserInfoResponse
6
+
7
+ get id(): string {
8
+ if (this.content) {
9
+ return this.content.user.id
10
+ }
11
+ throw new Error("The profile is not loaded.")
12
+ }
13
+
14
+ get name(): string {
15
+ if (this.content) {
16
+ return this.content.user.profile.name
17
+ }
18
+ throw new Error("The profile is not loaded.")
19
+ }
20
+
21
+ get email(): string {
22
+ if (this.content) {
23
+ return this.content.user.profile.email
24
+ }
25
+ throw new Error("The profile is not loaded.")
26
+ }
27
+
28
+ get isDeleted(): boolean {
29
+ if (this.content) {
30
+ return this.content.user.isDeleted
31
+ }
32
+ throw new Error("The profile is not loaded.")
33
+ }
34
+
35
+ get createdAt(): Date {
36
+ if (this.content) {
37
+ return new Date(this.content.user.created_at)
38
+ }
39
+ throw new Error("The profile is not loaded.")
40
+ }
41
+
42
+ get modifiedAt(): Date {
43
+ if (this.content) {
44
+ return new Date(this.content.user.modified_at)
45
+ }
46
+ throw new Error("The profile is not loaded.")
47
+ }
48
+
49
+ initFrom(content: UserInfoResponse) {
50
+ this.content = content
51
+ this.dispatch({
52
+ type: UserEvent.CHANGED,
53
+ data: this
54
+ })
55
+ }
56
+ }
@@ -1,9 +1,9 @@
1
- import { EventDispatcher } from '../events'
1
+ import { EventDispatcher } from "../events"
2
2
 
3
3
  export type UserId = string
4
4
 
5
5
  export enum UserEvent {
6
- CHANGED = 'changed'
6
+ CHANGED = "changed"
7
7
  }
8
8
 
9
9
  export abstract class UserProfile extends EventDispatcher<
@@ -0,0 +1,109 @@
1
+ import { Context } from "../context"
2
+ import { Files } from "./files"
3
+ import { Workspace, WorkspaceEvent } from "./workspace"
4
+ import { OrganizationImpl } from "./organization.impl"
5
+ import { WorkspaceDto } from "../dto/workspacesResponse"
6
+ import { RpcService } from "../services/rpcService"
7
+ import { FilesImpl } from "./files.impl"
8
+ import { ResponseUtils } from "../services/responseUtils"
9
+
10
+ export class WorkspaceImpl extends Workspace {
11
+ private _isMarkAsDeleted: boolean = false
12
+ private _workspace?: WorkspaceDto
13
+
14
+ private readonly _files: FilesImpl
15
+
16
+ constructor(
17
+ public readonly organization: OrganizationImpl,
18
+ public readonly context: Context
19
+ ) {
20
+ super()
21
+ this._files = new FilesImpl(this, context)
22
+ }
23
+
24
+ get id(): string {
25
+ if (this._workspace) {
26
+ return this._workspace.id
27
+ }
28
+ throw new Error("Workspace is not loaded.")
29
+ }
30
+
31
+ get name(): string {
32
+ if (this._workspace) {
33
+ return this._workspace.profile.name
34
+ }
35
+ throw new Error("Workspace is not loaded.")
36
+ }
37
+
38
+ get description(): string {
39
+ if (this._workspace) {
40
+ return this._workspace.profile.description
41
+ }
42
+ throw new Error("Workspace is not loaded.")
43
+ }
44
+
45
+ get files(): Files {
46
+ return this._files
47
+ }
48
+
49
+ async change(name: string, description: string): Promise<void> {
50
+ if (!this._workspace) {
51
+ throw new Error("Workspace is not loaded.")
52
+ }
53
+ if (this._isMarkAsDeleted) {
54
+ throw new Error("Workspace is marked as deleted.")
55
+ }
56
+ if (name === this.name && description === this.description) {
57
+ return Promise.resolve()
58
+ }
59
+ if (name === undefined || name === null || name.trim() === "") {
60
+ throw new Error("Name is required. Please provide a valid name.")
61
+ }
62
+ if (
63
+ description === undefined ||
64
+ description === null ||
65
+ description.trim() === ""
66
+ ) {
67
+ throw new Error(
68
+ "Description is required. Please provide a valid description."
69
+ )
70
+ }
71
+
72
+ const response = await this.context
73
+ .resolve(RpcService)
74
+ ?.requestBuilder("api/v1/Workspaces")
75
+ .sendPut({
76
+ workspaceId: this.id,
77
+ profile: {
78
+ name,
79
+ description
80
+ }
81
+ })
82
+
83
+ if (ResponseUtils.isFail(response)) {
84
+ await ResponseUtils.throwError("Failed to change workspace", response)
85
+ }
86
+
87
+ if (this._workspace) {
88
+ this._workspace.profile.name = name
89
+ this._workspace.profile.description = description
90
+ }
91
+
92
+ this.dispatch({
93
+ type: WorkspaceEvent.CHANGED,
94
+ data: this
95
+ })
96
+ }
97
+
98
+ async initFrom(workspace: WorkspaceDto) {
99
+ this._workspace = workspace
100
+ }
101
+
102
+ get isMarkAsDeleted(): boolean {
103
+ return this._isMarkAsDeleted
104
+ }
105
+
106
+ markToDelete(): void {
107
+ this._isMarkAsDeleted = true
108
+ }
109
+ }
@@ -0,0 +1,43 @@
1
+ import { EventDispatcher } from "../events"
2
+ import { Files } from "./files"
3
+ import { WorkspaceId } from "./workspaces"
4
+
5
+ /**
6
+ * Workspace event.
7
+ */
8
+ export enum WorkspaceEvent {
9
+ CHANGED = "changed"
10
+ }
11
+
12
+ /**
13
+ * Workspace.
14
+ */
15
+ export abstract class Workspace extends EventDispatcher<
16
+ WorkspaceEvent,
17
+ Workspace
18
+ > {
19
+ /**
20
+ * Workspace id.
21
+ */
22
+ abstract get id(): WorkspaceId
23
+
24
+ /**
25
+ * Workspace name.
26
+ */
27
+ abstract get name(): string
28
+
29
+ /**
30
+ * Workspace description.
31
+ */
32
+ abstract get description(): string
33
+
34
+ /**
35
+ * Workspace files.
36
+ */
37
+ abstract get files(): Files
38
+
39
+ /**
40
+ * Change workspace name and description.
41
+ */
42
+ abstract change(name: string, description: string): Promise<void>
43
+ }
@@ -0,0 +1,212 @@
1
+ import { WorkspaceId, Workspaces, WorkspacesEvent } from "./workspaces"
2
+ import { OrganizationImpl } from "./organization.impl"
3
+ import { Context } from "../context"
4
+ import { Workspace } from "./workspace"
5
+ import { WorkspaceImpl } from "./workspace.impl"
6
+ import { OrganizationId } from "./organizations"
7
+ import { RpcService } from "../services/rpcService"
8
+ import { OrganizationWorkspaces } from "../dto/userInfoResponse"
9
+ import { WorkspaceDto } from "../dto/workspacesResponse"
10
+ import { ResponseUtils } from "../services/responseUtils"
11
+
12
+ export class WorkspacesImpl extends Workspaces {
13
+ private readonly _workspaces: WorkspaceImpl[] = []
14
+
15
+ constructor(
16
+ private readonly organization: OrganizationImpl,
17
+ private readonly context: Context
18
+ ) {
19
+ super()
20
+ }
21
+
22
+ get collection(): readonly Workspace[] {
23
+ return this._workspaces
24
+ }
25
+
26
+ get(id: string): Workspace {
27
+ return <Workspace>this.tryGet(id)
28
+ }
29
+
30
+ tryGet(id: string): Workspace | undefined {
31
+ return this._workspaces.find(workspace => workspace.id === id)
32
+ }
33
+
34
+ contains(id: string): boolean {
35
+ return this._workspaces.find(workspace => workspace.id === id) !== undefined
36
+ }
37
+
38
+ /**
39
+ * Create workspace.
40
+ * @param name
41
+ * @param description
42
+ * @param regulation
43
+ */
44
+ async create(
45
+ name: string,
46
+ description: string,
47
+ regulation?: {
48
+ isCreateNewGroup: boolean
49
+ newGroupName: string
50
+ groupIds: string[]
51
+ }
52
+ ): Promise<Workspace> {
53
+ if (name === undefined || name === null || name.trim() === "") {
54
+ throw new Error("Name is required, must be not empty")
55
+ }
56
+ if (
57
+ description === undefined ||
58
+ description === null ||
59
+ description.trim() === ""
60
+ ) {
61
+ throw new Error("Description is required, must be not empty")
62
+ }
63
+ if (regulation) {
64
+ if (
65
+ regulation.isCreateNewGroup === undefined ||
66
+ regulation.isCreateNewGroup === null
67
+ ) {
68
+ throw new Error("isCreateNewGroup is required, must be not empty")
69
+ }
70
+ if (
71
+ regulation.newGroupName === undefined ||
72
+ regulation.newGroupName === null ||
73
+ regulation.newGroupName.trim() === ""
74
+ ) {
75
+ throw new Error("newGroupName is required, must be not empty")
76
+ }
77
+ if (
78
+ regulation.groupIds === undefined ||
79
+ regulation.groupIds === null ||
80
+ regulation.groupIds.length === 0
81
+ ) {
82
+ throw new Error("groupIds is required, must be not empty")
83
+ }
84
+ }
85
+
86
+ // send create request to the server
87
+ const response = await this.context
88
+ .resolve(RpcService)
89
+ ?.requestBuilder("api/v1/Workspaces")
90
+ .sendPost({
91
+ organizationId: this.organization.id,
92
+ profile: {
93
+ name: name,
94
+ description: description
95
+ },
96
+ regulation: {
97
+ isCreateNewGroup: regulation?.isCreateNewGroup ?? false,
98
+ newGroupName: regulation?.newGroupName ?? "",
99
+ groupIds: regulation?.groupIds ?? []
100
+ }
101
+ })
102
+
103
+ // check response status
104
+ if (ResponseUtils.isFail(response)) {
105
+ await ResponseUtils.throwError("Failed to create workspace", response)
106
+ }
107
+
108
+ // parse workspace from the server's response
109
+ const content = (await response!.json()).workspace as WorkspaceDto
110
+
111
+ // create workspace implementation
112
+ const workspace = new WorkspaceImpl(this.organization, this.context)
113
+ await workspace.initFrom(content)
114
+
115
+ // add workspace to the collection
116
+ this._workspaces.push(workspace)
117
+
118
+ // dispatch event
119
+ this.dispatch({
120
+ type: WorkspacesEvent.ADDED,
121
+ data: workspace
122
+ })
123
+
124
+ return workspace
125
+ }
126
+
127
+ /**
128
+ * Delete workspace.
129
+ * @param id
130
+ */
131
+ async delete(id: WorkspaceId): Promise<void> {
132
+ // get workspace by id
133
+ const workspace = <WorkspaceImpl>this.tryGet(id)
134
+
135
+ // check if workspace is found
136
+ if (!workspace) {
137
+ throw new Error(`Workspace ${id} is not found`)
138
+ }
139
+
140
+ // check if workspace is already marked as deleted
141
+ if (workspace.isMarkAsDeleted) {
142
+ throw new Error(`Workspace ${id} is already marked as deleted`)
143
+ }
144
+
145
+ // mark workspace as deleted
146
+ workspace.markToDelete()
147
+
148
+ // send delete request to the server
149
+ const response = await this.context
150
+ .resolve(RpcService)
151
+ ?.requestBuilder("api/v1/Workspaces")
152
+ .searchParam("id", id)
153
+ .sendDelete()
154
+
155
+ // check response status
156
+ if (ResponseUtils.isFail(response)) {
157
+ await ResponseUtils.throwError(
158
+ `Failed to delete workspace: ${workspace.organization.name}/${workspace.name}:${id}`,
159
+ response
160
+ )
161
+ }
162
+
163
+ // remove workspace from the collection
164
+ const index = this._workspaces.indexOf(<WorkspaceImpl>workspace)
165
+ if (index < 0) {
166
+ throw new Error(`Workspace ${id} is not found`)
167
+ }
168
+ this._workspaces.splice(index, 1)
169
+
170
+ // dispatch event
171
+ this.dispatch({
172
+ type: WorkspacesEvent.REMOVED,
173
+ data: workspace
174
+ })
175
+ }
176
+
177
+ async initFrom(organizationId: OrganizationId): Promise<void> {
178
+ // init workspaces from the server's response
179
+ const response = await this.context
180
+ .resolve(RpcService)
181
+ ?.requestBuilder("api/v1/Organizations")
182
+ .searchParam("id", organizationId)
183
+ .sendGet()
184
+
185
+ // check response status
186
+ if (ResponseUtils.isFail(response)) {
187
+ await ResponseUtils.throwError("Failed to fetch workspaces.", response)
188
+ }
189
+
190
+ // parse workspaces from the server's response
191
+ const workspaces = ((await response!.json()) as OrganizationWorkspaces)
192
+ .workspaces
193
+
194
+ // init workspaces from the server's response
195
+ for (const workspace of workspaces) {
196
+ // create workspace implementation
197
+ const workspaceImpl = new WorkspaceImpl(this.organization, this.context)
198
+
199
+ // init workspace from the server's response
200
+ await workspaceImpl.initFrom(workspace)
201
+
202
+ // add workspace to the collection
203
+ this._workspaces.push(workspaceImpl)
204
+
205
+ // dispatch event
206
+ this.dispatch({
207
+ type: WorkspacesEvent.ADDED,
208
+ data: workspaceImpl
209
+ })
210
+ }
211
+ }
212
+ }