eoapi-cdk 8.1.1 → 8.2.1
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/.jsii +698 -26
- package/lib/bastion-host/index.js +1 -1
- package/lib/database/index.d.ts +1 -0
- package/lib/database/index.js +5 -5
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/ingestor-api/index.js +1 -1
- package/lib/stac-api/index.js +1 -1
- package/lib/stac-browser/index.js +1 -1
- package/lib/stac-item-loader/index.d.ts +337 -0
- package/lib/stac-item-loader/index.js +255 -0
- package/lib/stac-item-loader/runtime/Dockerfile +18 -0
- package/lib/stac-item-loader/runtime/pyproject.toml +17 -0
- package/lib/stac-item-loader/runtime/src/stac_item_loader/handler.py +241 -0
- package/lib/stactools-item-generator/index.d.ts +258 -0
- package/lib/stactools-item-generator/index.js +208 -0
- package/lib/stactools-item-generator/runtime/Dockerfile +20 -0
- package/lib/stactools-item-generator/runtime/pyproject.toml +16 -0
- package/lib/stactools-item-generator/runtime/src/stactools_item_generator/__init__.py +2 -0
- package/lib/stactools-item-generator/runtime/src/stactools_item_generator/handler.py +176 -0
- package/lib/stactools-item-generator/runtime/src/stactools_item_generator/item.py +77 -0
- package/lib/tipg-api/index.js +1 -1
- package/lib/titiler-pgstac-api/index.js +1 -1
- package/package.json +1 -1
- package/pyproject.toml +45 -0
- package/uv.lock +1065 -0
- package/.devcontainer/devcontainer.json +0 -4
- package/.github/pull_request_template.md +0 -4
- package/.github/workflows/build.yaml +0 -73
- package/.github/workflows/build_and_release.yaml +0 -13
- package/.github/workflows/conventional-pr.yaml +0 -26
- package/.github/workflows/deploy.yaml +0 -84
- package/.github/workflows/distribute.yaml +0 -46
- package/.github/workflows/docs.yaml +0 -26
- package/.github/workflows/lint.yaml +0 -26
- package/.github/workflows/tox.yaml +0 -26
- package/.nvmrc +0 -1
- package/.pre-commit-config.yaml +0 -23
- package/CHANGELOG.md +0 -471
- package/diagrams/bastion_diagram.excalidraw +0 -1416
- package/diagrams/bastion_diagram.png +0 -0
- package/diagrams/ingestor_diagram.excalidraw +0 -2274
- package/diagrams/ingestor_diagram.png +0 -0
- package/integration_tests/cdk/README.md +0 -55
- package/integration_tests/cdk/app.py +0 -186
- package/integration_tests/cdk/cdk.json +0 -32
- package/integration_tests/cdk/config.py +0 -52
- package/integration_tests/cdk/package-lock.json +0 -42
- package/integration_tests/cdk/package.json +0 -7
- package/integration_tests/cdk/requirements.txt +0 -7
- package/lib/database/lambda/package-lock.json +0 -1324
- package/lib/ingestor-api/runtime/tests/conftest.py +0 -270
- package/lib/ingestor-api/runtime/tests/test_collection.py +0 -87
- package/lib/ingestor-api/runtime/tests/test_collection_endpoint.py +0 -41
- package/lib/ingestor-api/runtime/tests/test_ingestor.py +0 -60
- package/lib/ingestor-api/runtime/tests/test_registration.py +0 -207
- package/lib/ingestor-api/runtime/tests/test_utils.py +0 -35
- package/lib/ingestor-api/runtime/tests/test_validators.py +0 -164
- package/ruff.toml +0 -23
- package/tox.ini +0 -16
- package/tsconfig.tsbuildinfo +0 -1
- /package/lib/{ingestor-api/runtime/tests → stac-item-loader/runtime/src/stac_item_loader}/__init__.py +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { aws_ec2 as ec2, aws_lambda as lambda, aws_sqs as sqs, aws_sns as sns } from "aws-cdk-lib";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
import { PgStacDatabase } from "../database";
|
|
4
|
+
import { CustomLambdaFunctionProps } from "../utils";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration properties for the StacItemLoader construct.
|
|
7
|
+
*
|
|
8
|
+
* The StacItemLoader is part of a two-phase serverless STAC ingestion pipeline
|
|
9
|
+
* that loads STAC items into a pgstac database. This construct creates
|
|
10
|
+
* the infrastructure for receiving STAC items from multiple sources:
|
|
11
|
+
* 1. SNS messages containing STAC metadata (direct ingestion)
|
|
12
|
+
* 2. S3 event notifications for STAC items uploaded to S3 buckets
|
|
13
|
+
*
|
|
14
|
+
* Items from both sources are batched and inserted into PostgreSQL with the pgstac extension.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const loader = new StacItemLoader(this, 'ItemLoader', {
|
|
18
|
+
* pgstacDb: database,
|
|
19
|
+
* batchSize: 1000,
|
|
20
|
+
* maxBatchingWindowMinutes: 1,
|
|
21
|
+
* lambdaTimeoutSeconds: 300
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
export interface StacItemLoaderProps {
|
|
25
|
+
/**
|
|
26
|
+
* The PgSTAC database instance to load items into.
|
|
27
|
+
*
|
|
28
|
+
* This database must have the pgstac extension installed and be properly
|
|
29
|
+
* configured with collections before items can be loaded. The loader will
|
|
30
|
+
* use AWS Secrets Manager to securely access database credentials.
|
|
31
|
+
*/
|
|
32
|
+
readonly pgstacDb: PgStacDatabase;
|
|
33
|
+
/**
|
|
34
|
+
* VPC into which the lambda should be deployed.
|
|
35
|
+
*/
|
|
36
|
+
readonly vpc?: ec2.IVpc;
|
|
37
|
+
/**
|
|
38
|
+
* Subnet into which the lambda should be deployed.
|
|
39
|
+
*/
|
|
40
|
+
readonly subnetSelection?: ec2.SubnetSelection;
|
|
41
|
+
/**
|
|
42
|
+
* The lambda runtime to use for the item loading function.
|
|
43
|
+
*
|
|
44
|
+
* The function is implemented in Python and uses pypgstac for database
|
|
45
|
+
* operations. Ensure the runtime version is compatible with the pgstac
|
|
46
|
+
* version specified in the database configuration.
|
|
47
|
+
*
|
|
48
|
+
* @default lambda.Runtime.PYTHON_3_11
|
|
49
|
+
*/
|
|
50
|
+
readonly lambdaRuntime?: lambda.Runtime;
|
|
51
|
+
/**
|
|
52
|
+
* The timeout for the item load lambda in seconds.
|
|
53
|
+
*
|
|
54
|
+
* This should accommodate the time needed to process up to `batchSize`
|
|
55
|
+
* items and perform database insertions. The SQS visibility timeout
|
|
56
|
+
* will be set to this value plus 10 seconds.
|
|
57
|
+
*
|
|
58
|
+
* @default 300
|
|
59
|
+
*/
|
|
60
|
+
readonly lambdaTimeoutSeconds?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Memory size for the lambda function in MB.
|
|
63
|
+
*
|
|
64
|
+
* Higher memory allocation may improve performance when processing
|
|
65
|
+
* large batches of STAC items, especially for memory-intensive
|
|
66
|
+
* database operations.
|
|
67
|
+
*
|
|
68
|
+
* @default 1024
|
|
69
|
+
*/
|
|
70
|
+
readonly memorySize?: number;
|
|
71
|
+
/**
|
|
72
|
+
* SQS batch size for lambda event source.
|
|
73
|
+
*
|
|
74
|
+
* This determines the maximum number of STAC items that will be
|
|
75
|
+
* processed together in a single lambda invocation. Larger batch
|
|
76
|
+
* sizes improve database insertion efficiency but require more
|
|
77
|
+
* memory and longer processing time.
|
|
78
|
+
*
|
|
79
|
+
* **Batching Behavior**: SQS will wait to accumulate up to this many
|
|
80
|
+
* messages before triggering the Lambda, OR until the maxBatchingWindow
|
|
81
|
+
* timeout is reached, whichever comes first. This creates an efficient
|
|
82
|
+
* balance between throughput and latency.
|
|
83
|
+
*
|
|
84
|
+
* @default 500
|
|
85
|
+
*/
|
|
86
|
+
readonly batchSize?: number;
|
|
87
|
+
/**
|
|
88
|
+
* Maximum batching window in minutes.
|
|
89
|
+
*
|
|
90
|
+
* Even if the batch size isn't reached, the lambda will be triggered
|
|
91
|
+
* after this time period to ensure timely processing of items.
|
|
92
|
+
* This prevents items from waiting indefinitely in low-volume scenarios.
|
|
93
|
+
*
|
|
94
|
+
* **Important**: This timeout works in conjunction with batchSize - SQS
|
|
95
|
+
* will trigger the Lambda when EITHER the batch size is reached OR this
|
|
96
|
+
* time window expires, ensuring items are processed in a timely manner
|
|
97
|
+
* regardless of volume.
|
|
98
|
+
*
|
|
99
|
+
* @default 1
|
|
100
|
+
*/
|
|
101
|
+
readonly maxBatchingWindowMinutes?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Maximum concurrent executions for the StacItemLoader Lambda function
|
|
104
|
+
*
|
|
105
|
+
* This limit will be applied to the Lambda function and will control how
|
|
106
|
+
* many concurrent batches will be released from the SQS queue.
|
|
107
|
+
*
|
|
108
|
+
* @default 2
|
|
109
|
+
*/
|
|
110
|
+
readonly maxConcurrency?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Additional environment variables for the lambda function.
|
|
113
|
+
*
|
|
114
|
+
* These will be merged with the default environment variables including
|
|
115
|
+
* PGSTAC_SECRET_ARN. Use this for custom configuration or debugging flags.
|
|
116
|
+
*/
|
|
117
|
+
readonly environment?: {
|
|
118
|
+
[key: string]: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Can be used to override the default lambda function properties.
|
|
122
|
+
*
|
|
123
|
+
* @default - defined in the construct.
|
|
124
|
+
*/
|
|
125
|
+
readonly lambdaFunctionOptions?: CustomLambdaFunctionProps;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* AWS CDK Construct for STAC Item Loading Infrastructure
|
|
129
|
+
*
|
|
130
|
+
* The StacItemLoader creates a serverless, event-driven system for loading
|
|
131
|
+
* STAC (SpatioTemporal Asset Catalog) items into a PostgreSQL database with
|
|
132
|
+
* the pgstac extension. This construct supports multiple ingestion pathways
|
|
133
|
+
* for flexible STAC item loading.
|
|
134
|
+
*
|
|
135
|
+
* ## Architecture Overview
|
|
136
|
+
*
|
|
137
|
+
* This construct creates the following AWS resources:
|
|
138
|
+
* - **SNS Topic**: Entry point for STAC items and S3 event notifications
|
|
139
|
+
* - **SQS Queue**: Buffers and batches messages before processing (60-second visibility timeout)
|
|
140
|
+
* - **Dead Letter Queue**: Captures failed loading attempts after 5 retries
|
|
141
|
+
* - **Lambda Function**: Python function that processes batches and inserts items into pgstac
|
|
142
|
+
*
|
|
143
|
+
* ## Data Flow
|
|
144
|
+
*
|
|
145
|
+
* The loader supports two primary data ingestion patterns:
|
|
146
|
+
*
|
|
147
|
+
* ### Direct STAC Item Publishing
|
|
148
|
+
* 1. STAC items (JSON) are published directly to the SNS topic in message bodies
|
|
149
|
+
* 2. The SQS queue collects messages and batches them (up to {batchSize} items or 1 minute window)
|
|
150
|
+
* 3. The Lambda function receives batches, validates items, and inserts into pgstac
|
|
151
|
+
*
|
|
152
|
+
* ### S3 Event-Driven Loading
|
|
153
|
+
* 1. An S3 bucket is configured to send notifications to the SNS topic when json files are created
|
|
154
|
+
* 2. STAC items are uploaded to S3 buckets as JSON/GeoJSON files
|
|
155
|
+
* 3. S3 event notifications are sent to the SNS topic when items are uploaded
|
|
156
|
+
* 4. The Lambda function receives S3 events in the SQS message batch, fetches items from S3, and loads into pgstac
|
|
157
|
+
*
|
|
158
|
+
* ## Batching Behavior
|
|
159
|
+
*
|
|
160
|
+
* The SQS-to-Lambda integration uses intelligent batching to optimize performance:
|
|
161
|
+
*
|
|
162
|
+
* - **Batch Size**: Lambda waits to receive up to `batchSize` messages (default: 500)
|
|
163
|
+
* - **Batching Window**: If fewer than `batchSize` messages are available, Lambda
|
|
164
|
+
* triggers after `maxBatchingWindow` minutes (default: 1 minute)
|
|
165
|
+
* - **Trigger Condition**: Lambda executes when EITHER condition is met first
|
|
166
|
+
* - **Concurrency**: Limited to `maxConcurrency` concurrent executions to prevent database overload
|
|
167
|
+
* - **Partial Failures**: Uses `reportBatchItemFailures` to retry only failed items
|
|
168
|
+
*
|
|
169
|
+
* This approach balances throughput (larger batches = fewer database connections)
|
|
170
|
+
* with latency (time-based triggers prevent indefinite waiting).
|
|
171
|
+
*
|
|
172
|
+
* ## Error Handling and Dead Letter Queue
|
|
173
|
+
*
|
|
174
|
+
* Failed messages are sent to the dead letter queue after 5 processing attempts.
|
|
175
|
+
* **Important**: This construct provides NO automated handling of dead letter queue
|
|
176
|
+
* messages - monitoring, inspection, and reprocessing of failed items is the
|
|
177
|
+
* responsibility of the implementing application.
|
|
178
|
+
*
|
|
179
|
+
* Consider implementing:
|
|
180
|
+
* - CloudWatch alarms on dead letter queue depth
|
|
181
|
+
* - Manual or automated reprocessing workflows
|
|
182
|
+
* - Logging and alerting for failed items
|
|
183
|
+
* - Regular cleanup of old dead letter messages (14-day retention)
|
|
184
|
+
*
|
|
185
|
+
* ## Operational Characteristics
|
|
186
|
+
*
|
|
187
|
+
* - **Scalability**: Lambda scales automatically based on queue depth
|
|
188
|
+
* - **Reliability**: Dead letter queue captures failures for debugging
|
|
189
|
+
* - **Efficiency**: Batching optimizes database operations for high throughput
|
|
190
|
+
* - **Security**: Database credentials accessed via AWS Secrets Manager
|
|
191
|
+
* - **Observability**: CloudWatch logs retained for one week
|
|
192
|
+
*
|
|
193
|
+
* ## Prerequisites
|
|
194
|
+
*
|
|
195
|
+
* Before using this construct, ensure:
|
|
196
|
+
* - The pgstac database has collections loaded (items require existing collection IDs)
|
|
197
|
+
* - Database credentials are stored in AWS Secrets Manager
|
|
198
|
+
* - The pgstac extension is properly installed and configured
|
|
199
|
+
*
|
|
200
|
+
* ## Usage Example
|
|
201
|
+
*
|
|
202
|
+
* ```typescript
|
|
203
|
+
* // Create database first
|
|
204
|
+
* const database = new PgStacDatabase(this, 'Database', {
|
|
205
|
+
* pgstacVersion: '0.9.5'
|
|
206
|
+
* });
|
|
207
|
+
*
|
|
208
|
+
* // Create item loader
|
|
209
|
+
* const loader = new StacItemLoader(this, 'ItemLoader', {
|
|
210
|
+
* pgstacDb: database,
|
|
211
|
+
* batchSize: 1000, // Process up to 1000 items per batch
|
|
212
|
+
* maxBatchingWindowMinutes: 1, // Wait max 1 minute to fill batch
|
|
213
|
+
* lambdaTimeoutSeconds: 300 // Allow up to 300 seconds for database operations
|
|
214
|
+
* });
|
|
215
|
+
*
|
|
216
|
+
* // The topic ARN can be used by other services to publish items
|
|
217
|
+
* new CfnOutput(this, 'LoaderTopicArn', {
|
|
218
|
+
* value: loader.topic.topicArn
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* ## Direct Item Publishing
|
|
223
|
+
*
|
|
224
|
+
* External services can publish STAC items directly to the topic:
|
|
225
|
+
*
|
|
226
|
+
* ```bash
|
|
227
|
+
* aws sns publish --topic-arn $ITEM_LOAD_TOPIC --message '{
|
|
228
|
+
* "type": "Feature",
|
|
229
|
+
* "stac_version": "1.0.0",
|
|
230
|
+
* "id": "example-item",
|
|
231
|
+
* "properties": {"datetime": "2021-01-01T00:00:00Z"},
|
|
232
|
+
* "geometry": {"type": "Polygon", "coordinates": [...]},
|
|
233
|
+
* "collection": "example-collection"
|
|
234
|
+
* }'
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* ## S3 Event Configuration
|
|
238
|
+
*
|
|
239
|
+
* To enable S3 event-driven loading, configure S3 bucket notifications to send
|
|
240
|
+
* events to the SNS topic when STAC items (.json or .geojson files) are uploaded:
|
|
241
|
+
*
|
|
242
|
+
* ```typescript
|
|
243
|
+
* // Configure S3 bucket to send notifications to the loader topic
|
|
244
|
+
* bucket.addEventNotification(
|
|
245
|
+
* s3.EventType.OBJECT_CREATED,
|
|
246
|
+
* new s3n.SnsDestination(loader.topic),
|
|
247
|
+
* { suffix: '.json' }
|
|
248
|
+
* );
|
|
249
|
+
*
|
|
250
|
+
* bucket.addEventNotification(
|
|
251
|
+
* s3.EventType.OBJECT_CREATED,
|
|
252
|
+
* new s3n.SnsDestination(loader.topic),
|
|
253
|
+
* { suffix: '.geojson' }
|
|
254
|
+
* );
|
|
255
|
+
* ```
|
|
256
|
+
*
|
|
257
|
+
* When STAC items are uploaded to the configured S3 bucket, the loader will:
|
|
258
|
+
* 1. Receive S3 event notifications via SNS
|
|
259
|
+
* 2. Fetch the STAC item JSON from S3
|
|
260
|
+
* 3. Validate and load the item into the pgstac database
|
|
261
|
+
*
|
|
262
|
+
* ## Monitoring and Troubleshooting
|
|
263
|
+
*
|
|
264
|
+
* - Monitor Lambda logs: `/aws/lambda/{FunctionName}`
|
|
265
|
+
* - **Dead Letter Queue**: Check for failed items - **no automated handling provided**
|
|
266
|
+
* - Use batch item failure reporting for partial batch processing
|
|
267
|
+
* - CloudWatch metrics available for queue depth and Lambda performance
|
|
268
|
+
*
|
|
269
|
+
* ### Dead Letter Queue Management
|
|
270
|
+
*
|
|
271
|
+
* Applications must implement their own dead letter queue monitoring:
|
|
272
|
+
*
|
|
273
|
+
* ```typescript
|
|
274
|
+
* // Example: CloudWatch alarm for dead letter queue depth
|
|
275
|
+
* new cloudwatch.Alarm(this, 'DeadLetterAlarm', {
|
|
276
|
+
* metric: loader.deadLetterQueue.metricApproximateNumberOfVisibleMessages(),
|
|
277
|
+
* threshold: 1,
|
|
278
|
+
* evaluationPeriods: 1
|
|
279
|
+
* });
|
|
280
|
+
*
|
|
281
|
+
* // Example: Lambda to reprocess dead letter messages
|
|
282
|
+
* const reprocessFunction = new lambda.Function(this, 'Reprocess', {
|
|
283
|
+
* // Implementation to fetch and republish failed messages
|
|
284
|
+
* });
|
|
285
|
+
* ```
|
|
286
|
+
*
|
|
287
|
+
*/
|
|
288
|
+
export declare class StacItemLoader extends Construct {
|
|
289
|
+
/**
|
|
290
|
+
* The SNS topic that receives STAC items and S3 event notifications for loading.
|
|
291
|
+
*
|
|
292
|
+
* This topic serves as the entry point for two types of events:
|
|
293
|
+
* 1. Direct STAC item JSON documents published by external services
|
|
294
|
+
* 2. S3 event notifications when STAC items are uploaded to configured buckets
|
|
295
|
+
*
|
|
296
|
+
* The topic fans out to the SQS queue for batched processing.
|
|
297
|
+
*/
|
|
298
|
+
readonly topic: sns.Topic;
|
|
299
|
+
/**
|
|
300
|
+
* The SQS queue that buffers messages before processing.
|
|
301
|
+
*
|
|
302
|
+
* This queue collects both direct STAC items from SNS and S3 event
|
|
303
|
+
* notifications, batching them for efficient database operations.
|
|
304
|
+
* Configured with a visibility timeout that accommodates Lambda
|
|
305
|
+
* processing time plus buffer.
|
|
306
|
+
*/
|
|
307
|
+
readonly queue: sqs.Queue;
|
|
308
|
+
/**
|
|
309
|
+
* Dead letter queue for failed item loading attempts.
|
|
310
|
+
*
|
|
311
|
+
* Messages that fail processing after 5 attempts are sent here
|
|
312
|
+
* for inspection and potential replay. Retains messages for 14 days
|
|
313
|
+
* to allow for debugging and manual intervention.
|
|
314
|
+
*
|
|
315
|
+
* **User Responsibility**: This construct provides NO automated monitoring,
|
|
316
|
+
* alerting, or reprocessing of dead letter queue messages. Applications
|
|
317
|
+
* using this construct must implement their own:
|
|
318
|
+
* - Dead letter queue depth monitoring and alerting
|
|
319
|
+
* - Failed message inspection and debugging workflows
|
|
320
|
+
* - Manual or automated reprocessing mechanisms
|
|
321
|
+
* - Cleanup procedures for old failed messages
|
|
322
|
+
*/
|
|
323
|
+
readonly deadLetterQueue: sqs.Queue;
|
|
324
|
+
/**
|
|
325
|
+
* The Lambda function that loads STAC items into the pgstac database.
|
|
326
|
+
*
|
|
327
|
+
* This Python function receives batches of messages from SQS and processes
|
|
328
|
+
* them based on their type:
|
|
329
|
+
* - Direct STAC items: Validates and loads directly into pgstac
|
|
330
|
+
* - S3 events: Fetches STAC items from S3, validates, and loads into pgstac
|
|
331
|
+
*
|
|
332
|
+
* The function connects to PostgreSQL using credentials from Secrets Manager
|
|
333
|
+
* and uses pypgstac for efficient database operations.
|
|
334
|
+
*/
|
|
335
|
+
readonly lambdaFunction: lambda.Function;
|
|
336
|
+
constructor(scope: Construct, id: string, props: StacItemLoaderProps);
|
|
337
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.StacItemLoader = void 0;
|
|
5
|
+
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
6
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
7
|
+
const constructs_1 = require("constructs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
/**
|
|
10
|
+
* AWS CDK Construct for STAC Item Loading Infrastructure
|
|
11
|
+
*
|
|
12
|
+
* The StacItemLoader creates a serverless, event-driven system for loading
|
|
13
|
+
* STAC (SpatioTemporal Asset Catalog) items into a PostgreSQL database with
|
|
14
|
+
* the pgstac extension. This construct supports multiple ingestion pathways
|
|
15
|
+
* for flexible STAC item loading.
|
|
16
|
+
*
|
|
17
|
+
* ## Architecture Overview
|
|
18
|
+
*
|
|
19
|
+
* This construct creates the following AWS resources:
|
|
20
|
+
* - **SNS Topic**: Entry point for STAC items and S3 event notifications
|
|
21
|
+
* - **SQS Queue**: Buffers and batches messages before processing (60-second visibility timeout)
|
|
22
|
+
* - **Dead Letter Queue**: Captures failed loading attempts after 5 retries
|
|
23
|
+
* - **Lambda Function**: Python function that processes batches and inserts items into pgstac
|
|
24
|
+
*
|
|
25
|
+
* ## Data Flow
|
|
26
|
+
*
|
|
27
|
+
* The loader supports two primary data ingestion patterns:
|
|
28
|
+
*
|
|
29
|
+
* ### Direct STAC Item Publishing
|
|
30
|
+
* 1. STAC items (JSON) are published directly to the SNS topic in message bodies
|
|
31
|
+
* 2. The SQS queue collects messages and batches them (up to {batchSize} items or 1 minute window)
|
|
32
|
+
* 3. The Lambda function receives batches, validates items, and inserts into pgstac
|
|
33
|
+
*
|
|
34
|
+
* ### S3 Event-Driven Loading
|
|
35
|
+
* 1. An S3 bucket is configured to send notifications to the SNS topic when json files are created
|
|
36
|
+
* 2. STAC items are uploaded to S3 buckets as JSON/GeoJSON files
|
|
37
|
+
* 3. S3 event notifications are sent to the SNS topic when items are uploaded
|
|
38
|
+
* 4. The Lambda function receives S3 events in the SQS message batch, fetches items from S3, and loads into pgstac
|
|
39
|
+
*
|
|
40
|
+
* ## Batching Behavior
|
|
41
|
+
*
|
|
42
|
+
* The SQS-to-Lambda integration uses intelligent batching to optimize performance:
|
|
43
|
+
*
|
|
44
|
+
* - **Batch Size**: Lambda waits to receive up to `batchSize` messages (default: 500)
|
|
45
|
+
* - **Batching Window**: If fewer than `batchSize` messages are available, Lambda
|
|
46
|
+
* triggers after `maxBatchingWindow` minutes (default: 1 minute)
|
|
47
|
+
* - **Trigger Condition**: Lambda executes when EITHER condition is met first
|
|
48
|
+
* - **Concurrency**: Limited to `maxConcurrency` concurrent executions to prevent database overload
|
|
49
|
+
* - **Partial Failures**: Uses `reportBatchItemFailures` to retry only failed items
|
|
50
|
+
*
|
|
51
|
+
* This approach balances throughput (larger batches = fewer database connections)
|
|
52
|
+
* with latency (time-based triggers prevent indefinite waiting).
|
|
53
|
+
*
|
|
54
|
+
* ## Error Handling and Dead Letter Queue
|
|
55
|
+
*
|
|
56
|
+
* Failed messages are sent to the dead letter queue after 5 processing attempts.
|
|
57
|
+
* **Important**: This construct provides NO automated handling of dead letter queue
|
|
58
|
+
* messages - monitoring, inspection, and reprocessing of failed items is the
|
|
59
|
+
* responsibility of the implementing application.
|
|
60
|
+
*
|
|
61
|
+
* Consider implementing:
|
|
62
|
+
* - CloudWatch alarms on dead letter queue depth
|
|
63
|
+
* - Manual or automated reprocessing workflows
|
|
64
|
+
* - Logging and alerting for failed items
|
|
65
|
+
* - Regular cleanup of old dead letter messages (14-day retention)
|
|
66
|
+
*
|
|
67
|
+
* ## Operational Characteristics
|
|
68
|
+
*
|
|
69
|
+
* - **Scalability**: Lambda scales automatically based on queue depth
|
|
70
|
+
* - **Reliability**: Dead letter queue captures failures for debugging
|
|
71
|
+
* - **Efficiency**: Batching optimizes database operations for high throughput
|
|
72
|
+
* - **Security**: Database credentials accessed via AWS Secrets Manager
|
|
73
|
+
* - **Observability**: CloudWatch logs retained for one week
|
|
74
|
+
*
|
|
75
|
+
* ## Prerequisites
|
|
76
|
+
*
|
|
77
|
+
* Before using this construct, ensure:
|
|
78
|
+
* - The pgstac database has collections loaded (items require existing collection IDs)
|
|
79
|
+
* - Database credentials are stored in AWS Secrets Manager
|
|
80
|
+
* - The pgstac extension is properly installed and configured
|
|
81
|
+
*
|
|
82
|
+
* ## Usage Example
|
|
83
|
+
*
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // Create database first
|
|
86
|
+
* const database = new PgStacDatabase(this, 'Database', {
|
|
87
|
+
* pgstacVersion: '0.9.5'
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* // Create item loader
|
|
91
|
+
* const loader = new StacItemLoader(this, 'ItemLoader', {
|
|
92
|
+
* pgstacDb: database,
|
|
93
|
+
* batchSize: 1000, // Process up to 1000 items per batch
|
|
94
|
+
* maxBatchingWindowMinutes: 1, // Wait max 1 minute to fill batch
|
|
95
|
+
* lambdaTimeoutSeconds: 300 // Allow up to 300 seconds for database operations
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // The topic ARN can be used by other services to publish items
|
|
99
|
+
* new CfnOutput(this, 'LoaderTopicArn', {
|
|
100
|
+
* value: loader.topic.topicArn
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* ## Direct Item Publishing
|
|
105
|
+
*
|
|
106
|
+
* External services can publish STAC items directly to the topic:
|
|
107
|
+
*
|
|
108
|
+
* ```bash
|
|
109
|
+
* aws sns publish --topic-arn $ITEM_LOAD_TOPIC --message '{
|
|
110
|
+
* "type": "Feature",
|
|
111
|
+
* "stac_version": "1.0.0",
|
|
112
|
+
* "id": "example-item",
|
|
113
|
+
* "properties": {"datetime": "2021-01-01T00:00:00Z"},
|
|
114
|
+
* "geometry": {"type": "Polygon", "coordinates": [...]},
|
|
115
|
+
* "collection": "example-collection"
|
|
116
|
+
* }'
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* ## S3 Event Configuration
|
|
120
|
+
*
|
|
121
|
+
* To enable S3 event-driven loading, configure S3 bucket notifications to send
|
|
122
|
+
* events to the SNS topic when STAC items (.json or .geojson files) are uploaded:
|
|
123
|
+
*
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // Configure S3 bucket to send notifications to the loader topic
|
|
126
|
+
* bucket.addEventNotification(
|
|
127
|
+
* s3.EventType.OBJECT_CREATED,
|
|
128
|
+
* new s3n.SnsDestination(loader.topic),
|
|
129
|
+
* { suffix: '.json' }
|
|
130
|
+
* );
|
|
131
|
+
*
|
|
132
|
+
* bucket.addEventNotification(
|
|
133
|
+
* s3.EventType.OBJECT_CREATED,
|
|
134
|
+
* new s3n.SnsDestination(loader.topic),
|
|
135
|
+
* { suffix: '.geojson' }
|
|
136
|
+
* );
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* When STAC items are uploaded to the configured S3 bucket, the loader will:
|
|
140
|
+
* 1. Receive S3 event notifications via SNS
|
|
141
|
+
* 2. Fetch the STAC item JSON from S3
|
|
142
|
+
* 3. Validate and load the item into the pgstac database
|
|
143
|
+
*
|
|
144
|
+
* ## Monitoring and Troubleshooting
|
|
145
|
+
*
|
|
146
|
+
* - Monitor Lambda logs: `/aws/lambda/{FunctionName}`
|
|
147
|
+
* - **Dead Letter Queue**: Check for failed items - **no automated handling provided**
|
|
148
|
+
* - Use batch item failure reporting for partial batch processing
|
|
149
|
+
* - CloudWatch metrics available for queue depth and Lambda performance
|
|
150
|
+
*
|
|
151
|
+
* ### Dead Letter Queue Management
|
|
152
|
+
*
|
|
153
|
+
* Applications must implement their own dead letter queue monitoring:
|
|
154
|
+
*
|
|
155
|
+
* ```typescript
|
|
156
|
+
* // Example: CloudWatch alarm for dead letter queue depth
|
|
157
|
+
* new cloudwatch.Alarm(this, 'DeadLetterAlarm', {
|
|
158
|
+
* metric: loader.deadLetterQueue.metricApproximateNumberOfVisibleMessages(),
|
|
159
|
+
* threshold: 1,
|
|
160
|
+
* evaluationPeriods: 1
|
|
161
|
+
* });
|
|
162
|
+
*
|
|
163
|
+
* // Example: Lambda to reprocess dead letter messages
|
|
164
|
+
* const reprocessFunction = new lambda.Function(this, 'Reprocess', {
|
|
165
|
+
* // Implementation to fetch and republish failed messages
|
|
166
|
+
* });
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
*/
|
|
170
|
+
class StacItemLoader extends constructs_1.Construct {
|
|
171
|
+
constructor(scope, id, props) {
|
|
172
|
+
super(scope, id);
|
|
173
|
+
const timeoutSeconds = props.lambdaTimeoutSeconds ?? 300;
|
|
174
|
+
const lambdaRuntime = props.lambdaRuntime ?? aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_11;
|
|
175
|
+
const maxConcurrency = props.maxConcurrency ?? 2;
|
|
176
|
+
// Create dead letter queue
|
|
177
|
+
this.deadLetterQueue = new aws_cdk_lib_1.aws_sqs.Queue(this, "DeadLetterQueue", {
|
|
178
|
+
retentionPeriod: aws_cdk_lib_1.Duration.days(14),
|
|
179
|
+
});
|
|
180
|
+
// Create main queue
|
|
181
|
+
this.queue = new aws_cdk_lib_1.aws_sqs.Queue(this, "Queue", {
|
|
182
|
+
visibilityTimeout: aws_cdk_lib_1.Duration.seconds(timeoutSeconds + 10),
|
|
183
|
+
encryption: aws_cdk_lib_1.aws_sqs.QueueEncryption.SQS_MANAGED,
|
|
184
|
+
deadLetterQueue: {
|
|
185
|
+
maxReceiveCount: 5,
|
|
186
|
+
queue: this.deadLetterQueue,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
// Create SNS topic
|
|
190
|
+
this.topic = new aws_cdk_lib_1.aws_sns.Topic(this, "Topic", {
|
|
191
|
+
displayName: `${id}-StacItemLoaderTopic`,
|
|
192
|
+
});
|
|
193
|
+
// Subscribe the queue to the topic
|
|
194
|
+
this.topic.addSubscription(new aws_cdk_lib_1.aws_sns_subscriptions.SqsSubscription(this.queue));
|
|
195
|
+
// Create the lambda function
|
|
196
|
+
this.lambdaFunction = new aws_cdk_lib_1.aws_lambda.Function(this, "Function", {
|
|
197
|
+
runtime: lambdaRuntime,
|
|
198
|
+
handler: "stac_item_loader.handler.handler",
|
|
199
|
+
vpc: props.vpc,
|
|
200
|
+
vpcSubnets: props.subnetSelection,
|
|
201
|
+
code: aws_cdk_lib_1.aws_lambda.Code.fromDockerBuild(path.join(__dirname, ".."), {
|
|
202
|
+
file: "stac-item-loader/runtime/Dockerfile",
|
|
203
|
+
platform: "linux/amd64",
|
|
204
|
+
buildArgs: {
|
|
205
|
+
PYTHON_VERSION: lambdaRuntime.toString().replace("python", ""),
|
|
206
|
+
PGSTAC_VERSION: props.pgstacDb.pgstacVersion,
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
memorySize: props.memorySize ?? 1024,
|
|
210
|
+
timeout: aws_cdk_lib_1.Duration.seconds(timeoutSeconds),
|
|
211
|
+
reservedConcurrentExecutions: maxConcurrency,
|
|
212
|
+
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
|
|
213
|
+
environment: {
|
|
214
|
+
PGSTAC_SECRET_ARN: props.pgstacDb.pgstacSecret.secretArn,
|
|
215
|
+
...props.environment,
|
|
216
|
+
},
|
|
217
|
+
// overwrites defaults with user-provided configurable properties
|
|
218
|
+
...props.lambdaFunctionOptions,
|
|
219
|
+
});
|
|
220
|
+
// Grant permissions to read the database secret
|
|
221
|
+
props.pgstacDb.pgstacSecret.grantRead(this.lambdaFunction);
|
|
222
|
+
// Add SQS event source to the lambda
|
|
223
|
+
this.lambdaFunction.addEventSource(new aws_cdk_lib_1.aws_lambda_event_sources.SqsEventSource(this.queue, {
|
|
224
|
+
batchSize: props.batchSize ?? 500,
|
|
225
|
+
maxBatchingWindow: aws_cdk_lib_1.Duration.minutes(props.maxBatchingWindowMinutes ?? 1),
|
|
226
|
+
maxConcurrency: maxConcurrency,
|
|
227
|
+
reportBatchItemFailures: true,
|
|
228
|
+
}));
|
|
229
|
+
// Create outputs
|
|
230
|
+
new aws_cdk_lib_1.CfnOutput(this, "TopicArn", {
|
|
231
|
+
value: this.topic.topicArn,
|
|
232
|
+
description: "ARN of the StacItemLoader SNS Topic",
|
|
233
|
+
exportName: "stac-item-loader-topic-arn",
|
|
234
|
+
});
|
|
235
|
+
new aws_cdk_lib_1.CfnOutput(this, "QueueUrl", {
|
|
236
|
+
value: this.queue.queueUrl,
|
|
237
|
+
description: "URL of the StacItemLoader SQS Queue",
|
|
238
|
+
exportName: "stac-item-loader-queue-url",
|
|
239
|
+
});
|
|
240
|
+
new aws_cdk_lib_1.CfnOutput(this, "DeadLetterQueueUrl", {
|
|
241
|
+
value: this.deadLetterQueue.queueUrl,
|
|
242
|
+
description: "URL of the StacItemLoader Dead Letter Queue",
|
|
243
|
+
exportName: "stac-item-loader-deadletter-queue-url",
|
|
244
|
+
});
|
|
245
|
+
new aws_cdk_lib_1.CfnOutput(this, "FunctionName", {
|
|
246
|
+
value: this.lambdaFunction.functionName,
|
|
247
|
+
description: "Name of the StacItemLoader Lambda Function",
|
|
248
|
+
exportName: "stac-item-loader-function-name",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.StacItemLoader = StacItemLoader;
|
|
253
|
+
_a = JSII_RTTI_SYMBOL_1;
|
|
254
|
+
StacItemLoader[_a] = { fqn: "eoapi-cdk.StacItemLoader", version: "8.2.1" };
|
|
255
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDZDQVVxQjtBQUNyQiwyQ0FBdUM7QUFFdkMsNkJBQTZCO0FBc0k3Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdLRztBQUNILE1BQWEsY0FBZSxTQUFRLHNCQUFTO0lBb0QzQyxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQTBCO1FBQ2xFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxjQUFjLEdBQUcsS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQztRQUN6RCxNQUFNLGFBQWEsR0FBRyxLQUFLLENBQUMsYUFBYSxJQUFJLHdCQUFNLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUN4RSxNQUFNLGNBQWMsR0FBRyxLQUFLLENBQUMsY0FBYyxJQUFJLENBQUMsQ0FBQztRQUVqRCwyQkFBMkI7UUFDM0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLHFCQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxpQkFBaUIsRUFBRTtZQUM1RCxlQUFlLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1NBQ25DLENBQUMsQ0FBQztRQUVILG9CQUFvQjtRQUNwQixJQUFJLENBQUMsS0FBSyxHQUFHLElBQUkscUJBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRTtZQUN4QyxpQkFBaUIsRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxjQUFjLEdBQUcsRUFBRSxDQUFDO1lBQ3hELFVBQVUsRUFBRSxxQkFBRyxDQUFDLGVBQWUsQ0FBQyxXQUFXO1lBQzNDLGVBQWUsRUFBRTtnQkFDZixlQUFlLEVBQUUsQ0FBQztnQkFDbEIsS0FBSyxFQUFFLElBQUksQ0FBQyxlQUFlO2FBQzVCO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsbUJBQW1CO1FBQ25CLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxxQkFBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFO1lBQ3hDLFdBQVcsRUFBRSxHQUFHLEVBQUUsc0JBQXNCO1NBQ3pDLENBQUMsQ0FBQztRQUVILG1DQUFtQztRQUNuQyxJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FDeEIsSUFBSSxtQ0FBZ0IsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUNqRCxDQUFDO1FBRUYsNkJBQTZCO1FBQzdCLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSx3QkFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFO1lBQzFELE9BQU8sRUFBRSxhQUFhO1lBQ3RCLE9BQU8sRUFBRSxrQ0FBa0M7WUFDM0MsR0FBRyxFQUFFLEtBQUssQ0FBQyxHQUFHO1lBQ2QsVUFBVSxFQUFFLEtBQUssQ0FBQyxlQUFlO1lBQ2pDLElBQUksRUFBRSx3QkFBTSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLEVBQUU7Z0JBQzVELElBQUksRUFBRSxxQ0FBcUM7Z0JBQzNDLFFBQVEsRUFBRSxhQUFhO2dCQUN2QixTQUFTLEVBQUU7b0JBQ1QsY0FBYyxFQUFFLGFBQWEsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQztvQkFDOUQsY0FBYyxFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsYUFBYTtpQkFDN0M7YUFDRixDQUFDO1lBQ0YsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVLElBQUksSUFBSTtZQUNwQyxPQUFPLEVBQUUsc0JBQVEsQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDO1lBQ3pDLDRCQUE0QixFQUFFLGNBQWM7WUFDNUMsWUFBWSxFQUFFLHNCQUFJLENBQUMsYUFBYSxDQUFDLFFBQVE7WUFDekMsV0FBVyxFQUFFO2dCQUNYLGlCQUFpQixFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLFNBQVM7Z0JBQ3hELEdBQUcsS0FBSyxDQUFDLFdBQVc7YUFDckI7WUFDRCxpRUFBaUU7WUFDakUsR0FBRyxLQUFLLENBQUMscUJBQXFCO1NBQy9CLENBQUMsQ0FBQztRQUVILGdEQUFnRDtRQUNoRCxLQUFLLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBRTNELHFDQUFxQztRQUNyQyxJQUFJLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FDaEMsSUFBSSxzQ0FBa0IsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNoRCxTQUFTLEVBQUUsS0FBSyxDQUFDLFNBQVMsSUFBSSxHQUFHO1lBQ2pDLGlCQUFpQixFQUFFLHNCQUFRLENBQUMsT0FBTyxDQUNqQyxLQUFLLENBQUMsd0JBQXdCLElBQUksQ0FBQyxDQUNwQztZQUNELGNBQWMsRUFBRSxjQUFjO1lBQzlCLHVCQUF1QixFQUFFLElBQUk7U0FDOUIsQ0FBQyxDQUNILENBQUM7UUFFRixpQkFBaUI7UUFDakIsSUFBSSx1QkFBUyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDOUIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUTtZQUMxQixXQUFXLEVBQUUscUNBQXFDO1lBQ2xELFVBQVUsRUFBRSw0QkFBNEI7U0FDekMsQ0FBQyxDQUFDO1FBRUgsSUFBSSx1QkFBUyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDOUIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUTtZQUMxQixXQUFXLEVBQUUscUNBQXFDO1lBQ2xELFVBQVUsRUFBRSw0QkFBNEI7U0FDekMsQ0FBQyxDQUFDO1FBRUgsSUFBSSx1QkFBUyxDQUFDLElBQUksRUFBRSxvQkFBb0IsRUFBRTtZQUN4QyxLQUFLLEVBQUUsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRO1lBQ3BDLFdBQVcsRUFBRSw2Q0FBNkM7WUFDMUQsVUFBVSxFQUFFLHVDQUF1QztTQUNwRCxDQUFDLENBQUM7UUFFSCxJQUFJLHVCQUFTLENBQUMsSUFBSSxFQUFFLGNBQWMsRUFBRTtZQUNsQyxLQUFLLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZO1lBQ3ZDLFdBQVcsRUFBRSw0Q0FBNEM7WUFDekQsVUFBVSxFQUFFLGdDQUFnQztTQUM3QyxDQUFDLENBQUM7SUFDTCxDQUFDOztBQXJKSCx3Q0FzSkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBhd3NfZWMyIGFzIGVjMixcbiAgYXdzX2xhbWJkYSBhcyBsYW1iZGEsXG4gIGF3c19zcXMgYXMgc3FzLFxuICBhd3Nfc25zIGFzIHNucyxcbiAgYXdzX3Nuc19zdWJzY3JpcHRpb25zIGFzIHNuc1N1YnNjcmlwdGlvbnMsXG4gIGF3c19sYW1iZGFfZXZlbnRfc291cmNlcyBhcyBsYW1iZGFFdmVudFNvdXJjZXMsXG4gIGF3c19sb2dzIGFzIGxvZ3MsXG4gIER1cmF0aW9uLFxuICBDZm5PdXRwdXQsXG59IGZyb20gXCJhd3MtY2RrLWxpYlwiO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSBcImNvbnN0cnVjdHNcIjtcbmltcG9ydCB7IFBnU3RhY0RhdGFiYXNlIH0gZnJvbSBcIi4uL2RhdGFiYXNlXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBDdXN0b21MYW1iZGFGdW5jdGlvblByb3BzIH0gZnJvbSBcIi4uL3V0aWxzXCI7XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiBwcm9wZXJ0aWVzIGZvciB0aGUgU3RhY0l0ZW1Mb2FkZXIgY29uc3RydWN0LlxuICpcbiAqIFRoZSBTdGFjSXRlbUxvYWRlciBpcyBwYXJ0IG9mIGEgdHdvLXBoYXNlIHNlcnZlcmxlc3MgU1RBQyBpbmdlc3Rpb24gcGlwZWxpbmVcbiAqIHRoYXQgbG9hZHMgU1RBQyBpdGVtcyBpbnRvIGEgcGdzdGFjIGRhdGFiYXNlLiBUaGlzIGNvbnN0cnVjdCBjcmVhdGVzXG4gKiB0aGUgaW5mcmFzdHJ1Y3R1cmUgZm9yIHJlY2VpdmluZyBTVEFDIGl0ZW1zIGZyb20gbXVsdGlwbGUgc291cmNlczpcbiAqIDEuIFNOUyBtZXNzYWdlcyBjb250YWluaW5nIFNUQUMgbWV0YWRhdGEgKGRpcmVjdCBpbmdlc3Rpb24pXG4gKiAyLiBTMyBldmVudCBub3RpZmljYXRpb25zIGZvciBTVEFDIGl0ZW1zIHVwbG9hZGVkIHRvIFMzIGJ1Y2tldHNcbiAqXG4gKiBJdGVtcyBmcm9tIGJvdGggc291cmNlcyBhcmUgYmF0Y2hlZCBhbmQgaW5zZXJ0ZWQgaW50byBQb3N0Z3JlU1FMIHdpdGggdGhlIHBnc3RhYyBleHRlbnNpb24uXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGxvYWRlciA9IG5ldyBTdGFjSXRlbUxvYWRlcih0aGlzLCAnSXRlbUxvYWRlcicsIHtcbiAqICAgcGdzdGFjRGI6IGRhdGFiYXNlLFxuICogICBiYXRjaFNpemU6IDEwMDAsXG4gKiAgIG1heEJhdGNoaW5nV2luZG93TWludXRlczogMSxcbiAqICAgbGFtYmRhVGltZW91dFNlY29uZHM6IDMwMFxuICogfSk7XG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RhY0l0ZW1Mb2FkZXJQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgUGdTVEFDIGRhdGFiYXNlIGluc3RhbmNlIHRvIGxvYWQgaXRlbXMgaW50by5cbiAgICpcbiAgICogVGhpcyBkYXRhYmFzZSBtdXN0IGhhdmUgdGhlIHBnc3RhYyBleHRlbnNpb24gaW5zdGFsbGVkIGFuZCBiZSBwcm9wZXJseVxuICAgKiBjb25maWd1cmVkIHdpdGggY29sbGVjdGlvbnMgYmVmb3JlIGl0ZW1zIGNhbiBiZSBsb2FkZWQuIFRoZSBsb2FkZXIgd2lsbFxuICAgKiB1c2UgQVdTIFNlY3JldHMgTWFuYWdlciB0byBzZWN1cmVseSBhY2Nlc3MgZGF0YWJhc2UgY3JlZGVudGlhbHMuXG4gICAqL1xuICByZWFkb25seSBwZ3N0YWNEYjogUGdTdGFjRGF0YWJhc2U7XG5cbiAgLyoqXG4gICAqIFZQQyBpbnRvIHdoaWNoIHRoZSBsYW1iZGEgc2hvdWxkIGJlIGRlcGxveWVkLlxuICAgKi9cbiAgcmVhZG9ubHkgdnBjPzogZWMyLklWcGM7XG5cbiAgLyoqXG4gICAqIFN1Ym5ldCBpbnRvIHdoaWNoIHRoZSBsYW1iZGEgc2hvdWxkIGJlIGRlcGxveWVkLlxuICAgKi9cbiAgcmVhZG9ubHkgc3VibmV0U2VsZWN0aW9uPzogZWMyLlN1Ym5ldFNlbGVjdGlvbjtcblxuICAvKipcbiAgICogVGhlIGxhbWJkYSBydW50aW1lIHRvIHVzZSBmb3IgdGhlIGl0ZW0gbG9hZGluZyBmdW5jdGlvbi5cbiAgICpcbiAgICogVGhlIGZ1bmN0aW9uIGlzIGltcGxlbWVudGVkIGluIFB5dGhvbiBhbmQgdXNlcyBweXBnc3RhYyBmb3IgZGF0YWJhc2VcbiAgICogb3BlcmF0aW9ucy4gRW5zdXJlIHRoZSBydW50aW1lIHZlcnNpb24gaXMgY29tcGF0aWJsZSB3aXRoIHRoZSBwZ3N0YWNcbiAgICogdmVyc2lvbiBzcGVjaWZpZWQgaW4gdGhlIGRhdGFiYXNlIGNvbmZpZ3VyYXRpb24uXG4gICAqXG4gICAqIEBkZWZhdWx0IGxhbWJkYS5SdW50aW1lLlBZVEhPTl8zXzExXG4gICAqL1xuICByZWFkb25seSBsYW1iZGFSdW50aW1lPzogbGFtYmRhLlJ1bnRpbWU7XG5cbiAgLyoqXG4gICAqIFRoZSB0aW1lb3V0IGZvciB0aGUgaXRlbSBsb2FkIGxhbWJkYSBpbiBzZWNvbmRzLlxuICAgKlxuICAgKiBUaGlzIHNob3VsZCBhY2NvbW1vZGF0ZSB0aGUgdGltZSBuZWVkZWQgdG8gcHJvY2VzcyB1cCB0byBgYmF0Y2hTaXplYFxuICAgKiBpdGVtcyBhbmQgcGVyZm9ybSBkYXRhYmFzZSBpbnNlcnRpb25zLiBUaGUgU1FTIHZpc2liaWxpdHkgdGltZW91dFxuICAgKiB3aWxsIGJlIHNldCB0byB0aGlzIHZhbHVlIHBsdXMgMTAgc2Vjb25kcy5cbiAgICpcbiAgICogQGRlZmF1bHQgMzAwXG4gICAqL1xuICByZWFkb25seSBsYW1iZGFUaW1lb3V0U2Vjb25kcz86IG51bWJlcjtcblxuICAvKipcbiAgICogTWVtb3J5IHNpemUgZm9yIHRoZSBsYW1iZGEgZnVuY3Rpb24gaW4gTUIuXG4gICAqXG4gICAqIEhpZ2hlciBtZW1vcnkgYWxsb2NhdGlvbiBtYXkgaW1wcm92ZSBwZXJmb3JtYW5jZSB3aGVuIHByb2Nlc3NpbmdcbiAgICogbGFyZ2UgYmF0Y2hlcyBvZiBTVEFDIGl0ZW1zLCBlc3BlY2lhbGx5IGZvciBtZW1vcnktaW50ZW5zaXZlXG4gICAqIGRhdGFiYXNlIG9wZXJhdGlvbnMuXG4gICAqXG4gICAqIEBkZWZhdWx0IDEwMjRcbiAgICovXG4gIHJlYWRvbmx5IG1lbW9yeVNpemU/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFNRUyBiYXRjaCBzaXplIGZvciBsYW1iZGEgZXZlbnQgc291cmNlLlxuICAgKlxuICAgKiBUaGlzIGRldGVybWluZXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIFNUQUMgaXRlbXMgdGhhdCB3aWxsIGJlXG4gICAqIHByb2Nlc3NlZCB0b2dldGhlciBpbiBhIHNpbmdsZSBsYW1iZGEgaW52b2NhdGlvbi4gTGFyZ2VyIGJhdGNoXG4gICAqIHNpemVzIGltcHJvdmUgZGF0YWJhc2UgaW5zZXJ0aW9uIGVmZmljaWVuY3kgYnV0IHJlcXVpcmUgbW9yZVxuICAgKiBtZW1vcnkgYW5kIGxvbmdlciBwcm9jZXNzaW5nIHRpbWUuXG4gICAqXG4gICAqICoqQmF0Y2hpbmcgQmVoYXZpb3IqKjogU1FTIHdpbGwgd2FpdCB0byBhY2N1bXVsYXRlIHVwIHRvIHRoaXMgbWFueVxuICAgKiBtZXNzYWdlcyBiZWZvcmUgdHJpZ2dlcmluZyB0aGUgTGFtYmRhLCBPUiB1bnRpbCB0aGUgbWF4QmF0Y2hpbmdXaW5kb3dcbiAgICogdGltZW91dCBpcyByZWFjaGVkLCB3aGljaGV2ZXIgY29tZXMgZmlyc3QuIFRoaXMgY3JlYXRlcyBhbiBlZmZpY2llbnRcbiAgICogYmFsYW5jZSBiZXR3ZWVuIHRocm91Z2hwdXQgYW5kIGxhdGVuY3kuXG4gICAqXG4gICAqIEBkZWZhdWx0IDUwMFxuICAgKi9cbiAgcmVhZG9ubHkgYmF0Y2hTaXplPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIGJhdGNoaW5nIHdpbmRvdyBpbiBtaW51dGVzLlxuICAgKlxuICAgKiBFdmVuIGlmIHRoZSBiYXRjaCBzaXplIGlzbid0IHJlYWNoZWQsIHRoZSBsYW1iZGEgd2lsbCBiZSB0cmlnZ2VyZWRcbiAgICogYWZ0ZXIgdGhpcyB0aW1lIHBlcmlvZCB0byBlbnN1cmUgdGltZWx5IHByb2Nlc3Npbmcgb2YgaXRlbXMuXG4gICAqIFRoaXMgcHJldmVudHMgaXRlbXMgZnJvbSB3YWl0aW5nIGluZGVmaW5pdGVseSBpbiBsb3ctdm9sdW1lIHNjZW5hcmlvcy5cbiAgICpcbiAgICogKipJbXBvcnRhbnQqKjogVGhpcyB0aW1lb3V0IHdvcmtzIGluIGNvbmp1bmN0aW9uIHdpdGggYmF0Y2hTaXplIC0gU1FTXG4gICAqIHdpbGwgdHJpZ2dlciB0aGUgTGFtYmRhIHdoZW4gRUlUSEVSIHRoZSBiYXRjaCBzaXplIGlzIHJlYWNoZWQgT1IgdGhpc1xuICAgKiB0aW1lIHdpbmRvdyBleHBpcmVzLCBlbnN1cmluZyBpdGVtcyBhcmUgcHJvY2Vzc2VkIGluIGEgdGltZWx5IG1hbm5lclxuICAgKiByZWdhcmRsZXNzIG9mIHZvbHVtZS5cbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKi9cbiAgcmVhZG9ubHkgbWF4QmF0Y2hpbmdXaW5kb3dNaW51dGVzPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIGNvbmN1cnJlbnQgZXhlY3V0aW9ucyBmb3IgdGhlIFN0YWNJdGVtTG9hZGVyIExhbWJkYSBmdW5jdGlvblxuICAgKlxuICAgKiBUaGlzIGxpbWl0IHdpbGwgYmUgYXBwbGllZCB0byB0aGUgTGFtYmRhIGZ1bmN0aW9uIGFuZCB3aWxsIGNvbnRyb2wgaG93XG4gICAqIG1hbnkgY29uY3VycmVudCBiYXRjaGVzIHdpbGwgYmUgcmVsZWFzZWQgZnJvbSB0aGUgU1FTIHF1ZXVlLlxuICAgKlxuICAgKiBAZGVmYXVsdCAyXG4gICAqL1xuICByZWFkb25seSBtYXhDb25jdXJyZW5jeT86IG51bWJlcjtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZm9yIHRoZSBsYW1iZGEgZnVuY3Rpb24uXG4gICAqXG4gICAqIFRoZXNlIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGRlZmF1bHQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGluY2x1ZGluZ1xuICAgKiBQR1NUQUNfU0VDUkVUX0FSTi4gVXNlIHRoaXMgZm9yIGN1c3RvbSBjb25maWd1cmF0aW9uIG9yIGRlYnVnZ2luZyBmbGFncy5cbiAgICovXG4gIHJlYWRvbmx5IGVudmlyb25tZW50PzogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfTtcblxuICAvKipcbiAgICogQ2FuIGJlIHVzZWQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgbGFtYmRhIGZ1bmN0aW9uIHByb3BlcnRpZXMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZGVmaW5lZCBpbiB0aGUgY29uc3RydWN0LlxuICAgKi9cbiAgcmVhZG9ubHkgbGFtYmRhRnVuY3Rpb25PcHRpb25zPzogQ3VzdG9tTGFtYmRhRnVuY3Rpb25Qcm9wcztcbn1cblxuLyoqXG4gKiBBV1MgQ0RLIENvbnN0cnVjdCBmb3IgU1RBQyBJdGVtIExvYWRpbmcgSW5mcmFzdHJ1Y3R1cmVcbiAqXG4gKiBUaGUgU3RhY0l0ZW1Mb2FkZXIgY3JlYXRlcyBhIHNlcnZlcmxlc3MsIGV2ZW50LWRyaXZlbiBzeXN0ZW0gZm9yIGxvYWRpbmdcbiAqIFNUQUMgKFNwYXRpb1RlbXBvcmFsIEFzc2V0IENhdGFsb2cpIGl0ZW1zIGludG8gYSBQb3N0Z3JlU1FMIGRhdGFiYXNlIHdpdGhcbiAqIHRoZSBwZ3N0YWMgZXh0ZW5zaW9uLiBUaGlzIGNvbnN0cnVjdCBzdXBwb3J0cyBtdWx0aXBsZSBpbmdlc3Rpb24gcGF0aHdheXNcbiAqIGZvciBmbGV4aWJsZSBTVEFDIGl0ZW0gbG9hZGluZy5cbiAqXG4gKiAjIyBBcmNoaXRlY3R1cmUgT3ZlcnZpZXdcbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBjcmVhdGVzIHRoZSBmb2xsb3dpbmcgQVdTIHJlc291cmNlczpcbiAqIC0gKipTTlMgVG9waWMqKjogRW50cnkgcG9pbnQgZm9yIFNUQUMgaXRlbXMgYW5kIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnNcbiAqIC0gKipTUVMgUXVldWUqKjogQnVmZmVycyBhbmQgYmF0Y2hlcyBtZXNzYWdlcyBiZWZvcmUgcHJvY2Vzc2luZyAoNjAtc2Vjb25kIHZpc2liaWxpdHkgdGltZW91dClcbiAqIC0gKipEZWFkIExldHRlciBRdWV1ZSoqOiBDYXB0dXJlcyBmYWlsZWQgbG9hZGluZyBhdHRlbXB0cyBhZnRlciA1IHJldHJpZXNcbiAqIC0gKipMYW1iZGEgRnVuY3Rpb24qKjogUHl0aG9uIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGJhdGNoZXMgYW5kIGluc2VydHMgaXRlbXMgaW50byBwZ3N0YWNcbiAqXG4gKiAjIyBEYXRhIEZsb3dcbiAqXG4gKiBUaGUgbG9hZGVyIHN1cHBvcnRzIHR3byBwcmltYXJ5IGRhdGEgaW5nZXN0aW9uIHBhdHRlcm5zOlxuICpcbiAqICMjIyBEaXJlY3QgU1RBQyBJdGVtIFB1Ymxpc2hpbmdcbiAqIDEuIFNUQUMgaXRlbXMgKEpTT04pIGFyZSBwdWJsaXNoZWQgZGlyZWN0bHkgdG8gdGhlIFNOUyB0b3BpYyBpbiBtZXNzYWdlIGJvZGllc1xuICogMi4gVGhlIFNRUyBxdWV1ZSBjb2xsZWN0cyBtZXNzYWdlcyBhbmQgYmF0Y2hlcyB0aGVtICh1cCB0byB7YmF0Y2hTaXplfSBpdGVtcyBvciAxIG1pbnV0ZSB3aW5kb3cpXG4gKiAzLiBUaGUgTGFtYmRhIGZ1bmN0aW9uIHJlY2VpdmVzIGJhdGNoZXMsIHZhbGlkYXRlcyBpdGVtcywgYW5kIGluc2VydHMgaW50byBwZ3N0YWNcbiAqXG4gKiAjIyMgUzMgRXZlbnQtRHJpdmVuIExvYWRpbmdcbiAqIDEuIEFuIFMzIGJ1Y2tldCBpcyBjb25maWd1cmVkIHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0byB0aGUgU05TIHRvcGljIHdoZW4ganNvbiBmaWxlcyBhcmUgY3JlYXRlZFxuICogMi4gU1RBQyBpdGVtcyBhcmUgdXBsb2FkZWQgdG8gUzMgYnVja2V0cyBhcyBKU09OL0dlb0pTT04gZmlsZXNcbiAqIDMuIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnMgYXJlIHNlbnQgdG8gdGhlIFNOUyB0b3BpYyB3aGVuIGl0ZW1zIGFyZSB1cGxvYWRlZFxuICogNC4gVGhlIExhbWJkYSBmdW5jdGlvbiByZWNlaXZlcyBTMyBldmVudHMgaW4gdGhlIFNRUyBtZXNzYWdlIGJhdGNoLCBmZXRjaGVzIGl0ZW1zIGZyb20gUzMsIGFuZCBsb2FkcyBpbnRvIHBnc3RhY1xuICpcbiAqICMjIEJhdGNoaW5nIEJlaGF2aW9yXG4gKlxuICogVGhlIFNRUy10by1MYW1iZGEgaW50ZWdyYXRpb24gdXNlcyBpbnRlbGxpZ2VudCBiYXRjaGluZyB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZTpcbiAqXG4gKiAtICoqQmF0Y2ggU2l6ZSoqOiBMYW1iZGEgd2FpdHMgdG8gcmVjZWl2ZSB1cCB0byBgYmF0Y2hTaXplYCBtZXNzYWdlcyAoZGVmYXVsdDogNTAwKVxuICogLSAqKkJhdGNoaW5nIFdpbmRvdyoqOiBJZiBmZXdlciB0aGFuIGBiYXRjaFNpemVgIG1lc3NhZ2VzIGFyZSBhdmFpbGFibGUsIExhbWJkYVxuICogICB0cmlnZ2VycyBhZnRlciBgbWF4QmF0Y2hpbmdXaW5kb3dgIG1pbnV0ZXMgKGRlZmF1bHQ6IDEgbWludXRlKVxuICogLSAqKlRyaWdnZXIgQ29uZGl0aW9uKio6IExhbWJkYSBleGVjdXRlcyB3aGVuIEVJVEhFUiBjb25kaXRpb24gaXMgbWV0IGZpcnN0XG4gKiAtICoqQ29uY3VycmVuY3kqKjogTGltaXRlZCB0byBgbWF4Q29uY3VycmVuY3lgIGNvbmN1cnJlbnQgZXhlY3V0aW9ucyB0byBwcmV2ZW50IGRhdGFiYXNlIG92ZXJsb2FkXG4gKiAtICoqUGFydGlhbCBGYWlsdXJlcyoqOiBVc2VzIGByZXBvcnRCYXRjaEl0ZW1GYWlsdXJlc2AgdG8gcmV0cnkgb25seSBmYWlsZWQgaXRlbXNcbiAqXG4gKiBUaGlzIGFwcHJvYWNoIGJhbGFuY2VzIHRocm91Z2hwdXQgKGxhcmdlciBiYXRjaGVzID0gZmV3ZXIgZGF0YWJhc2UgY29ubmVjdGlvbnMpXG4gKiB3aXRoIGxhdGVuY3kgKHRpbWUtYmFzZWQgdHJpZ2dlcnMgcHJldmVudCBpbmRlZmluaXRlIHdhaXRpbmcpLlxuICpcbiAqICMjIEVycm9yIEhhbmRsaW5nIGFuZCBEZWFkIExldHRlciBRdWV1ZVxuICpcbiAqIEZhaWxlZCBtZXNzYWdlcyBhcmUgc2VudCB0byB0aGUgZGVhZCBsZXR0ZXIgcXVldWUgYWZ0ZXIgNSBwcm9jZXNzaW5nIGF0dGVtcHRzLlxuICogKipJbXBvcnRhbnQqKjogVGhpcyBjb25zdHJ1Y3QgcHJvdmlkZXMgTk8gYXV0b21hdGVkIGhhbmRsaW5nIG9mIGRlYWQgbGV0dGVyIHF1ZXVlXG4gKiBtZXNzYWdlcyAtIG1vbml0b3JpbmcsIGluc3BlY3Rpb24sIGFuZCByZXByb2Nlc3Npbmcgb2YgZmFpbGVkIGl0ZW1zIGlzIHRoZVxuICogcmVzcG9uc2liaWxpdHkgb2YgdGhlIGltcGxlbWVudGluZyBhcHBsaWNhdGlvbi5cbiAqXG4gKiBDb25zaWRlciBpbXBsZW1lbnRpbmc6XG4gKiAtIENsb3VkV2F0Y2ggYWxhcm1zIG9uIGRlYWQgbGV0dGVyIHF1ZXVlIGRlcHRoXG4gKiAtIE1hbnVhbCBvciBhdXRvbWF0ZWQgcmVwcm9jZXNzaW5nIHdvcmtmbG93c1xuICogLSBMb2dnaW5nIGFuZCBhbGVydGluZyBmb3IgZmFpbGVkIGl0ZW1zXG4gKiAtIFJlZ3VsYXIgY2xlYW51cCBvZiBvbGQgZGVhZCBsZXR0ZXIgbWVzc2FnZXMgKDE0LWRheSByZXRlbnRpb24pXG4gKlxuICogIyMgT3BlcmF0aW9uYWwgQ2hhcmFjdGVyaXN0aWNzXG4gKlxuICogLSAqKlNjYWxhYmlsaXR5Kio6IExhbWJkYSBzY2FsZXMgYXV0b21hdGljYWxseSBiYXNlZCBvbiBxdWV1ZSBkZXB0aFxuICogLSAqKlJlbGlhYmlsaXR5Kio6IERlYWQgbGV0dGVyIHF1ZXVlIGNhcHR1cmVzIGZhaWx1cmVzIGZvciBkZWJ1Z2dpbmdcbiAqIC0gKipFZmZpY2llbmN5Kio6IEJhdGNoaW5nIG9wdGltaXplcyBkYXRhYmFzZSBvcGVyYXRpb25zIGZvciBoaWdoIHRocm91Z2hwdXRcbiAqIC0gKipTZWN1cml0eSoqOiBEYXRhYmFzZSBjcmVkZW50aWFscyBhY2Nlc3NlZCB2aWEgQVdTIFNlY3JldHMgTWFuYWdlclxuICogLSAqKk9ic2VydmFiaWxpdHkqKjogQ2xvdWRXYXRjaCBsb2dzIHJldGFpbmVkIGZvciBvbmUgd2Vla1xuICpcbiAqICMjIFByZXJlcXVpc2l0ZXNcbiAqXG4gKiBCZWZvcmUgdXNpbmcgdGhpcyBjb25zdHJ1Y3QsIGVuc3VyZTpcbiAqIC0gVGhlIHBnc3RhYyBkYXRhYmFzZSBoYXMgY29sbGVjdGlvbnMgbG9hZGVkIChpdGVtcyByZXF1aXJlIGV4aXN0aW5nIGNvbGxlY3Rpb24gSURzKVxuICogLSBEYXRhYmFzZSBjcmVkZW50aWFscyBhcmUgc3RvcmVkIGluIEFXUyBTZWNyZXRzIE1hbmFnZXJcbiAqIC0gVGhlIHBnc3RhYyBleHRlbnNpb24gaXMgcHJvcGVybHkgaW5zdGFsbGVkIGFuZCBjb25maWd1cmVkXG4gKlxuICogIyMgVXNhZ2UgRXhhbXBsZVxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0ZSBkYXRhYmFzZSBmaXJzdFxuICogY29uc3QgZGF0YWJhc2UgPSBuZXcgUGdTdGFjRGF0YWJhc2UodGhpcywgJ0RhdGFiYXNlJywge1xuICogICBwZ3N0YWNWZXJzaW9uOiAnMC45LjUnXG4gKiB9KTtcbiAqXG4gKiAvLyBDcmVhdGUgaXRlbSBsb2FkZXJcbiAqIGNvbnN0IGxvYWRlciA9IG5ldyBTdGFjSXRlbUxvYWRlcih0aGlzLCAnSXRlbUxvYWRlcicsIHtcbiAqICAgcGdzdGFjRGI6IGRhdGFiYXNlLFxuICogICBiYXRjaFNpemU6IDEwMDAsICAgICAgICAgIC8vIFByb2Nlc3MgdXAgdG8gMTAwMCBpdGVtcyBwZXIgYmF0Y2hcbiAqICAgbWF4QmF0Y2hpbmdXaW5kb3dNaW51dGVzOiAxLCAvLyBXYWl0IG1heCAxIG1pbnV0ZSB0byBmaWxsIGJhdGNoXG4gKiAgIGxhbWJkYVRpbWVvdXRTZWNvbmRzOiAzMDAgICAgIC8vIEFsbG93IHVwIHRvIDMwMCBzZWNvbmRzIGZvciBkYXRhYmFzZSBvcGVyYXRpb25zXG4gKiB9KTtcbiAqXG4gKiAvLyBUaGUgdG9waWMgQVJOIGNhbiBiZSB1c2VkIGJ5IG90aGVyIHNlcnZpY2VzIHRvIHB1Ymxpc2ggaXRlbXNcbiAqIG5ldyBDZm5PdXRwdXQodGhpcywgJ0xvYWRlclRvcGljQXJuJywge1xuICogICB2YWx1ZTogbG9hZGVyLnRvcGljLnRvcGljQXJuXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqICMjIERpcmVjdCBJdGVtIFB1Ymxpc2hpbmdcbiAqXG4gKiBFeHRlcm5hbCBzZXJ2aWNlcyBjYW4gcHVibGlzaCBTVEFDIGl0ZW1zIGRpcmVjdGx5IHRvIHRoZSB0b3BpYzpcbiAqXG4gKiBgYGBiYXNoXG4gKiBhd3Mgc25zIHB1Ymxpc2ggLS10b3BpYy1hcm4gJElURU1fTE9BRF9UT1BJQyAtLW1lc3NhZ2UgJ3tcbiAqICAgXCJ0eXBlXCI6IFwiRmVhdHVyZVwiLFxuICogICBcInN0YWNfdmVyc2lvblwiOiBcIjEuMC4wXCIsXG4gKiAgIFwiaWRcIjogXCJleGFtcGxlLWl0ZW1cIixcbiAqICAgXCJwcm9wZXJ0aWVzXCI6IHtcImRhdGV0aW1lXCI6IFwiMjAyMS0wMS0wMVQwMDowMDowMFpcIn0sXG4gKiAgIFwiZ2VvbWV0cnlcIjoge1widHlwZVwiOiBcIlBvbHlnb25cIiwgXCJjb29yZGluYXRlc1wiOiBbLi4uXX0sXG4gKiAgIFwiY29sbGVjdGlvblwiOiBcImV4YW1wbGUtY29sbGVjdGlvblwiXG4gKiB9J1xuICogYGBgXG4gKlxuICogIyMgUzMgRXZlbnQgQ29uZmlndXJhdGlvblxuICpcbiAqIFRvIGVuYWJsZSBTMyBldmVudC1kcml2ZW4gbG9hZGluZywgY29uZmlndXJlIFMzIGJ1Y2tldCBub3RpZmljYXRpb25zIHRvIHNlbmRcbiAqIGV2ZW50cyB0byB0aGUgU05TIHRvcGljIHdoZW4gU1RBQyBpdGVtcyAoLmpzb24gb3IgLmdlb2pzb24gZmlsZXMpIGFyZSB1cGxvYWRlZDpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBDb25maWd1cmUgUzMgYnVja2V0IHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0byB0aGUgbG9hZGVyIHRvcGljXG4gKiBidWNrZXQuYWRkRXZlbnROb3RpZmljYXRpb24oXG4gKiAgIHMzLkV2ZW50VHlwZS5PQkpFQ1RfQ1JFQVRFRCxcbiAqICAgbmV3IHMzbi5TbnNEZXN0aW5hdGlvbihsb2FkZXIudG9waWMpLFxuICogICB7IHN1ZmZpeDogJy5qc29uJyB9XG4gKiApO1xuICpcbiAqIGJ1Y2tldC5hZGRFdmVudE5vdGlmaWNhdGlvbihcbiAqICAgczMuRXZlbnRUeXBlLk9CSkVDVF9DUkVBVEVELFxuICogICBuZXcgczNuLlNuc0Rlc3RpbmF0aW9uKGxvYWRlci50b3BpYyksXG4gKiAgIHsgc3VmZml4OiAnLmdlb2pzb24nIH1cbiAqICk7XG4gKiBgYGBcbiAqXG4gKiBXaGVuIFNUQUMgaXRlbXMgYXJlIHVwbG9hZGVkIHRvIHRoZSBjb25maWd1cmVkIFMzIGJ1Y2tldCwgdGhlIGxvYWRlciB3aWxsOlxuICogMS4gUmVjZWl2ZSBTMyBldmVudCBub3RpZmljYXRpb25zIHZpYSBTTlNcbiAqIDIuIEZldGNoIHRoZSBTVEFDIGl0ZW0gSlNPTiBmcm9tIFMzXG4gKiAzLiBWYWxpZGF0ZSBhbmQgbG9hZCB0aGUgaXRlbSBpbnRvIHRoZSBwZ3N0YWMgZGF0YWJhc2VcbiAqXG4gKiAjIyBNb25pdG9yaW5nIGFuZCBUcm91Ymxlc2hvb3RpbmdcbiAqXG4gKiAtIE1vbml0b3IgTGFtYmRhIGxvZ3M6IGAvYXdzL2xhbWJkYS97RnVuY3Rpb25OYW1lfWBcbiAqIC0gKipEZWFkIExldHRlciBRdWV1ZSoqOiBDaGVjayBmb3IgZmFpbGVkIGl0ZW1zIC0gKipubyBhdXRvbWF0ZWQgaGFuZGxpbmcgcHJvdmlkZWQqKlxuICogLSBVc2UgYmF0Y2ggaXRlbSBmYWlsdXJlIHJlcG9ydGluZyBmb3IgcGFydGlhbCBiYXRjaCBwcm9jZXNzaW5nXG4gKiAtIENsb3VkV2F0Y2ggbWV0cmljcyBhdmFpbGFibGUgZm9yIHF1ZXVlIGRlcHRoIGFuZCBMYW1iZGEgcGVyZm9ybWFuY2VcbiAqXG4gKiAjIyMgRGVhZCBMZXR0ZXIgUXVldWUgTWFuYWdlbWVudFxuICpcbiAqIEFwcGxpY2F0aW9ucyBtdXN0IGltcGxlbWVudCB0aGVpciBvd24gZGVhZCBsZXR0ZXIgcXVldWUgbW9uaXRvcmluZzpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBFeGFtcGxlOiBDbG91ZFdhdGNoIGFsYXJtIGZvciBkZWFkIGxldHRlciBxdWV1ZSBkZXB0aFxuICogbmV3IGNsb3Vkd2F0Y2guQWxhcm0odGhpcywgJ0RlYWRMZXR0ZXJBbGFybScsIHtcbiAqICAgbWV0cmljOiBsb2FkZXIuZGVhZExldHRlclF1ZXVlLm1ldHJpY0FwcHJveGltYXRlTnVtYmVyT2ZWaXNpYmxlTWVzc2FnZXMoKSxcbiAqICAgdGhyZXNob2xkOiAxLFxuICogICBldmFsdWF0aW9uUGVyaW9kczogMVxuICogfSk7XG4gKlxuICogLy8gRXhhbXBsZTogTGFtYmRhIHRvIHJlcHJvY2VzcyBkZWFkIGxldHRlciBtZXNzYWdlc1xuICogY29uc3QgcmVwcm9jZXNzRnVuY3Rpb24gPSBuZXcgbGFtYmRhLkZ1bmN0aW9uKHRoaXMsICdSZXByb2Nlc3MnLCB7XG4gKiAgIC8vIEltcGxlbWVudGF0aW9uIHRvIGZldGNoIGFuZCByZXB1Ymxpc2ggZmFpbGVkIG1lc3NhZ2VzXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqL1xuZXhwb3J0IGNsYXNzIFN0YWNJdGVtTG9hZGVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgLyoqXG4gICAqIFRoZSBTTlMgdG9waWMgdGhhdCByZWNlaXZlcyBTVEFDIGl0ZW1zIGFuZCBTMyBldmVudCBub3RpZmljYXRpb25zIGZvciBsb2FkaW5nLlxuICAgKlxuICAgKiBUaGlzIHRvcGljIHNlcnZlcyBhcyB0aGUgZW50cnkgcG9pbnQgZm9yIHR3byB0eXBlcyBvZiBldmVudHM6XG4gICAqIDEuIERpcmVjdCBTVEFDIGl0ZW0gSlNPTiBkb2N1bWVudHMgcHVibGlzaGVkIGJ5IGV4dGVybmFsIHNlcnZpY2VzXG4gICAqIDIuIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnMgd2hlbiBTVEFDIGl0ZW1zIGFyZSB1cGxvYWRlZCB0byBjb25maWd1cmVkIGJ1Y2tldHNcbiAgICpcbiAgICogVGhlIHRvcGljIGZhbnMgb3V0IHRvIHRoZSBTUVMgcXVldWUgZm9yIGJhdGNoZWQgcHJvY2Vzc2luZy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0b3BpYzogc25zLlRvcGljO1xuXG4gIC8qKlxuICAgKiBUaGUgU1FTIHF1ZXVlIHRoYXQgYnVmZmVycyBtZXNzYWdlcyBiZWZvcmUgcHJvY2Vzc2luZy5cbiAgICpcbiAgICogVGhpcyBxdWV1ZSBjb2xsZWN0cyBib3RoIGRpcmVjdCBTVEFDIGl0ZW1zIGZyb20gU05TIGFuZCBTMyBldmVudFxuICAgKiBub3RpZmljYXRpb25zLCBiYXRjaGluZyB0aGVtIGZvciBlZmZpY2llbnQgZGF0YWJhc2Ugb3BlcmF0aW9ucy5cbiAgICogQ29uZmlndXJlZCB3aXRoIGEgdmlzaWJpbGl0eSB0aW1lb3V0IHRoYXQgYWNjb21tb2RhdGVzIExhbWJkYVxuICAgKiBwcm9jZXNzaW5nIHRpbWUgcGx1cyBidWZmZXIuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcXVldWU6IHNxcy5RdWV1ZTtcblxuICAvKipcbiAgICogRGVhZCBsZXR0ZXIgcXVldWUgZm9yIGZhaWxlZCBpdGVtIGxvYWRpbmcgYXR0ZW1wdHMuXG4gICAqXG4gICAqIE1lc3NhZ2VzIHRoYXQgZmFpbCBwcm9jZXNzaW5nIGFmdGVyIDUgYXR0ZW1wdHMgYXJlIHNlbnQgaGVyZVxuICAgKiBmb3IgaW5zcGVjdGlvbiBhbmQgcG90ZW50aWFsIHJlcGxheS4gUmV0YWlucyBtZXNzYWdlcyBmb3IgMTQgZGF5c1xuICAgKiB0byBhbGxvdyBmb3IgZGVidWdnaW5nIGFuZCBtYW51YWwgaW50ZXJ2ZW50aW9uLlxuICAgKlxuICAgKiAqKlVzZXIgUmVzcG9uc2liaWxpdHkqKjogVGhpcyBjb25zdHJ1Y3QgcHJvdmlkZXMgTk8gYXV0b21hdGVkIG1vbml0b3JpbmcsXG4gICAqIGFsZXJ0aW5nLCBvciByZXByb2Nlc3Npbmcgb2YgZGVhZCBsZXR0ZXIgcXVldWUgbWVzc2FnZXMuIEFwcGxpY2F0aW9uc1xuICAgKiB1c2luZyB0aGlzIGNvbnN0cnVjdCBtdXN0IGltcGxlbWVudCB0aGVpciBvd246XG4gICAqIC0gRGVhZCBsZXR0ZXIgcXVldWUgZGVwdGggbW9uaXRvcmluZyBhbmQgYWxlcnRpbmdcbiAgICogLSBGYWlsZWQgbWVzc2FnZSBpbnNwZWN0aW9uIGFuZCBkZWJ1Z2dpbmcgd29ya2Zsb3dzXG4gICAqIC0gTWFudWFsIG9yIGF1dG9tYXRlZCByZXByb2Nlc3NpbmcgbWVjaGFuaXNtc1xuICAgKiAtIENsZWFudXAgcHJvY2VkdXJlcyBmb3Igb2xkIGZhaWxlZCBtZXNzYWdlc1xuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRlYWRMZXR0ZXJRdWV1ZTogc3FzLlF1ZXVlO1xuXG4gIC8qKlxuICAgKiBUaGUgTGFtYmRhIGZ1bmN0aW9uIHRoYXQgbG9hZHMgU1RBQyBpdGVtcyBpbnRvIHRoZSBwZ3N0YWMgZGF0YWJhc2UuXG4gICAqXG4gICAqIFRoaXMgUHl0aG9uIGZ1bmN0aW9uIHJlY2VpdmVzIGJhdGNoZXMgb2YgbWVzc2FnZXMgZnJvbSBTUVMgYW5kIHByb2Nlc3Nlc1xuICAgKiB0aGVtIGJhc2VkIG9uIHRoZWlyIHR5cGU6XG4gICAqIC0gRGlyZWN0IFNUQUMgaXRlbXM6IFZhbGlkYXRlcyBhbmQgbG9hZHMgZGlyZWN0bHkgaW50byBwZ3N0YWNcbiAgICogLSBTMyBldmVudHM6IEZldGNoZXMgU1RBQyBpdGVtcyBmcm9tIFMzLCB2YWxpZGF0ZXMsIGFuZCBsb2FkcyBpbnRvIHBnc3RhY1xuICAgKlxuICAgKiBUaGUgZnVuY3Rpb24gY29ubmVjdHMgdG8gUG9zdGdyZVNRTCB1c2luZyBjcmVkZW50aWFscyBmcm9tIFNlY3JldHMgTWFuYWdlclxuICAgKiBhbmQgdXNlcyBweXBnc3RhYyBmb3IgZWZmaWNpZW50IGRhdGFiYXNlIG9wZXJhdGlvbnMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbGFtYmRhRnVuY3Rpb246IGxhbWJkYS5GdW5jdGlvbjtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogU3RhY0l0ZW1Mb2FkZXJQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCB0aW1lb3V0U2Vjb25kcyA9IHByb3BzLmxhbWJkYVRpbWVvdXRTZWNvbmRzID8/IDMwMDtcbiAgICBjb25zdCBsYW1iZGFSdW50aW1lID0gcHJvcHMubGFtYmRhUnVudGltZSA/PyBsYW1iZGEuUnVudGltZS5QWVRIT05fM18xMTtcbiAgICBjb25zdCBtYXhDb25jdXJyZW5jeSA9IHByb3BzLm1heENvbmN1cnJlbmN5ID8/IDI7XG5cbiAgICAvLyBDcmVhdGUgZGVhZCBsZXR0ZXIgcXVldWVcbiAgICB0aGlzLmRlYWRMZXR0ZXJRdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgXCJEZWFkTGV0dGVyUXVldWVcIiwge1xuICAgICAgcmV0ZW50aW9uUGVyaW9kOiBEdXJhdGlvbi5kYXlzKDE0KSxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBtYWluIHF1ZXVlXG4gICAgdGhpcy5xdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgXCJRdWV1ZVwiLCB7XG4gICAgICB2aXNpYmlsaXR5VGltZW91dDogRHVyYXRpb24uc2Vjb25kcyh0aW1lb3V0U2Vjb25kcyArIDEwKSxcbiAgICAgIGVuY3J5cHRpb246IHNxcy5RdWV1ZUVuY3J5cHRpb24uU1FTX01BTkFHRUQsXG4gICAgICBkZWFkTGV0dGVyUXVldWU6IHtcbiAgICAgICAgbWF4UmVjZWl2ZUNvdW50OiA1LFxuICAgICAgICBxdWV1ZTogdGhpcy5kZWFkTGV0dGVyUXVldWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gQ3JlYXRlIFNOUyB0b3BpY1xuICAgIHRoaXMudG9waWMgPSBuZXcgc25zLlRvcGljKHRoaXMsIFwiVG9waWNcIiwge1xuICAgICAgZGlzcGxheU5hbWU6IGAke2lkfS1TdGFjSXRlbUxvYWRlclRvcGljYCxcbiAgICB9KTtcblxuICAgIC8vIFN1YnNjcmliZSB0aGUgcXVldWUgdG8gdGhlIHRvcGljXG4gICAgdGhpcy50b3BpYy5hZGRTdWJzY3JpcHRpb24oXG4gICAgICBuZXcgc25zU3Vic2NyaXB0aW9ucy5TcXNTdWJzY3JpcHRpb24odGhpcy5xdWV1ZSlcbiAgICApO1xuXG4gICAgLy8gQ3JlYXRlIHRoZSBsYW1iZGEgZnVuY3Rpb25cbiAgICB0aGlzLmxhbWJkYUZ1bmN0aW9uID0gbmV3IGxhbWJkYS5GdW5jdGlvbih0aGlzLCBcIkZ1bmN0aW9uXCIsIHtcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYVJ1bnRpbWUsXG4gICAgICBoYW5kbGVyOiBcInN0YWNfaXRlbV9sb2FkZXIuaGFuZGxlci5oYW5kbGVyXCIsXG4gICAgICB2cGM6IHByb3BzLnZwYyxcbiAgICAgIHZwY1N1Ym5ldHM6IHByb3BzLnN1Ym5ldFNlbGVjdGlvbixcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Eb2NrZXJCdWlsZChwYXRoLmpvaW4oX19kaXJuYW1lLCBcIi4uXCIpLCB7XG4gICAgICAgIGZpbGU6IFwic3RhYy1pdGVtLWxvYWRlci9ydW50aW1lL0RvY2tlcmZpbGVcIixcbiAgICAgICAgcGxhdGZvcm06IFwibGludXgvYW1kNjRcIixcbiAgICAgICAgYnVpbGRBcmdzOiB7XG4gICAgICAgICAgUFlUSE9OX1ZFUlNJT046IGxhbWJkYVJ1bnRpbWUudG9TdHJpbmcoKS5yZXBsYWNlKFwicHl0aG9uXCIsIFwiXCIpLFxuICAgICAgICAgIFBHU1RBQ19WRVJTSU9OOiBwcm9wcy5wZ3N0YWNEYi5wZ3N0YWNWZXJzaW9uLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgICBtZW1vcnlTaXplOiBwcm9wcy5tZW1vcnlTaXplID8/IDEwMjQsXG4gICAgICB0aW1lb3V0OiBEdXJhdGlvbi5zZWNvbmRzKHRpbWVvdXRTZWNvbmRzKSxcbiAgICAgIHJlc2VydmVkQ29uY3VycmVudEV4ZWN1dGlvbnM6IG1heENvbmN1cnJlbmN5LFxuICAgICAgbG9nUmV0ZW50aW9uOiBsb2dzLlJldGVudGlvbkRheXMuT05FX1dFRUssXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBQR1NUQUNfU0VDUkVUX0FSTjogcHJvcHMucGdzdGFjRGIucGdzdGFjU2VjcmV0LnNlY3JldEFybixcbiAgICAgICAgLi4ucHJvcHMuZW52aXJvbm1lbnQsXG4gICAgICB9LFxuICAgICAgLy8gb3ZlcndyaXRlcyBkZWZhdWx0cyB3aXRoIHVzZXItcHJvdmlkZWQgY29uZmlndXJhYmxlIHByb3BlcnRpZXNcbiAgICAgIC4uLnByb3BzLmxhbWJkYUZ1bmN0aW9uT3B0aW9ucyxcbiAgICB9KTtcblxuICAgIC8vIEdyYW50IHBlcm1pc3Npb25zIHRvIHJlYWQgdGhlIGRhdGFiYXNlIHNlY3JldFxuICAgIHByb3BzLnBnc3RhY0RiLnBnc3RhY1NlY3JldC5ncmFudFJlYWQodGhpcy5sYW1iZGFGdW5jdGlvbik7XG5cbiAgICAvLyBBZGQgU1FTIGV2ZW50IHNvdXJjZSB0byB0aGUgbGFtYmRhXG4gICAgdGhpcy5sYW1iZGFGdW5jdGlvbi5hZGRFdmVudFNvdXJjZShcbiAgICAgIG5ldyBsYW1iZGFFdmVudFNvdXJjZXMuU3FzRXZlbnRTb3VyY2UodGhpcy5xdWV1ZSwge1xuICAgICAgICBiYXRjaFNpemU6IHByb3BzLmJhdGNoU2l6ZSA/PyA1MDAsXG4gICAgICAgIG1heEJhdGNoaW5nV2luZG93OiBEdXJhdGlvbi5taW51dGVzKFxuICAgICAgICAgIHByb3BzLm1heEJhdGNoaW5nV2luZG93TWludXRlcyA/PyAxXG4gICAgICAgICksXG4gICAgICAgIG1heENvbmN1cnJlbmN5OiBtYXhDb25jdXJyZW5jeSxcbiAgICAgICAgcmVwb3J0QmF0Y2hJdGVtRmFpbHVyZXM6IHRydWUsXG4gICAgICB9KVxuICAgICk7XG5cbiAgICAvLyBDcmVhdGUgb3V0cHV0c1xuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgXCJUb3BpY0FyblwiLCB7XG4gICAgICB2YWx1ZTogdGhpcy50b3BpYy50b3BpY0FybixcbiAgICAgIGRlc2NyaXB0aW9uOiBcIkFSTiBvZiB0aGUgU3RhY0l0ZW1Mb2FkZXIgU05TIFRvcGljXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItdG9waWMtYXJuXCIsXG4gICAgfSk7XG5cbiAgICBuZXcgQ2ZuT3V0cHV0KHRoaXMsIFwiUXVldWVVcmxcIiwge1xuICAgICAgdmFsdWU6IHRoaXMucXVldWUucXVldWVVcmwsXG4gICAgICBkZXNjcmlwdGlvbjogXCJVUkwgb2YgdGhlIFN0YWNJdGVtTG9hZGVyIFNRUyBRdWV1ZVwiLFxuICAgICAgZXhwb3J0TmFtZTogXCJzdGFjLWl0ZW0tbG9hZGVyLXF1ZXVlLXVybFwiLFxuICAgIH0pO1xuXG4gICAgbmV3IENmbk91dHB1dCh0aGlzLCBcIkRlYWRMZXR0ZXJRdWV1ZVVybFwiLCB7XG4gICAgICB2YWx1ZTogdGhpcy5kZWFkTGV0dGVyUXVldWUucXVldWVVcmwsXG4gICAgICBkZXNjcmlwdGlvbjogXCJVUkwgb2YgdGhlIFN0YWNJdGVtTG9hZGVyIERlYWQgTGV0dGVyIFF1ZXVlXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItZGVhZGxldHRlci1xdWV1ZS11cmxcIixcbiAgICB9KTtcblxuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgXCJGdW5jdGlvbk5hbWVcIiwge1xuICAgICAgdmFsdWU6IHRoaXMubGFtYmRhRnVuY3Rpb24uZnVuY3Rpb25OYW1lLFxuICAgICAgZGVzY3JpcHRpb246IFwiTmFtZSBvZiB0aGUgU3RhY0l0ZW1Mb2FkZXIgTGFtYmRhIEZ1bmN0aW9uXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItZnVuY3Rpb24tbmFtZVwiLFxuICAgIH0pO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
ARG PYTHON_VERSION=3.11
|
|
2
|
+
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}
|
|
3
|
+
COPY --from=ghcr.io/astral-sh/uv:0.7.8 /uv /uvx /bin/
|
|
4
|
+
|
|
5
|
+
ENV UV_COMPILE_BYTECODE=1
|
|
6
|
+
ENV PYTHONUNBUFFERED=1
|
|
7
|
+
|
|
8
|
+
WORKDIR /asset
|
|
9
|
+
|
|
10
|
+
COPY stac-item-loader/runtime/pyproject.toml pyproject.toml
|
|
11
|
+
COPY stac-item-loader/runtime/src/stac_item_loader/ stac_item_loader/
|
|
12
|
+
|
|
13
|
+
ARG PGSTAC_VERSION=0.9.6
|
|
14
|
+
RUN uv add --no-sync pypgstac==${PGSTAC_VERSION} && \
|
|
15
|
+
uv export --no-dev --no-editable -o requirements.txt && \
|
|
16
|
+
uv pip install --target /asset -r requirements.txt
|
|
17
|
+
|
|
18
|
+
CMD ["stac_item_loader.handler.handler"]
|