@better-giving/endowment 2.0.6 → 3.0.2
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/dist/cloudsearch.d.mts +1 -1
- package/dist/cloudsearch.mjs +3 -3
- package/dist/db.d.mts +138 -115
- package/dist/db.mjs +355 -4
- package/dist/index.d.mts +2 -20
- package/dist/index.mjs +2 -1
- package/dist/interfaces.d.mts +41 -0
- package/dist/interfaces.mjs +1 -0
- package/dist/media.d.mts +11 -0
- package/dist/media.mjs +48 -0
- package/dist/npo.d.mts +4 -0
- package/dist/npo.mjs +41 -0
- package/dist/schema.d.mts +42 -43
- package/dist/schema.mjs +36 -40
- package/package.json +7 -2
- package/src/cloudsearch.mts +3 -2
- package/src/db.mts +414 -124
- package/src/index.mts +2 -45
- package/src/interfaces.mts +73 -0
- package/src/media.mts +67 -0
- package/src/npo.mts +47 -0
- package/src/schema.mts +57 -89
package/src/db.mts
CHANGED
|
@@ -1,151 +1,441 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
DeleteCommand,
|
|
3
|
+
GetCommand,
|
|
4
|
+
PutCommand,
|
|
5
|
+
QueryCommand,
|
|
6
|
+
TransactWriteCommand,
|
|
7
|
+
UpdateCommand,
|
|
8
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
9
|
+
import { Db, Txs, UpdateBuilder } from "@better-giving/db";
|
|
10
|
+
import KSUID from "ksuid";
|
|
2
11
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
IMedia,
|
|
13
|
+
IMediaDb,
|
|
14
|
+
IMediaPage,
|
|
15
|
+
INpoReferredBy,
|
|
16
|
+
INpoWithRegNum,
|
|
17
|
+
INpoWithRid,
|
|
18
|
+
TBinFlag,
|
|
19
|
+
TNpoDbKeys,
|
|
20
|
+
TNpoDbProjectedTo,
|
|
21
|
+
} from "./interfaces.mjs";
|
|
22
|
+
import { med_key_filter, med_sk, to_imedia } from "./media.mjs";
|
|
23
|
+
import { projection } from "./npo.mjs";
|
|
24
|
+
import type {
|
|
25
|
+
IMediaSearchObj,
|
|
26
|
+
IMilestone,
|
|
27
|
+
IMilestoneUpdate,
|
|
28
|
+
INpo,
|
|
29
|
+
INpoUpdate,
|
|
30
|
+
IProgram,
|
|
31
|
+
IProgramDb,
|
|
32
|
+
IProgramNew,
|
|
33
|
+
IProgramUpdate,
|
|
34
|
+
TMediaType,
|
|
19
35
|
} from "./schema.mjs";
|
|
20
36
|
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
export class NpoDb extends Db {
|
|
38
|
+
static override readonly name = "endowments_v3";
|
|
39
|
+
static readonly slug_env_gsi = "slug-env-gsi" as const;
|
|
40
|
+
static readonly regnum_env_gsi = "regnum-env-gsi" as const;
|
|
41
|
+
key_npo(id: number) {
|
|
42
|
+
return {
|
|
43
|
+
PK: `Endow#${id}`,
|
|
44
|
+
SK: this.env,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
get key_count() {
|
|
48
|
+
return {
|
|
49
|
+
PK: "Count",
|
|
50
|
+
SK: this.env,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
key_npo_program(id: string, npo: number) {
|
|
54
|
+
return {
|
|
55
|
+
PK: `Endow#${npo}#${this.env}`,
|
|
56
|
+
SK: `Prog#${id}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
25
59
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
key_prog_milestone(id: string, prog: string) {
|
|
61
|
+
return {
|
|
62
|
+
PK: `Prog#${prog}#${this.env}`,
|
|
63
|
+
SK: `Mile#${id}`,
|
|
64
|
+
};
|
|
30
65
|
}
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
|
|
67
|
+
key_npo_med(ksuid: string, npo: number) {
|
|
68
|
+
return {
|
|
69
|
+
PK: `Endow#${npo}#${this.env}`,
|
|
70
|
+
SK: `Media#${ksuid}`,
|
|
71
|
+
};
|
|
33
72
|
}
|
|
34
|
-
}
|
|
35
73
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export type NonKeyAttributes = EndowmentShape & {
|
|
43
|
-
/** referral id */
|
|
44
|
-
gsi1PK?: `ReferredBy#${string}`;
|
|
45
|
-
/** expiry / date onboarded */
|
|
46
|
-
gsi1SK?: string;
|
|
47
|
-
/** Rid#referral_id */
|
|
48
|
-
gsi2PK?: `Rid#${string}`;
|
|
49
|
-
gsi2SK?: `Rid#${string}`;
|
|
50
|
-
|
|
51
|
-
/** will only be present for unclaimed NPOs */
|
|
52
|
-
updated_at_auto?: string;
|
|
53
|
-
/** in USD @deprecated */
|
|
54
|
-
payout_minimum?: number;
|
|
55
|
-
/** @deprecated */
|
|
56
|
-
splitLiqPct?: number;
|
|
57
|
-
/** @deprecated */
|
|
58
|
-
splitFixed?: boolean;
|
|
59
|
-
/** @deprecated */
|
|
60
|
-
sfCompounded?: boolean;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export interface DbRecord extends Keys, NonKeyAttributes {}
|
|
64
|
-
}
|
|
74
|
+
gsi1_npo(id: string) {
|
|
75
|
+
return {
|
|
76
|
+
gsi1PK: `ReferredBy#${id}`,
|
|
77
|
+
gsi1SK: this.env,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Endow.Keys,
|
|
73
|
-
Pick<Endow.NonKeyAttributes, "claimed" | "name" | "hq_country" | "id"> {}
|
|
74
|
-
}
|
|
81
|
+
gsi2_npo(id: string) {
|
|
82
|
+
return {
|
|
83
|
+
gsi2PK: `Rid#${id}`,
|
|
84
|
+
gsi2SK: `Rid#${id}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
88
|
+
gsi1_npo_med(id: string, npo: number, featured: TBinFlag, type: TMediaType) {
|
|
89
|
+
return {
|
|
90
|
+
gsi1PK: this.key_npo_med(id, npo).PK,
|
|
91
|
+
gsi1SK: med_sk(id, type, featured),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async npo_media(npo: number, opts: IMediaSearchObj): Promise<IMediaPage> {
|
|
95
|
+
const PK: string = this.key_npo_med("ksuid-sk", npo).PK;
|
|
96
|
+
const [expression, values] = med_key_filter(PK, opts);
|
|
97
|
+
|
|
98
|
+
const cmd = new QueryCommand({
|
|
99
|
+
TableName: NpoDb.name,
|
|
100
|
+
IndexName: "gsi1",
|
|
101
|
+
Limit: opts.limit,
|
|
102
|
+
KeyConditionExpression: expression,
|
|
103
|
+
ExpressionAttributeValues: values,
|
|
104
|
+
ExclusiveStartKey: this.key_to_obj(opts.next),
|
|
105
|
+
});
|
|
81
106
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
SK: `Prog#${ProgramId}`;
|
|
107
|
+
const res = await this.client.send(cmd);
|
|
108
|
+
const page = this.to_page(res, to_imedia);
|
|
109
|
+
return page;
|
|
86
110
|
}
|
|
87
111
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
async npo_referred_by(id: string): Promise<INpoReferredBy[]> {
|
|
113
|
+
const cmd = new QueryCommand({
|
|
114
|
+
TableName: NpoDb.name,
|
|
115
|
+
IndexName: "gsi1",
|
|
116
|
+
KeyConditionExpression: "#pk = :pk",
|
|
117
|
+
ExpressionAttributeValues: {
|
|
118
|
+
":pk": this.gsi1_npo(id).gsi1PK,
|
|
119
|
+
},
|
|
120
|
+
ExpressionAttributeNames: { "#pk": "gsi1PK" },
|
|
121
|
+
});
|
|
122
|
+
const { Items = [] } = await this.client.send(cmd);
|
|
123
|
+
return Items.map((x) => this.sans_keys(x));
|
|
124
|
+
}
|
|
91
125
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
126
|
+
async npo_with_rid(id: string): Promise<INpoWithRid | undefined> {
|
|
127
|
+
const cmd = new QueryCommand({
|
|
128
|
+
TableName: NpoDb.name,
|
|
129
|
+
IndexName: "gsi2",
|
|
130
|
+
KeyConditionExpression: "gsi2PK = :pk",
|
|
131
|
+
ExpressionAttributeValues: {
|
|
132
|
+
":pk": this.gsi2_npo(id).gsi2PK,
|
|
133
|
+
},
|
|
134
|
+
Limit: 1,
|
|
135
|
+
});
|
|
136
|
+
const { Items = [] } = await this.client.send(cmd);
|
|
137
|
+
const i = Items[0];
|
|
138
|
+
return i && this.sans_keys(i);
|
|
96
139
|
}
|
|
97
|
-
export interface DbRecord extends Keys, MilestoneShape {}
|
|
98
|
-
}
|
|
99
140
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
141
|
+
async npo_with_regnum(
|
|
142
|
+
regnum: string,
|
|
143
|
+
country = "United States"
|
|
144
|
+
): Promise<INpoWithRegNum | undefined> {
|
|
145
|
+
const cmd = new QueryCommand({
|
|
146
|
+
TableName: NpoDb.name,
|
|
147
|
+
IndexName: NpoDb.regnum_env_gsi,
|
|
148
|
+
Limit: 1,
|
|
149
|
+
KeyConditionExpression: "#rn = :rn AND #env = :env",
|
|
150
|
+
|
|
151
|
+
FilterExpression: "#country = :country",
|
|
152
|
+
ExpressionAttributeNames: {
|
|
153
|
+
"#rn": "registration_number" satisfies TNpoDbKeys,
|
|
154
|
+
"#env": "env" satisfies TNpoDbKeys,
|
|
155
|
+
"#country": "hq_country" satisfies TNpoDbKeys,
|
|
156
|
+
},
|
|
157
|
+
ExpressionAttributeValues: {
|
|
158
|
+
":rn": regnum,
|
|
159
|
+
":env": this.env,
|
|
160
|
+
":country": country,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const { Items = [] } = await this.client.send(cmd);
|
|
165
|
+
const i = Items[0];
|
|
166
|
+
return i && this.sans_keys(i);
|
|
104
167
|
}
|
|
105
168
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
dateCreated: string;
|
|
169
|
+
npo_record(data: INpo) {
|
|
170
|
+
return {
|
|
171
|
+
...this.key_npo(data.id),
|
|
172
|
+
...data,
|
|
173
|
+
...(data.referrer ? this.gsi1_npo(data.referrer) : {}),
|
|
174
|
+
...(data.referral_id ? this.gsi2_npo(data.referral_id) : {}),
|
|
175
|
+
};
|
|
114
176
|
}
|
|
115
177
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
178
|
+
npo_prog_record(npo: number, data: IProgramDb) {
|
|
179
|
+
return {
|
|
180
|
+
...this.key_npo_program(data.id, npo),
|
|
181
|
+
...data,
|
|
182
|
+
};
|
|
119
183
|
}
|
|
120
184
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
185
|
+
prog_milestone_record(prog: string, data: IMilestone) {
|
|
186
|
+
return {
|
|
187
|
+
...this.key_prog_milestone(data.id, prog),
|
|
188
|
+
...data,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
124
191
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
192
|
+
npo_med_record(
|
|
193
|
+
npo_id: number,
|
|
194
|
+
{ featured /** not saved as attribute */, ...d }: IMedia
|
|
195
|
+
) {
|
|
196
|
+
return {
|
|
197
|
+
...this.key_npo_med(d.id, npo_id),
|
|
198
|
+
...this.gsi1_npo_med(d.id, npo_id, featured ? "1" : "0", d.type),
|
|
199
|
+
...(d satisfies IMediaDb),
|
|
200
|
+
};
|
|
130
201
|
}
|
|
131
202
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
203
|
+
async npo_med_put(npo: number, url: string) {
|
|
204
|
+
const ksuid = KSUID.randomSync();
|
|
205
|
+
const mid = ksuid.string;
|
|
206
|
+
const item = this.npo_med_record(npo, {
|
|
207
|
+
id: mid,
|
|
208
|
+
url,
|
|
209
|
+
type: "video",
|
|
210
|
+
dateCreated: ksuid.date.toISOString(),
|
|
211
|
+
featured: false,
|
|
212
|
+
});
|
|
213
|
+
const command = new PutCommand({
|
|
214
|
+
TableName: NpoDb.name,
|
|
215
|
+
Item: item,
|
|
216
|
+
});
|
|
217
|
+
await this.client.send(command);
|
|
218
|
+
return mid;
|
|
140
219
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
220
|
+
|
|
221
|
+
async npo_med(npo: number, mid: string): Promise<IMedia | undefined> {
|
|
222
|
+
const cmd = new GetCommand({
|
|
223
|
+
TableName: NpoDb.name,
|
|
224
|
+
Key: this.key_npo_med(mid, npo),
|
|
225
|
+
});
|
|
226
|
+
const { Item: i } = await this.client.send(cmd);
|
|
227
|
+
return i && to_imedia(i);
|
|
148
228
|
}
|
|
149
|
-
}
|
|
150
229
|
|
|
151
|
-
|
|
230
|
+
async npo_med_delete(npo: number, mid: string) {
|
|
231
|
+
const cmd = new DeleteCommand({
|
|
232
|
+
TableName: NpoDb.name,
|
|
233
|
+
Key: this.key_npo_med(mid, npo),
|
|
234
|
+
});
|
|
235
|
+
return this.client.send(cmd);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async npo<T extends TNpoDbKeys[]>(
|
|
239
|
+
id: string | number,
|
|
240
|
+
fields?: T
|
|
241
|
+
): Promise<TNpoDbProjectedTo<T> | undefined> {
|
|
242
|
+
const { names, expression } = projection(fields);
|
|
243
|
+
|
|
244
|
+
if (typeof id === "string") {
|
|
245
|
+
const cmd = new QueryCommand({
|
|
246
|
+
TableName: NpoDb.name,
|
|
247
|
+
IndexName: NpoDb.slug_env_gsi,
|
|
248
|
+
KeyConditionExpression: "#slug = :slug and #env = :env",
|
|
249
|
+
ExpressionAttributeValues: {
|
|
250
|
+
":slug": id,
|
|
251
|
+
":env": this.env,
|
|
252
|
+
},
|
|
253
|
+
ProjectionExpression: expression,
|
|
254
|
+
ExpressionAttributeNames: {
|
|
255
|
+
...names,
|
|
256
|
+
"#env": "env",
|
|
257
|
+
"#slug": "slug",
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
const [x] = await this.client.send(cmd).then(({ Items: x = [] }) => x);
|
|
261
|
+
return x ? this.sans_keys(x) : undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const cmd = new GetCommand({
|
|
265
|
+
TableName: NpoDb.name,
|
|
266
|
+
Key: this.key_npo(id),
|
|
267
|
+
ProjectionExpression: expression,
|
|
268
|
+
ExpressionAttributeNames: names,
|
|
269
|
+
});
|
|
270
|
+
const { Item: i } = await this.client.send(cmd);
|
|
271
|
+
return i ? this.sans_keys(i) : undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async npo_update(
|
|
275
|
+
id: number,
|
|
276
|
+
{ target, slug, social_media_urls, ...update }: INpoUpdate
|
|
277
|
+
) {
|
|
278
|
+
const updates = new UpdateBuilder();
|
|
279
|
+
|
|
280
|
+
if (slug) updates.set("slug", slug);
|
|
281
|
+
if (slug === "") updates.remove("slug");
|
|
282
|
+
|
|
283
|
+
if (target || target === "0") {
|
|
284
|
+
updates.set("target", target);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (social_media_urls) {
|
|
288
|
+
for (const [k, v] of Object.entries(social_media_urls)) {
|
|
289
|
+
if (v === undefined) continue;
|
|
290
|
+
updates.set(`social_media_urls.${k}`, v);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
for (const [k, v] of Object.entries(update)) {
|
|
295
|
+
if (v === undefined) continue;
|
|
296
|
+
updates.set(k, v);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const cmd = new UpdateCommand({
|
|
300
|
+
TableName: NpoDb.name,
|
|
301
|
+
Key: this.key_npo(id),
|
|
302
|
+
ReturnValues: "ALL_NEW",
|
|
303
|
+
...updates.collect(),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return this.client.send(cmd);
|
|
307
|
+
}
|
|
308
|
+
async prog_milestones(id: string): Promise<IMilestone[]> {
|
|
309
|
+
const command = new QueryCommand({
|
|
310
|
+
TableName: NpoDb.name,
|
|
311
|
+
KeyConditionExpression: `PK = :PK`,
|
|
312
|
+
ExpressionAttributeValues: {
|
|
313
|
+
":PK": this.key_prog_milestone("not-used", id).PK,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
return this.client
|
|
317
|
+
.send(command)
|
|
318
|
+
.then(({ Items: x = [] }) => x.map((i) => this.sans_keys(i)));
|
|
319
|
+
}
|
|
320
|
+
async prog_milestone_delete(pid: string, mid: string) {
|
|
321
|
+
const cmd = new DeleteCommand({
|
|
322
|
+
TableName: NpoDb.name,
|
|
323
|
+
Key: this.key_prog_milestone(mid, pid),
|
|
324
|
+
});
|
|
325
|
+
return this.client.send(cmd);
|
|
326
|
+
}
|
|
327
|
+
async prog_milestone_update(
|
|
328
|
+
pid: string,
|
|
329
|
+
mid: string,
|
|
330
|
+
update: IMilestoneUpdate
|
|
331
|
+
) {
|
|
332
|
+
const upd8 = new UpdateBuilder();
|
|
333
|
+
for (const [key, value] of Object.entries(update)) {
|
|
334
|
+
upd8.set(key, value);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const cmd = new UpdateCommand({
|
|
338
|
+
TableName: NpoDb.name,
|
|
339
|
+
Key: this.key_prog_milestone(mid, pid),
|
|
340
|
+
...upd8.collect(),
|
|
341
|
+
ReturnValues: "ALL_NEW",
|
|
342
|
+
});
|
|
343
|
+
return this.client.send(cmd).then((res) => res.Attributes);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async npo_program(id: string, npo_id: number): Promise<IProgram | undefined> {
|
|
347
|
+
const cmd = new GetCommand({
|
|
348
|
+
TableName: NpoDb.name,
|
|
349
|
+
Key: this.key_npo_program(id, npo_id),
|
|
350
|
+
});
|
|
351
|
+
const { Item: p } = await this.client.send(cmd);
|
|
352
|
+
if (!p) return undefined;
|
|
353
|
+
|
|
354
|
+
const milestones = await this.prog_milestones(id);
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
...this.sans_keys<IProgramDb>(p),
|
|
358
|
+
milestones: milestones.toSorted((a, b) => a.date.localeCompare(b.date)),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async npo_programs(id: number): Promise<IProgramDb[]> {
|
|
362
|
+
const cmd = new QueryCommand({
|
|
363
|
+
TableName: NpoDb.name,
|
|
364
|
+
KeyConditionExpression: `PK = :PK and begins_with(SK, :SK)`,
|
|
365
|
+
ExpressionAttributeValues: {
|
|
366
|
+
":PK": this.key_npo_program("not-used", id).PK,
|
|
367
|
+
":SK": "Prog#",
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const { Items: x = [] } = await this.client.send(cmd);
|
|
372
|
+
return x.map((i) => this.sans_keys(i));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async npo_program_put(npo: number, content: IProgramNew): Promise<string> {
|
|
376
|
+
const pid = crypto.randomUUID();
|
|
377
|
+
|
|
378
|
+
const { milestones, ...prog } = content;
|
|
379
|
+
const txs = new Txs();
|
|
380
|
+
const db_prog = this.npo_prog_record(npo, {
|
|
381
|
+
...prog,
|
|
382
|
+
id: pid,
|
|
383
|
+
totalDonations: 0,
|
|
384
|
+
});
|
|
385
|
+
txs.put({
|
|
386
|
+
TableName: NpoDb.name,
|
|
387
|
+
Item: db_prog,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
for (const m of milestones || []) {
|
|
391
|
+
const mid = crypto.randomUUID();
|
|
392
|
+
txs.put({
|
|
393
|
+
TableName: NpoDb.name,
|
|
394
|
+
Item: this.prog_milestone_record(pid, { ...m, id: mid }),
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const cmd = new TransactWriteCommand({
|
|
399
|
+
TransactItems: txs.all,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
await this.client.send(cmd);
|
|
403
|
+
return pid;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async npo_prog_del(npo: number, prog: string) {
|
|
407
|
+
const milestones = await this.prog_milestones(prog);
|
|
408
|
+
const txs = new Txs();
|
|
409
|
+
txs.del({
|
|
410
|
+
TableName: NpoDb.name,
|
|
411
|
+
Key: this.key_npo_program(prog, npo),
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
for (const m of milestones) {
|
|
415
|
+
txs.del({
|
|
416
|
+
TableName: NpoDb.name,
|
|
417
|
+
Key: this.key_prog_milestone(m.id, prog),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const cmd = new TransactWriteCommand({
|
|
422
|
+
TransactItems: txs.all,
|
|
423
|
+
});
|
|
424
|
+
return this.client.send(cmd);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async npo_prog_update(npo: number, prog: string, update: IProgramUpdate) {
|
|
428
|
+
const upd8 = new UpdateBuilder();
|
|
429
|
+
for (const [key, value] of Object.entries(update)) {
|
|
430
|
+
upd8.set(key, value);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const cmd = new UpdateCommand({
|
|
434
|
+
TableName: NpoDb.name,
|
|
435
|
+
Key: this.key_npo_program(prog, npo),
|
|
436
|
+
...upd8.collect(),
|
|
437
|
+
ReturnValues: "ALL_NEW",
|
|
438
|
+
});
|
|
439
|
+
return this.client.send(cmd).then((res) => res.Attributes);
|
|
440
|
+
}
|
|
441
|
+
}
|
package/src/index.mts
CHANGED
|
@@ -1,50 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { Media } from "./db.mjs";
|
|
4
|
-
import type { Milestone, Program as ProgramShape } from "./schema.mjs";
|
|
5
|
-
export type {
|
|
6
|
-
Allocation,
|
|
7
|
-
DonateMethodId,
|
|
8
|
-
EndowDesignation,
|
|
9
|
-
Endowment as Endow,
|
|
10
|
-
EndowFields,
|
|
11
|
-
EndowQueryParams,
|
|
12
|
-
EndowUpdate,
|
|
13
|
-
Environment,
|
|
14
|
-
Increment,
|
|
15
|
-
MediaQueryParams,
|
|
16
|
-
MediaQueryParamsObj,
|
|
17
|
-
MediaType,
|
|
18
|
-
MediaUpdate,
|
|
19
|
-
Milestone,
|
|
20
|
-
MilestoneUpdate,
|
|
21
|
-
NewMilestone,
|
|
22
|
-
NewProgram,
|
|
23
|
-
ProgramUpdate,
|
|
24
|
-
SocialMediaURLs,
|
|
25
|
-
UnSdgNum,
|
|
26
|
-
} from "./schema.mjs";
|
|
1
|
+
export * from "./interfaces.mjs";
|
|
2
|
+
export { NpoDb } from "./db.mjs";
|
|
27
3
|
export type {
|
|
28
4
|
CloudsearchEndow as EndowItem,
|
|
29
5
|
CloudsearchEndowsQueryParams as EndowsQueryParams,
|
|
30
6
|
CloudsearchEndowsQueryParamsParsed as EndowsQueryParamsParsed,
|
|
31
7
|
} from "./cloudsearch.mjs";
|
|
32
|
-
|
|
33
|
-
/** client responsible on T depending on keys projected */
|
|
34
|
-
export interface EndowsPage<T extends keyof EndowItem = keyof EndowItem> {
|
|
35
|
-
items: Pick<EndowItem, T>[];
|
|
36
|
-
page: number;
|
|
37
|
-
pages: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface Program extends ProgramShape {
|
|
41
|
-
milestones: Milestone[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** web-app format */
|
|
45
|
-
export interface IMedia
|
|
46
|
-
extends Omit<Media.DbRecord, "PK" | "SK" | "gsi1PK" | "gsi1SK"> {
|
|
47
|
-
featured: boolean;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface MediaPage extends IPageKeyed<IMedia> {}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { IPageKeyed } from "@better-giving/types/api";
|
|
2
|
+
import type { Ensure } from "@better-giving/types/utils";
|
|
3
|
+
import type { IMediaUpdate, INpo, TMediaType } from "./schema.mjs";
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
IAllocation,
|
|
7
|
+
INpo,
|
|
8
|
+
INpoFields,
|
|
9
|
+
INposSearch,
|
|
10
|
+
INpoUpdate,
|
|
11
|
+
IIncrement,
|
|
12
|
+
IMediaSearch,
|
|
13
|
+
IMediaSearchObj,
|
|
14
|
+
TMediaType,
|
|
15
|
+
IMediaUpdate,
|
|
16
|
+
IMilestone,
|
|
17
|
+
IMilestoneUpdate,
|
|
18
|
+
IMilestoneNew,
|
|
19
|
+
IProgramNew,
|
|
20
|
+
IProgramUpdate,
|
|
21
|
+
ISocialMediaURLs,
|
|
22
|
+
} from "./schema.mjs";
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
type OrgDesignation as EndowDesignation,
|
|
26
|
+
type Environment,
|
|
27
|
+
type UnSdgNum,
|
|
28
|
+
type DonateMethodId,
|
|
29
|
+
https_url,
|
|
30
|
+
} from "@better-giving/schemas";
|
|
31
|
+
|
|
32
|
+
export interface INpoDb extends INpo {
|
|
33
|
+
/** will only be present for unclaimed NPOs */
|
|
34
|
+
updated_at_auto?: string;
|
|
35
|
+
/** in USD @deprecated */
|
|
36
|
+
payout_minimum?: number;
|
|
37
|
+
/** @deprecated */
|
|
38
|
+
splitLiqPct?: number;
|
|
39
|
+
/** @deprecated */
|
|
40
|
+
splitFixed?: boolean;
|
|
41
|
+
/** @deprecated */
|
|
42
|
+
sfCompounded?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface INpoReferredBy
|
|
46
|
+
extends Ensure<INpo, "referrer" | "referrer_expiry"> {}
|
|
47
|
+
|
|
48
|
+
export interface INpoWithRid extends Ensure<INpo, "referral_id"> {}
|
|
49
|
+
|
|
50
|
+
export interface INpoWithRegNum
|
|
51
|
+
extends Pick<INpo, "claimed" | "name" | "hq_country" | "id"> {}
|
|
52
|
+
|
|
53
|
+
export interface IMedia extends Required<IMediaUpdate> {
|
|
54
|
+
id: string;
|
|
55
|
+
type: Extract<TMediaType, "video">; // only video for now
|
|
56
|
+
dateCreated: string;
|
|
57
|
+
}
|
|
58
|
+
export interface IMediaDb extends Omit<IMedia, "featured"> {}
|
|
59
|
+
export interface IMediaPage extends IPageKeyed<IMedia> {}
|
|
60
|
+
|
|
61
|
+
export type TNpoDbKeys = keyof INpoDb;
|
|
62
|
+
export type TArrayValues<T extends readonly unknown[]> = T[number];
|
|
63
|
+
export type TNpoDbProjectedTo<T> = T extends TNpoDbKeys[]
|
|
64
|
+
? Pick<INpoDb, TArrayValues<T>>
|
|
65
|
+
: INpoDb;
|
|
66
|
+
|
|
67
|
+
export type TBinFlag = "0" | "1";
|
|
68
|
+
|
|
69
|
+
export interface INposPage<T extends keyof INpoDb = keyof INpoDb> {
|
|
70
|
+
items: Pick<INpoDb, T>[];
|
|
71
|
+
page: number;
|
|
72
|
+
pages: number;
|
|
73
|
+
}
|