@shushed/helpers 0.0.209 → 0.0.210
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.
|
@@ -42,71 +42,107 @@ class AirtableHelper extends runtime_1.default {
|
|
|
42
42
|
}
|
|
43
43
|
return existingRecord;
|
|
44
44
|
}
|
|
45
|
-
async updateMultiple(payload, options = {}
|
|
46
|
-
updatedRecords: [],
|
|
47
|
-
createdRecords: [],
|
|
48
|
-
records: []
|
|
49
|
-
}) {
|
|
45
|
+
async updateMultiple(payload, options = {}) {
|
|
50
46
|
let response = null;
|
|
51
47
|
const tableUrl = `https://api.airtable.com/v0/${this.baseId}/${this.tableId}`;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return record;
|
|
79
|
-
})
|
|
80
|
-
}),
|
|
48
|
+
const batchSize = 10;
|
|
49
|
+
let collectedResult = {
|
|
50
|
+
updatedRecords: [],
|
|
51
|
+
createdRecords: [],
|
|
52
|
+
records: []
|
|
53
|
+
};
|
|
54
|
+
const fieldsToMergeOn = (options.fieldsToMergeOn || [this.primaryKeyFieldName]);
|
|
55
|
+
const fieldsToMergeOnIds = fieldsToMergeOn.map(x => this.dictionary[x] || x);
|
|
56
|
+
let callIdx = 0;
|
|
57
|
+
const payloadDeduplicated = payload.filter((x, idx, self) => idx === self.findIndex((t) => fieldsToMergeOn.every(field => (0, lodash_isequal_1.default)(t[field], x[field]))));
|
|
58
|
+
const maxCallIdx = Math.ceil(payloadDeduplicated.length / batchSize);
|
|
59
|
+
while (callIdx < maxCallIdx) {
|
|
60
|
+
const currentBatch = payloadDeduplicated.slice(callIdx * batchSize, (callIdx + 1) * batchSize);
|
|
61
|
+
const recordsInBatch = currentBatch.map(x => {
|
|
62
|
+
const recordId = x.$recordId;
|
|
63
|
+
const fieldsWithoutRecordId = { ...x };
|
|
64
|
+
delete fieldsWithoutRecordId.$recordId;
|
|
65
|
+
const record = {
|
|
66
|
+
fields: AirtableHelper.convertToDictionary(this.dictionary, this.primaryKeyWritable === false
|
|
67
|
+
? AirtableHelper.removePrimaryKey(fieldsWithoutRecordId, this.primaryKeyFieldName)
|
|
68
|
+
: fieldsWithoutRecordId),
|
|
69
|
+
};
|
|
70
|
+
if (recordId) {
|
|
71
|
+
record.id = recordId;
|
|
72
|
+
}
|
|
73
|
+
return record;
|
|
81
74
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
try {
|
|
76
|
+
response = await fetch(`${tableUrl}`, {
|
|
77
|
+
method: "PATCH",
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
performUpsert: {
|
|
84
|
+
fieldsToMergeOn: fieldsToMergeOnIds,
|
|
85
|
+
},
|
|
86
|
+
returnFieldsByFieldId: true,
|
|
87
|
+
typecast: options.typecast || false,
|
|
88
|
+
records: recordsInBatch
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok && response) {
|
|
92
|
+
const text = await response.text().catch(() => `${response?.status || 'unknown'}`);
|
|
93
|
+
throw new Error(text);
|
|
94
|
+
}
|
|
95
|
+
const resp = (await response.json());
|
|
96
|
+
collectedResult = {
|
|
97
|
+
updatedRecords: collectedResult.updatedRecords.concat(resp.updatedRecords),
|
|
98
|
+
createdRecords: collectedResult.createdRecords.concat(resp.createdRecords),
|
|
99
|
+
records: collectedResult.records.concat(resp.records.map(x => AirtableHelper.translateFields(this.dictionary, x)))
|
|
100
|
+
};
|
|
85
101
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
catch (err) {
|
|
103
|
+
const errorMessage = `Failed to update records in ${this.tableId} table (baseId: ${this.baseId}). Status: ${response?.status || 'unknown'}. Error: ${err.message}`;
|
|
104
|
+
const batchErrors = currentBatch.map(() => new Error(errorMessage));
|
|
105
|
+
collectedResult = {
|
|
106
|
+
updatedRecords: collectedResult.updatedRecords,
|
|
107
|
+
createdRecords: collectedResult.createdRecords,
|
|
108
|
+
records: collectedResult.records.concat(batchErrors)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
callIdx += 1;
|
|
94
113
|
}
|
|
95
|
-
return nextCollectedResult;
|
|
96
114
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
const resultInOrder = [];
|
|
116
|
+
for (let i = 0; i < payload.length; i++) {
|
|
117
|
+
let j = 0;
|
|
118
|
+
let foundMatchingRecord = null;
|
|
119
|
+
while (j < collectedResult.records.length && foundMatchingRecord === null) {
|
|
120
|
+
let isMatching = true;
|
|
121
|
+
let k = 0;
|
|
122
|
+
while (k < fieldsToMergeOn.length && isMatching) {
|
|
123
|
+
const field = fieldsToMergeOn[k];
|
|
124
|
+
if (!(0, lodash_isequal_1.default)(collectedResult.records[j].fields[field], payload[i][field])) {
|
|
125
|
+
isMatching = false;
|
|
126
|
+
}
|
|
127
|
+
k += 1;
|
|
128
|
+
}
|
|
129
|
+
if (isMatching) {
|
|
130
|
+
foundMatchingRecord = j;
|
|
131
|
+
}
|
|
132
|
+
j += 1;
|
|
133
|
+
}
|
|
134
|
+
if (foundMatchingRecord !== null) {
|
|
135
|
+
resultInOrder.push(collectedResult.records[foundMatchingRecord]);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
resultInOrder.push(new Error(`Record ${payload[i][this.primaryKeyFieldName]} does not match any record in the response`));
|
|
107
139
|
}
|
|
108
|
-
return nextCollectedResult;
|
|
109
140
|
}
|
|
141
|
+
return {
|
|
142
|
+
updatedRecords: collectedResult.updatedRecords,
|
|
143
|
+
createdRecords: collectedResult.createdRecords,
|
|
144
|
+
records: resultInOrder
|
|
145
|
+
};
|
|
110
146
|
}
|
|
111
147
|
async upsert(payload) {
|
|
112
148
|
const existingRecord = await this.getExistingRecord(payload);
|
|
@@ -258,9 +294,10 @@ class AirtableHelper extends runtime_1.default {
|
|
|
258
294
|
const escapeFormulaValue = (value) => {
|
|
259
295
|
return value.replace(/"/g, '\\"');
|
|
260
296
|
};
|
|
297
|
+
const dedupedKeys = keys.filter((x, idx, self) => self.indexOf(x) === idx);
|
|
261
298
|
const batchSize = 50;
|
|
262
|
-
for (let i = 0; i <
|
|
263
|
-
const batch =
|
|
299
|
+
for (let i = 0; i < dedupedKeys.length; i += batchSize) {
|
|
300
|
+
const batch = dedupedKeys.slice(i, i + batchSize);
|
|
264
301
|
const orConditions = batch.map(key => `${this.dictionary[this.primaryKeyFieldName]} = "${escapeFormulaValue(key)}"`).join(', ');
|
|
265
302
|
const formula = `OR(${orConditions})`;
|
|
266
303
|
const records = await this.getExistingRecords(formula);
|
|
@@ -45,15 +45,6 @@ declare class AirtableHelper<T extends Record<string, string>, K extends keyof T
|
|
|
45
45
|
fieldsToMergeOn?: Array<keyof T>;
|
|
46
46
|
primaryKeyWritable?: boolean;
|
|
47
47
|
typecast?: boolean;
|
|
48
|
-
}, callIdx?: number, collectedResult?: {
|
|
49
|
-
updatedRecords: Array<string>;
|
|
50
|
-
createdRecords: Array<string>;
|
|
51
|
-
records: Array<{
|
|
52
|
-
id: string;
|
|
53
|
-
fields: {
|
|
54
|
-
[key in T[keyof T]]: any;
|
|
55
|
-
};
|
|
56
|
-
} | Error>;
|
|
57
48
|
}): Promise<{
|
|
58
49
|
updatedRecords: Array<string>;
|
|
59
50
|
createdRecords: Array<string>;
|