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

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 (64) hide show
  1. package/.editorconfig +4 -1
  2. package/.eslintrc.json +1 -1
  3. package/README.md +91 -2
  4. package/jest.config.ts +3 -3
  5. package/jest.setup.ts +2 -2
  6. package/package.json +3 -2
  7. package/src/appBuilder.ts +6 -6
  8. package/src/appSdk.ts +6 -6
  9. package/src/commands/startCommandHandler.ts +2 -2
  10. package/src/context.ts +3 -3
  11. package/src/credentials.ts +29 -7
  12. package/src/disposable.ts +3 -4
  13. package/src/dto/accessGroupResponse.ts +35 -0
  14. package/src/dto/chatResponse.ts +104 -0
  15. package/src/dto/userInfoResponse.ts +11 -1
  16. package/src/dto/workspacesResponse.ts +49 -0
  17. package/src/events.ts +13 -13
  18. package/src/index.ts +24 -12
  19. package/src/internal/app.impl.ts +25 -28
  20. package/src/internal/appBuilder.impl.ts +16 -16
  21. package/src/internal/createApp.impl.ts +3 -3
  22. package/src/services/commandService.ts +3 -3
  23. package/src/services/credentialService.ts +3 -3
  24. package/src/services/middlewareService.ts +4 -4
  25. package/src/services/organizationService.ts +18 -116
  26. package/src/services/requestBuilder.ts +40 -15
  27. package/src/services/responseUtils.ts +32 -0
  28. package/src/services/rpcService.ts +28 -11
  29. package/src/services/service.ts +10 -8
  30. package/src/services/userProfileService.ts +18 -66
  31. package/src/storages/chat.ts +21 -0
  32. package/src/storages/chats.ts +17 -0
  33. package/src/storages/file.impl.ts +69 -0
  34. package/src/storages/file.ts +28 -0
  35. package/src/storages/files.impl.ts +213 -0
  36. package/src/storages/files.ts +38 -0
  37. package/src/storages/filesPage.ts +27 -0
  38. package/src/storages/groups.impl.ts +337 -0
  39. package/src/storages/groups.ts +43 -0
  40. package/src/storages/organization.impl.ts +68 -0
  41. package/src/storages/organization.ts +33 -0
  42. package/src/storages/organizations.impl.ts +191 -0
  43. package/src/storages/organizations.ts +8 -28
  44. package/src/storages/userProfile.impl.ts +56 -0
  45. package/src/storages/userProfile.ts +2 -2
  46. package/src/storages/workspace.impl.ts +109 -0
  47. package/src/storages/workspace.ts +49 -0
  48. package/src/storages/workspaces.impl.ts +212 -0
  49. package/src/storages/workspaces.ts +53 -0
  50. package/test/commands.test.ts +8 -8
  51. package/test/data/test_file.pdf +0 -0
  52. package/test/disposable.test.ts +3 -3
  53. package/test/events.test.ts +4 -4
  54. package/test/files.test.ts +52 -0
  55. package/test/index.test.ts +42 -83
  56. package/test/organization.test.ts +57 -0
  57. package/test/registry.test.ts +8 -8
  58. package/test/services.test.ts +15 -15
  59. package/test/setup.ts +52 -0
  60. package/test/unitTest.test.ts +2 -2
  61. package/test/workspace.test.ts +71 -0
  62. package/src/services/organizationImpl.ts +0 -51
  63. package/src/services/organizationsImpl.ts +0 -55
  64. package/src/types.ts +0 -86
@@ -1,62 +1,10 @@
1
- import { Service } from './service'
2
- import { RpcService } from './rpcService'
3
- import { UserEvent, UserProfile } from '../storages/userProfile'
4
- import { UserInfoResponse } from '../dto/userInfoResponse'
5
- import { OrganizationService } from './organizationService'
6
-
7
- class UserProfileImpl extends UserProfile {
8
- private content?: UserInfoResponse
9
-
10
- get id(): string {
11
- if (this.content) {
12
- return this.content.user.id
13
- }
14
- throw new Error('The profile is not loaded.')
15
- }
16
-
17
- get name(): string {
18
- if (this.content) {
19
- return this.content.user.profile.name
20
- }
21
- throw new Error('The profile is not loaded.')
22
- }
23
-
24
- get email(): string {
25
- if (this.content) {
26
- return this.content.user.profile.email
27
- }
28
- throw new Error('The profile is not loaded.')
29
- }
30
-
31
- get isDeleted(): boolean {
32
- if (this.content) {
33
- return this.content.user.isDeleted
34
- }
35
- throw new Error('The profile is not loaded.')
36
- }
37
-
38
- get createdAt(): Date {
39
- if (this.content) {
40
- return new Date(this.content.user.created_at)
41
- }
42
- throw new Error('The profile is not loaded.')
43
- }
44
-
45
- get modifiedAt(): Date {
46
- if (this.content) {
47
- return new Date(this.content.user.modified_at)
48
- }
49
- throw new Error('The profile is not loaded.')
50
- }
51
-
52
- initFrom(content: UserInfoResponse) {
53
- this.content = content
54
- this.dispatch({
55
- type: UserEvent.CHANGED,
56
- data: this
57
- })
58
- }
59
- }
1
+ import { Service } from "./service"
2
+ import { RpcService } from "./rpcService"
3
+ import { UserProfile } from "../storages/userProfile"
4
+ import { UserInfoResponse } from "../dto/userInfoResponse"
5
+ import { OrganizationService } from "./organizationService"
6
+ import { UserProfileImpl } from "../storages/userProfile.impl"
7
+ import { ResponseUtils } from "./responseUtils"
60
8
 
61
9
  export class UserProfileService extends Service {
62
10
  private readonly impl: UserProfileImpl = new UserProfileImpl()
@@ -65,22 +13,26 @@ export class UserProfileService extends Service {
65
13
  return this.impl
66
14
  }
67
15
 
68
- async fetch(fireError: boolean = true) {
16
+ async fetch() {
69
17
  const rpc = this.resolve(RpcService) as RpcService
70
- const response = await rpc.requestBuilder('api/v1/Users/self2').sendGet()
71
- if (fireError && !response.ok) {
72
- throw new Error('Failed to fetch user profile.')
18
+ const response = await rpc.requestBuilder("api/v1/Users/self2").sendGet()
19
+ if (ResponseUtils.isFail(response)) {
20
+ await ResponseUtils.throwError("Failed to fetch user profile", response)
73
21
  }
74
22
  const content = (await response.json()) as UserInfoResponse
23
+
24
+ // init user profile from the server's response
75
25
  this.impl.initFrom(content)
76
26
 
77
27
  const organizationService = this.resolve(
78
28
  OrganizationService
79
29
  ) as OrganizationService
80
- organizationService.initFrom(
81
- content.user.settings,
30
+
31
+ // init organization service from user profile
32
+ await organizationService.initFrom(
82
33
  content.adminInOrganization,
83
- content.organizations
34
+ content.organizations,
35
+ content.user.settings
84
36
  )
85
37
  }
86
38
  }
@@ -0,0 +1,21 @@
1
+ export type ChatId = string
2
+
3
+ export enum ChatAnswer {
4
+ SHORT = "short",
5
+ LONG = "long"
6
+ }
7
+
8
+ export abstract class Chat {
9
+ /**
10
+ * Chat id.
11
+ */
12
+ abstract get id(): ChatId
13
+
14
+ /**
15
+ * Chat name.
16
+ */
17
+ abstract get name(): string
18
+
19
+ abstract question(message: string, answer?: ChatAnswer): Promise<void>
20
+ }
21
+
@@ -0,0 +1,17 @@
1
+ import { EventDispatcher } from "../events"
2
+ import { Chat } from "./chat"
3
+
4
+ export enum ChatsEvent {
5
+ ADDED = "added",
6
+ REMOVED = "removed"
7
+ }
8
+
9
+ /**
10
+ * Chats storage.
11
+ */
12
+ export abstract class Chats extends EventDispatcher<ChatsEvent, Chat> {
13
+ /**
14
+ * Create new chat.
15
+ */
16
+ abstract create(): Promise<Chat>
17
+ }
@@ -0,0 +1,69 @@
1
+ import { Context } from "../context"
2
+ import { Disposable } from "../disposable"
3
+ import { FileDto, FileProgressDto } from "../dto/workspacesResponse"
4
+ import { RpcService } from "../services/rpcService"
5
+ import { ResponseUtils } from "../services/responseUtils"
6
+ import { File } from "./file"
7
+
8
+ export class FileImpl extends File implements Disposable {
9
+ private _isDisposed: boolean = false
10
+ private _content?: FileDto
11
+
12
+ constructor(private readonly context: Context) {
13
+ super()
14
+ }
15
+
16
+ public initFrom(file: FileDto): File {
17
+ this._content = file
18
+
19
+ return this
20
+ }
21
+
22
+ get isDisposed(): boolean {
23
+ return this._isDisposed
24
+ }
25
+
26
+ dispose(): void {
27
+ this._isDisposed = true
28
+ }
29
+
30
+ get id(): string {
31
+ return <string>this._content?.id
32
+ }
33
+
34
+ get name(): string {
35
+ return <string>this._content?.name
36
+ }
37
+
38
+ async url(): Promise<string> {
39
+ const response = await this.context
40
+ .resolve(RpcService)
41
+ ?.requestBuilder("api/v1/Files/url")
42
+ .searchParam("id", this.id)
43
+ .sendGet()
44
+
45
+ if (ResponseUtils.isFail(response)) {
46
+ await ResponseUtils.throwError(
47
+ `Failed to get file ${this.id} url`,
48
+ response
49
+ )
50
+ }
51
+
52
+ return (await response!.json()).url
53
+ }
54
+
55
+ async status(): Promise<FileProgressDto> {
56
+ const response = await this.context
57
+ .resolve(RpcService)
58
+ ?.requestBuilder("api/v1/Files/fetch")
59
+ .searchParam("id", this.id)
60
+ .sendGet()
61
+
62
+ if (ResponseUtils.isFail(response)) {
63
+ await ResponseUtils.throwError(`Failed to get file ${this.id}`, response)
64
+ }
65
+
66
+ const content = await response!.json()
67
+ return content.progress as FileProgressDto
68
+ }
69
+ }
@@ -0,0 +1,28 @@
1
+ import { FileProgressDto } from "../dto/workspacesResponse"
2
+
3
+ export type FileId = string
4
+
5
+ /**
6
+ * File.
7
+ */
8
+ export abstract class File {
9
+ /**
10
+ * File id.
11
+ */
12
+ abstract get id(): FileId
13
+
14
+ /**
15
+ * File name.
16
+ */
17
+ abstract get name(): string
18
+
19
+ /**
20
+ * Get temporary url.
21
+ */
22
+ abstract url(): Promise<string>
23
+
24
+ /**
25
+ * Get file status.
26
+ */
27
+ abstract status(): Promise<FileProgressDto>
28
+ }
@@ -0,0 +1,213 @@
1
+ import { Context } from "../context"
2
+ import { Disposable } from "../disposable"
3
+ import { FileDto, FileListResponse } from "../dto/workspacesResponse"
4
+ import { RpcService } from "../services/rpcService"
5
+ import { FileImpl } from "./file.impl"
6
+ import { Files, FilesEvent, UploadFile } from "./files"
7
+ import { WorkspaceImpl } from "./workspace.impl"
8
+ import { ResponseUtils } from "../services/responseUtils"
9
+ import { File } from "./file"
10
+ import { FilesPage } from "./filesPage"
11
+
12
+ export class FilesPageImpl extends FilesPage implements Disposable {
13
+ private _isDisposed: boolean = false
14
+
15
+ public files: File[] = []
16
+ public total: number = 0
17
+ public filesPerPage: number = 0
18
+ public page: number = 0
19
+
20
+ get pages(): number {
21
+ return Math.ceil(Math.max(this.total / this.filesPerPage, 1.0))
22
+ }
23
+
24
+ get isDisposed(): boolean {
25
+ return this._isDisposed
26
+ }
27
+
28
+ dispose(): void {
29
+ this._isDisposed = true
30
+ }
31
+ }
32
+
33
+ export class FilesImpl extends Files {
34
+ constructor(
35
+ private readonly workspace: WorkspaceImpl,
36
+ private readonly context: Context
37
+ ) {
38
+ super()
39
+ }
40
+
41
+ // Object used as files page data, returned by "query"
42
+ public filesList?: FilesPage
43
+
44
+ async upload(file: any): Promise<File> {
45
+ return await this.internalUpload(file)
46
+ }
47
+
48
+ async delete(id: string): Promise<void> {
49
+ return await this.internalDeleteFile(id)
50
+ }
51
+
52
+ async query(query: string, page: number, limit: number): Promise<FilesPage> {
53
+ return await this.internalQuery(query, page, limit)
54
+ }
55
+
56
+ //----------------------------------------------------------------------------
57
+ // INTERNALS
58
+ //----------------------------------------------------------------------------
59
+
60
+ /**
61
+ * Delete organization.
62
+ * @param id
63
+ */
64
+ async internalDeleteFile(id: string): Promise<void> {
65
+ if (id === undefined || id === null) {
66
+ throw new Error("File delete, id is undefined or null")
67
+ }
68
+ if (id.length === 0 || id.trim().length === 0) {
69
+ throw new Error("File delete, id is empty")
70
+ }
71
+
72
+ const response = await this.context
73
+ .resolve(RpcService)
74
+ ?.requestBuilder("/api/v1/Files")
75
+ .searchParam("id", id)
76
+ .sendDelete()
77
+ if (ResponseUtils.isFail(response)) {
78
+ await ResponseUtils.throwError(`File ${id} delete, failed`, response)
79
+ }
80
+ const file = <FileImpl>this.filesList!.files.find(f => f.id === id)
81
+ const index = this.filesList!.files.indexOf(file)
82
+ if (index < 0) {
83
+ throw new Error("Organization delete, index is not found")
84
+ }
85
+
86
+ // remove file from collection
87
+ this.filesList!.files.splice(index, 1)
88
+
89
+ // dispatch event, file removed
90
+ this.dispatch({
91
+ type: FilesEvent.REMOVED,
92
+ data: file
93
+ })
94
+
95
+ // dispose file
96
+ file.dispose()
97
+ }
98
+
99
+ async internalQuery(
100
+ query: string,
101
+ page: number,
102
+ limit: number
103
+ ): Promise<FilesPage> {
104
+
105
+ // check page
106
+ if (page === undefined || page === null) {
107
+ throw new Error("File fetch, page is undefined or null")
108
+ }
109
+ if (page < 0) {
110
+ throw new Error("File fetch, page is negative")
111
+ }
112
+
113
+ // check limit
114
+ if (limit === undefined || limit === null) {
115
+ throw new Error("File fetch, limit is undefined or null")
116
+ }
117
+ if (limit === 0) {
118
+ throw new Error("File fetch, limit is 0")
119
+ }
120
+
121
+ // send request to the server
122
+ const response = await this.context
123
+ .resolve(RpcService)
124
+ ?.requestBuilder("api/v1/Files/list")
125
+
126
+ .searchParam("workspaceId", this.workspace.id)
127
+ .searchParam("organizationId", this.workspace.organization.id)
128
+ .searchParam("query", query)
129
+ .searchParam("page", page.toString())
130
+ .searchParam("limit", limit.toString())
131
+ .sendGet()
132
+
133
+ // check response status
134
+ if (ResponseUtils.isFail(response)) {
135
+ await ResponseUtils.throwError(
136
+ `Files fetch query:${query}, page:${page}, limit:${limit}, failed`,
137
+ response
138
+ )
139
+ }
140
+
141
+ // parse files from the server's response
142
+ const files = (await response!.json()) as FileListResponse
143
+
144
+ // create files list
145
+ const filesList = new FilesPageImpl()
146
+ filesList.total = files.totalFilesCount
147
+ filesList.filesPerPage = files.filesPerPage
148
+ filesList.page = page
149
+
150
+ // init files from the server's response
151
+ for (const fl of files.files) {
152
+
153
+ // create file implementation
154
+ const file = new FileImpl(this.context).initFrom(fl)
155
+
156
+ // add file to the collection
157
+ filesList.files.push(file)
158
+
159
+ // dispatch event, file added
160
+ this.dispatch({
161
+ type: FilesEvent.ADDED,
162
+ data: file
163
+ })
164
+ }
165
+
166
+ // set files list
167
+ this.filesList = filesList
168
+
169
+ return filesList
170
+ }
171
+
172
+ async internalUpload(file: UploadFile): Promise<File> {
173
+ // check file
174
+ if (file === undefined || file === null) {
175
+ throw new Error("File upload, file is undefined or null")
176
+ }
177
+
178
+ // form data to send
179
+ const form = new FormData()
180
+ form.append("organizationId", this.workspace.organization.id)
181
+ form.append("workspaceId", this.workspace.id)
182
+ form.append("name", file.name)
183
+ form.append("file", file, file.name)
184
+
185
+ // send request to the server
186
+ const response = await this.context
187
+ .resolve(RpcService)
188
+ ?.requestBuilder("api/v1/Files")
189
+ .sendPostFormData(form)
190
+
191
+ // check response status
192
+ if (ResponseUtils.isFail(response)) {
193
+ await ResponseUtils.throwError(`File upload ${file.name}`, response)
194
+ }
195
+
196
+ // parse file from the server's response
197
+ const result = (await response!.json()).file as FileDto
198
+
199
+ // create file implementation
200
+ const fileImpl = new FileImpl(this.context).initFrom(result)
201
+
202
+ // TODO: why is this here?
203
+ this.filesList?.files.push(fileImpl)
204
+
205
+ // dispatch event, file added
206
+ this.dispatch({
207
+ type: FilesEvent.ADDED,
208
+ data: fileImpl
209
+ })
210
+
211
+ return fileImpl
212
+ }
213
+ }
@@ -0,0 +1,38 @@
1
+ import { EventDispatcher } from "../events"
2
+ import { File, FileId } from "./file"
3
+ import { FilesPage } from "./filesPage"
4
+
5
+ /**
6
+ * Files event.
7
+ */
8
+ export enum FilesEvent {
9
+ ADDED = "added",
10
+ REMOVED = "removed"
11
+ }
12
+
13
+ /**
14
+ * Upload file.
15
+ */
16
+ export type UploadFile = globalThis.File
17
+
18
+ /**
19
+ * Files storage.
20
+ */
21
+ export abstract class Files extends EventDispatcher<FilesEvent, File> {
22
+ /**
23
+ * Get file by id.
24
+ */
25
+ abstract upload(file: UploadFile): Promise<File>
26
+
27
+ /**
28
+ * Delete file.
29
+ * @param id
30
+ */
31
+ abstract delete(id: FileId): Promise<void>
32
+
33
+ /**
34
+ * Query files.
35
+ */
36
+ abstract query(query: string, page: number, limit: number): Promise<FilesPage>
37
+ }
38
+
@@ -0,0 +1,27 @@
1
+ import { File } from "./file"
2
+
3
+ /**
4
+ * Files page.
5
+ */
6
+ export abstract class FilesPage {
7
+
8
+ /**
9
+ * Get files.
10
+ */
11
+ abstract get files(): File[]
12
+
13
+ /**
14
+ * Get pages count.
15
+ */
16
+ abstract get pages(): number
17
+
18
+ /**
19
+ * Get total count.
20
+ */
21
+ abstract get total(): number
22
+
23
+ /**
24
+ * Get current page.
25
+ */
26
+ abstract get page(): number
27
+ }