@stamhoofd/backend 2.83.5 → 2.84.1
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/index.ts +19 -4
- package/package.json +18 -14
- package/src/crons/amazon-ses.ts +26 -5
- package/src/crons/balance-emails.ts +18 -17
- package/src/email-recipient-loaders/registrations.ts +87 -0
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +5 -2
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +40 -40
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +28 -22
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +81 -49
- package/src/endpoints/global/files/UploadFile.ts +11 -16
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +234 -0
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +117 -43
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +1054 -0
- package/src/endpoints/global/members/GetMembersEndpoint.ts +163 -141
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +6 -6
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +0 -16
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +73 -0
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +1 -2
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +43 -0
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1016 -0
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +234 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +5 -5
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +474 -554
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +191 -52
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +107 -9
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +89 -0
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +9 -6
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +88 -0
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +0 -6
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -6
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +10 -25
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +0 -5
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +0 -5
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +4 -0
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -0
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +44 -19
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +140 -25
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +40 -10
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +4 -1
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +2 -2
- package/src/excel-loaders/members.ts +233 -232
- package/src/excel-loaders/payments.ts +1 -1
- package/src/excel-loaders/receivable-balances.ts +1 -1
- package/src/excel-loaders/registrations.ts +153 -0
- package/src/helpers/AdminPermissionChecker.ts +65 -37
- package/src/helpers/AuthenticatedStructures.ts +43 -3
- package/src/helpers/Context.ts +29 -1
- package/src/helpers/GlobalHelper.ts +3 -1
- package/src/helpers/GroupedThrottledQueue.test.ts +219 -0
- package/src/helpers/GroupedThrottledQueue.ts +108 -0
- package/src/helpers/LimitedFilteredRequestHelper.ts +26 -1
- package/src/helpers/MemberCharger.ts +0 -5
- package/src/helpers/MembershipCharger.ts +3 -9
- package/src/helpers/OrganizationCharger.ts +0 -5
- package/src/helpers/ThrottledQueue.test.ts +194 -0
- package/src/helpers/ThrottledQueue.ts +145 -0
- package/src/helpers/XlsxTransformerColumnHelper.ts +44 -1
- package/src/middleware/ContextMiddleware.ts +1 -1
- package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +2 -1
- package/src/seeds/1735577912-update-cached-outstanding-balance-from-items.ts +2 -1
- package/src/services/BalanceItemPaymentService.ts +1 -33
- package/src/services/BalanceItemService.ts +167 -48
- package/src/services/FileSignService.ts +18 -13
- package/src/services/MemberRecordStore.ts +28 -19
- package/src/services/PaymentReallocationService.test.ts +25 -14
- package/src/services/PaymentReallocationService.ts +29 -10
- package/src/services/PaymentService.ts +4 -16
- package/src/services/PlatformMembershipService.ts +8 -4
- package/src/services/RegistrationService.ts +66 -2
- package/src/sql-filters/base-registration-filter-compilers.ts +43 -0
- package/src/sql-filters/groups.ts +67 -0
- package/src/sql-filters/members.ts +33 -58
- package/src/sql-filters/organization-registration-periods.ts +8 -0
- package/src/sql-filters/registration-periods.ts +8 -0
- package/src/sql-filters/registrations.ts +11 -22
- package/src/sql-sorters/groups.ts +24 -0
- package/src/sql-sorters/organization-registration-periods.ts +24 -0
- package/src/sql-sorters/registration-periods.ts +47 -0
- package/src/sql-sorters/registrations.ts +77 -0
- package/tests/actions/patchOrganizationMember.ts +27 -0
- package/tests/actions/patchPaymentStatus.ts +45 -0
- package/tests/actions/patchUserMember.ts +27 -0
- package/tests/assertions/assertBalances.ts +49 -0
- package/tests/e2e/api-rate-limits.test.ts +5 -5
- package/tests/e2e/bundle-discounts.test.ts +4060 -0
- package/tests/e2e/charge-members.test.ts +27 -24
- package/tests/e2e/documents.test.ts +398 -0
- package/tests/e2e/register.test.ts +292 -312
- package/tests/helpers/PayconiqMocker.ts +55 -0
- package/tests/init/index.ts +5 -0
- package/tests/init/initAdmin.ts +14 -0
- package/tests/init/initBundleDiscount.ts +47 -0
- package/tests/init/initPayconiq.ts +9 -0
- package/tests/init/initPlatformAdmin.ts +13 -0
- package/tests/init/initStripe.ts +21 -0
- package/tests/jest.setup.ts +29 -0
- package/src/seeds-temporary/1736266448-recall-balance-item-price-paid.ts +0 -70
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { GroupFactory, Organization, OrganizationFactory, OrganizationRegistrationPeriodFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { BundleDiscountGroupPriceSettings, GroupPrice, GroupType, LimitedFilteredRequest, StamhoofdFilter } from '@stamhoofd/structures';
|
|
4
|
+
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
5
|
+
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
6
|
+
import { initAdmin, initBundleDiscount, initPlatformAdmin } from '../../../../tests/init';
|
|
7
|
+
import { GetGroupsEndpoint } from './GetGroupsEndpoint';
|
|
8
|
+
import { Database } from '@simonbackx/simple-database';
|
|
9
|
+
|
|
10
|
+
const baseUrl = `/groups`;
|
|
11
|
+
|
|
12
|
+
describe('Endpoint.GetGroupsEndpoint', () => {
|
|
13
|
+
const endpoint = new GetGroupsEndpoint();
|
|
14
|
+
|
|
15
|
+
let registrationPeriod1: RegistrationPeriod;
|
|
16
|
+
let registrationPeriod2: RegistrationPeriod;
|
|
17
|
+
|
|
18
|
+
const get = async ({ organization, token, filter }: { organization?: Organization; token: Token; filter?: StamhoofdFilter }) => {
|
|
19
|
+
const request = Request.get({
|
|
20
|
+
path: baseUrl,
|
|
21
|
+
host: organization?.getApiHost() ?? '',
|
|
22
|
+
query: new LimitedFilteredRequest({
|
|
23
|
+
limit: 100,
|
|
24
|
+
filter,
|
|
25
|
+
}),
|
|
26
|
+
headers: {
|
|
27
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return (await testServer.test(endpoint, request)).body.results;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
beforeAll(async () => {
|
|
34
|
+
TestUtils.setPermanentEnvironment('userMode', 'platform');
|
|
35
|
+
|
|
36
|
+
registrationPeriod2 = await new RegistrationPeriodFactory({
|
|
37
|
+
startDate: new Date(2022, 0, 1),
|
|
38
|
+
endDate: new Date(2022, 11, 31),
|
|
39
|
+
}).create();
|
|
40
|
+
|
|
41
|
+
registrationPeriod1 = await new RegistrationPeriodFactory({
|
|
42
|
+
startDate: new Date(2023, 0, 1),
|
|
43
|
+
endDate: new Date(2023, 11, 31),
|
|
44
|
+
previousPeriodId: registrationPeriod2.id,
|
|
45
|
+
}).create();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(async () => {
|
|
49
|
+
await Database.delete('DELETE FROM `groups`');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const initOrganization = async (registrationPeriod: RegistrationPeriod) => {
|
|
53
|
+
const organization = await new OrganizationFactory({ period: registrationPeriod })
|
|
54
|
+
.create();
|
|
55
|
+
|
|
56
|
+
const organizationRegistrationPeriod = await new OrganizationRegistrationPeriodFactory({
|
|
57
|
+
organization,
|
|
58
|
+
period: registrationPeriod1,
|
|
59
|
+
}).create();
|
|
60
|
+
|
|
61
|
+
return { organization, organizationRegistrationPeriod };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
async function initTest() {
|
|
65
|
+
const { organization, organizationRegistrationPeriod } = await initOrganization(registrationPeriod1);
|
|
66
|
+
const { organization: otherOrganization } = await initOrganization(registrationPeriod1);
|
|
67
|
+
|
|
68
|
+
const user = await new UserFactory({ organization }).create();
|
|
69
|
+
const token = await Token.createToken(user);
|
|
70
|
+
|
|
71
|
+
const waitingList = await new GroupFactory({
|
|
72
|
+
organization,
|
|
73
|
+
type: GroupType.WaitingList,
|
|
74
|
+
}).create();
|
|
75
|
+
|
|
76
|
+
const event = await new GroupFactory({
|
|
77
|
+
organization,
|
|
78
|
+
type: GroupType.EventRegistration,
|
|
79
|
+
}).create();
|
|
80
|
+
|
|
81
|
+
const group = await new GroupFactory({
|
|
82
|
+
organization,
|
|
83
|
+
type: GroupType.Membership,
|
|
84
|
+
}).create();
|
|
85
|
+
|
|
86
|
+
// Create a group that should not be returned
|
|
87
|
+
const otherGroup = await new GroupFactory({
|
|
88
|
+
organization: otherOrganization,
|
|
89
|
+
type: GroupType.Membership,
|
|
90
|
+
}).create();
|
|
91
|
+
|
|
92
|
+
return { organizationRegistrationPeriod, organization, token, waitingList, event, group, otherGroup };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
it('Can return all groups for an organization', async () => {
|
|
96
|
+
const { organization, token, waitingList, event, group } = await initTest();
|
|
97
|
+
const result = await get({ organization, token });
|
|
98
|
+
expect(result).toHaveLength(3);
|
|
99
|
+
expect(result).toIncludeSameMembers([
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
id: waitingList.id,
|
|
102
|
+
}),
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
id: event.id,
|
|
105
|
+
}),
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
id: group.id,
|
|
108
|
+
}),
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('Can return all groups to a platform admin', async () => {
|
|
113
|
+
const { waitingList, event, group, otherGroup } = await initTest();
|
|
114
|
+
const { adminToken } = await initPlatformAdmin();
|
|
115
|
+
|
|
116
|
+
const result = await get({ token: adminToken });
|
|
117
|
+
expect(result).toHaveLength(4);
|
|
118
|
+
expect(result).toIncludeSameMembers([
|
|
119
|
+
expect.objectContaining({
|
|
120
|
+
id: waitingList.id,
|
|
121
|
+
}),
|
|
122
|
+
expect.objectContaining({
|
|
123
|
+
id: event.id,
|
|
124
|
+
}),
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
id: group.id,
|
|
127
|
+
}),
|
|
128
|
+
expect.objectContaining({
|
|
129
|
+
id: otherGroup.id,
|
|
130
|
+
}),
|
|
131
|
+
]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('Cannot return all groups to a normal admin', async () => {
|
|
135
|
+
const { organization } = await initTest();
|
|
136
|
+
const { adminToken } = await initAdmin({ organization });
|
|
137
|
+
await expect(get({ token: adminToken })).rejects.toThrow(STExpect.simpleError({ code: 'permission_denied' }));
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('Can return a group by ID for an organization', async () => {
|
|
141
|
+
const { organization, token, group } = await initTest();
|
|
142
|
+
const result = await get({
|
|
143
|
+
organization,
|
|
144
|
+
token,
|
|
145
|
+
filter: {
|
|
146
|
+
id: group.id,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
expect(result).toIncludeSameMembers([
|
|
150
|
+
expect.objectContaining({
|
|
151
|
+
id: group.id,
|
|
152
|
+
}),
|
|
153
|
+
]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('Can return a group by bundle discount id', async () => {
|
|
157
|
+
const { organizationRegistrationPeriod, organization, token, group, event } = await initTest();
|
|
158
|
+
const bundleDiscount = await initBundleDiscount({ organizationRegistrationPeriod, discount: {} });
|
|
159
|
+
|
|
160
|
+
// Add a price to event with a bundle discount
|
|
161
|
+
const eventGroupPriceAdded = GroupPrice.create({
|
|
162
|
+
bundleDiscounts: new Map([
|
|
163
|
+
[bundleDiscount.id, BundleDiscountGroupPriceSettings.create({ name: bundleDiscount.name })],
|
|
164
|
+
]),
|
|
165
|
+
});
|
|
166
|
+
event.settings.prices.push(eventGroupPriceAdded);
|
|
167
|
+
await event.save();
|
|
168
|
+
|
|
169
|
+
// Set the main group price to the group
|
|
170
|
+
group.settings.prices[0].bundleDiscounts.set(bundleDiscount.id, BundleDiscountGroupPriceSettings.create({
|
|
171
|
+
name: bundleDiscount.name,
|
|
172
|
+
}));
|
|
173
|
+
await group.save();
|
|
174
|
+
|
|
175
|
+
const result = await get({
|
|
176
|
+
organization,
|
|
177
|
+
token,
|
|
178
|
+
filter: {
|
|
179
|
+
bundleDiscounts: {
|
|
180
|
+
[bundleDiscount.id]: {
|
|
181
|
+
$neq: null,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
expect(result).toIncludeSameMembers([
|
|
187
|
+
expect.objectContaining({
|
|
188
|
+
id: event.id,
|
|
189
|
+
}),
|
|
190
|
+
expect.objectContaining({
|
|
191
|
+
id: group.id,
|
|
192
|
+
}),
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('Safely escapes ids', async () => {
|
|
197
|
+
const { organizationRegistrationPeriod, organization, token, group, event } = await initTest();
|
|
198
|
+
const bundleDiscount = await initBundleDiscount({ organizationRegistrationPeriod, discount: {} });
|
|
199
|
+
const id = `This Id. contains "[10] and .**' inside of it... \\"\\'"'`;
|
|
200
|
+
const differentId = `This Id. contains "[10] and .**' inside of it... "\\'"'`;
|
|
201
|
+
|
|
202
|
+
// Add a price to event with a bundle discount
|
|
203
|
+
const eventGroupPriceAdded = GroupPrice.create({
|
|
204
|
+
bundleDiscounts: new Map([
|
|
205
|
+
[id, BundleDiscountGroupPriceSettings.create({ name: bundleDiscount.name })],
|
|
206
|
+
]),
|
|
207
|
+
});
|
|
208
|
+
event.settings.prices.push(eventGroupPriceAdded);
|
|
209
|
+
await event.save();
|
|
210
|
+
|
|
211
|
+
// Set the main group price to the group
|
|
212
|
+
group.settings.prices[0].bundleDiscounts.set(differentId, BundleDiscountGroupPriceSettings.create({
|
|
213
|
+
name: bundleDiscount.name,
|
|
214
|
+
}));
|
|
215
|
+
await group.save();
|
|
216
|
+
|
|
217
|
+
const result = await get({
|
|
218
|
+
organization,
|
|
219
|
+
token,
|
|
220
|
+
filter: {
|
|
221
|
+
bundleDiscounts: {
|
|
222
|
+
[id]: {
|
|
223
|
+
$neq: null,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
expect(result).toIncludeSameMembers([
|
|
229
|
+
expect.objectContaining({
|
|
230
|
+
id: event.id,
|
|
231
|
+
}),
|
|
232
|
+
]);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -1,34 +1,25 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import { Group,
|
|
3
|
-
import { GroupsWithOrganizations } from '@stamhoofd/structures';
|
|
2
|
+
import { assertSort, CountFilteredRequest, getSortFilter, Group as GroupStruct, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter } from '@stamhoofd/structures';
|
|
4
3
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { Decoder } from '@simonbackx/simple-encoding';
|
|
5
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
|
+
import { Group } from '@stamhoofd/models';
|
|
7
|
+
import { applySQLSorter, compileToModernSQLFilter, SQLModernFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
9
9
|
import { Context } from '../../../helpers/Context';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Query extends AutoEncoder {
|
|
14
|
-
@field({ decoder: new StringArrayDecoder(StringDecoder) })
|
|
15
|
-
ids: string[];
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* List of organizations the requester already knows and doesn't need to be included in the response
|
|
19
|
-
*/
|
|
20
|
-
@field({ decoder: new StringArrayDecoder(StringDecoder), optional: true })
|
|
21
|
-
excludeOrganizationIds: string[] = [];
|
|
22
|
-
}
|
|
10
|
+
import { groupFilterCompilers } from '../../../sql-filters/groups';
|
|
11
|
+
import { groupSorters } from '../../../sql-sorters/groups';
|
|
23
12
|
|
|
13
|
+
type Params = Record<string, never>;
|
|
14
|
+
type Query = LimitedFilteredRequest;
|
|
24
15
|
type Body = undefined;
|
|
25
|
-
type ResponseBody =
|
|
16
|
+
type ResponseBody = PaginatedResponse<GroupStruct[], LimitedFilteredRequest>;
|
|
17
|
+
|
|
18
|
+
const filterCompilers: SQLModernFilterDefinitions = groupFilterCompilers;
|
|
19
|
+
const sorters: SQLSortDefinitions<Group> = groupSorters;
|
|
26
20
|
|
|
27
|
-
/**
|
|
28
|
-
* Get the members of the user
|
|
29
|
-
*/
|
|
30
21
|
export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
31
|
-
queryDecoder =
|
|
22
|
+
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
|
|
32
23
|
|
|
33
24
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
34
25
|
if (request.method !== 'GET') {
|
|
@@ -40,40 +31,123 @@ export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
40
31
|
if (params) {
|
|
41
32
|
return [true, params as Params];
|
|
42
33
|
}
|
|
43
|
-
|
|
44
34
|
return [false];
|
|
45
35
|
}
|
|
46
36
|
|
|
37
|
+
static async buildQuery(q: CountFilteredRequest | LimitedFilteredRequest) {
|
|
38
|
+
const organization = Context.organization;
|
|
39
|
+
let scopeFilter: StamhoofdFilter | undefined = undefined;
|
|
40
|
+
|
|
41
|
+
if (organization) {
|
|
42
|
+
scopeFilter = {
|
|
43
|
+
organizationId: organization.id,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const query = Group.select();
|
|
48
|
+
|
|
49
|
+
if (scopeFilter) {
|
|
50
|
+
query.where(await compileToModernSQLFilter(scopeFilter, filterCompilers));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (q.filter) {
|
|
54
|
+
query.where(await compileToModernSQLFilter(q.filter, filterCompilers));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (q.search) {
|
|
58
|
+
let searchFilter: StamhoofdFilter | null = null;
|
|
59
|
+
|
|
60
|
+
searchFilter = {
|
|
61
|
+
id: q.search,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (searchFilter) {
|
|
65
|
+
query.where(await compileToModernSQLFilter(searchFilter, filterCompilers));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (q instanceof LimitedFilteredRequest) {
|
|
70
|
+
if (q.pageFilter) {
|
|
71
|
+
query.where(await compileToModernSQLFilter(q.pageFilter, filterCompilers));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
75
|
+
applySQLSorter(query, q.sort, sorters);
|
|
76
|
+
query.limit(q.limit);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return query;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static async buildData(requestQuery: LimitedFilteredRequest) {
|
|
83
|
+
const query = await GetGroupsEndpoint.buildQuery(requestQuery);
|
|
84
|
+
const groups = await query.fetch();
|
|
85
|
+
|
|
86
|
+
let next: LimitedFilteredRequest | undefined;
|
|
87
|
+
|
|
88
|
+
if (groups.length >= requestQuery.limit) {
|
|
89
|
+
const lastObject = groups[groups.length - 1];
|
|
90
|
+
const nextFilter = getSortFilter(lastObject, sorters, requestQuery.sort);
|
|
91
|
+
|
|
92
|
+
next = new LimitedFilteredRequest({
|
|
93
|
+
filter: requestQuery.filter,
|
|
94
|
+
pageFilter: nextFilter,
|
|
95
|
+
sort: requestQuery.sort,
|
|
96
|
+
limit: requestQuery.limit,
|
|
97
|
+
search: requestQuery.search,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (JSON.stringify(nextFilter) === JSON.stringify(requestQuery.pageFilter)) {
|
|
101
|
+
console.error('Found infinite loading loop for', requestQuery);
|
|
102
|
+
next = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return new PaginatedResponse<GroupStruct[], LimitedFilteredRequest>({
|
|
107
|
+
results: await AuthenticatedStructures.groups(groups),
|
|
108
|
+
next,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
47
112
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
48
|
-
|
|
113
|
+
if (request.request.getVersion() < 373) {
|
|
114
|
+
throw new SimpleError({
|
|
115
|
+
code: 'client_update_required',
|
|
116
|
+
statusCode: 400,
|
|
117
|
+
message: 'Er is een noodzakelijke update beschikbaar. Herlaad de pagina en wis indien nodig de cache van jouw browser.',
|
|
118
|
+
human: $t(`adb0e7c8-aed7-43f5-bfcc-a350f03aaabe`),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
49
123
|
await Context.optionalAuthenticate();
|
|
50
124
|
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
organizations: [],
|
|
56
|
-
}),
|
|
57
|
-
);
|
|
125
|
+
if (!organization) {
|
|
126
|
+
if (!Context.auth.hasSomePlatformAccess()) {
|
|
127
|
+
throw Context.auth.error();
|
|
128
|
+
}
|
|
58
129
|
}
|
|
59
130
|
|
|
60
|
-
|
|
131
|
+
const maxLimit = Context.auth.hasSomePlatformAccess() ? 1000 : 100;
|
|
132
|
+
|
|
133
|
+
if (request.query.limit > maxLimit) {
|
|
61
134
|
throw new SimpleError({
|
|
62
|
-
code: '
|
|
63
|
-
|
|
135
|
+
code: 'invalid_field',
|
|
136
|
+
field: 'limit',
|
|
137
|
+
message: 'Limit can not be more than ' + maxLimit,
|
|
64
138
|
});
|
|
65
139
|
}
|
|
66
140
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
141
|
+
if (request.query.limit < 1) {
|
|
142
|
+
throw new SimpleError({
|
|
143
|
+
code: 'invalid_field',
|
|
144
|
+
field: 'limit',
|
|
145
|
+
message: 'Limit can not be less than 1',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
71
148
|
|
|
72
149
|
return new Response(
|
|
73
|
-
|
|
74
|
-
groups: await AuthenticatedStructures.groups(groups),
|
|
75
|
-
organizations: await AuthenticatedStructures.organizations(organizations),
|
|
76
|
-
}),
|
|
150
|
+
await GetGroupsEndpoint.buildData(request.query),
|
|
77
151
|
);
|
|
78
152
|
}
|
|
79
153
|
}
|