@stamhoofd/backend 2.43.2 → 2.43.3
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/endpoints/global/events/PatchEventsEndpoint.ts +10 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +57 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +123 -13
- package/src/helpers/AuthenticatedStructures.ts +25 -2
- package/src/helpers/LimitedFilteredRequestHelper.ts +24 -0
- package/src/sql-filters/orders.ts +27 -0
- package/src/sql-sorters/orders.ts +58 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.43.
|
|
3
|
+
"version": "2.43.3",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"@simonbackx/simple-encoding": "2.15.1",
|
|
37
37
|
"@simonbackx/simple-endpoints": "1.14.0",
|
|
38
38
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
39
|
-
"@stamhoofd/backend-i18n": "2.43.
|
|
40
|
-
"@stamhoofd/backend-middleware": "2.43.
|
|
41
|
-
"@stamhoofd/email": "2.43.
|
|
42
|
-
"@stamhoofd/models": "2.43.
|
|
43
|
-
"@stamhoofd/queues": "2.43.
|
|
44
|
-
"@stamhoofd/sql": "2.43.
|
|
45
|
-
"@stamhoofd/structures": "2.43.
|
|
46
|
-
"@stamhoofd/utility": "2.43.
|
|
39
|
+
"@stamhoofd/backend-i18n": "2.43.3",
|
|
40
|
+
"@stamhoofd/backend-middleware": "2.43.3",
|
|
41
|
+
"@stamhoofd/email": "2.43.3",
|
|
42
|
+
"@stamhoofd/models": "2.43.3",
|
|
43
|
+
"@stamhoofd/queues": "2.43.3",
|
|
44
|
+
"@stamhoofd/sql": "2.43.3",
|
|
45
|
+
"@stamhoofd/structures": "2.43.3",
|
|
46
|
+
"@stamhoofd/utility": "2.43.3",
|
|
47
47
|
"archiver": "^7.0.1",
|
|
48
48
|
"aws-sdk": "^2.885.0",
|
|
49
49
|
"axios": "1.6.8",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"postmark": "^4.0.5",
|
|
61
61
|
"stripe": "^16.6.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "1258338deacc6d61a53da939dda894dca3415c84"
|
|
64
64
|
}
|
|
@@ -53,6 +53,11 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
53
53
|
event.organizationId = put.organizationId;
|
|
54
54
|
event.meta = put.meta;
|
|
55
55
|
|
|
56
|
+
if (event.organizationId === null && event.meta.groups !== null) {
|
|
57
|
+
event.meta.groups = null;
|
|
58
|
+
console.error('Removed groups because organizationId is null for new event');
|
|
59
|
+
}
|
|
60
|
+
|
|
56
61
|
if (event.meta.groups && event.meta.groups.length === 0) {
|
|
57
62
|
throw new SimpleError({
|
|
58
63
|
code: 'invalid_field',
|
|
@@ -131,6 +136,11 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
131
136
|
event.organizationId = patch.organizationId;
|
|
132
137
|
}
|
|
133
138
|
|
|
139
|
+
if (event.organizationId === null && event.meta.groups !== null) {
|
|
140
|
+
event.meta.groups = null;
|
|
141
|
+
console.error('Removed groups because organizationId is null for event', event.id);
|
|
142
|
+
}
|
|
143
|
+
|
|
134
144
|
if (event.meta.groups && event.meta.groups.length === 0) {
|
|
135
145
|
throw new SimpleError({
|
|
136
146
|
code: 'invalid_field',
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { CountFilteredRequest, CountResponse, PermissionLevel } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { Webshop } from '@stamhoofd/models';
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
import { GetWebshopOrdersEndpoint } from './GetWebshopOrdersEndpoint';
|
|
8
|
+
|
|
9
|
+
type Params = { id: string };
|
|
10
|
+
type Query = CountFilteredRequest;
|
|
11
|
+
type Body = undefined;
|
|
12
|
+
type ResponseBody = CountResponse;
|
|
13
|
+
|
|
14
|
+
export class GetWebshopOrdersCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
|
+
queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
|
|
16
|
+
|
|
17
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
18
|
+
if (request.method !== 'GET') {
|
|
19
|
+
return [false];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const params = Endpoint.parseParameters(request.url, '/webshop/@id/orders/count', { id: String });
|
|
23
|
+
|
|
24
|
+
if (params) {
|
|
25
|
+
return [true, params as Params];
|
|
26
|
+
}
|
|
27
|
+
return [false];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
31
|
+
const organization = await Context.setOrganizationScope();
|
|
32
|
+
await Context.authenticate();
|
|
33
|
+
|
|
34
|
+
// Fast throw first (more in depth checking for patches later)
|
|
35
|
+
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
36
|
+
throw Context.auth.error();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const webshopId = request.params.id;
|
|
40
|
+
|
|
41
|
+
const webshop = await Webshop.getByID(webshopId);
|
|
42
|
+
if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Read)) {
|
|
43
|
+
throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang tot de bestellingen van deze webshop');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const query = GetWebshopOrdersEndpoint.buildQuery(webshopId, request.query);
|
|
47
|
+
|
|
48
|
+
const count = await query
|
|
49
|
+
.count();
|
|
50
|
+
|
|
51
|
+
return new Response(
|
|
52
|
+
CountResponse.create({
|
|
53
|
+
count,
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import {
|
|
4
|
-
import { PaginatedResponse, PermissionLevel, PrivateOrder, WebshopOrdersQuery } from '@stamhoofd/structures';
|
|
3
|
+
import { assertSort, CountFilteredRequest, getSortFilter, LimitedFilteredRequest, PaginatedResponse, PermissionLevel, PrivateOrder, StamhoofdFilter } from '@stamhoofd/structures';
|
|
5
4
|
|
|
5
|
+
import { Order, Webshop } from '@stamhoofd/models';
|
|
6
|
+
import { compileToSQLFilter, compileToSQLSorter, SQL, SQLFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
7
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
6
8
|
import { Context } from '../../../../helpers/Context';
|
|
9
|
+
import { LimitedFilteredRequestHelper } from '../../../../helpers/LimitedFilteredRequestHelper';
|
|
10
|
+
import { orderFilterCompilers } from '../../../../sql-filters/orders';
|
|
11
|
+
import { orderSorters } from '../../../../sql-sorters/orders';
|
|
7
12
|
|
|
8
13
|
type Params = { id: string };
|
|
9
|
-
type Query =
|
|
14
|
+
type Query = LimitedFilteredRequest;
|
|
10
15
|
type Body = undefined;
|
|
11
|
-
type ResponseBody = PaginatedResponse<PrivateOrder[],
|
|
16
|
+
type ResponseBody = PaginatedResponse<PrivateOrder[], LimitedFilteredRequest>;
|
|
17
|
+
|
|
18
|
+
const filterCompilers: SQLFilterDefinitions = orderFilterCompilers;
|
|
19
|
+
const sorters: SQLSortDefinitions<Order> = orderSorters;
|
|
12
20
|
|
|
13
21
|
export class GetWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
-
queryDecoder =
|
|
22
|
+
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
|
|
15
23
|
|
|
16
24
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
25
|
if (request.method !== 'GET') {
|
|
@@ -26,22 +34,124 @@ export class GetWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
26
34
|
return [false];
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
static buildQuery(webshopId: string, q: CountFilteredRequest | LimitedFilteredRequest) {
|
|
38
|
+
// todo: filter userId???
|
|
39
|
+
const organization = Context.organization!;
|
|
40
|
+
|
|
41
|
+
if (!webshopId) {
|
|
42
|
+
// todo
|
|
43
|
+
throw new Error();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ordersTable: string = Order.table;
|
|
47
|
+
|
|
48
|
+
const query = SQL
|
|
49
|
+
.select(SQL.wildcard(ordersTable))
|
|
50
|
+
.from(SQL.table(ordersTable))
|
|
51
|
+
// todo: extra check on webshopId to prevent all orders are returned if webshopId is null?
|
|
52
|
+
.where('webshopId', webshopId)
|
|
53
|
+
.where(compileToSQLFilter({
|
|
54
|
+
$or: [
|
|
55
|
+
{
|
|
56
|
+
organizationId: organization.id,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
organizationId: null,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
}, filterCompilers));
|
|
63
|
+
|
|
64
|
+
if (q.filter) {
|
|
65
|
+
query.where(compileToSQLFilter(q.filter, filterCompilers));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (q.search) {
|
|
69
|
+
let searchFilter: StamhoofdFilter | null = null;
|
|
70
|
+
|
|
71
|
+
// todo: detect special search patterns and adjust search filter if needed
|
|
72
|
+
searchFilter = {
|
|
73
|
+
name: {
|
|
74
|
+
$contains: q.search,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (searchFilter) {
|
|
79
|
+
query.where(compileToSQLFilter(searchFilter, filterCompilers));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (q instanceof LimitedFilteredRequest) {
|
|
84
|
+
if (q.pageFilter) {
|
|
85
|
+
query.where(compileToSQLFilter(q.pageFilter, filterCompilers));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
89
|
+
query.orderBy(compileToSQLSorter(q.sort, sorters));
|
|
90
|
+
query.limit(q.limit);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return query;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static async buildData(webshopId: string, requestQuery: LimitedFilteredRequest) {
|
|
97
|
+
const query = this.buildQuery(webshopId, requestQuery);
|
|
98
|
+
const data = await query.fetch();
|
|
99
|
+
|
|
100
|
+
const orders: Order[] = Order.fromRows(data, Order.table);
|
|
101
|
+
|
|
102
|
+
let next: LimitedFilteredRequest | undefined;
|
|
103
|
+
|
|
104
|
+
if (orders.length >= requestQuery.limit) {
|
|
105
|
+
const lastObject = orders[orders.length - 1];
|
|
106
|
+
const nextFilter = getSortFilter(lastObject, sorters, requestQuery.sort);
|
|
107
|
+
|
|
108
|
+
next = new LimitedFilteredRequest({
|
|
109
|
+
filter: requestQuery.filter,
|
|
110
|
+
pageFilter: nextFilter,
|
|
111
|
+
sort: requestQuery.sort,
|
|
112
|
+
limit: requestQuery.limit,
|
|
113
|
+
search: requestQuery.search,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (JSON.stringify(nextFilter) === JSON.stringify(requestQuery.pageFilter)) {
|
|
117
|
+
console.error('Found infinite loading loop for', requestQuery);
|
|
118
|
+
next = undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return new PaginatedResponse<PrivateOrder[], LimitedFilteredRequest>({
|
|
123
|
+
results: await AuthenticatedStructures.orders(orders),
|
|
124
|
+
next,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async handle(request: DecodedRequest<Params, Query, Body>): Promise<Response<ResponseBody>> {
|
|
129
|
+
const organization = await Context.setOrganizationScope();
|
|
130
|
+
await Context.authenticate();
|
|
34
131
|
|
|
35
132
|
// Fast throw first (more in depth checking for patches later)
|
|
36
133
|
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
37
|
-
throw Context.auth.error()
|
|
134
|
+
throw Context.auth.error();
|
|
38
135
|
}
|
|
39
136
|
|
|
40
|
-
|
|
137
|
+
LimitedFilteredRequestHelper.throwIfInvalidLimit({
|
|
138
|
+
request: request.query,
|
|
139
|
+
maxLimit: Context.auth.hasSomePlatformAccess() ? 1000 : 100,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const webshopId = request.params.id;
|
|
143
|
+
|
|
144
|
+
const webshop = await Webshop.getByID(webshopId);
|
|
41
145
|
if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Read)) {
|
|
42
|
-
throw Context.auth.notFoundOrNoAccess(
|
|
146
|
+
throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang tot de bestellingen van deze webshop');
|
|
43
147
|
}
|
|
44
148
|
|
|
149
|
+
return new Response(
|
|
150
|
+
await GetWebshopOrdersEndpoint.buildData(webshopId, request.query),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
|
|
45
155
|
let orders: Order[] | undefined = undefined
|
|
46
156
|
const limit = 50
|
|
47
157
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
|
-
import { CachedOutstandingBalance, Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, User, Webshop } from '@stamhoofd/models';
|
|
3
|
-
import { AccessRight, CachedOutstandingBalanceObject, CachedOutstandingBalanceObjectContact, CachedOutstandingBalance as CachedOutstandingBalanceStruct, CachedOutstandingBalanceType, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, PrivateWebshop, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
|
|
2
|
+
import { CachedOutstandingBalance, Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, User, Webshop } from '@stamhoofd/models';
|
|
3
|
+
import { AccessRight, CachedOutstandingBalanceObject, CachedOutstandingBalanceObjectContact, CachedOutstandingBalance as CachedOutstandingBalanceStruct, CachedOutstandingBalanceType, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, PrivateOrder, PrivateWebshop, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { Formatter } from '@stamhoofd/utility';
|
|
6
6
|
import { Context } from './Context';
|
|
@@ -423,6 +423,29 @@ export class AuthenticatedStructures {
|
|
|
423
423
|
return result;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
static async orders(orders: Order[]): Promise<PrivateOrder[]> {
|
|
427
|
+
// Load groups
|
|
428
|
+
// const groupIds = orders.map(e => e.groupId).filter(id => id !== null);
|
|
429
|
+
// const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : [];
|
|
430
|
+
// const groupStructs = await this.groups(groups);
|
|
431
|
+
|
|
432
|
+
const result: PrivateOrder[] = [];
|
|
433
|
+
|
|
434
|
+
for (const order of orders) {
|
|
435
|
+
// const group = groupStructs.find(g => g.id == event.groupId) ?? null;
|
|
436
|
+
|
|
437
|
+
const struct = PrivateOrder.create({
|
|
438
|
+
...order,
|
|
439
|
+
// todo!!!!!
|
|
440
|
+
balanceItems: [],
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
result.push(struct);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
|
|
426
449
|
static async cachedOutstandingBalances(balances: CachedOutstandingBalance[]): Promise<CachedOutstandingBalanceStruct[]> {
|
|
427
450
|
if (balances.length === 0) {
|
|
428
451
|
return [];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
|
+
import { LimitedFilteredRequest } from '@stamhoofd/structures';
|
|
3
|
+
|
|
4
|
+
export class LimitedFilteredRequestHelper {
|
|
5
|
+
static throwIfInvalidLimit({ request, maxLimit }: { request: LimitedFilteredRequest; maxLimit: number }) {
|
|
6
|
+
const requestLimit = request.limit;
|
|
7
|
+
|
|
8
|
+
if (requestLimit > maxLimit) {
|
|
9
|
+
throw new SimpleError({
|
|
10
|
+
code: 'invalid_field',
|
|
11
|
+
field: 'limit',
|
|
12
|
+
message: 'Limit can not be more than ' + maxLimit,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (requestLimit < 1) {
|
|
17
|
+
throw new SimpleError({
|
|
18
|
+
code: 'invalid_field',
|
|
19
|
+
field: 'limit',
|
|
20
|
+
message: 'Limit can not be less than 1',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { baseSQLFilterCompilers, createSQLColumnFilterCompiler, SQLFilterDefinitions } from '@stamhoofd/sql';
|
|
2
|
+
|
|
3
|
+
export const orderFilterCompilers: SQLFilterDefinitions = {
|
|
4
|
+
...baseSQLFilterCompilers,
|
|
5
|
+
'id': createSQLColumnFilterCompiler('id'),
|
|
6
|
+
'name': createSQLColumnFilterCompiler('name'),
|
|
7
|
+
'organizationId': createSQLColumnFilterCompiler('organizationId'),
|
|
8
|
+
'#': createSQLColumnFilterCompiler('number'),
|
|
9
|
+
// 'startDate': createSQLColumnFilterCompiler('startDate'),
|
|
10
|
+
// 'endDate': createSQLColumnFilterCompiler('endDate'),
|
|
11
|
+
// 'groupIds': createSQLExpressionFilterCompiler(
|
|
12
|
+
// SQL.jsonValue(SQL.column('meta'), '$.value.groups[*].id'),
|
|
13
|
+
// { isJSONValue: true, isJSONObject: true },
|
|
14
|
+
// ),
|
|
15
|
+
// 'defaultAgeGroupIds': createSQLExpressionFilterCompiler(
|
|
16
|
+
// SQL.jsonValue(SQL.column('meta'), '$.value.defaultAgeGroupIds'),
|
|
17
|
+
// { isJSONValue: true, isJSONObject: true },
|
|
18
|
+
// ),
|
|
19
|
+
// 'organizationTagIds': createSQLExpressionFilterCompiler(
|
|
20
|
+
// SQL.jsonValue(SQL.column('meta'), '$.value.organizationTagIds'),
|
|
21
|
+
// { isJSONValue: true, isJSONObject: true },
|
|
22
|
+
// ),
|
|
23
|
+
// 'meta.visible': createSQLExpressionFilterCompiler(
|
|
24
|
+
// SQL.jsonValue(SQL.column('meta'), '$.value.visible'),
|
|
25
|
+
// { isJSONValue: true, type: SQLValueType.JSONBoolean },
|
|
26
|
+
// ),
|
|
27
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Order } from '@stamhoofd/models';
|
|
2
|
+
import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
3
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
4
|
+
|
|
5
|
+
export const orderSorters: SQLSortDefinitions<Order> = {
|
|
6
|
+
// WARNING! TEST NEW SORTERS THOROUGHLY!
|
|
7
|
+
// Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
|
|
8
|
+
// An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
|
|
9
|
+
// You might be tempted to use ORDER BY firstName, lastName, but that will not work as expected and it needs to be ORDER BY CONCAT(firstName, ' ', lastName)
|
|
10
|
+
// Why? Because ORDER BY firstName, lastName produces a different order dan ORDER BY CONCAT(firstName, ' ', lastName) if there are multiple people with spaces in the first name
|
|
11
|
+
// And that again causes issues with pagination because the next query will append a filter of name > 'John Doe' - causing duplicate and/or skipped results
|
|
12
|
+
// What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
|
|
13
|
+
|
|
14
|
+
'#': {
|
|
15
|
+
getValue(a) {
|
|
16
|
+
return a.number;
|
|
17
|
+
},
|
|
18
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
19
|
+
return new SQLOrderBy({
|
|
20
|
+
column: SQL.column('number'),
|
|
21
|
+
direction,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
'id': {
|
|
26
|
+
getValue(a) {
|
|
27
|
+
return a.id;
|
|
28
|
+
},
|
|
29
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
30
|
+
return new SQLOrderBy({
|
|
31
|
+
column: SQL.column('id'),
|
|
32
|
+
direction,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
'name': {
|
|
37
|
+
getValue(a) {
|
|
38
|
+
return a.id;
|
|
39
|
+
},
|
|
40
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
41
|
+
return new SQLOrderBy({
|
|
42
|
+
column: SQL.column('name'),
|
|
43
|
+
direction,
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
'createdAt': {
|
|
48
|
+
getValue(a) {
|
|
49
|
+
return Formatter.dateTimeIso(a.createdAt);
|
|
50
|
+
},
|
|
51
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
52
|
+
return new SQLOrderBy({
|
|
53
|
+
column: SQL.column('createdAt'),
|
|
54
|
+
direction,
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|