@stamhoofd/backend 2.80.1 → 2.82.0

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 (32) hide show
  1. package/index.ts +1 -4
  2. package/package.json +10 -10
  3. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
  4. package/src/endpoints/global/email/PatchEmailEndpoint.ts +28 -5
  5. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
  6. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +8 -8
  7. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
  8. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
  9. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +5 -7
  10. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  11. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +1 -1
  12. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  13. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +1 -1
  14. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
  15. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
  16. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
  17. package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +24 -5
  18. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
  19. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
  20. package/src/excel-loaders/event-notifications.ts +133 -0
  21. package/src/excel-loaders/index.ts +5 -0
  22. package/src/excel-loaders/members.ts +15 -38
  23. package/src/excel-loaders/organizations.ts +2 -2
  24. package/src/excel-loaders/payments.ts +2 -2
  25. package/src/helpers/AuthenticatedStructures.ts +8 -0
  26. package/src/helpers/CheckSettlements.ts +1 -1
  27. package/src/helpers/Context.ts +28 -12
  28. package/src/helpers/StripeHelper.ts +12 -1
  29. package/src/helpers/{xlsxAddressTransformerColumnFactory.ts → XlsxTransformerColumnHelper.ts} +68 -2
  30. package/tests/e2e/api-rate-limits.test.ts +188 -0
  31. package/tests/helpers/StripeMocker.ts +7 -1
  32. /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
@@ -0,0 +1,106 @@
1
+ import { Request } from '@simonbackx/simple-endpoints';
2
+ import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
+
4
+ import { testServer } from '../../../../../tests/helpers/TestServer';
5
+ import { PatchApiUserEndpoint } from './PatchApiUserEndpoint';
6
+ import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
7
+ import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
8
+ import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
9
+
10
+ describe('Endpoint.CreateApiUserEndpoint', () => {
11
+ // Test endpoint
12
+ const endpoint = new CreateApiUserEndpoint();
13
+
14
+ beforeEach(() => {
15
+ TestUtils.setEnvironment('userMode', 'platform');
16
+ });
17
+
18
+ test('Only a platform admin can set the rate limits of a key', async () => {
19
+ const organization = await new OrganizationFactory({}).create();
20
+ const user = await new UserFactory({
21
+ globalPermissions: Permissions.create({
22
+ level: PermissionLevel.Full,
23
+ }),
24
+ }).create();
25
+ const token = await Token.createToken(user);
26
+
27
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
28
+ permissions: UserPermissions.create({
29
+ organizationPermissions: new Map([
30
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
31
+ ]),
32
+ }),
33
+ meta: UserMeta.create({
34
+ rateLimits: ApiUserRateLimits.High,
35
+ }),
36
+ }));
37
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
38
+ const response = await testServer.test(endpoint, createRequest);
39
+
40
+ expect(response.body).toBeDefined();
41
+ expect(response.body.meta?.rateLimits).toEqual(ApiUserRateLimits.High);
42
+ });
43
+
44
+ test('An organization admin cannot set rate limits', async () => {
45
+ const organization = await new OrganizationFactory({}).create();
46
+ const user = await new UserFactory({
47
+ permissions: Permissions.create({
48
+ level: PermissionLevel.Full,
49
+ }),
50
+ organization,
51
+ }).create();
52
+ const token = await Token.createToken(user);
53
+
54
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
55
+ permissions: UserPermissions.create({
56
+ organizationPermissions: new Map([
57
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
58
+ ]),
59
+ }),
60
+ meta: UserMeta.create({
61
+ rateLimits: ApiUserRateLimits.High,
62
+ }),
63
+ }));
64
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
65
+
66
+ await expect(testServer.test(endpoint, createRequest)).rejects.toThrow(SHExpect.simpleError({
67
+ code: 'permission_denied',
68
+ }));
69
+ });
70
+
71
+ test('An API-key cannot have permissions outside its organization', async () => {
72
+ const organization = await new OrganizationFactory({}).create();
73
+ const user = await new UserFactory({
74
+ permissions: Permissions.create({
75
+ level: PermissionLevel.Full,
76
+ }),
77
+ organization,
78
+ }).create();
79
+ const token = await Token.createToken(user);
80
+
81
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
82
+ permissions: UserPermissions.create({
83
+ organizationPermissions: new Map([
84
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
85
+ ['other', Permissions.create({ level: PermissionLevel.Full })],
86
+ ]),
87
+ globalPermissions: Permissions.create({
88
+ level: PermissionLevel.Full,
89
+ }),
90
+ }),
91
+ }));
92
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
93
+
94
+ const response = await testServer.test(endpoint, createRequest);
95
+
96
+ expect(response.body).toBeDefined();
97
+ expect(response.body.permissions).toEqual(
98
+ UserPermissions.create({
99
+ organizationPermissions: new Map([
100
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
101
+ ]),
102
+ globalPermissions: null,
103
+ }),
104
+ );
105
+ });
106
+ });
@@ -2,15 +2,15 @@ import { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Token, User } from '@stamhoofd/models';
5
- import { ApiUser, ApiUserWithToken, UserPermissions } from '@stamhoofd/structures';
5
+ import { ApiUser, ApiUserWithToken, UserMeta, UserPermissions } from '@stamhoofd/structures';
6
6
 
7
7
  import { Context } from '../../../../helpers/Context';
8
8
  type Params = Record<string, never>;
9
9
  type Query = undefined;
10
10
  type Body = ApiUser;
11
- type ResponseBody = ApiUser;
11
+ type ResponseBody = ApiUserWithToken;
12
12
 
13
- export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
+ export class CreateApiUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
14
14
  bodyDecoder = ApiUser as Decoder<ApiUser>;
15
15
 
16
16
  protected doesMatch(request: Request): [true, Params] | [false] {
@@ -56,6 +56,19 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
56
56
  }
57
57
 
58
58
  admin.permissions = UserPermissions.limitedAdd(null, request.body.permissions, organization.id);
59
+
60
+ if (request.body.meta) {
61
+ const rateLimits = request.body.meta.rateLimits;
62
+ if (rateLimits) {
63
+ if (!Context.auth.hasPlatformFullAccess()) {
64
+ throw Context.auth.error($t('Je hebt geen rechten om de rate limits van API-keys te wijzigen'));
65
+ }
66
+
67
+ admin.meta = admin.meta ?? UserMeta.create({});
68
+ admin.meta.rateLimits = rateLimits;
69
+ }
70
+ }
71
+
59
72
  await admin.save();
60
73
 
61
74
  // Set id
@@ -0,0 +1,247 @@
1
+ import { Request } from '@simonbackx/simple-endpoints';
2
+ import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
+
4
+ import { testServer } from '../../../../../tests/helpers/TestServer';
5
+ import { PatchApiUserEndpoint } from './PatchApiUserEndpoint';
6
+ import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
7
+ import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, PermissionsResourceType, ResourcePermissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
8
+ import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
9
+ import { PatchMap } from '@simonbackx/simple-encoding';
10
+
11
+ describe('Endpoint.PatchApiUserEndpoint', () => {
12
+ // Test endpoint
13
+ const createEndpoint = new CreateApiUserEndpoint();
14
+ const endpoint = new PatchApiUserEndpoint();
15
+
16
+ beforeEach(() => {
17
+ TestUtils.setEnvironment('userMode', 'platform');
18
+ });
19
+
20
+ test('Only a platform admin can alter the rate limits of a key', async () => {
21
+ const organization = await new OrganizationFactory({}).create();
22
+ const user = await new UserFactory({
23
+ globalPermissions: Permissions.create({
24
+ level: PermissionLevel.Full,
25
+ }),
26
+ }).create();
27
+ const token = await Token.createToken(user);
28
+
29
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
30
+ permissions: UserPermissions.create({
31
+ organizationPermissions: new Map([
32
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
33
+ ]),
34
+ }),
35
+ }));
36
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
37
+ const createResponse = await testServer.test(createEndpoint, createRequest);
38
+
39
+ const apiUserId = createResponse.body.id;
40
+
41
+ const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
42
+ id: apiUserId,
43
+ meta: UserMeta.patch({
44
+ rateLimits: ApiUserRateLimits.High,
45
+ }),
46
+ }));
47
+ r.headers.authorization = 'Bearer ' + token.accessToken;
48
+
49
+ const response = await testServer.test(endpoint, r);
50
+ expect(response.body).toBeDefined();
51
+ expect(response.body.id).toEqual(apiUserId);
52
+ expect(response.body.meta?.rateLimits).toEqual(ApiUserRateLimits.High);
53
+ });
54
+
55
+ test('An organization admin cannot alter rate limits', async () => {
56
+ const organization = await new OrganizationFactory({}).create();
57
+ const user = await new UserFactory({
58
+ permissions: Permissions.create({
59
+ level: PermissionLevel.Full,
60
+ }),
61
+ organization,
62
+ }).create();
63
+ const token = await Token.createToken(user);
64
+
65
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
66
+ permissions: UserPermissions.create({
67
+ organizationPermissions: new Map([
68
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
69
+ ]),
70
+ }),
71
+ }));
72
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
73
+ const createResponse = await testServer.test(createEndpoint, createRequest);
74
+
75
+ const apiUserId = createResponse.body.id;
76
+
77
+ const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
78
+ id: apiUserId,
79
+ meta: UserMeta.patch({
80
+ rateLimits: ApiUserRateLimits.High,
81
+ }),
82
+ }));
83
+ r.headers.authorization = 'Bearer ' + token.accessToken;
84
+
85
+ await expect(testServer.test(endpoint, r)).rejects.toThrow(SHExpect.simpleError({
86
+ code: 'permission_denied',
87
+ }));
88
+ });
89
+
90
+ test('You cannot grant an API-key permissions outside its organization using a put', async () => {
91
+ const organization = await new OrganizationFactory({}).create();
92
+ const user = await new UserFactory({
93
+ permissions: Permissions.create({
94
+ level: PermissionLevel.Full,
95
+ }),
96
+ organization,
97
+ }).create();
98
+ const token = await Token.createToken(user);
99
+
100
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
101
+ permissions: UserPermissions.create({
102
+ organizationPermissions: new Map([
103
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
104
+ ]),
105
+ }),
106
+ }));
107
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
108
+ const createResponse = await testServer.test(createEndpoint, createRequest);
109
+ const apiUserId = createResponse.body.id;
110
+
111
+ const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
112
+ id: apiUserId,
113
+ permissions: UserPermissions.create({
114
+ organizationPermissions: new Map([
115
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
116
+ ['other', Permissions.create({ level: PermissionLevel.Full })],
117
+ ]),
118
+ globalPermissions: Permissions.create({
119
+ level: PermissionLevel.Full,
120
+ }),
121
+ }),
122
+ }));
123
+ r.headers.authorization = 'Bearer ' + token.accessToken;
124
+
125
+ const response = await testServer.test(endpoint, r);
126
+ expect(response.body).toBeDefined();
127
+ expect(response.body.id).toEqual(apiUserId);
128
+ expect(response.body.permissions).toEqual(
129
+ UserPermissions.create({
130
+ organizationPermissions: new Map([
131
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
132
+ ]),
133
+ globalPermissions: null,
134
+ }),
135
+ );
136
+ });
137
+
138
+ test('You cannot grant an API-key permissions outside its organization using a patch', async () => {
139
+ const organization = await new OrganizationFactory({}).create();
140
+ const user = await new UserFactory({
141
+ permissions: Permissions.create({
142
+ level: PermissionLevel.Full,
143
+ }),
144
+ organization,
145
+ }).create();
146
+ const token = await Token.createToken(user);
147
+
148
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
149
+ permissions: UserPermissions.create({
150
+ organizationPermissions: new Map([
151
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
152
+ ]),
153
+ }),
154
+ }));
155
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
156
+ const createResponse = await testServer.test(createEndpoint, createRequest);
157
+ const apiUserId = createResponse.body.id;
158
+
159
+ const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
160
+ id: apiUserId,
161
+ permissions: UserPermissions.patch({
162
+ organizationPermissions: new PatchMap([
163
+ ['other', Permissions.create({ level: PermissionLevel.Full })],
164
+ ]),
165
+ globalPermissions: Permissions.create({
166
+ level: PermissionLevel.Full,
167
+ }),
168
+ }),
169
+ }));
170
+ r.headers.authorization = 'Bearer ' + token.accessToken;
171
+
172
+ const response = await testServer.test(endpoint, r);
173
+ expect(response.body).toBeDefined();
174
+ expect(response.body.id).toEqual(apiUserId);
175
+ expect(response.body.permissions).toEqual(
176
+ UserPermissions.create({
177
+ organizationPermissions: new Map([
178
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
179
+ ]),
180
+ globalPermissions: null,
181
+ }),
182
+ );
183
+ });
184
+
185
+ test('You can grant an API-key permissions using a patch', async () => {
186
+ const organization = await new OrganizationFactory({}).create();
187
+ const user = await new UserFactory({
188
+ permissions: Permissions.create({
189
+ level: PermissionLevel.Full,
190
+ }),
191
+ organization,
192
+ }).create();
193
+ const token = await Token.createToken(user);
194
+
195
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
196
+ permissions: UserPermissions.create({
197
+ organizationPermissions: new Map([
198
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
199
+ ]),
200
+ }),
201
+ }));
202
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
203
+ const createResponse = await testServer.test(createEndpoint, createRequest);
204
+ const apiUserId = createResponse.body.id;
205
+
206
+ const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
207
+ id: apiUserId,
208
+ permissions: UserPermissions.patch({
209
+ organizationPermissions: new PatchMap([
210
+ [organization.id, Permissions.patch({
211
+ resources: new PatchMap([[
212
+ PermissionsResourceType.Groups,
213
+ new Map([
214
+ ['group-id', ResourcePermissions.create({
215
+ level: PermissionLevel.Full,
216
+ })],
217
+ ]),
218
+ ]]),
219
+ })],
220
+ ]),
221
+ }),
222
+ }));
223
+ r.headers.authorization = 'Bearer ' + token.accessToken;
224
+
225
+ const response = await testServer.test(endpoint, r);
226
+ expect(response.body).toBeDefined();
227
+ expect(response.body.id).toEqual(apiUserId);
228
+ expect(response.body.permissions).toEqual(
229
+ UserPermissions.create({
230
+ organizationPermissions: new Map([
231
+ [organization.id, Permissions.create({
232
+ level: PermissionLevel.Read,
233
+ resources: new Map([[
234
+ PermissionsResourceType.Groups,
235
+ new Map([
236
+ ['group-id', ResourcePermissions.create({
237
+ level: PermissionLevel.Full,
238
+ })],
239
+ ]),
240
+ ]]),
241
+ })],
242
+ ]),
243
+ globalPermissions: null,
244
+ }),
245
+ );
246
+ });
247
+ });
@@ -1,17 +1,16 @@
1
- import { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
1
+ import { AutoEncoderPatchType, Decoder, isPatch } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Token, User } from '@stamhoofd/models';
5
- import { ApiUser, PermissionLevel, UserPermissions } from '@stamhoofd/structures';
6
-
7
- import { Context } from '../../helpers/Context';
5
+ import { ApiUser, PermissionLevel, UserMeta, UserPermissions } from '@stamhoofd/structures';
6
+ import { Context } from '../../../../helpers/Context';
8
7
 
9
8
  type Params = { id: string };
10
9
  type Query = undefined;
11
10
  type Body = AutoEncoderPatchType<ApiUser>;
12
11
  type ResponseBody = ApiUser;
13
12
 
14
- export class PatchUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
+ export class PatchApiUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
15
14
  bodyDecoder = ApiUser.patchType() as Decoder<AutoEncoderPatchType<ApiUser>>;
16
15
 
17
16
  protected doesMatch(request: Request): [true, Params] | [false] {
@@ -85,6 +84,26 @@ export class PatchUserEndpoint extends Endpoint<Params, Query, Body, ResponseBod
85
84
  }
86
85
  }
87
86
 
87
+ if (request.body.meta) {
88
+ if (!isPatch(request.body.meta)) {
89
+ throw new SimpleError({
90
+ code: 'invalid_request',
91
+ message: 'Invalid request: meta is not a patch',
92
+ statusCode: 400,
93
+ });
94
+ }
95
+
96
+ const rateLimits = request.body.meta.rateLimits;
97
+ if (rateLimits && rateLimits !== editUser.meta?.rateLimits) {
98
+ if (!Context.auth.hasPlatformFullAccess()) {
99
+ throw Context.auth.error($t('Je hebt geen rechten om de rate limits van API-keys te wijzigen'));
100
+ }
101
+
102
+ editUser.meta = editUser.meta ?? UserMeta.create({});
103
+ editUser.meta.rateLimits = rateLimits;
104
+ }
105
+ }
106
+
88
107
  await editUser.save();
89
108
 
90
109
  return new Response(await Token.getAPIUserWithToken(editUser));
@@ -2,6 +2,7 @@ import { Request } from '@simonbackx/simple-endpoints';
2
2
  import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
3
  import { Organization, PermissionLevel, Permissions } from '@stamhoofd/structures';
4
4
 
5
+ import { TestUtils } from '@stamhoofd/test-utils';
5
6
  import { testServer } from '../../../../../tests/helpers/TestServer';
6
7
  import { GetOrganizationEndpoint } from './GetOrganizationEndpoint';
7
8
 
@@ -9,6 +10,10 @@ describe('Endpoint.GetOrganization', () => {
9
10
  // Test endpoint
10
11
  const endpoint = new GetOrganizationEndpoint();
11
12
 
13
+ beforeEach(async () => {
14
+ TestUtils.setEnvironment('userMode', 'platform');
15
+ });
16
+
12
17
  test('Get organization as signed in user', async () => {
13
18
  const organization = await new OrganizationFactory({}).create();
14
19
  const user = await new UserFactory({ organization }).create();
@@ -1,7 +1,8 @@
1
1
  import { Request } from '@simonbackx/simple-endpoints';
2
2
  import { OrganizationFactory, Token, UserFactory, WebshopFactory } from '@stamhoofd/models';
3
- import { PermissionLevel, Permissions, PrivateWebshop, Webshop as WebshopStruct } from '@stamhoofd/structures';
3
+ import { PermissionLevel, Permissions } from '@stamhoofd/structures';
4
4
 
5
+ import { TestUtils } from '@stamhoofd/test-utils';
5
6
  import { testServer } from '../../../../tests/helpers/TestServer';
6
7
  import { GetWebshopEndpoint } from './GetWebshopEndpoint';
7
8
 
@@ -9,6 +10,10 @@ describe('Endpoint.GetWebshop', () => {
9
10
  // Test endpoint
10
11
  const endpoint = new GetWebshopEndpoint();
11
12
 
13
+ beforeEach(async () => {
14
+ TestUtils.setEnvironment('userMode', 'platform');
15
+ });
16
+
12
17
  test('Get webshop as signed in user', async () => {
13
18
  const organization = await new OrganizationFactory({}).create();
14
19
  const user = await new UserFactory({ organization }).create();
@@ -0,0 +1,133 @@
1
+ import { XlsxBuiltInNumberFormat, XlsxTransformerSheet } from '@stamhoofd/excel-writer';
2
+ import { EventNotification, EventNotificationStatus, EventNotificationStatusHelper, ExcelExportType, LimitedFilteredRequest, Platform as PlatformStruct } from '@stamhoofd/structures';
3
+ import { Formatter } from '@stamhoofd/utility';
4
+ import { GetEventNotificationsEndpoint } from '../endpoints/global/events/GetEventNotificationsEndpoint';
5
+ import { ExportToExcelEndpoint } from '../endpoints/global/files/ExportToExcelEndpoint';
6
+ import { XlsxTransformerColumnHelper } from '../helpers/XlsxTransformerColumnHelper';
7
+
8
+ // Assign to a typed variable to assure we have correct type checking in place
9
+ const sheet: XlsxTransformerSheet<EventNotification, EventNotification> = {
10
+ id: 'event-notifications',
11
+ name: 'Meldingen',
12
+ columns: [
13
+ {
14
+ id: 'id',
15
+ name: 'ID',
16
+ width: 40,
17
+ getValue: (notification: EventNotification) => ({
18
+ value: notification.id,
19
+ }),
20
+ },
21
+ {
22
+ id: 'name',
23
+ name: 'Naam activiteit',
24
+ width: 40,
25
+ getValue: (notification: EventNotification) => ({
26
+ value: notification.events.map(e => e.name).join(', '),
27
+ }),
28
+ },
29
+ {
30
+ id: 'organization.name',
31
+ name: 'Groep',
32
+ width: 40,
33
+ getValue: (notification: EventNotification) => ({
34
+ value: notification.organization.name,
35
+ }),
36
+ },
37
+ {
38
+ id: 'organization.uri',
39
+ name: 'Groepsnummer',
40
+ width: 30,
41
+ getValue: (notification: EventNotification) => ({
42
+ value: notification.organization.uri,
43
+ }),
44
+ },
45
+ {
46
+ id: 'status',
47
+ name: 'Status',
48
+ width: 30,
49
+ getValue: (notification: EventNotification) => ({
50
+ value: Formatter.capitalizeFirstLetter(EventNotificationStatusHelper.getName(notification.status)),
51
+ }),
52
+ },
53
+ {
54
+ id: 'feedbackText',
55
+ name: 'Opmerkingen',
56
+ width: 80,
57
+ getValue: (notification: EventNotification) => ({
58
+ value: notification.status !== EventNotificationStatus.Accepted ? notification.feedbackText : null,
59
+ style: {
60
+ alignment: {
61
+ wrapText: true,
62
+ },
63
+ },
64
+ }),
65
+ },
66
+ {
67
+ id: 'startDate',
68
+ name: 'Startdatum',
69
+ width: 20,
70
+ getValue: (notification: EventNotification) => ({
71
+ value: notification.startDate,
72
+ style: {
73
+ numberFormat: {
74
+ id: XlsxBuiltInNumberFormat.DateSlash,
75
+ },
76
+ },
77
+ }),
78
+ },
79
+ {
80
+ id: 'endDate',
81
+ name: 'Einddatum',
82
+ width: 20,
83
+ getValue: (notification: EventNotification) => ({
84
+ value: notification.endDate,
85
+ style: {
86
+ numberFormat: {
87
+ id: XlsxBuiltInNumberFormat.DateSlash,
88
+ },
89
+ },
90
+ }),
91
+ },
92
+ {
93
+ id: 'submittedAt',
94
+ name: 'Ingediend op',
95
+ width: 20,
96
+ getValue: (notification: EventNotification) => ({
97
+ value: notification.submittedAt,
98
+ style: {
99
+ numberFormat: {
100
+ id: XlsxBuiltInNumberFormat.DateSlash,
101
+ },
102
+ },
103
+ }),
104
+ },
105
+ {
106
+ id: 'submittedBy',
107
+ name: 'Ingediend door',
108
+ width: 40,
109
+ getValue: (notification: EventNotification) => ({
110
+ value: notification.submittedBy?.name ?? '',
111
+ }),
112
+ },
113
+
114
+ // Dynamic records
115
+ XlsxTransformerColumnHelper.createRecordAnswersColumns({
116
+ matchId: 'recordAnswers',
117
+ getRecordAnswers: (notification: EventNotification) => notification.recordAnswers,
118
+ getRecordCategories: () => {
119
+ const platform = PlatformStruct.shared;
120
+ return platform.config.eventNotificationTypes.flatMap(r => r.recordCategories);
121
+ },
122
+ }),
123
+ ],
124
+ };
125
+
126
+ ExportToExcelEndpoint.loaders.set(ExcelExportType.EventNotifications, {
127
+ fetch: async (query: LimitedFilteredRequest) => {
128
+ return await GetEventNotificationsEndpoint.buildData(query);
129
+ },
130
+ sheets: [
131
+ sheet,
132
+ ],
133
+ });
@@ -0,0 +1,5 @@
1
+ import './members';
2
+ import './payments';
3
+ import './organizations';
4
+ import './receivable-balances';
5
+ import './event-notifications';