@neuralinnovations/dataisland-sdk 0.0.1-dev1 → 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 (58) hide show
  1. package/.editorconfig +4 -1
  2. package/.eslintrc.json +1 -1
  3. package/jest.config.ts +4 -4
  4. package/jest.setup.ts +2 -0
  5. package/package.json +4 -2
  6. package/src/appBuilder.ts +24 -5
  7. package/src/appSdk.ts +31 -12
  8. package/src/commands/startCommandHandler.ts +14 -0
  9. package/src/context.ts +31 -0
  10. package/src/credentials.ts +31 -9
  11. package/src/disposable.ts +2 -2
  12. package/src/dto/accessGroupResponse.ts +35 -0
  13. package/src/dto/chatResponse.ts +104 -0
  14. package/src/dto/userInfoResponse.ts +47 -0
  15. package/src/dto/workspacesResponse.ts +49 -0
  16. package/src/events.ts +1 -5
  17. package/src/index.ts +17 -11
  18. package/src/internal/app.impl.ts +98 -30
  19. package/src/internal/appBuilder.impl.ts +39 -12
  20. package/src/internal/createApp.impl.ts +3 -3
  21. package/src/middleware.ts +1 -1
  22. package/src/services/commandService.ts +44 -0
  23. package/src/services/credentialService.ts +3 -3
  24. package/src/services/middlewareService.ts +7 -5
  25. package/src/services/organizationService.ts +28 -0
  26. package/src/services/requestBuilder.ts +102 -0
  27. package/src/services/responseUtils.ts +32 -0
  28. package/src/services/rpcService.ts +113 -53
  29. package/src/services/service.ts +3 -3
  30. package/src/services/userProfileService.ts +38 -0
  31. package/src/storages/chat.ts +37 -0
  32. package/src/storages/file.impl.ts +68 -0
  33. package/src/storages/files.impl.ts +192 -0
  34. package/src/storages/files.ts +67 -0
  35. package/src/storages/groups.impl.ts +337 -0
  36. package/src/storages/groups.ts +43 -0
  37. package/src/storages/organization.impl.ts +68 -0
  38. package/src/storages/organization.ts +33 -0
  39. package/src/storages/organizations.impl.ts +191 -0
  40. package/src/storages/organizations.ts +56 -0
  41. package/src/storages/userProfile.impl.ts +56 -0
  42. package/src/storages/userProfile.ts +42 -0
  43. package/src/storages/workspace.impl.ts +109 -0
  44. package/src/storages/workspace.ts +43 -0
  45. package/src/storages/workspaces.impl.ts +212 -0
  46. package/src/storages/workspaces.ts +53 -0
  47. package/src/unitTest.ts +42 -0
  48. package/test/commands.test.ts +24 -0
  49. package/test/disposable.test.ts +3 -3
  50. package/test/events.test.ts +4 -4
  51. package/test/index.test.ts +204 -62
  52. package/test/registry.test.ts +8 -8
  53. package/test/services.test.ts +56 -0
  54. package/test/setup.ts +2 -0
  55. package/test/unitTest.test.ts +21 -0
  56. package/test_file.pdf +0 -0
  57. package/src/internal/context.ts +0 -13
  58. package/src/types.ts +0 -110
@@ -0,0 +1,337 @@
1
+ import { Context } from "../context"
2
+ import { Disposable } from "../disposable"
3
+ import { AccessGroupDto, AccessGroupResponse, AccessGroupsResponse } from "../dto/accessGroupResponse"
4
+ import { UserDto } from "../dto/userInfoResponse"
5
+ import { WorkspaceDto, WorkspacesResponse } from "../dto/workspacesResponse"
6
+ import { RpcService } from "../services/rpcService"
7
+ import { Group, GroupEvent, GroupId, Groups } from "./groups"
8
+ import { OrganizationImpl } from "./organization.impl"
9
+ import { OrganizationId } from "./organizations"
10
+
11
+ export class GroupImpl extends Group implements Disposable {
12
+ private _isDisposed: boolean = false
13
+ private _content?: AccessGroupDto
14
+ private _members?: UserDto[]
15
+
16
+ constructor(
17
+ private readonly context: Context
18
+ ) {
19
+ super()
20
+ }
21
+
22
+ async initFrom(id: GroupId): Promise<Group>{
23
+ const response = await this.context.resolve(RpcService)
24
+ ?.requestBuilder("api/v1/AccessGroups")
25
+ .searchParam("id", id)
26
+ .sendGet()
27
+
28
+ if (!response?.ok) {
29
+ let text: string = ""
30
+ try {
31
+ text = (await response?.text()) ?? ""
32
+ } catch (e) {
33
+ console.error(e)
34
+ }
35
+
36
+ throw new Error(
37
+ `Get group, response is not ok, status: ${response?.status},${response?.statusText} ${text}`
38
+ )
39
+ }
40
+
41
+ const group = (await response.json()) as AccessGroupResponse
42
+
43
+ this._content = group.group
44
+ this._members = group.members
45
+
46
+ return this
47
+ }
48
+
49
+ get id(): GroupId {
50
+ if (this._content) {
51
+ return this._content.id
52
+ }
53
+ throw new Error("Access group is not loaded.")
54
+ }
55
+
56
+ get group(): AccessGroupDto {
57
+ if (this._content) {
58
+ return this._content
59
+ }
60
+ throw new Error("Access group is not loaded.")
61
+ }
62
+
63
+ async getWorkspaces(): Promise<WorkspaceDto[]> {
64
+ const response = await this.context.resolve(RpcService)
65
+ ?.requestBuilder("api/v1/Organizations/workspaces")
66
+ .searchParam("groupId", this.id)
67
+ .sendGet()
68
+
69
+ if (!response?.ok) {
70
+ let text: string = ""
71
+ try {
72
+ text = (await response?.text()) ?? ""
73
+ } catch (e) {
74
+ console.error(e)
75
+ }
76
+
77
+ throw new Error(
78
+ `Groups get workspaces, response is not ok, status: ${response?.status},${response?.statusText} ${text}`
79
+ )
80
+ }
81
+
82
+ const workspaces = (await response.json()) as WorkspacesResponse
83
+
84
+ return workspaces.workspaces
85
+ }
86
+
87
+ get members(): UserDto[] {
88
+ if (this._members){
89
+ return this._members
90
+ }
91
+ throw new Error("Access group is not loaded.")
92
+ }
93
+
94
+ async setName(name: string): Promise<void> {
95
+ if (name === undefined || name === null) {
96
+ throw new Error("Groups change, name is undefined or null")
97
+ }
98
+ if (name.length === 0 || name.trim().length === 0) {
99
+ throw new Error("Groups change, name is empty")
100
+ }
101
+ const response = await this.context
102
+ .resolve(RpcService)
103
+ ?.requestBuilder("api/v1/AccessGroups/name")
104
+ .sendPut({
105
+ groupId: this.id,
106
+ name: name
107
+ })
108
+
109
+ if (!response?.ok) {
110
+ throw new Error(
111
+ `Failed to set new name. ${response?.status}, ${response?.statusText}`
112
+ )
113
+ }
114
+ }
115
+
116
+ async setPermits(permits: {isAdmin: boolean}) : Promise<void> {
117
+ const response = await this.context
118
+ .resolve(RpcService)
119
+ ?.requestBuilder("api/v1/AccessGroups/permits")
120
+ .sendPut({
121
+ groupId: this.id,
122
+ permits: permits
123
+ })
124
+
125
+ if (!response?.ok) {
126
+ throw new Error(
127
+ `Failed to set new permits. ${response?.status}, ${response?.statusText}`
128
+ )
129
+ }
130
+ }
131
+
132
+
133
+ async setWorkspaces(workspaces: string[]): Promise<void> {
134
+ if (workspaces === null || workspaces === undefined){
135
+ throw new Error("Group add workspaces, workspaces is undefined or null")
136
+ }
137
+
138
+ const response = await this.context
139
+ .resolve(RpcService)
140
+ ?.requestBuilder("api/v1/AccessGroups/workspaces")
141
+ .sendPut({
142
+ groupId: this.id,
143
+ actualWorkspaceIds: workspaces
144
+ })
145
+
146
+ if (!response?.ok) {
147
+ throw new Error(
148
+ `Failed to set new member Ids. ${response?.status}, ${response?.statusText}`
149
+ )
150
+ }
151
+ }
152
+
153
+
154
+ async setMembersIds(members: string[]) {
155
+ if (members === null || members === undefined){
156
+ throw new Error("Group add members, members is undefined or null")
157
+ }
158
+
159
+ const response = await this.context
160
+ .resolve(RpcService)
161
+ ?.requestBuilder("api/v1/AccessGroups/members")
162
+ .sendPut({
163
+ groupId: this.id,
164
+ memberIds: members
165
+ })
166
+
167
+ if (!response?.ok) {
168
+ throw new Error(
169
+ `Failed to set new member Ids. ${response?.status}, ${response?.statusText}`
170
+ )
171
+ }
172
+
173
+ }
174
+
175
+ get isDisposed(): boolean {
176
+ return this._isDisposed
177
+ }
178
+
179
+ dispose(): void {
180
+ this._isDisposed = true
181
+ }
182
+ }
183
+
184
+ export class GroupsImpl extends Groups {
185
+
186
+ private _groups: Group[] = []
187
+
188
+ constructor(
189
+ private readonly organization: OrganizationImpl,
190
+ private readonly context: Context
191
+ ) {
192
+ super()
193
+ }
194
+
195
+ async initialize(){
196
+ await this.internalInit()
197
+ }
198
+
199
+ async create(name: string, organizationId: OrganizationId, permits: { isAdmin: boolean }, memberIds: string[]): Promise<Group> {
200
+ return await this.internalCreate(name, organizationId, permits, memberIds)
201
+ }
202
+ async get(id: GroupId): Promise<Group | undefined> {
203
+ return await this._groups.find(group => group.id === id)
204
+ }
205
+ async delete(id: GroupId): Promise<void> {
206
+ return await this.internalDeleteGroup(id)
207
+ }
208
+
209
+
210
+ //----------------------------------------------------------------------------
211
+ // INTERNALS
212
+ //----------------------------------------------------------------------------
213
+
214
+ /**
215
+ * Init access groups.
216
+ */
217
+ async internalInit(): Promise<void> {
218
+ const response = await this.context.resolve(RpcService)
219
+ ?.requestBuilder("api/v1/Organizations/access_groups")
220
+ .searchParam("id", this.organization.id)
221
+ .sendGet()
222
+
223
+ if (!response?.ok) {
224
+ let text: string = ""
225
+ try {
226
+ text = (await response?.text()) ?? ""
227
+ } catch (e) {
228
+ console.error(e)
229
+ }
230
+
231
+ throw new Error(
232
+ `Groups init, response is not ok, status: ${response?.status},${response?.statusText} ${text}`
233
+ )
234
+ }
235
+
236
+ const groups = (await response.json()) as AccessGroupsResponse
237
+
238
+ for (const gr of groups.groups){
239
+ const group = await new GroupImpl(this.context).initFrom(gr.id)
240
+
241
+ this._groups.push(group)
242
+
243
+ this.dispatch({
244
+ type: GroupEvent.ADDED,
245
+ data: group
246
+ })
247
+ }
248
+ }
249
+
250
+ async internalCreate(name: string, organizationId: OrganizationId, permits: { isAdmin: boolean }, memberIds: string[]): Promise<Group>{
251
+ if (name === undefined || name === null) {
252
+ throw new Error("Group create, name is undefined or null")
253
+ }
254
+ if (name.length === 0 || name.trim().length === 0) {
255
+ throw new Error("Group create, name is empty")
256
+ }
257
+
258
+ const response = await this.context
259
+ .resolve(RpcService)
260
+ ?.requestBuilder("api/v1/AccessGroups")
261
+ .sendPost({
262
+ name: name,
263
+ organizationId: organizationId,
264
+ permits: permits,
265
+ memberIds: memberIds
266
+ })
267
+
268
+ if (!response?.ok) {
269
+ throw new Error(
270
+ `Organization create, response is not ok: ${response?.status}/${response?.statusText}`
271
+ )
272
+ }
273
+ const content = (await response.json()) as AccessGroupResponse
274
+
275
+ const group = await new GroupImpl(this.context).initFrom(content.group.id)
276
+
277
+ this._groups.push(group)
278
+
279
+ this.dispatch({
280
+ type: GroupEvent.ADDED,
281
+ data: group
282
+ })
283
+
284
+ return group
285
+ }
286
+
287
+ /**
288
+ * Delete group.
289
+ * @param id
290
+ */
291
+ async internalDeleteGroup(id: GroupId): Promise<void> {
292
+ if (id === undefined || id === null) {
293
+ throw new Error("Group delete, id is undefined or null")
294
+ }
295
+ if (id.length === 0 || id.trim().length === 0) {
296
+ throw new Error("Group delete, id is empty")
297
+ }
298
+
299
+ const response = await this.context
300
+ .resolve(RpcService)
301
+ ?.requestBuilder("/api/v1/AccessGroups")
302
+ .searchParam("groupId", id)
303
+ .sendDelete()
304
+ if (!response?.ok) {
305
+ let text: string = ""
306
+ try {
307
+ text = (await response?.text()) ?? ""
308
+ } catch (e) {
309
+ console.error(e)
310
+ }
311
+
312
+ throw new Error(
313
+ `Group ${id} delete, response is not ok, status: ${response?.status},${response?.statusText} ${text}`
314
+ )
315
+ }
316
+
317
+ const group = <GroupImpl>this._groups.find(f => f.id === id)
318
+ const index = this._groups.indexOf(group)
319
+ if (index < 0) {
320
+ throw new Error("Group delete, index is not found")
321
+ }
322
+
323
+ // remove group from collection
324
+ this._groups.splice(index, 1)
325
+
326
+ // dispatch event, group removed
327
+ this.dispatch({
328
+ type: GroupEvent.REMOVED,
329
+ data: group
330
+ })
331
+
332
+ // dispose group
333
+ group.dispose()
334
+ }
335
+
336
+
337
+ }
@@ -0,0 +1,43 @@
1
+ import { AccessGroupDto } from "../dto/accessGroupResponse"
2
+ import { UserDto } from "../dto/userInfoResponse"
3
+ import { WorkspaceDto } from "../dto/workspacesResponse"
4
+ import { EventDispatcher } from "../events"
5
+ import { OrganizationId } from "./organizations"
6
+
7
+ export type GroupId = string
8
+
9
+ export enum GroupEvent {
10
+ ADDED = "added",
11
+ REMOVED = "removed",
12
+ UPDATED = "updated"
13
+ }
14
+
15
+ export abstract class Group extends EventDispatcher<GroupEvent, Group> {
16
+
17
+ abstract get id(): GroupId
18
+
19
+ abstract get group(): AccessGroupDto
20
+
21
+ abstract get members(): UserDto[]
22
+
23
+ abstract getWorkspaces() : Promise<WorkspaceDto[]>
24
+
25
+ abstract setWorkspaces(workspaces: string[]): Promise<void>
26
+
27
+ abstract setName(name: string): Promise<void>
28
+
29
+ abstract setPermits(permits: {isAdmin: boolean}): Promise<void>
30
+
31
+ abstract setMembersIds(members: string[]): Promise<void>
32
+ }
33
+
34
+
35
+ export abstract class Groups extends EventDispatcher<GroupEvent, Group>{
36
+
37
+ abstract create(name: string, organizationId: OrganizationId, permits: { isAdmin: boolean }, memberIds: string[]): Promise<Group>
38
+
39
+ abstract get(id: GroupId): Promise<Group | undefined>
40
+
41
+ abstract delete(id: GroupId): Promise<void>
42
+
43
+ }
@@ -0,0 +1,68 @@
1
+ import { OrganizationId } from "./organizations"
2
+ import { Disposable } from "../disposable"
3
+ import { OrganizationDto } from "../dto/userInfoResponse"
4
+ import { Workspaces } from "./workspaces"
5
+ import { WorkspacesImpl } from "./workspaces.impl"
6
+ import { Context } from "../context"
7
+ import { Organization } from "./organization"
8
+ import { GroupsImpl } from "./groups.impl"
9
+ import { Groups } from "./groups"
10
+
11
+ export class OrganizationImpl extends Organization implements Disposable {
12
+ private _isDisposed: boolean = false
13
+ private _isAdmin: boolean = false
14
+ private _content?: OrganizationDto
15
+ private readonly _workspaces: WorkspacesImpl
16
+ private readonly _accessGroups: GroupsImpl
17
+
18
+ constructor(private readonly context: Context) {
19
+ super()
20
+ this._workspaces = new WorkspacesImpl(this, this.context)
21
+ this._accessGroups = new GroupsImpl(this, this.context)
22
+ }
23
+
24
+ public async initFrom(
25
+ content: OrganizationDto,
26
+ isAdmin: boolean
27
+ ): Promise<OrganizationImpl> {
28
+ this._content = content
29
+ this._isAdmin = isAdmin
30
+
31
+ // init workspaces by organization id
32
+ await this._workspaces.initFrom(content.id)
33
+
34
+ return this
35
+ }
36
+
37
+ get isAdmin(): boolean {
38
+ return this._isAdmin
39
+ }
40
+
41
+ get isDisposed(): boolean {
42
+ return this._isDisposed
43
+ }
44
+
45
+ dispose(): void {
46
+ this._isDisposed = true
47
+ }
48
+
49
+ get id(): OrganizationId {
50
+ return <OrganizationId>this._content?.id
51
+ }
52
+
53
+ get name(): string {
54
+ return <OrganizationId>this._content?.profile.name
55
+ }
56
+
57
+ get description(): string {
58
+ return <OrganizationId>this._content?.profile.description
59
+ }
60
+
61
+ get workspaces(): Workspaces {
62
+ return this._workspaces
63
+ }
64
+
65
+ get accessGroups(): Groups {
66
+ return this._accessGroups
67
+ }
68
+ }
@@ -0,0 +1,33 @@
1
+ import { Workspaces } from "./workspaces"
2
+ import { OrganizationId } from "./organizations"
3
+ import { Groups } from "./groups"
4
+
5
+ /**
6
+ * Organization.
7
+ */
8
+ export abstract class Organization {
9
+ /**
10
+ * Organization id.
11
+ */
12
+ abstract get id(): OrganizationId
13
+
14
+ /**
15
+ * Organization name.
16
+ */
17
+ abstract get name(): string
18
+
19
+ /**
20
+ * Organization description.
21
+ */
22
+ abstract get description(): string
23
+
24
+ /**
25
+ * Workspaces.
26
+ */
27
+ abstract get workspaces(): Workspaces
28
+
29
+ /**
30
+ * Groups.
31
+ */
32
+ abstract get accessGroups(): Groups
33
+ }
@@ -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
+ }