flexdataset 0.21.2 → 0.22.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/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +4 -4
- package/dist/src/model.d.ts +4 -3
- package/dist/src/model.js +3 -3
- package/dist/src/schema/table.schema.json +1 -1
- package/dist/src/table-types/{iceberg-parquet.d.ts → iceberg-parquet-v2.d.ts} +17 -8
- package/dist/src/table-types/iceberg-parquet-v2.js +469 -0
- package/dist/src/table-types/registry.js +3 -3
- package/package.json +1 -1
- package/dist/src/table-types/iceberg-parquet.js +0 -471
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Iceberg (parquet) table type.
|
|
4
|
-
*
|
|
5
|
-
* Iceberg partitions are transforms applied to a data column, so duplicate
|
|
6
|
-
* detection keys on the (source column, normalized transform) pair — `year(ts)`
|
|
7
|
-
* and `month(ts)` are distinct, while `day` and `DAY` collapse.
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.IcebergParquetTable = void 0;
|
|
11
|
-
const table_type_1 = require("../table-type");
|
|
12
|
-
const iceberg_1 = require("../iceberg");
|
|
13
|
-
/**
|
|
14
|
-
* Whether a string is a positive integer (digits only, value > 0). Iceberg
|
|
15
|
-
* table-property values are strings, so numeric checks parse the string.
|
|
16
|
-
*
|
|
17
|
-
* @param value Property value string.
|
|
18
|
-
* @returns True when `value` is a positive integer.
|
|
19
|
-
*/
|
|
20
|
-
function isPositiveInteger(value) {
|
|
21
|
-
return /^\d+$/.test(value) && Number(value) > 0;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Whether a string is a non-negative integer (digits only, value >= 0).
|
|
25
|
-
*
|
|
26
|
-
* @param value Property value string.
|
|
27
|
-
* @returns True when `value` is a non-negative integer.
|
|
28
|
-
*/
|
|
29
|
-
function isNonNegativeInteger(value) {
|
|
30
|
-
return /^\d+$/.test(value);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Whether a string is an integer within an inclusive range.
|
|
34
|
-
*
|
|
35
|
-
* @param value Property value string.
|
|
36
|
-
* @param min Inclusive lower bound.
|
|
37
|
-
* @param max Inclusive upper bound.
|
|
38
|
-
* @returns True when `value` is an integer in `[min, max]`.
|
|
39
|
-
*/
|
|
40
|
-
function isIntegerInRange(value, min, max) {
|
|
41
|
-
if (!/^\d+$/.test(value)) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
const parsed = Number(value);
|
|
45
|
-
return parsed >= min && parsed <= max;
|
|
46
|
-
}
|
|
47
|
-
/** Iceberg (parquet) table. */
|
|
48
|
-
class IcebergParquetTable extends table_type_1.TableTypeBase {
|
|
49
|
-
/**
|
|
50
|
-
* Validate a column type against the Iceberg v0 type registry.
|
|
51
|
-
*
|
|
52
|
-
* @param type Type string from a column definition.
|
|
53
|
-
* @returns True when the type is a valid Iceberg type.
|
|
54
|
-
*/
|
|
55
|
-
isValidColumnType(type) {
|
|
56
|
-
return (0, iceberg_1.isValidIcebergType)(type);
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Iceberg partition rules.
|
|
60
|
-
*
|
|
61
|
-
* Emits: `NO_DUPLICATE_PARTITIONS` (keyed by `${name} ${transformKind}:${param}`,
|
|
62
|
-
* falling back to a lowercased trim of the raw transform when it doesn't
|
|
63
|
-
* parse, so whitespace / case variants of the same transform collapse),
|
|
64
|
-
* `ICEBERG_TRANSFORM_SOURCE_EXISTS`, `ICEBERG_TRANSFORM_VALID`, and
|
|
65
|
-
* `ICEBERG_TRANSFORM_SOURCE_TYPE_LEGAL`.
|
|
66
|
-
*
|
|
67
|
-
* @returns Every partition-related violation.
|
|
68
|
-
*/
|
|
69
|
-
partitionViolations() {
|
|
70
|
-
const violations = [];
|
|
71
|
-
const { partitions, } = this.definition;
|
|
72
|
-
const seen = new Set();
|
|
73
|
-
partitions.forEach((partition, index) => {
|
|
74
|
-
const key = this.partitionKey(partition);
|
|
75
|
-
if (seen.has(key)) {
|
|
76
|
-
violations.push(this.violation({
|
|
77
|
-
level: 'error',
|
|
78
|
-
code: 'NO_DUPLICATE_PARTITIONS',
|
|
79
|
-
field: `partitions[${index}]`,
|
|
80
|
-
message: `duplicate partition: "${partition.name}" with transform "${partition.type}"`,
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
seen.add(key);
|
|
84
|
-
});
|
|
85
|
-
partitions.forEach((partition, index) => {
|
|
86
|
-
const sourceColumn = this.definition.columns.find((column) => column.name === partition.name);
|
|
87
|
-
if (sourceColumn === undefined) {
|
|
88
|
-
violations.push(this.violation({
|
|
89
|
-
level: 'error',
|
|
90
|
-
code: 'ICEBERG_TRANSFORM_SOURCE_EXISTS',
|
|
91
|
-
field: `partitions[${index}].name`,
|
|
92
|
-
message: `Iceberg partition source column "${partition.name}" is not defined in columns`,
|
|
93
|
-
}));
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
const transform = (0, iceberg_1.parseIcebergTransform)(partition.type);
|
|
97
|
-
if (transform === null) {
|
|
98
|
-
violations.push(this.violation({
|
|
99
|
-
level: 'error',
|
|
100
|
-
code: 'ICEBERG_TRANSFORM_VALID',
|
|
101
|
-
field: `partitions[${index}].type`,
|
|
102
|
-
message: `"${partition.type}" is not a valid Iceberg partition transform`,
|
|
103
|
-
}));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const sourceType = (0, iceberg_1.parseIcebergType)(sourceColumn.type);
|
|
107
|
-
if (sourceType !== null && !(0, iceberg_1.transformLegalOnType)(transform, sourceType)) {
|
|
108
|
-
violations.push(this.violation({
|
|
109
|
-
level: 'error',
|
|
110
|
-
code: 'ICEBERG_TRANSFORM_SOURCE_TYPE_LEGAL',
|
|
111
|
-
field: `partitions[${index}].type`,
|
|
112
|
-
message: `transform "${partition.type}" is not legal on `
|
|
113
|
-
+ `column "${partition.name}" of type "${sourceColumn.type}"`,
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
return violations;
|
|
118
|
-
}
|
|
119
|
-
/// The Iceberg format versions this validator understands.
|
|
120
|
-
static FORMAT_VERSIONS = new Set([
|
|
121
|
-
1,
|
|
122
|
-
2,
|
|
123
|
-
3,
|
|
124
|
-
]);
|
|
125
|
-
/// Table-property keys whose value must come from a closed set.
|
|
126
|
-
static ENUM_PROPERTIES = {
|
|
127
|
-
'write.format.default': [
|
|
128
|
-
'parquet',
|
|
129
|
-
'avro',
|
|
130
|
-
'orc',
|
|
131
|
-
],
|
|
132
|
-
'write.parquet.compression-codec': [
|
|
133
|
-
'zstd',
|
|
134
|
-
'gzip',
|
|
135
|
-
'snappy',
|
|
136
|
-
'lz4',
|
|
137
|
-
'none',
|
|
138
|
-
],
|
|
139
|
-
'write.avro.compression-codec': [
|
|
140
|
-
'gzip',
|
|
141
|
-
'zstd',
|
|
142
|
-
'snappy',
|
|
143
|
-
'uncompressed',
|
|
144
|
-
],
|
|
145
|
-
'write.orc.compression-codec': [
|
|
146
|
-
'zstd',
|
|
147
|
-
'lz4',
|
|
148
|
-
'lzo',
|
|
149
|
-
'zlib',
|
|
150
|
-
'snappy',
|
|
151
|
-
'none',
|
|
152
|
-
],
|
|
153
|
-
'write.distribution-mode': [
|
|
154
|
-
'none',
|
|
155
|
-
'hash',
|
|
156
|
-
'range',
|
|
157
|
-
],
|
|
158
|
-
'write.metadata.compression-codec': [
|
|
159
|
-
'none',
|
|
160
|
-
'gzip',
|
|
161
|
-
],
|
|
162
|
-
};
|
|
163
|
-
/// Table-property keys whose value must be a positive integer.
|
|
164
|
-
static POSITIVE_INT_PROPERTIES = [
|
|
165
|
-
'write.target-file-size-bytes',
|
|
166
|
-
'history.expire.max-snapshot-age-ms',
|
|
167
|
-
'history.expire.min-snapshots-to-keep',
|
|
168
|
-
'history.expire.max-ref-age-ms',
|
|
169
|
-
'write.metadata.previous-versions-max',
|
|
170
|
-
'commit.retry.min-wait-ms',
|
|
171
|
-
'commit.retry.max-wait-ms',
|
|
172
|
-
'commit.retry.total-timeout-ms',
|
|
173
|
-
];
|
|
174
|
-
/// Table-property keys whose value must be a non-negative integer.
|
|
175
|
-
static NON_NEGATIVE_INT_PROPERTIES = [
|
|
176
|
-
'commit.retry.num-retries',
|
|
177
|
-
];
|
|
178
|
-
/// Table-property keys whose value must be an integer within an inclusive range.
|
|
179
|
-
static INT_RANGE_PROPERTIES = {
|
|
180
|
-
'write.parquet.compression-level': {
|
|
181
|
-
min: 1,
|
|
182
|
-
max: 22,
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
/// Legal sort directions and null orderings.
|
|
186
|
-
static SORT_DIRECTIONS = new Set([
|
|
187
|
-
'asc',
|
|
188
|
-
'desc',
|
|
189
|
-
]);
|
|
190
|
-
static SORT_NULL_ORDERS = new Set([
|
|
191
|
-
'nulls-first',
|
|
192
|
-
'nulls-last',
|
|
193
|
-
]);
|
|
194
|
-
/**
|
|
195
|
-
* Iceberg engine-specific rules: format version, table properties, and sort
|
|
196
|
-
* order.
|
|
197
|
-
*
|
|
198
|
-
* @returns Every engine-specific violation.
|
|
199
|
-
*/
|
|
200
|
-
engineSpecificViolations() {
|
|
201
|
-
return [
|
|
202
|
-
...this.formatVersionViolations(),
|
|
203
|
-
...this.tablePropertyViolations(),
|
|
204
|
-
...this.sortOrderViolations(),
|
|
205
|
-
...this.identifierFieldViolations(),
|
|
206
|
-
];
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Validate the Iceberg sort order.
|
|
210
|
-
*
|
|
211
|
-
* Emits `NO_DUPLICATE_SORT_FIELDS` (keyed by source column + normalized
|
|
212
|
-
* transform), `ICEBERG_SORT_COLUMN_EXISTS`, `ICEBERG_SORT_DIRECTION_VALID`,
|
|
213
|
-
* `ICEBERG_SORT_NULL_ORDER_VALID`, `ICEBERG_SORT_TRANSFORM_VALID`, and
|
|
214
|
-
* `ICEBERG_SORT_TRANSFORM_TYPE_LEGAL`.
|
|
215
|
-
*
|
|
216
|
-
* @returns Every sort-order violation.
|
|
217
|
-
*/
|
|
218
|
-
sortOrderViolations() {
|
|
219
|
-
const violations = [];
|
|
220
|
-
const { sortOrder, } = this.definition;
|
|
221
|
-
const seen = new Set();
|
|
222
|
-
sortOrder.forEach((field, index) => {
|
|
223
|
-
const key = this.sortFieldKey(field);
|
|
224
|
-
if (seen.has(key)) {
|
|
225
|
-
violations.push(this.violation({
|
|
226
|
-
level: 'error',
|
|
227
|
-
code: 'NO_DUPLICATE_SORT_FIELDS',
|
|
228
|
-
field: `sortOrder[${index}]`,
|
|
229
|
-
message: `duplicate sort field: "${field.column}"`
|
|
230
|
-
+ `${field.transform ? ` with transform "${field.transform}"` : ''}`,
|
|
231
|
-
}));
|
|
232
|
-
}
|
|
233
|
-
seen.add(key);
|
|
234
|
-
});
|
|
235
|
-
sortOrder.forEach((field, index) => {
|
|
236
|
-
if (!IcebergParquetTable.SORT_DIRECTIONS.has(field.direction)) {
|
|
237
|
-
violations.push(this.violation({
|
|
238
|
-
level: 'error',
|
|
239
|
-
code: 'ICEBERG_SORT_DIRECTION_VALID',
|
|
240
|
-
field: `sortOrder[${index}].direction`,
|
|
241
|
-
message: `sort direction "${field.direction}" must be "asc" or "desc"`,
|
|
242
|
-
}));
|
|
243
|
-
}
|
|
244
|
-
if (!IcebergParquetTable.SORT_NULL_ORDERS.has(field.nullOrder)) {
|
|
245
|
-
violations.push(this.violation({
|
|
246
|
-
level: 'error',
|
|
247
|
-
code: 'ICEBERG_SORT_NULL_ORDER_VALID',
|
|
248
|
-
field: `sortOrder[${index}].nullOrder`,
|
|
249
|
-
message: `sort null order "${field.nullOrder}" must be "nulls-first" or "nulls-last"`,
|
|
250
|
-
}));
|
|
251
|
-
}
|
|
252
|
-
const sourceColumn = this.definition.columns.find((column) => column.name === field.column);
|
|
253
|
-
if (sourceColumn === undefined) {
|
|
254
|
-
violations.push(this.violation({
|
|
255
|
-
level: 'error',
|
|
256
|
-
code: 'ICEBERG_SORT_COLUMN_EXISTS',
|
|
257
|
-
field: `sortOrder[${index}].column`,
|
|
258
|
-
message: `sort column "${field.column}" is not defined in columns`,
|
|
259
|
-
}));
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
if (field.transform === undefined) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const transform = (0, iceberg_1.parseIcebergTransform)(field.transform);
|
|
266
|
-
if (transform === null) {
|
|
267
|
-
violations.push(this.violation({
|
|
268
|
-
level: 'error',
|
|
269
|
-
code: 'ICEBERG_SORT_TRANSFORM_VALID',
|
|
270
|
-
field: `sortOrder[${index}].transform`,
|
|
271
|
-
message: `"${field.transform}" is not a valid Iceberg transform`,
|
|
272
|
-
}));
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const sourceType = (0, iceberg_1.parseIcebergType)(sourceColumn.type);
|
|
276
|
-
if (sourceType !== null && !(0, iceberg_1.transformLegalOnType)(transform, sourceType)) {
|
|
277
|
-
violations.push(this.violation({
|
|
278
|
-
level: 'error',
|
|
279
|
-
code: 'ICEBERG_SORT_TRANSFORM_TYPE_LEGAL',
|
|
280
|
-
field: `sortOrder[${index}].transform`,
|
|
281
|
-
message: `transform "${field.transform}" is not legal on `
|
|
282
|
-
+ `column "${field.column}" of type "${sourceColumn.type}"`,
|
|
283
|
-
}));
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
return violations;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Normalize a sort field into its identity key. An omitted transform and an
|
|
290
|
-
* explicit `identity` collapse to the same key so they count as duplicates.
|
|
291
|
-
*
|
|
292
|
-
* @param field Sort field to key.
|
|
293
|
-
* @returns The identity string for duplicate detection.
|
|
294
|
-
*/
|
|
295
|
-
sortFieldKey(field) {
|
|
296
|
-
if (field.transform === undefined) {
|
|
297
|
-
return `${field.column} identity:`;
|
|
298
|
-
}
|
|
299
|
-
const transform = (0, iceberg_1.parseIcebergTransform)(field.transform);
|
|
300
|
-
const normalizedTransform = transform
|
|
301
|
-
? `${transform.kind}:${transform.param ?? ''}`
|
|
302
|
-
: field.transform.trim().toLowerCase();
|
|
303
|
-
return `${field.column} ${normalizedTransform}`;
|
|
304
|
-
}
|
|
305
|
-
/// ICEBERG_FORMAT_VERSION_VALID — `formatVersion`, when set, is 1, 2, or 3.
|
|
306
|
-
formatVersionViolations() {
|
|
307
|
-
const { formatVersion, } = this.definition;
|
|
308
|
-
if (formatVersion === undefined || IcebergParquetTable.FORMAT_VERSIONS.has(formatVersion)) {
|
|
309
|
-
return [];
|
|
310
|
-
}
|
|
311
|
-
return [
|
|
312
|
-
this.violation({
|
|
313
|
-
level: 'error',
|
|
314
|
-
code: 'ICEBERG_FORMAT_VERSION_VALID',
|
|
315
|
-
field: 'formatVersion',
|
|
316
|
-
message: `Iceberg formatVersion "${formatVersion}" must be 1, 2, or 3`,
|
|
317
|
-
}),
|
|
318
|
-
];
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Validate the closed-domain Iceberg table properties. Keys outside the
|
|
322
|
-
* known set pass through unvalidated.
|
|
323
|
-
*
|
|
324
|
-
* Emits `ICEBERG_PROPERTY_ENUM_VALID`, `ICEBERG_PROPERTY_POSITIVE_INT`,
|
|
325
|
-
* `ICEBERG_PROPERTY_NON_NEGATIVE_INT`, `ICEBERG_PROPERTY_INT_RANGE`, and
|
|
326
|
-
* `ICEBERG_COMMIT_RETRY_ORDERING`.
|
|
327
|
-
*
|
|
328
|
-
* @returns Every table-property violation.
|
|
329
|
-
*/
|
|
330
|
-
tablePropertyViolations() {
|
|
331
|
-
const violations = [];
|
|
332
|
-
const properties = this.definition.tableProperties;
|
|
333
|
-
for (const [key, value,] of Object.entries(properties)) {
|
|
334
|
-
const allowed = IcebergParquetTable.ENUM_PROPERTIES[key];
|
|
335
|
-
if (allowed !== undefined && !allowed.includes(value)) {
|
|
336
|
-
violations.push(this.violation({
|
|
337
|
-
level: 'error',
|
|
338
|
-
code: 'ICEBERG_PROPERTY_ENUM_VALID',
|
|
339
|
-
field: `tableProperties["${key}"]`,
|
|
340
|
-
message: `table property "${key}" value "${value}" must be one of: ${allowed.join(', ')}`,
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
if (IcebergParquetTable.POSITIVE_INT_PROPERTIES.includes(key) && !isPositiveInteger(value)) {
|
|
344
|
-
violations.push(this.violation({
|
|
345
|
-
level: 'error',
|
|
346
|
-
code: 'ICEBERG_PROPERTY_POSITIVE_INT',
|
|
347
|
-
field: `tableProperties["${key}"]`,
|
|
348
|
-
message: `table property "${key}" value "${value}" must be a positive integer`,
|
|
349
|
-
}));
|
|
350
|
-
}
|
|
351
|
-
if (IcebergParquetTable.NON_NEGATIVE_INT_PROPERTIES.includes(key) && !isNonNegativeInteger(value)) {
|
|
352
|
-
violations.push(this.violation({
|
|
353
|
-
level: 'error',
|
|
354
|
-
code: 'ICEBERG_PROPERTY_NON_NEGATIVE_INT',
|
|
355
|
-
field: `tableProperties["${key}"]`,
|
|
356
|
-
message: `table property "${key}" value "${value}" must be a non-negative integer`,
|
|
357
|
-
}));
|
|
358
|
-
}
|
|
359
|
-
const range = IcebergParquetTable.INT_RANGE_PROPERTIES[key];
|
|
360
|
-
if (range !== undefined && !isIntegerInRange(value, range.min, range.max)) {
|
|
361
|
-
violations.push(this.violation({
|
|
362
|
-
level: 'error',
|
|
363
|
-
code: 'ICEBERG_PROPERTY_INT_RANGE',
|
|
364
|
-
field: `tableProperties["${key}"]`,
|
|
365
|
-
message: `table property "${key}" value "${value}" must be an integer between `
|
|
366
|
-
+ `${range.min} and ${range.max}`,
|
|
367
|
-
}));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
violations.push(...this.commitRetryOrderingViolations(properties));
|
|
371
|
-
return violations;
|
|
372
|
-
}
|
|
373
|
-
/// ICEBERG_COMMIT_RETRY_ORDERING — commit.retry waits obey
|
|
374
|
-
/// min-wait-ms <= max-wait-ms <= total-timeout-ms when all are positive ints.
|
|
375
|
-
commitRetryOrderingViolations(properties) {
|
|
376
|
-
const values = [
|
|
377
|
-
properties['commit.retry.min-wait-ms'],
|
|
378
|
-
properties['commit.retry.max-wait-ms'],
|
|
379
|
-
properties['commit.retry.total-timeout-ms'],
|
|
380
|
-
];
|
|
381
|
-
if (!values.every((value) => value !== undefined && isPositiveInteger(value))) {
|
|
382
|
-
return [];
|
|
383
|
-
}
|
|
384
|
-
const [min, max, total,] = values.map(Number);
|
|
385
|
-
if (min <= max && max <= total) {
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
return [
|
|
389
|
-
this.violation({
|
|
390
|
-
level: 'error',
|
|
391
|
-
code: 'ICEBERG_COMMIT_RETRY_ORDERING',
|
|
392
|
-
field: 'tableProperties["commit.retry.min-wait-ms"]',
|
|
393
|
-
message: 'commit.retry waits must satisfy min-wait-ms <= max-wait-ms <= total-timeout-ms',
|
|
394
|
-
}),
|
|
395
|
-
];
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Validate Iceberg identifier fields (the row-identity / equality-delete
|
|
399
|
-
* key). Equality deletes are a format-version 2+ feature, and identifier
|
|
400
|
-
* fields must be required primitive columns that are not float/double.
|
|
401
|
-
*
|
|
402
|
-
* Emits `ICEBERG_IDENTIFIER_NEEDS_FORMAT_V2`, `ICEBERG_IDENTIFIER_COLUMN_EXISTS`,
|
|
403
|
-
* `ICEBERG_IDENTIFIER_REQUIRED`, and `ICEBERG_IDENTIFIER_TYPE_PRIMITIVE`.
|
|
404
|
-
*
|
|
405
|
-
* @returns Every identifier-field violation.
|
|
406
|
-
*/
|
|
407
|
-
identifierFieldViolations() {
|
|
408
|
-
const violations = [];
|
|
409
|
-
const { identifierFields, formatVersion, } = this.definition;
|
|
410
|
-
if (identifierFields.length === 0) {
|
|
411
|
-
return [];
|
|
412
|
-
}
|
|
413
|
-
if (formatVersion === 1) {
|
|
414
|
-
violations.push(this.violation({
|
|
415
|
-
level: 'error',
|
|
416
|
-
code: 'ICEBERG_IDENTIFIER_NEEDS_FORMAT_V2',
|
|
417
|
-
field: 'identifierFields',
|
|
418
|
-
message: 'identifier fields require formatVersion 2 or higher (equality deletes are a v2 feature)',
|
|
419
|
-
}));
|
|
420
|
-
}
|
|
421
|
-
identifierFields.forEach((name, index) => {
|
|
422
|
-
const field = `identifierFields[${index}]`;
|
|
423
|
-
const column = this.definition.columns.find((candidate) => candidate.name === name);
|
|
424
|
-
if (column === undefined) {
|
|
425
|
-
violations.push(this.violation({
|
|
426
|
-
level: 'error',
|
|
427
|
-
code: 'ICEBERG_IDENTIFIER_COLUMN_EXISTS',
|
|
428
|
-
field,
|
|
429
|
-
message: `identifier field "${name}" is not defined in columns`,
|
|
430
|
-
}));
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
if (column.nullable === true) {
|
|
434
|
-
violations.push(this.violation({
|
|
435
|
-
level: 'error',
|
|
436
|
-
code: 'ICEBERG_IDENTIFIER_REQUIRED',
|
|
437
|
-
field,
|
|
438
|
-
message: `identifier field "${name}" must be required (nullable: false)`,
|
|
439
|
-
}));
|
|
440
|
-
}
|
|
441
|
-
const type = (0, iceberg_1.parseIcebergType)(column.type);
|
|
442
|
-
if (type === null || type.kind === iceberg_1.IcebergTypeKind.FLOAT || type.kind === iceberg_1.IcebergTypeKind.DOUBLE) {
|
|
443
|
-
violations.push(this.violation({
|
|
444
|
-
level: 'error',
|
|
445
|
-
code: 'ICEBERG_IDENTIFIER_TYPE_PRIMITIVE',
|
|
446
|
-
field,
|
|
447
|
-
message: `identifier field "${name}" must be a primitive type other than float or double`,
|
|
448
|
-
}));
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
return violations;
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Normalize a partition into its identity key. When the transform parses,
|
|
455
|
-
* collapse on `${kind}:${param}` so `day` and `DAY`, `bucket[16]` and
|
|
456
|
-
* `bucket[ 16 ]` are the same. When the transform doesn't parse, the
|
|
457
|
-
* lowercased trim of the raw string is the best identity we have.
|
|
458
|
-
*
|
|
459
|
-
* @param partition Partition to key.
|
|
460
|
-
* @returns The identity string for duplicate detection.
|
|
461
|
-
*/
|
|
462
|
-
partitionKey(partition) {
|
|
463
|
-
const transform = (0, iceberg_1.parseIcebergTransform)(partition.type);
|
|
464
|
-
const normalizedTransform = transform
|
|
465
|
-
? `${transform.kind}:${transform.param ?? ''}`
|
|
466
|
-
: partition.type.trim().toLowerCase();
|
|
467
|
-
return `${partition.name} ${normalizedTransform}`;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
exports.IcebergParquetTable = IcebergParquetTable;
|
|
471
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWNlYmVyZy1wYXJxdWV0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RhYmxlLXR5cGVzL2ljZWJlcmctcGFycXVldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7OztHQU1HOzs7QUFPSCw4Q0FFdUI7QUFDdkIsd0NBTW9CO0FBRXBCOzs7Ozs7R0FNRztBQUNILFNBQVMsaUJBQWlCLENBQUMsS0FBYTtJQUNwQyxPQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNwRCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLG9CQUFvQixDQUFDLEtBQWE7SUFDdkMsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQy9CLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFhLEVBQUUsR0FBVyxFQUFFLEdBQVc7SUFDN0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN2QixPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzdCLE9BQU8sTUFBTSxJQUFJLEdBQUcsSUFBSSxNQUFNLElBQUksR0FBRyxDQUFDO0FBQzFDLENBQUM7QUFFRCwrQkFBK0I7QUFDL0IsTUFBYSxtQkFBb0IsU0FBUSwwQkFBYTtJQUNsRDs7Ozs7T0FLRztJQUNJLGlCQUFpQixDQUFDLElBQVk7UUFDakMsT0FBTyxJQUFBLDRCQUFrQixFQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0ksbUJBQW1CO1FBQ3RCLE1BQU0sVUFBVSxHQUFnQixFQUFFLENBQUM7UUFDbkMsTUFBTSxFQUNGLFVBQVUsR0FDYixHQUFHLElB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QkFBcUIsSUFBSSx1REFBdUQ7aUJBQzVGLENBQUMsQ0FBQyxDQUFDO1lBQ1IsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO1FBRUgsT0FBTyxVQUFVLENBQUM7SUFDdEIsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ssWUFBWSxDQUFDLFNBQW9CO1FBQ3JDLE1BQU0sU0FBUyxHQUFHLElBQUEsK0JBQXFCLEVBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hELE1BQU0sbUJBQW1CLEdBQUcsU0FBUztZQUNqQyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxLQUFLLElBQUksRUFBRSxFQUFFO1lBQzlDLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzFDLE9BQU8sR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLG1CQUFtQixFQUFFLENBQUM7SUFDdEQsQ0FBQzs7QUF0ZEwsa0RBdWRDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJY2ViZXJnIChwYXJxdWV0KSB0YWJsZSB0eXBlLlxuICpcbiAqIEljZWJlcmcgcGFydGl0aW9ucyBhcmUgdHJhbnNmb3JtcyBhcHBsaWVkIHRvIGEgZGF0YSBjb2x1bW4sIHNvIGR1cGxpY2F0ZVxuICogZGV0ZWN0aW9uIGtleXMgb24gdGhlIChzb3VyY2UgY29sdW1uLCBub3JtYWxpemVkIHRyYW5zZm9ybSkgcGFpciDigJQgYHllYXIodHMpYFxuICogYW5kIGBtb250aCh0cylgIGFyZSBkaXN0aW5jdCwgd2hpbGUgYGRheWAgYW5kIGBEQVlgIGNvbGxhcHNlLlxuICovXG5cbmltcG9ydCB7XG4gICAgUGFydGl0aW9uLFxuICAgIFNvcnRGaWVsZCxcbiAgICBWaW9sYXRpb24sXG59IGZyb20gJy4uL21vZGVsJztcbmltcG9ydCB7XG4gICAgVGFibGVUeXBlQmFzZSxcbn0gZnJvbSAnLi4vdGFibGUtdHlwZSc7XG5pbXBvcnQge1xuICAgIEljZWJlcmdUeXBlS2luZCxcbiAgICBpc1ZhbGlkSWNlYmVyZ1R5cGUsXG4gICAgcGFyc2VJY2ViZXJnVHJhbnNmb3JtLFxuICAgIHBhcnNlSWNlYmVyZ1R5cGUsXG4gICAgdHJhbnNmb3JtTGVnYWxPblR5cGUsXG59IGZyb20gJy4uL2ljZWJlcmcnO1xuXG4vKipcbiAqIFdoZXRoZXIgYSBzdHJpbmcgaXMgYSBwb3NpdGl2ZSBpbnRlZ2VyIChkaWdpdHMgb25seSwgdmFsdWUgPiAwKS4gSWNlYmVyZ1xuICogdGFibGUtcHJvcGVydHkgdmFsdWVzIGFyZSBzdHJpbmdzLCBzbyBudW1lcmljIGNoZWNrcyBwYXJzZSB0aGUgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSBQcm9wZXJ0eSB2YWx1ZSBzdHJpbmcuXG4gKiBAcmV0dXJucyBUcnVlIHdoZW4gYHZhbHVlYCBpcyBhIHBvc2l0aXZlIGludGVnZXIuXG4gKi9cbmZ1bmN0aW9uIGlzUG9zaXRpdmVJbnRlZ2VyKHZhbHVlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gL15cXGQrJC8udGVzdCh2YWx1ZSkgJiYgTnVtYmVyKHZhbHVlKSA+IDA7XG59XG5cbi8qKlxuICogV2hldGhlciBhIHN0cmluZyBpcyBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyIChkaWdpdHMgb25seSwgdmFsdWUgPj0gMCkuXG4gKlxuICogQHBhcmFtIHZhbHVlIFByb3BlcnR5IHZhbHVlIHN0cmluZy5cbiAqIEByZXR1cm5zIFRydWUgd2hlbiBgdmFsdWVgIGlzIGEgbm9uLW5lZ2F0aXZlIGludGVnZXIuXG4gKi9cbmZ1bmN0aW9uIGlzTm9uTmVnYXRpdmVJbnRlZ2VyKHZhbHVlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gL15cXGQrJC8udGVzdCh2YWx1ZSk7XG59XG5cbi8qKlxuICogV2hldGhlciBhIHN0cmluZyBpcyBhbiBpbnRlZ2VyIHdpdGhpbiBhbiBpbmNsdXNpdmUgcmFuZ2UuXG4gKlxuICogQHBhcmFtIHZhbHVlIFByb3BlcnR5IHZhbHVlIHN0cmluZy5cbiAqIEBwYXJhbSBtaW4gSW5jbHVzaXZlIGxvd2VyIGJvdW5kLlxuICogQHBhcmFtIG1heCBJbmNsdXNpdmUgdXBwZXIgYm91bmQuXG4gKiBAcmV0dXJucyBUcnVlIHdoZW4gYHZhbHVlYCBpcyBhbiBpbnRlZ2VyIGluIGBbbWluLCBtYXhdYC5cbiAqL1xuZnVuY3Rpb24gaXNJbnRlZ2VySW5SYW5nZSh2YWx1ZTogc3RyaW5nLCBtaW46IG51bWJlciwgbWF4OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICBpZiAoIS9eXFxkKyQvLnRlc3QodmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3QgcGFyc2VkID0gTnVtYmVyKHZhbHVlKTtcbiAgICByZXR1cm4gcGFyc2VkID49IG1pbiAmJiBwYXJzZWQgPD0gbWF4O1xufVxuXG4vKiogSWNlYmVyZyAocGFycXVldCkgdGFibGUuICovXG5leHBvcnQgY2xhc3MgSWNlYmVyZ1BhcnF1ZXRUYWJsZSBleHRlbmRzIFRhYmxlVHlwZUJhc2Uge1xuICAgIC8qKlxuICAgICAqIFZhbGlkYXRlIGEgY29sdW1uIHR5cGUgYWdhaW5zdCB0aGUgSWNlYmVyZyB2MCB0eXBlIHJlZ2lzdHJ5LlxuICAgICAqXG4gICAgICogQHBhcmFtIHR5cGUgVHlwZSBzdHJpbmcgZnJvbSBhIGNvbHVtbiBkZWZpbml0aW9uLlxuICAgICAqIEByZXR1cm5zIFRydWUgd2hlbiB0aGUgdHlwZSBpcyBhIHZhbGlkIEljZWJlcmcgdHlwZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgaXNWYWxpZENvbHVtblR5cGUodHlwZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBpc1ZhbGlkSWNlYmVyZ1R5cGUodHlwZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWNlYmVyZyBwYXJ0aXRpb24gcnVsZXMuXG4gICAgICpcbiAgICAgKiBFbWl0czogYE5PX0RVUExJQ0FURV9QQVJUSVRJT05TYCAoa2V5ZWQgYnkgYCR7bmFtZX0gJHt0cmFuc2Zvcm1LaW5kfToke3BhcmFtfWAsXG4gICAgICogZmFsbGluZyBiYWNrIHRvIGEgbG93ZXJjYXNlZCB0cmltIG9mIHRoZSByYXcgdHJhbnNmb3JtIHdoZW4gaXQgZG9lc24ndFxuICAgICAqIHBhcnNlLCBzbyB3aGl0ZXNwYWNlIC8gY2FzZSB2YXJpYW50cyBvZiB0aGUgc2FtZSB0cmFuc2Zvcm0gY29sbGFwc2UpLFxuICAgICAqIGBJQ0VCRVJHX1RSQU5TRk9STV9TT1VSQ0VfRVhJU1RTYCwgYElDRUJFUkdfVFJBTlNGT1JNX1ZBTElEYCwgYW5kXG4gICAgICogYElDRUJFUkdfVFJBTlNGT1JNX1NPVVJDRV9UWVBFX0xFR0FMYC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIEV2ZXJ5IHBhcnRpdGlvbi1yZWxhdGVkIHZpb2xhdGlvbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgcGFydGl0aW9uVmlvbGF0aW9ucygpOiBWaW9sYXRpb25bXSB7XG4gICAgICAgIGNvbnN0IHZpb2xhdGlvbnM6IFZpb2xhdGlvbltdID0gW107XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIHBhcnRpdGlvbnMsXG4gICAgICAgIH0gPSB0aGlzLmRlZmluaXRpb247XG5cbiAgICAgICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgICAgICBwYXJ0aXRpb25zLmZvckVhY2goKHBhcnRpdGlvbiwgaW5kZXgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGtleSA9IHRoaXMucGFydGl0aW9uS2V5KHBhcnRpdGlvbik7XG4gICAgICAgICAgICBpZiAoc2Vlbi5oYXMoa2V5KSkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnTk9fRFVQTElDQVRFX1BBUlRJVElPTlMnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHBhcnRpdGlvbnNbJHtpbmRleH1dYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYGR1cGxpY2F0ZSBwYXJ0aXRpb246IFwiJHtwYXJ0aXRpb24ubmFtZX1cIiB3aXRoIHRyYW5zZm9ybSBcIiR7cGFydGl0aW9uLnR5cGV9XCJgLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlZW4uYWRkKGtleSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHBhcnRpdGlvbnMuZm9yRWFjaCgocGFydGl0aW9uLCBpbmRleCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc291cmNlQ29sdW1uID0gdGhpcy5kZWZpbml0aW9uLmNvbHVtbnMuZmluZCgoY29sdW1uKSA9PiBjb2x1bW4ubmFtZSA9PT0gcGFydGl0aW9uLm5hbWUpO1xuICAgICAgICAgICAgaWYgKHNvdXJjZUNvbHVtbiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1RSQU5TRk9STV9TT1VSQ0VfRVhJU1RTJyxcbiAgICAgICAgICAgICAgICAgICAgZmllbGQ6IGBwYXJ0aXRpb25zWyR7aW5kZXh9XS5uYW1lYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYEljZWJlcmcgcGFydGl0aW9uIHNvdXJjZSBjb2x1bW4gXCIke3BhcnRpdGlvbi5uYW1lfVwiIGlzIG5vdCBkZWZpbmVkIGluIGNvbHVtbnNgLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCB0cmFuc2Zvcm0gPSBwYXJzZUljZWJlcmdUcmFuc2Zvcm0ocGFydGl0aW9uLnR5cGUpO1xuICAgICAgICAgICAgaWYgKHRyYW5zZm9ybSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnSUNFQkVSR19UUkFOU0ZPUk1fVkFMSUQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHBhcnRpdGlvbnNbJHtpbmRleH1dLnR5cGVgLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgXCIke3BhcnRpdGlvbi50eXBlfVwiIGlzIG5vdCBhIHZhbGlkIEljZWJlcmcgcGFydGl0aW9uIHRyYW5zZm9ybWAsXG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHNvdXJjZVR5cGUgPSBwYXJzZUljZWJlcmdUeXBlKHNvdXJjZUNvbHVtbi50eXBlKTtcbiAgICAgICAgICAgIGlmIChzb3VyY2VUeXBlICE9PSBudWxsICYmICF0cmFuc2Zvcm1MZWdhbE9uVHlwZSh0cmFuc2Zvcm0sIHNvdXJjZVR5cGUpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1RSQU5TRk9STV9TT1VSQ0VfVFlQRV9MRUdBTCcsXG4gICAgICAgICAgICAgICAgICAgIGZpZWxkOiBgcGFydGl0aW9uc1ske2luZGV4fV0udHlwZWAsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGB0cmFuc2Zvcm0gXCIke3BhcnRpdGlvbi50eXBlfVwiIGlzIG5vdCBsZWdhbCBvbiBgXG4gICAgICAgICAgICAgICAgICAgICAgICArIGBjb2x1bW4gXCIke3BhcnRpdGlvbi5uYW1lfVwiIG9mIHR5cGUgXCIke3NvdXJjZUNvbHVtbi50eXBlfVwiYCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiB2aW9sYXRpb25zO1xuICAgIH1cblxuICAgIC8vLyBUaGUgSWNlYmVyZyBmb3JtYXQgdmVyc2lvbnMgdGhpcyB2YWxpZGF0b3IgdW5kZXJzdGFuZHMuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRk9STUFUX1ZFUlNJT05TID0gbmV3IFNldChbXG4gICAgICAgIDEsXG4gICAgICAgIDIsXG4gICAgICAgIDMsXG4gICAgXSk7XG5cbiAgICAvLy8gVGFibGUtcHJvcGVydHkga2V5cyB3aG9zZSB2YWx1ZSBtdXN0IGNvbWUgZnJvbSBhIGNsb3NlZCBzZXQuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRU5VTV9QUk9QRVJUSUVTOiBSZWNvcmQ8c3RyaW5nLCByZWFkb25seSBzdHJpbmdbXT4gPSB7XG4gICAgICAgICd3cml0ZS5mb3JtYXQuZGVmYXVsdCc6IFtcbiAgICAgICAgICAgICdwYXJxdWV0JyxcbiAgICAgICAgICAgICdhdnJvJyxcbiAgICAgICAgICAgICdvcmMnLFxuICAgICAgICBdLFxuICAgICAgICAnd3JpdGUucGFycXVldC5jb21wcmVzc2lvbi1jb2RlYyc6IFtcbiAgICAgICAgICAgICd6c3RkJyxcbiAgICAgICAgICAgICdnemlwJyxcbiAgICAgICAgICAgICdzbmFwcHknLFxuICAgICAgICAgICAgJ2x6NCcsXG4gICAgICAgICAgICAnbm9uZScsXG4gICAgICAgIF0sXG4gICAgICAgICd3cml0ZS5hdnJvLmNvbXByZXNzaW9uLWNvZGVjJzogW1xuICAgICAgICAgICAgJ2d6aXAnLFxuICAgICAgICAgICAgJ3pzdGQnLFxuICAgICAgICAgICAgJ3NuYXBweScsXG4gICAgICAgICAgICAndW5jb21wcmVzc2VkJyxcbiAgICAgICAgXSxcbiAgICAgICAgJ3dyaXRlLm9yYy5jb21wcmVzc2lvbi1jb2RlYyc6IFtcbiAgICAgICAgICAgICd6c3RkJyxcbiAgICAgICAgICAgICdsejQnLFxuICAgICAgICAgICAgJ2x6bycsXG4gICAgICAgICAgICAnemxpYicsXG4gICAgICAgICAgICAnc25hcHB5JyxcbiAgICAgICAgICAgICdub25lJyxcbiAgICAgICAgXSxcbiAgICAgICAgJ3dyaXRlLmRpc3RyaWJ1dGlvbi1tb2RlJzogW1xuICAgICAgICAgICAgJ25vbmUnLFxuICAgICAgICAgICAgJ2hhc2gnLFxuICAgICAgICAgICAgJ3JhbmdlJyxcbiAgICAgICAgXSxcbiAgICAgICAgJ3dyaXRlLm1ldGFkYXRhLmNvbXByZXNzaW9uLWNvZGVjJzogW1xuICAgICAgICAgICAgJ25vbmUnLFxuICAgICAgICAgICAgJ2d6aXAnLFxuICAgICAgICBdLFxuICAgIH07XG5cbiAgICAvLy8gVGFibGUtcHJvcGVydHkga2V5cyB3aG9zZSB2YWx1ZSBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlci5cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBQT1NJVElWRV9JTlRfUFJPUEVSVElFUzogcmVhZG9ubHkgc3RyaW5nW10gPSBbXG4gICAgICAgICd3cml0ZS50YXJnZXQtZmlsZS1zaXplLWJ5dGVzJyxcbiAgICAgICAgJ2hpc3RvcnkuZXhwaXJlLm1heC1zbmFwc2hvdC1hZ2UtbXMnLFxuICAgICAgICAnaGlzdG9yeS5leHBpcmUubWluLXNuYXBzaG90cy10by1rZWVwJyxcbiAgICAgICAgJ2hpc3RvcnkuZXhwaXJlLm1heC1yZWYtYWdlLW1zJyxcbiAgICAgICAgJ3dyaXRlLm1ldGFkYXRhLnByZXZpb3VzLXZlcnNpb25zLW1heCcsXG4gICAgICAgICdjb21taXQucmV0cnkubWluLXdhaXQtbXMnLFxuICAgICAgICAnY29tbWl0LnJldHJ5Lm1heC13YWl0LW1zJyxcbiAgICAgICAgJ2NvbW1pdC5yZXRyeS50b3RhbC10aW1lb3V0LW1zJyxcbiAgICBdO1xuXG4gICAgLy8vIFRhYmxlLXByb3BlcnR5IGtleXMgd2hvc2UgdmFsdWUgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyLlxuICAgIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IE5PTl9ORUdBVElWRV9JTlRfUFJPUEVSVElFUzogcmVhZG9ubHkgc3RyaW5nW10gPSBbXG4gICAgICAgICdjb21taXQucmV0cnkubnVtLXJldHJpZXMnLFxuICAgIF07XG5cbiAgICAvLy8gVGFibGUtcHJvcGVydHkga2V5cyB3aG9zZSB2YWx1ZSBtdXN0IGJlIGFuIGludGVnZXIgd2l0aGluIGFuIGluY2x1c2l2ZSByYW5nZS5cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBJTlRfUkFOR0VfUFJPUEVSVElFUzogUmVjb3JkPHN0cmluZywgeyBtaW46IG51bWJlcjsgbWF4OiBudW1iZXIgfT4gPSB7XG4gICAgICAgICd3cml0ZS5wYXJxdWV0LmNvbXByZXNzaW9uLWxldmVsJzoge1xuICAgICAgICAgICAgbWluOiAxLFxuICAgICAgICAgICAgbWF4OiAyMixcbiAgICAgICAgfSxcbiAgICB9O1xuXG4gICAgLy8vIExlZ2FsIHNvcnQgZGlyZWN0aW9ucyBhbmQgbnVsbCBvcmRlcmluZ3MuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgU09SVF9ESVJFQ1RJT05TID0gbmV3IFNldChbXG4gICAgICAgICdhc2MnLFxuICAgICAgICAnZGVzYycsXG4gICAgXSk7XG5cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBTT1JUX05VTExfT1JERVJTID0gbmV3IFNldChbXG4gICAgICAgICdudWxscy1maXJzdCcsXG4gICAgICAgICdudWxscy1sYXN0JyxcbiAgICBdKTtcblxuICAgIC8qKlxuICAgICAqIEljZWJlcmcgZW5naW5lLXNwZWNpZmljIHJ1bGVzOiBmb3JtYXQgdmVyc2lvbiwgdGFibGUgcHJvcGVydGllcywgYW5kIHNvcnRcbiAgICAgKiBvcmRlci5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIEV2ZXJ5IGVuZ2luZS1zcGVjaWZpYyB2aW9sYXRpb24uXG4gICAgICovXG4gICAgcHVibGljIGVuZ2luZVNwZWNpZmljVmlvbGF0aW9ucygpOiBWaW9sYXRpb25bXSB7XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAuLi50aGlzLmZvcm1hdFZlcnNpb25WaW9sYXRpb25zKCksXG4gICAgICAgICAgICAuLi50aGlzLnRhYmxlUHJvcGVydHlWaW9sYXRpb25zKCksXG4gICAgICAgICAgICAuLi50aGlzLnNvcnRPcmRlclZpb2xhdGlvbnMoKSxcbiAgICAgICAgICAgIC4uLnRoaXMuaWRlbnRpZmllckZpZWxkVmlvbGF0aW9ucygpLFxuICAgICAgICBdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFZhbGlkYXRlIHRoZSBJY2ViZXJnIHNvcnQgb3JkZXIuXG4gICAgICpcbiAgICAgKiBFbWl0cyBgTk9fRFVQTElDQVRFX1NPUlRfRklFTERTYCAoa2V5ZWQgYnkgc291cmNlIGNvbHVtbiArIG5vcm1hbGl6ZWRcbiAgICAgKiB0cmFuc2Zvcm0pLCBgSUNFQkVSR19TT1JUX0NPTFVNTl9FWElTVFNgLCBgSUNFQkVSR19TT1JUX0RJUkVDVElPTl9WQUxJRGAsXG4gICAgICogYElDRUJFUkdfU09SVF9OVUxMX09SREVSX1ZBTElEYCwgYElDRUJFUkdfU09SVF9UUkFOU0ZPUk1fVkFMSURgLCBhbmRcbiAgICAgKiBgSUNFQkVSR19TT1JUX1RSQU5TRk9STV9UWVBFX0xFR0FMYC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIEV2ZXJ5IHNvcnQtb3JkZXIgdmlvbGF0aW9uLlxuICAgICAqL1xuICAgIHByaXZhdGUgc29ydE9yZGVyVmlvbGF0aW9ucygpOiBWaW9sYXRpb25bXSB7XG4gICAgICAgIGNvbnN0IHZpb2xhdGlvbnM6IFZpb2xhdGlvbltdID0gW107XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIHNvcnRPcmRlcixcbiAgICAgICAgfSA9IHRoaXMuZGVmaW5pdGlvbjtcblxuICAgICAgICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgICAgIHNvcnRPcmRlci5mb3JFYWNoKChmaWVsZCwgaW5kZXgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGtleSA9IHRoaXMuc29ydEZpZWxkS2V5KGZpZWxkKTtcbiAgICAgICAgICAgIGlmIChzZWVuLmhhcyhrZXkpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdOT19EVVBMSUNBVEVfU09SVF9GSUVMRFMnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHNvcnRPcmRlclske2luZGV4fV1gLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgZHVwbGljYXRlIHNvcnQgZmllbGQ6IFwiJHtmaWVsZC5jb2x1bW59XCJgXG4gICAgICAgICAgICAgICAgICAgICAgICArIGAke2ZpZWxkLnRyYW5zZm9ybSA/IGAgd2l0aCB0cmFuc2Zvcm0gXCIke2ZpZWxkLnRyYW5zZm9ybX1cImAgOiAnJ31gLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlZW4uYWRkKGtleSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHNvcnRPcmRlci5mb3JFYWNoKChmaWVsZCwgaW5kZXgpID0+IHtcbiAgICAgICAgICAgIGlmICghSWNlYmVyZ1BhcnF1ZXRUYWJsZS5TT1JUX0RJUkVDVElPTlMuaGFzKGZpZWxkLmRpcmVjdGlvbikpIHtcbiAgICAgICAgICAgICAgICB2aW9sYXRpb25zLnB1c2godGhpcy52aW9sYXRpb24oe1xuICAgICAgICAgICAgICAgICAgICBsZXZlbDogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfU09SVF9ESVJFQ1RJT05fVkFMSUQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHNvcnRPcmRlclske2luZGV4fV0uZGlyZWN0aW9uYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYHNvcnQgZGlyZWN0aW9uIFwiJHtmaWVsZC5kaXJlY3Rpb259XCIgbXVzdCBiZSBcImFzY1wiIG9yIFwiZGVzY1wiYCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIUljZWJlcmdQYXJxdWV0VGFibGUuU09SVF9OVUxMX09SREVSUy5oYXMoZmllbGQubnVsbE9yZGVyKSkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnSUNFQkVSR19TT1JUX05VTExfT1JERVJfVkFMSUQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHNvcnRPcmRlclske2luZGV4fV0ubnVsbE9yZGVyYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYHNvcnQgbnVsbCBvcmRlciBcIiR7ZmllbGQubnVsbE9yZGVyfVwiIG11c3QgYmUgXCJudWxscy1maXJzdFwiIG9yIFwibnVsbHMtbGFzdFwiYCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHNvdXJjZUNvbHVtbiA9IHRoaXMuZGVmaW5pdGlvbi5jb2x1bW5zLmZpbmQoKGNvbHVtbikgPT4gY29sdW1uLm5hbWUgPT09IGZpZWxkLmNvbHVtbik7XG4gICAgICAgICAgICBpZiAoc291cmNlQ29sdW1uID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB2aW9sYXRpb25zLnB1c2godGhpcy52aW9sYXRpb24oe1xuICAgICAgICAgICAgICAgICAgICBsZXZlbDogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfU09SVF9DT0xVTU5fRVhJU1RTJyxcbiAgICAgICAgICAgICAgICAgICAgZmllbGQ6IGBzb3J0T3JkZXJbJHtpbmRleH1dLmNvbHVtbmAsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBzb3J0IGNvbHVtbiBcIiR7ZmllbGQuY29sdW1ufVwiIGlzIG5vdCBkZWZpbmVkIGluIGNvbHVtbnNgLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChmaWVsZC50cmFuc2Zvcm0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHRyYW5zZm9ybSA9IHBhcnNlSWNlYmVyZ1RyYW5zZm9ybShmaWVsZC50cmFuc2Zvcm0pO1xuICAgICAgICAgICAgaWYgKHRyYW5zZm9ybSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnSUNFQkVSR19TT1JUX1RSQU5TRk9STV9WQUxJRCcsXG4gICAgICAgICAgICAgICAgICAgIGZpZWxkOiBgc29ydE9yZGVyWyR7aW5kZXh9XS50cmFuc2Zvcm1gLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgXCIke2ZpZWxkLnRyYW5zZm9ybX1cIiBpcyBub3QgYSB2YWxpZCBJY2ViZXJnIHRyYW5zZm9ybWAsXG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHNvdXJjZVR5cGUgPSBwYXJzZUljZWJlcmdUeXBlKHNvdXJjZUNvbHVtbi50eXBlKTtcbiAgICAgICAgICAgIGlmIChzb3VyY2VUeXBlICE9PSBudWxsICYmICF0cmFuc2Zvcm1MZWdhbE9uVHlwZSh0cmFuc2Zvcm0sIHNvdXJjZVR5cGUpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1NPUlRfVFJBTlNGT1JNX1RZUEVfTEVHQUwnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHNvcnRPcmRlclske2luZGV4fV0udHJhbnNmb3JtYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYHRyYW5zZm9ybSBcIiR7ZmllbGQudHJhbnNmb3JtfVwiIGlzIG5vdCBsZWdhbCBvbiBgXG4gICAgICAgICAgICAgICAgICAgICAgICArIGBjb2x1bW4gXCIke2ZpZWxkLmNvbHVtbn1cIiBvZiB0eXBlIFwiJHtzb3VyY2VDb2x1bW4udHlwZX1cImAsXG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gdmlvbGF0aW9ucztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBOb3JtYWxpemUgYSBzb3J0IGZpZWxkIGludG8gaXRzIGlkZW50aXR5IGtleS4gQW4gb21pdHRlZCB0cmFuc2Zvcm0gYW5kIGFuXG4gICAgICogZXhwbGljaXQgYGlkZW50aXR5YCBjb2xsYXBzZSB0byB0aGUgc2FtZSBrZXkgc28gdGhleSBjb3VudCBhcyBkdXBsaWNhdGVzLlxuICAgICAqXG4gICAgICogQHBhcmFtIGZpZWxkIFNvcnQgZmllbGQgdG8ga2V5LlxuICAgICAqIEByZXR1cm5zIFRoZSBpZGVudGl0eSBzdHJpbmcgZm9yIGR1cGxpY2F0ZSBkZXRlY3Rpb24uXG4gICAgICovXG4gICAgcHJpdmF0ZSBzb3J0RmllbGRLZXkoZmllbGQ6IFNvcnRGaWVsZCk6IHN0cmluZyB7XG4gICAgICAgIGlmIChmaWVsZC50cmFuc2Zvcm0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIGAke2ZpZWxkLmNvbHVtbn0gaWRlbnRpdHk6YDtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB0cmFuc2Zvcm0gPSBwYXJzZUljZWJlcmdUcmFuc2Zvcm0oZmllbGQudHJhbnNmb3JtKTtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZFRyYW5zZm9ybSA9IHRyYW5zZm9ybVxuICAgICAgICAgICAgPyBgJHt0cmFuc2Zvcm0ua2luZH06JHt0cmFuc2Zvcm0ucGFyYW0gPz8gJyd9YFxuICAgICAgICAgICAgOiBmaWVsZC50cmFuc2Zvcm0udHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIHJldHVybiBgJHtmaWVsZC5jb2x1bW59ICR7bm9ybWFsaXplZFRyYW5zZm9ybX1gO1xuICAgIH1cblxuICAgIC8vLyBJQ0VCRVJHX0ZPUk1BVF9WRVJTSU9OX1ZBTElEIOKAlCBgZm9ybWF0VmVyc2lvbmAsIHdoZW4gc2V0LCBpcyAxLCAyLCBvciAzLlxuICAgIHByaXZhdGUgZm9ybWF0VmVyc2lvblZpb2xhdGlvbnMoKTogVmlvbGF0aW9uW10ge1xuICAgICAgICBjb25zdCB7XG4gICAgICAgICAgICBmb3JtYXRWZXJzaW9uLFxuICAgICAgICB9ID0gdGhpcy5kZWZpbml0aW9uO1xuICAgICAgICBpZiAoZm9ybWF0VmVyc2lvbiA9PT0gdW5kZWZpbmVkIHx8IEljZWJlcmdQYXJxdWV0VGFibGUuRk9STUFUX1ZFUlNJT05TLmhhcyhmb3JtYXRWZXJzaW9uKSkge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICB0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfRk9STUFUX1ZFUlNJT05fVkFMSUQnLFxuICAgICAgICAgICAgICAgIGZpZWxkOiAnZm9ybWF0VmVyc2lvbicsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYEljZWJlcmcgZm9ybWF0VmVyc2lvbiBcIiR7Zm9ybWF0VmVyc2lvbn1cIiBtdXN0IGJlIDEsIDIsIG9yIDNgLFxuICAgICAgICAgICAgfSksXG4gICAgICAgIF07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVmFsaWRhdGUgdGhlIGNsb3NlZC1kb21haW4gSWNlYmVyZyB0YWJsZSBwcm9wZXJ0aWVzLiBLZXlzIG91dHNpZGUgdGhlXG4gICAgICoga25vd24gc2V0IHBhc3MgdGhyb3VnaCB1bnZhbGlkYXRlZC5cbiAgICAgKlxuICAgICAqIEVtaXRzIGBJQ0VCRVJHX1BST1BFUlRZX0VOVU1fVkFMSURgLCBgSUNFQkVSR19QUk9QRVJUWV9QT1NJVElWRV9JTlRgLFxuICAgICAqIGBJQ0VCRVJHX1BST1BFUlRZX05PTl9ORUdBVElWRV9JTlRgLCBgSUNFQkVSR19QUk9QRVJUWV9JTlRfUkFOR0VgLCBhbmRcbiAgICAgKiBgSUNFQkVSR19DT01NSVRfUkVUUllfT1JERVJJTkdgLlxuICAgICAqXG4gICAgICogQHJldHVybnMgRXZlcnkgdGFibGUtcHJvcGVydHkgdmlvbGF0aW9uLlxuICAgICAqL1xuICAgIHByaXZhdGUgdGFibGVQcm9wZXJ0eVZpb2xhdGlvbnMoKTogVmlvbGF0aW9uW10ge1xuICAgICAgICBjb25zdCB2aW9sYXRpb25zOiBWaW9sYXRpb25bXSA9IFtdO1xuICAgICAgICBjb25zdCBwcm9wZXJ0aWVzID0gdGhpcy5kZWZpbml0aW9uLnRhYmxlUHJvcGVydGllcztcblxuICAgICAgICBmb3IgKGNvbnN0IFtcbiAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgIHZhbHVlLFxuICAgICAgICBdIG9mIE9iamVjdC5lbnRyaWVzKHByb3BlcnRpZXMpKSB7XG4gICAgICAgICAgICBjb25zdCBhbGxvd2VkID0gSWNlYmVyZ1BhcnF1ZXRUYWJsZS5FTlVNX1BST1BFUlRJRVNba2V5XTtcbiAgICAgICAgICAgIGlmIChhbGxvd2VkICE9PSB1bmRlZmluZWQgJiYgIWFsbG93ZWQuaW5jbHVkZXModmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1BST1BFUlRZX0VOVU1fVkFMSUQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHRhYmxlUHJvcGVydGllc1tcIiR7a2V5fVwiXWAsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGB0YWJsZSBwcm9wZXJ0eSBcIiR7a2V5fVwiIHZhbHVlIFwiJHt2YWx1ZX1cIiBtdXN0IGJlIG9uZSBvZjogJHthbGxvd2VkLmpvaW4oJywgJyl9YCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChJY2ViZXJnUGFycXVldFRhYmxlLlBPU0lUSVZFX0lOVF9QUk9QRVJUSUVTLmluY2x1ZGVzKGtleSkgJiYgIWlzUG9zaXRpdmVJbnRlZ2VyKHZhbHVlKSkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnSUNFQkVSR19QUk9QRVJUWV9QT1NJVElWRV9JTlQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHRhYmxlUHJvcGVydGllc1tcIiR7a2V5fVwiXWAsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGB0YWJsZSBwcm9wZXJ0eSBcIiR7a2V5fVwiIHZhbHVlIFwiJHt2YWx1ZX1cIiBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlcmAsXG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoSWNlYmVyZ1BhcnF1ZXRUYWJsZS5OT05fTkVHQVRJVkVfSU5UX1BST1BFUlRJRVMuaW5jbHVkZXMoa2V5KSAmJiAhaXNOb25OZWdhdGl2ZUludGVnZXIodmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1BST1BFUlRZX05PTl9ORUdBVElWRV9JTlQnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZDogYHRhYmxlUHJvcGVydGllc1tcIiR7a2V5fVwiXWAsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGB0YWJsZSBwcm9wZXJ0eSBcIiR7a2V5fVwiIHZhbHVlIFwiJHt2YWx1ZX1cIiBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIGludGVnZXJgLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgcmFuZ2UgPSBJY2ViZXJnUGFycXVldFRhYmxlLklOVF9SQU5HRV9QUk9QRVJUSUVTW2tleV07XG4gICAgICAgICAgICBpZiAocmFuZ2UgIT09IHVuZGVmaW5lZCAmJiAhaXNJbnRlZ2VySW5SYW5nZSh2YWx1ZSwgcmFuZ2UubWluLCByYW5nZS5tYXgpKSB7XG4gICAgICAgICAgICAgICAgdmlvbGF0aW9ucy5wdXNoKHRoaXMudmlvbGF0aW9uKHtcbiAgICAgICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgIGNvZGU6ICdJQ0VCRVJHX1BST1BFUlRZX0lOVF9SQU5HRScsXG4gICAgICAgICAgICAgICAgICAgIGZpZWxkOiBgdGFibGVQcm9wZXJ0aWVzW1wiJHtrZXl9XCJdYCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYHRhYmxlIHByb3BlcnR5IFwiJHtrZXl9XCIgdmFsdWUgXCIke3ZhbHVlfVwiIG11c3QgYmUgYW4gaW50ZWdlciBiZXR3ZWVuIGBcbiAgICAgICAgICAgICAgICAgICAgICAgICsgYCR7cmFuZ2UubWlufSBhbmQgJHtyYW5nZS5tYXh9YCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB2aW9sYXRpb25zLnB1c2goLi4udGhpcy5jb21taXRSZXRyeU9yZGVyaW5nVmlvbGF0aW9ucyhwcm9wZXJ0aWVzKSk7XG5cbiAgICAgICAgcmV0dXJuIHZpb2xhdGlvbnM7XG4gICAgfVxuXG4gICAgLy8vIElDRUJFUkdfQ09NTUlUX1JFVFJZX09SREVSSU5HIOKAlCBjb21taXQucmV0cnkgd2FpdHMgb2JleVxuICAgIC8vLyBtaW4td2FpdC1tcyA8PSBtYXgtd2FpdC1tcyA8PSB0b3RhbC10aW1lb3V0LW1zIHdoZW4gYWxsIGFyZSBwb3NpdGl2ZSBpbnRzLlxuICAgIHByaXZhdGUgY29tbWl0UmV0cnlPcmRlcmluZ1Zpb2xhdGlvbnMocHJvcGVydGllczogUmVjb3JkPHN0cmluZywgc3RyaW5nPik6IFZpb2xhdGlvbltdIHtcbiAgICAgICAgY29uc3QgdmFsdWVzID0gW1xuICAgICAgICAgICAgcHJvcGVydGllc1snY29tbWl0LnJldHJ5Lm1pbi13YWl0LW1zJ10sXG4gICAgICAgICAgICBwcm9wZXJ0aWVzWydjb21taXQucmV0cnkubWF4LXdhaXQtbXMnXSxcbiAgICAgICAgICAgIHByb3BlcnRpZXNbJ2NvbW1pdC5yZXRyeS50b3RhbC10aW1lb3V0LW1zJ10sXG4gICAgICAgIF07XG4gICAgICAgIGlmICghdmFsdWVzLmV2ZXJ5KCh2YWx1ZSkgPT4gdmFsdWUgIT09IHVuZGVmaW5lZCAmJiBpc1Bvc2l0aXZlSW50ZWdlcih2YWx1ZSkpKSB7XG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgW1xuICAgICAgICAgICAgbWluLFxuICAgICAgICAgICAgbWF4LFxuICAgICAgICAgICAgdG90YWwsXG4gICAgICAgIF0gPSB2YWx1ZXMubWFwKE51bWJlcik7XG4gICAgICAgIGlmIChtaW4gPD0gbWF4ICYmIG1heCA8PSB0b3RhbCkge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICB0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfQ09NTUlUX1JFVFJZX09SREVSSU5HJyxcbiAgICAgICAgICAgICAgICBmaWVsZDogJ3RhYmxlUHJvcGVydGllc1tcImNvbW1pdC5yZXRyeS5taW4td2FpdC1tc1wiXScsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogJ2NvbW1pdC5yZXRyeSB3YWl0cyBtdXN0IHNhdGlzZnkgbWluLXdhaXQtbXMgPD0gbWF4LXdhaXQtbXMgPD0gdG90YWwtdGltZW91dC1tcycsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBWYWxpZGF0ZSBJY2ViZXJnIGlkZW50aWZpZXIgZmllbGRzICh0aGUgcm93LWlkZW50aXR5IC8gZXF1YWxpdHktZGVsZXRlXG4gICAgICoga2V5KS4gRXF1YWxpdHkgZGVsZXRlcyBhcmUgYSBmb3JtYXQtdmVyc2lvbiAyKyBmZWF0dXJlLCBhbmQgaWRlbnRpZmllclxuICAgICAqIGZpZWxkcyBtdXN0IGJlIHJlcXVpcmVkIHByaW1pdGl2ZSBjb2x1bW5zIHRoYXQgYXJlIG5vdCBmbG9hdC9kb3VibGUuXG4gICAgICpcbiAgICAgKiBFbWl0cyBgSUNFQkVSR19JREVOVElGSUVSX05FRURTX0ZPUk1BVF9WMmAsIGBJQ0VCRVJHX0lERU5USUZJRVJfQ09MVU1OX0VYSVNUU2AsXG4gICAgICogYElDRUJFUkdfSURFTlRJRklFUl9SRVFVSVJFRGAsIGFuZCBgSUNFQkVSR19JREVOVElGSUVSX1RZUEVfUFJJTUlUSVZFYC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIEV2ZXJ5IGlkZW50aWZpZXItZmllbGQgdmlvbGF0aW9uLlxuICAgICAqL1xuICAgIHByaXZhdGUgaWRlbnRpZmllckZpZWxkVmlvbGF0aW9ucygpOiBWaW9sYXRpb25bXSB7XG4gICAgICAgIGNvbnN0IHZpb2xhdGlvbnM6IFZpb2xhdGlvbltdID0gW107XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIGlkZW50aWZpZXJGaWVsZHMsXG4gICAgICAgICAgICBmb3JtYXRWZXJzaW9uLFxuICAgICAgICB9ID0gdGhpcy5kZWZpbml0aW9uO1xuXG4gICAgICAgIGlmIChpZGVudGlmaWVyRmllbGRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGZvcm1hdFZlcnNpb24gPT09IDEpIHtcbiAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgbGV2ZWw6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfSURFTlRJRklFUl9ORUVEU19GT1JNQVRfVjInLFxuICAgICAgICAgICAgICAgIGZpZWxkOiAnaWRlbnRpZmllckZpZWxkcycsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogJ2lkZW50aWZpZXIgZmllbGRzIHJlcXVpcmUgZm9ybWF0VmVyc2lvbiAyIG9yIGhpZ2hlciAoZXF1YWxpdHkgZGVsZXRlcyBhcmUgYSB2MiBmZWF0dXJlKScsXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZGVudGlmaWVyRmllbGRzLmZvckVhY2goKG5hbWUsIGluZGV4KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBmaWVsZCA9IGBpZGVudGlmaWVyRmllbGRzWyR7aW5kZXh9XWA7XG4gICAgICAgICAgICBjb25zdCBjb2x1bW4gPSB0aGlzLmRlZmluaXRpb24uY29sdW1ucy5maW5kKChjYW5kaWRhdGUpID0+IGNhbmRpZGF0ZS5uYW1lID09PSBuYW1lKTtcbiAgICAgICAgICAgIGlmIChjb2x1bW4gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHZpb2xhdGlvbnMucHVzaCh0aGlzLnZpb2xhdGlvbih7XG4gICAgICAgICAgICAgICAgICAgIGxldmVsOiAnZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICBjb2RlOiAnSUNFQkVSR19JREVOVElGSUVSX0NPTFVNTl9FWElTVFMnLFxuICAgICAgICAgICAgICAgICAgICBmaWVsZCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYGlkZW50aWZpZXIgZmllbGQgXCIke25hbWV9XCIgaXMgbm90IGRlZmluZWQgaW4gY29sdW1uc2AsXG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChjb2x1bW4ubnVsbGFibGUgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICB2aW9sYXRpb25zLnB1c2godGhpcy52aW9sYXRpb24oe1xuICAgICAgICAgICAgICAgICAgICBsZXZlbDogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfSURFTlRJRklFUl9SRVFVSVJFRCcsXG4gICAgICAgICAgICAgICAgICAgIGZpZWxkLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgaWRlbnRpZmllciBmaWVsZCBcIiR7bmFtZX1cIiBtdXN0IGJlIHJlcXVpcmVkIChudWxsYWJsZTogZmFsc2UpYCxcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCB0eXBlID0gcGFyc2VJY2ViZXJnVHlwZShjb2x1bW4udHlwZSk7XG4gICAgICAgICAgICBpZiAodHlwZSA9PT0gbnVsbCB8fCB0eXBlLmtpbmQgPT09IEljZWJlcmdUeXBlS2luZC5GTE9BVCB8fCB0eXBlLmtpbmQgPT09IEljZWJlcmdUeXBlS2luZC5ET1VCTEUpIHtcbiAgICAgICAgICAgICAgICB2aW9sYXRpb25zLnB1c2godGhpcy52aW9sYXRpb24oe1xuICAgICAgICAgICAgICAgICAgICBsZXZlbDogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJ0lDRUJFUkdfSURFTlRJRklFUl9UWVBFX1BSSU1JVElWRScsXG4gICAgICAgICAgICAgICAgICAgIGZpZWxkLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgaWRlbnRpZmllciBmaWVsZCBcIiR7bmFtZX1cIiBtdXN0IGJlIGEgcHJpbWl0aXZlIHR5cGUgb3RoZXIgdGhhbiBmbG9hdCBvciBkb3VibGVgLFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHZpb2xhdGlvbnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTm9ybWFsaXplIGEgcGFydGl0aW9uIGludG8gaXRzIGlkZW50aXR5IGtleS4gV2hlbiB0aGUgdHJhbnNmb3JtIHBhcnNlcyxcbiAgICAgKiBjb2xsYXBzZSBvbiBgJHtraW5kfToke3BhcmFtfWAgc28gYGRheWAgYW5kIGBEQVlgLCBgYnVja2V0WzE2XWAgYW5kXG4gICAgICogYGJ1Y2tldFsgMTYgXWAgYXJlIHRoZSBzYW1lLiBXaGVuIHRoZSB0cmFuc2Zvcm0gZG9lc24ndCBwYXJzZSwgdGhlXG4gICAgICogbG93ZXJjYXNlZCB0cmltIG9mIHRoZSByYXcgc3RyaW5nIGlzIHRoZSBiZXN0IGlkZW50aXR5IHdlIGhhdmUuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcGFydGl0aW9uIFBhcnRpdGlvbiB0byBrZXkuXG4gICAgICogQHJldHVybnMgVGhlIGlkZW50aXR5IHN0cmluZyBmb3IgZHVwbGljYXRlIGRldGVjdGlvbi5cbiAgICAgKi9cbiAgICBwcml2YXRlIHBhcnRpdGlvbktleShwYXJ0aXRpb246IFBhcnRpdGlvbik6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IHRyYW5zZm9ybSA9IHBhcnNlSWNlYmVyZ1RyYW5zZm9ybShwYXJ0aXRpb24udHlwZSk7XG4gICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRUcmFuc2Zvcm0gPSB0cmFuc2Zvcm1cbiAgICAgICAgICAgID8gYCR7dHJhbnNmb3JtLmtpbmR9OiR7dHJhbnNmb3JtLnBhcmFtID8/ICcnfWBcbiAgICAgICAgICAgIDogcGFydGl0aW9uLnR5cGUudHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIHJldHVybiBgJHtwYXJ0aXRpb24ubmFtZX0gJHtub3JtYWxpemVkVHJhbnNmb3JtfWA7XG4gICAgfVxufVxuIl19
|