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