@stamhoofd/backend 2.81.0 → 2.83.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.
- package/package.json +10 -10
- package/src/audit-logs/GroupLogger.ts +3 -3
- package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
- package/src/audit-logs/OrderLogger.ts +1 -1
- package/src/audit-logs/RegistrationLogger.ts +1 -1
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +4 -4
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +5 -5
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -8
- package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
- package/src/endpoints/auth/CreateTokenEndpoint.ts +10 -10
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -2
- package/src/endpoints/auth/PatchUserEndpoint.ts +9 -9
- package/src/endpoints/auth/SignupEndpoint.ts +2 -2
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +3 -3
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +30 -7
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +22 -16
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
- package/src/endpoints/global/files/UploadFile.ts +14 -2
- package/src/endpoints/global/files/UploadImage.ts +2 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +19 -19
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +34 -34
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +5 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +15 -17
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +4 -4
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +37 -37
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +5 -5
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +3 -3
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +11 -11
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +13 -13
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -16
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
- package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +25 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +2 -2
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +3 -3
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +10 -8
- package/src/excel-loaders/event-notifications.ts +11 -11
- package/src/excel-loaders/members.ts +34 -34
- package/src/excel-loaders/organizations.ts +23 -23
- package/src/excel-loaders/payments.ts +39 -39
- package/src/excel-loaders/receivable-balances.ts +21 -21
- package/src/helpers/AddressValidator.ts +6 -6
- package/src/helpers/AdminPermissionChecker.ts +7 -4
- package/src/helpers/AuthenticatedStructures.ts +16 -8
- package/src/helpers/BuckarooHelper.ts +1 -1
- package/src/helpers/CheckSettlements.ts +1 -1
- package/src/helpers/Context.ts +31 -15
- package/src/helpers/FileCache.ts +7 -7
- package/src/helpers/ForwardHandler.ts +1 -1
- package/src/helpers/GlobalHelper.ts +6 -4
- package/src/helpers/MembershipCharger.ts +2 -2
- package/src/helpers/SetupStepUpdater.ts +1 -1
- package/src/helpers/StripeHelper.ts +18 -7
- package/src/helpers/XlsxTransformerColumnHelper.ts +18 -18
- package/src/services/DocumentService.ts +1 -1
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/MemberNumberService.ts +3 -3
- package/src/services/SSOService.ts +5 -5
- package/src/sql-filters/members.ts +1 -1
- package/tests/e2e/api-rate-limits.test.ts +188 -0
- package/tests/e2e/private-files.test.ts +3 -3
- package/tests/helpers/StripeMocker.ts +7 -1
- /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { Email, Organization, OrganizationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, User, UserFactory } from "@stamhoofd/models";
|
|
3
|
+
import { EmailStatus, Email as EmailStruct, PermissionLevel, Permissions, Version } from "@stamhoofd/structures";
|
|
4
|
+
import { TestUtils } from "@stamhoofd/test-utils";
|
|
5
|
+
import { testServer } from "../../../../tests/helpers/TestServer";
|
|
6
|
+
import { PatchEmailEndpoint } from "./PatchEmailEndpoint";
|
|
7
|
+
|
|
8
|
+
const baseUrl = `/v${Version}/email`;
|
|
9
|
+
|
|
10
|
+
describe('Endpoint.PatchEmailEndpoint', () => {
|
|
11
|
+
const endpoint = new PatchEmailEndpoint();
|
|
12
|
+
let period: RegistrationPeriod;
|
|
13
|
+
let organization: Organization;
|
|
14
|
+
let token: Token;
|
|
15
|
+
let user: User;
|
|
16
|
+
|
|
17
|
+
const patchEmail = async (email: EmailStruct, token: Token, organization?: Organization) => {
|
|
18
|
+
const id = email.id;
|
|
19
|
+
const request = Request.buildJson('PATCH', `${baseUrl}/${id}`, organization?.getApiHost(), email);
|
|
20
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
21
|
+
return await testServer.test(endpoint, request);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
beforeAll(async () => {
|
|
29
|
+
period = await new RegistrationPeriodFactory({
|
|
30
|
+
startDate: new Date(2023, 0, 1),
|
|
31
|
+
endDate: new Date(2023, 11, 31),
|
|
32
|
+
}).create();
|
|
33
|
+
|
|
34
|
+
organization = await new OrganizationFactory({ period })
|
|
35
|
+
.create();
|
|
36
|
+
|
|
37
|
+
user = await new UserFactory({
|
|
38
|
+
organization,
|
|
39
|
+
permissions: Permissions.create({
|
|
40
|
+
level: PermissionLevel.Read,
|
|
41
|
+
}),
|
|
42
|
+
})
|
|
43
|
+
.create();
|
|
44
|
+
|
|
45
|
+
token = await Token.createToken(user);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('Should throw error if no unsubscribe button in email html', async () => {
|
|
49
|
+
const email = new Email();
|
|
50
|
+
email.subject = 'test subject';
|
|
51
|
+
email.status = EmailStatus.Draft;
|
|
52
|
+
email.text = 'test email {{unsubscribeUrl}}';
|
|
53
|
+
email.html = `<!DOCTYPE html>
|
|
54
|
+
<html>
|
|
55
|
+
|
|
56
|
+
<head>
|
|
57
|
+
<meta charset="utf-8" />
|
|
58
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
59
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
|
60
|
+
<title>test</title>
|
|
61
|
+
</head>
|
|
62
|
+
|
|
63
|
+
<body>
|
|
64
|
+
<p style="margin: 0; padding: 0; line-height: 1.4;">test email</p>
|
|
65
|
+
</body>
|
|
66
|
+
|
|
67
|
+
</html>`;
|
|
68
|
+
email.json = {
|
|
69
|
+
"content": [
|
|
70
|
+
{
|
|
71
|
+
"content": [
|
|
72
|
+
{
|
|
73
|
+
"text": "test email",
|
|
74
|
+
"type": "text"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"type": "paragraph"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"type": "doc"
|
|
81
|
+
};
|
|
82
|
+
email.userId = user.id;
|
|
83
|
+
email.organizationId = organization.id;
|
|
84
|
+
|
|
85
|
+
await email.save();
|
|
86
|
+
|
|
87
|
+
const body = EmailStruct.create({...email, fromAddress:'test@test.be', status: EmailStatus.Sending})
|
|
88
|
+
|
|
89
|
+
await expect(async () => await patchEmail(body, token, organization))
|
|
90
|
+
.rejects
|
|
91
|
+
.toThrow('Missing unsubscribe button');
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('Should throw error if no unsubscribe button in email text', async () => {
|
|
95
|
+
const email = new Email();
|
|
96
|
+
email.subject = 'test subject';
|
|
97
|
+
email.status = EmailStatus.Draft;
|
|
98
|
+
email.text = 'test email';
|
|
99
|
+
email.html = `<!DOCTYPE html>
|
|
100
|
+
<html>
|
|
101
|
+
|
|
102
|
+
<head>
|
|
103
|
+
<meta charset="utf-8" />
|
|
104
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
105
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
|
106
|
+
<title>test</title>
|
|
107
|
+
</head>
|
|
108
|
+
|
|
109
|
+
<body>
|
|
110
|
+
<p style="margin: 0; padding: 0; line-height: 1.4;">test email {{unsubscribeUrl}}</p>
|
|
111
|
+
</body>
|
|
112
|
+
|
|
113
|
+
</html>`;
|
|
114
|
+
email.json = {
|
|
115
|
+
"content": [
|
|
116
|
+
{
|
|
117
|
+
"content": [
|
|
118
|
+
{
|
|
119
|
+
"text": "test email",
|
|
120
|
+
"type": "text"
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"type": "paragraph"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"type": "doc"
|
|
127
|
+
};
|
|
128
|
+
email.userId = user.id;
|
|
129
|
+
email.organizationId = organization.id;
|
|
130
|
+
|
|
131
|
+
await email.save();
|
|
132
|
+
|
|
133
|
+
const body = EmailStruct.create({...email, fromAddress:'test@test.be', status: EmailStatus.Sending})
|
|
134
|
+
|
|
135
|
+
await expect(async () => await patchEmail(body, token, organization))
|
|
136
|
+
.rejects
|
|
137
|
+
.toThrow('Missing unsubscribe button');
|
|
138
|
+
})
|
|
139
|
+
})
|
|
@@ -2,19 +2,15 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
|
|
|
2
2
|
import { Email } from '@stamhoofd/models';
|
|
3
3
|
import { EmailPreview, EmailStatus, Email as EmailStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
|
+
import { AutoEncoderPatchType, Decoder, patchObject } from '@simonbackx/simple-encoding';
|
|
5
6
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
7
|
import { Context } from '../../../helpers/Context';
|
|
7
|
-
import { AutoEncoderPatchType, Decoder, patchObject } from '@simonbackx/simple-encoding';
|
|
8
8
|
|
|
9
9
|
type Params = { id: string };
|
|
10
10
|
type Query = undefined;
|
|
11
11
|
type Body = AutoEncoderPatchType<EmailStruct>;
|
|
12
12
|
type ResponseBody = EmailPreview;
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
14
|
export class PatchEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
19
15
|
bodyDecoder = EmailStruct.patchType() as Decoder<AutoEncoderPatchType<EmailStruct>>;
|
|
20
16
|
|
|
@@ -44,7 +40,7 @@ export class PatchEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
44
40
|
throw new SimpleError({
|
|
45
41
|
code: 'not_found',
|
|
46
42
|
human: 'Email not found',
|
|
47
|
-
message:
|
|
43
|
+
message: $t(`9ddb6616-f62d-4c91-82a9-e5cf398e4c4a`),
|
|
48
44
|
statusCode: 404,
|
|
49
45
|
});
|
|
50
46
|
}
|
|
@@ -53,7 +49,7 @@ export class PatchEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
53
49
|
throw new SimpleError({
|
|
54
50
|
code: 'not_draft',
|
|
55
51
|
human: 'Email is not a draft',
|
|
56
|
-
message:
|
|
52
|
+
message: $t(`298b5a46-2899-4aa1-89df-9b634c20806b`),
|
|
57
53
|
statusCode: 400,
|
|
58
54
|
});
|
|
59
55
|
}
|
|
@@ -107,6 +103,33 @@ export class PatchEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
107
103
|
|
|
108
104
|
if (request.body.status === EmailStatus.Sending || request.body.status === EmailStatus.Sent) {
|
|
109
105
|
model.throwIfNotReadyToSend();
|
|
106
|
+
|
|
107
|
+
const replacement = '{{unsubscribeUrl}}';
|
|
108
|
+
|
|
109
|
+
if (model.html) {
|
|
110
|
+
// Check email contains an unsubscribe button
|
|
111
|
+
if (!model.html.includes(replacement)) {
|
|
112
|
+
throw new SimpleError({
|
|
113
|
+
code: "missing_unsubscribe_button",
|
|
114
|
+
message: "Missing unsubscribe button",
|
|
115
|
+
human: $t(`dd55e04b-e5d9-4d9a-befc-443eef4175a8`),
|
|
116
|
+
field: "html"
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (model.text) {
|
|
122
|
+
// Check email contains an unsubscribe button
|
|
123
|
+
if (!model.text.includes(replacement)) {
|
|
124
|
+
throw new SimpleError({
|
|
125
|
+
code: "missing_unsubscribe_button",
|
|
126
|
+
message: "Missing unsubscribe button",
|
|
127
|
+
human: $t(`dd55e04b-e5d9-4d9a-befc-443eef4175a8`),
|
|
128
|
+
field: "text"
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
110
133
|
model.send().catch(console.error);
|
|
111
134
|
}
|
|
112
135
|
|
|
@@ -142,7 +142,7 @@ export class GetEventNotificationsEndpoint extends Endpoint<Params, Query, Body,
|
|
|
142
142
|
|
|
143
143
|
for (const notification of notifications) {
|
|
144
144
|
if (!await Context.auth.canAccessEventNotification(notification)) {
|
|
145
|
-
throw Context.auth.error(
|
|
145
|
+
throw Context.auth.error($t(`f18f19ca-c56b-49ad-b131-244cbebb6b1f`));
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -1,36 +1,17 @@
|
|
|
1
1
|
import { PatchableArray, PatchMap, patchObject } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import { EventNotificationFactory, EventFactory, EventNotificationTypeFactory, Organization, OrganizationFactory, Token, User, UserFactory, EmailTemplateFactory, RecordCategoryFactory, RegistrationPeriodFactory, RecordAnswerFactory, EventNotification } from '@stamhoofd/models';
|
|
4
|
-
import { AccessRight, BaseOrganization, EmailTemplateType, Event, EventNotificationStatus, EventNotification as EventNotificationStruct, PermissionLevel, Permissions, PermissionsResourceType, RecordAnswer, RecordType, ResourcePermissions } from '@stamhoofd/structures';
|
|
5
|
-
import { TestUtils } from '@stamhoofd/test-utils';
|
|
6
|
-
import { PatchEventNotificationsEndpoint } from './PatchEventNotificationsEndpoint';
|
|
7
|
-
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
8
3
|
import { EmailMocker } from '@stamhoofd/email';
|
|
4
|
+
import { EmailTemplateFactory, EventFactory, EventNotification, EventNotificationFactory, EventNotificationTypeFactory, Organization, OrganizationFactory, RecordAnswerFactory, RecordCategoryFactory, RegistrationPeriodFactory, Token, User, UserFactory } from '@stamhoofd/models';
|
|
5
|
+
import { AccessRight, BaseOrganization, EmailTemplateType, Event, EventNotificationStatus, EventNotification as EventNotificationStruct, Permissions, PermissionsResourceType, RecordType, ResourcePermissions } from '@stamhoofd/structures';
|
|
6
|
+
import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
7
|
+
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
8
|
+
import { PatchEventNotificationsEndpoint } from './PatchEventNotificationsEndpoint';
|
|
9
9
|
|
|
10
10
|
const baseUrl = `/event-notifications`;
|
|
11
11
|
const endpoint = new PatchEventNotificationsEndpoint();
|
|
12
12
|
type EndpointType = typeof endpoint;
|
|
13
13
|
type Body = EndpointType extends Endpoint<any, any, infer B, any> ? B : never;
|
|
14
14
|
|
|
15
|
-
const errorWithCode = (code: string) => expect.objectContaining({ code }) as jest.Constructable;
|
|
16
|
-
const errorWithMessage = (message: string) => expect.objectContaining({ message }) as jest.Constructable;
|
|
17
|
-
const simpleError = (data: {
|
|
18
|
-
code?: string;
|
|
19
|
-
message?: string;
|
|
20
|
-
field?: string;
|
|
21
|
-
}) => {
|
|
22
|
-
const d = {
|
|
23
|
-
code: data.code ?? expect.any(String),
|
|
24
|
-
message: data.message ?? expect.any(String),
|
|
25
|
-
field: data.field ?? expect.anything(),
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
if (!data.field) {
|
|
29
|
-
delete d.field;
|
|
30
|
-
}
|
|
31
|
-
return expect.objectContaining(d) as jest.Constructable;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
15
|
const minimumUserPermissions = Permissions.create({
|
|
35
16
|
resources: new Map([
|
|
36
17
|
[PermissionsResourceType.Groups, new Map([
|
|
@@ -169,7 +150,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
169
150
|
);
|
|
170
151
|
|
|
171
152
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
172
|
-
simpleError({ code: 'invalid_field', field: 'typeId' }),
|
|
153
|
+
SHExpect.simpleError({ code: 'invalid_field', field: 'typeId' }),
|
|
173
154
|
);
|
|
174
155
|
});
|
|
175
156
|
|
|
@@ -194,7 +175,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
194
175
|
);
|
|
195
176
|
|
|
196
177
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
197
|
-
simpleError({ code: 'invalid_field', field: 'events' }),
|
|
178
|
+
SHExpect.simpleError({ code: 'invalid_field', field: 'events' }),
|
|
198
179
|
);
|
|
199
180
|
});
|
|
200
181
|
|
|
@@ -228,7 +209,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
228
209
|
}),
|
|
229
210
|
);
|
|
230
211
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
231
|
-
simpleError({ code: 'invalid_period', field: 'startDate' }),
|
|
212
|
+
SHExpect.simpleError({ code: 'invalid_period', field: 'startDate' }),
|
|
232
213
|
);
|
|
233
214
|
});
|
|
234
215
|
|
|
@@ -256,7 +237,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
256
237
|
);
|
|
257
238
|
|
|
258
239
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
259
|
-
simpleError({ code: 'invalid_field', field: 'events' }),
|
|
240
|
+
SHExpect.simpleError({ code: 'invalid_field', field: 'events' }),
|
|
260
241
|
);
|
|
261
242
|
});
|
|
262
243
|
|
|
@@ -416,7 +397,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
416
397
|
);
|
|
417
398
|
|
|
418
399
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
419
|
-
simpleError({ code: 'permission_denied' }),
|
|
400
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
420
401
|
);
|
|
421
402
|
});
|
|
422
403
|
|
|
@@ -442,7 +423,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
442
423
|
);
|
|
443
424
|
|
|
444
425
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
445
|
-
simpleError({ code: 'permission_denied' }),
|
|
426
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
446
427
|
);
|
|
447
428
|
});
|
|
448
429
|
|
|
@@ -468,7 +449,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
468
449
|
);
|
|
469
450
|
|
|
470
451
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
471
|
-
simpleError({ code: 'permission_denied' }),
|
|
452
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
472
453
|
);
|
|
473
454
|
});
|
|
474
455
|
|
|
@@ -494,7 +475,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
494
475
|
);
|
|
495
476
|
|
|
496
477
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
497
|
-
simpleError({ code: 'permission_denied' }),
|
|
478
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
498
479
|
);
|
|
499
480
|
});
|
|
500
481
|
|
|
@@ -520,7 +501,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
520
501
|
);
|
|
521
502
|
|
|
522
503
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
523
|
-
simpleError({ code: 'permission_denied' }),
|
|
504
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
524
505
|
);
|
|
525
506
|
});
|
|
526
507
|
|
|
@@ -603,7 +584,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
603
584
|
);
|
|
604
585
|
|
|
605
586
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
606
|
-
simpleError({ code: 'permission_denied' }),
|
|
587
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
607
588
|
);
|
|
608
589
|
});
|
|
609
590
|
|
|
@@ -991,7 +972,7 @@ describe('Endpoint.PatchEventNotificationsEndpoint', () => {
|
|
|
991
972
|
);
|
|
992
973
|
|
|
993
974
|
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(
|
|
994
|
-
simpleError({ code: 'permission_denied' }),
|
|
975
|
+
SHExpect.simpleError({ code: 'permission_denied' }),
|
|
995
976
|
);
|
|
996
977
|
});
|
|
997
978
|
});
|
|
@@ -69,7 +69,7 @@ export class PatchEventNotificationsEndpoint extends Endpoint<Params, Query, Bod
|
|
|
69
69
|
throw new SimpleError({
|
|
70
70
|
code: 'invalid_field',
|
|
71
71
|
message: 'Invalid event',
|
|
72
|
-
human:
|
|
72
|
+
human: $t(`4275fbbe-9921-454e-852c-9c53a9803f1f`),
|
|
73
73
|
field: 'events',
|
|
74
74
|
});
|
|
75
75
|
}
|
|
@@ -63,7 +63,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
63
63
|
throw new SimpleError({
|
|
64
64
|
code: 'invalid_field',
|
|
65
65
|
message: 'Empty groups',
|
|
66
|
-
human:
|
|
66
|
+
human: $t(`93faf169-b78d-4ad2-b13b-3b974267a632`),
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -71,7 +71,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
71
71
|
throw new SimpleError({
|
|
72
72
|
code: 'invalid_field',
|
|
73
73
|
message: 'Empty default age groups',
|
|
74
|
-
human:
|
|
74
|
+
human: $t(`2712befc-5cc5-4013-b8df-ec0861a82c36`),
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -79,7 +79,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
79
79
|
throw new SimpleError({
|
|
80
80
|
code: 'invalid_field',
|
|
81
81
|
message: 'Empty organization tag ids',
|
|
82
|
-
human:
|
|
82
|
+
human: $t(`30230574-2956-4e40-ba11-5523c24c0af8`),
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -136,7 +136,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
136
136
|
throw new SimpleError({
|
|
137
137
|
code: 'not_found',
|
|
138
138
|
message: 'Event not found',
|
|
139
|
-
human:
|
|
139
|
+
human: $t(`c5f3d2c3-9d7a-473d-ba91-63ce104a2de5`),
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -146,7 +146,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
146
146
|
throw new SimpleError({
|
|
147
147
|
code: 'invalid_field',
|
|
148
148
|
message: 'Cannot patch organizationCache',
|
|
149
|
-
human:
|
|
149
|
+
human: $t(`74e6eba6-596c-4e55-b178-b2a5fdbca581`),
|
|
150
150
|
field: 'meta.organizationCache',
|
|
151
151
|
});
|
|
152
152
|
}
|
|
@@ -166,7 +166,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
166
166
|
throw new SimpleError({
|
|
167
167
|
code: 'invalid_field',
|
|
168
168
|
message: 'Empty groups',
|
|
169
|
-
human:
|
|
169
|
+
human: $t(`93faf169-b78d-4ad2-b13b-3b974267a632`),
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -174,7 +174,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
174
174
|
throw new SimpleError({
|
|
175
175
|
code: 'invalid_field',
|
|
176
176
|
message: 'Empty default age groups',
|
|
177
|
-
human:
|
|
177
|
+
human: $t(`2712befc-5cc5-4013-b8df-ec0861a82c36`),
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -182,7 +182,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
182
182
|
throw new SimpleError({
|
|
183
183
|
code: 'invalid_field',
|
|
184
184
|
message: 'Empty organization tag ids',
|
|
185
|
-
human:
|
|
185
|
+
human: $t(`30230574-2956-4e40-ba11-5523c24c0af8`),
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -320,7 +320,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
320
320
|
throw new SimpleError({
|
|
321
321
|
code: 'invalid_field',
|
|
322
322
|
message: 'Invalid typeId',
|
|
323
|
-
human:
|
|
323
|
+
human: $t(`6b36fc82-d88c-49cc-ae94-4653ad37b3e3`),
|
|
324
324
|
field: 'typeId',
|
|
325
325
|
});
|
|
326
326
|
}
|
|
@@ -334,7 +334,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
334
334
|
throw new SimpleError({
|
|
335
335
|
code: 'invalid_field',
|
|
336
336
|
message: 'Name is too short',
|
|
337
|
-
human:
|
|
337
|
+
human: $t(`53a66432-0bf5-4193-9eb8-dbd52d86a1f8`),
|
|
338
338
|
field: 'name',
|
|
339
339
|
});
|
|
340
340
|
}
|
|
@@ -343,7 +343,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
343
343
|
throw new SimpleError({
|
|
344
344
|
code: 'invalid_dates',
|
|
345
345
|
message: 'End date is before start date',
|
|
346
|
-
human:
|
|
346
|
+
human: $t(`318924c0-7a79-4cfa-b206-ffc27c4d32b7`),
|
|
347
347
|
field: 'endDate',
|
|
348
348
|
});
|
|
349
349
|
}
|
|
@@ -360,7 +360,10 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
360
360
|
throw new SimpleError({
|
|
361
361
|
code: 'minimum_days',
|
|
362
362
|
message: 'An event with this type has a minimum of ' + type.minimumDays + ' days',
|
|
363
|
-
human:
|
|
363
|
+
human: $t(`04ff85c0-eb98-46b8-975b-8fd136ddc49a`, {
|
|
364
|
+
name: type.name,
|
|
365
|
+
days: Formatter.pluralText(type.minimumDays, $t(`a6279389-a070-49c9-a085-bb312555e419`), $t(`fca0ce20-d696-4966-a50c-441f54f046c4`)),
|
|
366
|
+
}),
|
|
364
367
|
field: 'startDate',
|
|
365
368
|
});
|
|
366
369
|
}
|
|
@@ -369,7 +372,10 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
369
372
|
throw new SimpleError({
|
|
370
373
|
code: 'maximum_days',
|
|
371
374
|
message: 'An event with this type has a maximum of ' + type.maximumDays + ' days',
|
|
372
|
-
human:
|
|
375
|
+
human: $t(`a7d005aa-ceaa-4323-8fac-a02fce174023`, {
|
|
376
|
+
name: type.name,
|
|
377
|
+
days: Formatter.pluralText(type.maximumDays, $t(`a6279389-a070-49c9-a085-bb312555e419`), $t(`fca0ce20-d696-4966-a50c-441f54f046c4`)),
|
|
378
|
+
}),
|
|
373
379
|
field: 'startDate',
|
|
374
380
|
});
|
|
375
381
|
}
|
|
@@ -393,7 +399,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
393
399
|
throw new SimpleError({
|
|
394
400
|
code: 'type_maximum_reached',
|
|
395
401
|
message: 'Maximum number of events with this type reached',
|
|
396
|
-
human:
|
|
402
|
+
human: $t(`fb7df531-0f89-4841-b685-7b2cfb5b507d`) + ' ' + type.name + ' ' + $t(`073d20dc-88f3-4145-89f5-13cc8ad90207`) + type.maximum + ')',
|
|
397
403
|
field: 'typeId',
|
|
398
404
|
});
|
|
399
405
|
}
|
|
@@ -402,7 +408,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
402
408
|
throw new SimpleError({
|
|
403
409
|
code: 'invalid_period',
|
|
404
410
|
message: 'No period found for this start date',
|
|
405
|
-
human:
|
|
411
|
+
human: $t(`7a38bf9d-4df7-4827-85dc-327ffe6cd50a`),
|
|
406
412
|
field: 'startDate',
|
|
407
413
|
});
|
|
408
414
|
}
|
|
@@ -420,7 +426,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
420
426
|
throw new SimpleError({
|
|
421
427
|
code: 'invalid_field',
|
|
422
428
|
message: 'Empty number',
|
|
423
|
-
human:
|
|
429
|
+
human: $t(`6b72f8bd-cd5b-423f-a556-be102d3c22e9`),
|
|
424
430
|
field: 'event_required',
|
|
425
431
|
});
|
|
426
432
|
}
|
|
@@ -65,7 +65,7 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
65
65
|
throw new SimpleError({
|
|
66
66
|
code: 'not_allowed',
|
|
67
67
|
message: 'Export is pending',
|
|
68
|
-
human:
|
|
68
|
+
human: $t(`a77e6624-bd1f-4374-af5a-d25cc60ee4da`),
|
|
69
69
|
statusCode: 403,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
@@ -58,7 +58,7 @@ export class UploadFile extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
58
58
|
await Context.setOptionalOrganizationScope();
|
|
59
59
|
const { user } = await Context.authenticate();
|
|
60
60
|
|
|
61
|
-
if (!Context.auth.canUpload()) {
|
|
61
|
+
if (!Context.auth.canUpload({ private: request.query.isPrivate })) {
|
|
62
62
|
throw Context.auth.error();
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -126,6 +126,18 @@ export class UploadFile extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
126
126
|
prefix += '/';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
prefix += (STAMHOOFD.environment ?? 'development') === 'development' ? ('development/') : ('');
|
|
130
|
+
|
|
131
|
+
// Prepend user id to the file path
|
|
132
|
+
if (request.query.isPrivate && user) {
|
|
133
|
+
// Private files
|
|
134
|
+
prefix += 'users/' + user.id + '/';
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Public files
|
|
138
|
+
prefix += 'p/';
|
|
139
|
+
}
|
|
140
|
+
|
|
129
141
|
// Also include the source, in private mode
|
|
130
142
|
const fileId = uuidv4();
|
|
131
143
|
let uploadExt = '';
|
|
@@ -162,7 +174,7 @@ export class UploadFile extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
const filenameWithoutExt = file.originalFilename?.split('.').slice(0, -1).join('.') ?? fileId;
|
|
165
|
-
const key = prefix +
|
|
177
|
+
const key = prefix + fileId + '/' + (Formatter.slug(filenameWithoutExt) + (uploadExt ? ('.' + uploadExt) : ''));
|
|
166
178
|
const params = {
|
|
167
179
|
Bucket: STAMHOOFD.SPACES_BUCKET,
|
|
168
180
|
Key: key,
|
|
@@ -64,7 +64,7 @@ export class UploadImage extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
64
64
|
await Context.setOptionalOrganizationScope();
|
|
65
65
|
const { user } = await Context.authenticate();
|
|
66
66
|
|
|
67
|
-
if (!Context.auth.canUpload()) {
|
|
67
|
+
if (!Context.auth.canUpload({ private: request.query.isPrivate })) {
|
|
68
68
|
throw Context.auth.error();
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -131,7 +131,7 @@ export class UploadImage extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
const fileContent = await fs.readFile(file.filepath);
|
|
134
|
-
const image = await Image.create(fileContent, file.mimetype ?? undefined, resolutions, request.query.isPrivate);
|
|
134
|
+
const image = await Image.create(fileContent, file.mimetype ?? undefined, resolutions, request.query.isPrivate, user);
|
|
135
135
|
return new Response(ImageStruct.create(image));
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -55,7 +55,7 @@ export class GetMemberFamilyEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
55
55
|
|
|
56
56
|
// Check access to this member (this will automatically give access to the family)
|
|
57
57
|
if (!await Context.auth.canAccessMember(member)) {
|
|
58
|
-
throw Context.auth.error(
|
|
58
|
+
throw Context.auth.error($t(`c9797487-3e24-4686-9bdc-2a7ec9cee8ed`));
|
|
59
59
|
}
|
|
60
60
|
validatedMembers.push(member);
|
|
61
61
|
continue;
|
|
@@ -67,7 +67,7 @@ export class GetMemberFamilyEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (!foundMember) {
|
|
70
|
-
throw Context.auth.error(
|
|
70
|
+
throw Context.auth.error($t(`c9797487-3e24-4686-9bdc-2a7ec9cee8ed`));
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
return new Response(
|
|
@@ -245,7 +245,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
245
245
|
throw new SimpleError({
|
|
246
246
|
code: 'timeout',
|
|
247
247
|
message: 'Query took too long',
|
|
248
|
-
human:
|
|
248
|
+
human: $t(`dce51638-6129-448b-8a15-e6d778f3a76a`),
|
|
249
249
|
});
|
|
250
250
|
}
|
|
251
251
|
throw error;
|