@kensio/yulin 1.21.20 → 1.21.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/service/athena/engine/sim-athena-engine-result.d.ts +9 -1
- package/dist/service/athena/engine/sim-athena-engine-result.js +15 -4
- package/dist/service/athena/engine/sim-athena-engine-run.d.ts +2 -0
- package/dist/service/athena/engine/sim-athena-engine-run.js +6 -1
- package/dist/service/athena/engine/sim-athena-query-engine.js +1 -0
- package/dist/service/athena/engine/sim-athena-result-columns.d.ts +1 -1
- package/dist/service/athena/engine/sim-athena-result-columns.js +11 -4
- package/dist/service/athena/engine/sim-athena-round-scales.d.ts +19 -0
- package/dist/service/athena/engine/sim-athena-round-scales.js +80 -0
- package/dist/service/athena/engine/sim-athena-sql-translation.d.ts +9 -0
- package/dist/service/athena/engine/sim-athena-sql-translation.js +17 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-names.js +1 -5
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +5 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +7 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -1
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +1 -0
- package/dist/service/lambda/command/event-source-mapping/event-source-mapping.command.d.ts +2 -1
- package/dist/service/lambda/command/event-source-mapping/event-source-mapping.command.js +0 -6
- package/dist/service/lambda/event-source/filter/sim-lambda-filter-criteria.d.ts +48 -0
- package/dist/service/lambda/event-source/filter/sim-lambda-filter-criteria.js +129 -0
- package/dist/service/lambda/event-source/filter/sim-lambda-filtered-delivery.d.ts +27 -0
- package/dist/service/lambda/event-source/filter/sim-lambda-filtered-delivery.js +15 -0
- package/dist/service/lambda/event-source/filter/sim-lambda-filtered-records.d.ts +24 -0
- package/dist/service/lambda/event-source/filter/sim-lambda-filtered-records.js +35 -0
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +7 -4
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +9 -6
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-delivery.d.ts +16 -4
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-delivery.js +36 -5
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-event-source-poller.d.ts +4 -3
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-event-source-poller.js +6 -7
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-event.d.ts +14 -2
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-stream-event.js +21 -2
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-delivery.d.ts +19 -6
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-delivery.js +41 -7
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +7 -3
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +10 -9
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event.d.ts +6 -2
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event.js +6 -2
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-delivery.d.ts +21 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-delivery.js +32 -2
- package/dist/service/lambda/event-source/poll/sim-lambda-sqs-delivery.js +1 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-sqs-event.d.ts +8 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-sqs-event.js +11 -0
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -0
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +11 -0
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-cascade-guard.d.ts +35 -8
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-cascade-guard.js +35 -12
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-cascade.error.d.ts +13 -4
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-cascade.error.js +12 -6
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-halt.d.ts +18 -0
- package/dist/service/lambda/event-source/stream/sim-lambda-stream-halt.js +22 -0
- package/dist/service/lambda/function/invoke/sim-lambda-invocation-deadline.d.ts +4 -0
- package/dist/service/lambda/function/invoke/sim-lambda-invocation-deadline.js +21 -0
- package/dist/service/lambda/function/invoke/sim-lambda-stall-watch.d.ts +38 -0
- package/dist/service/lambda/function/invoke/sim-lambda-stall-watch.js +78 -0
- package/dist/service/lambda/function/invoke/sim-lambda-stalled.error.d.ts +23 -0
- package/dist/service/lambda/function/invoke/sim-lambda-stalled.error.js +22 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-clock-timer.d.ts +4 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-clock-timer.js +8 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-invocation-timers.d.ts +10 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-invocation-timers.js +28 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-pending-timer.d.ts +9 -0
- package/dist/service/lambda/function/invoke/timer/sim-lambda-pending-timer.js +1 -0
- package/dist/service/scheduler/command/schedule/schedule.command.d.ts +1 -0
- package/dist/service/scheduler/command/schedule/sim-scheduler-described-schedule.js +1 -0
- package/dist/service/scheduler/command/schedule/sim-scheduler-schedule-writer.js +2 -1
- package/dist/service/scheduler/command/schedule/sim-scheduler-unsimulated-input.d.ts +3 -3
- package/dist/service/scheduler/command/schedule/sim-scheduler-unsimulated-input.js +19 -11
- package/dist/service/scheduler/schedule/sim-scheduler-schedule-expression.d.ts +5 -3
- package/dist/service/scheduler/schedule/sim-scheduler-schedule-expression.js +6 -4
- package/dist/service/scheduler/schedule/sim-scheduler-schedule.d.ts +9 -0
- package/dist/service/scheduler/schedule/sim-scheduler-schedule.js +8 -0
- package/dist/util/schedule/at/sim-at-expression.d.ts +2 -1
- package/dist/util/schedule/at/sim-at-expression.js +7 -3
- package/dist/util/schedule/cron/sim-cron-expression.d.ts +6 -3
- package/dist/util/schedule/cron/sim-cron-expression.js +24 -21
- package/dist/util/schedule/sim-schedule-zone-clock.d.ts +26 -0
- package/dist/util/schedule/sim-schedule-zone-clock.js +56 -0
- package/dist/util/schedule/sim-schedule-zone.d.ts +58 -0
- package/dist/util/schedule/sim-schedule-zone.js +83 -0
- package/dist/util/schedule/sim-schedule.d.ts +1 -1
- package/dist/util/schedule/sim-schedule.js +6 -5
- package/docs/services/athena/README.md +5 -0
- package/docs/services/lambda/README.md +159 -15
- package/docs/services/scheduler/README.md +35 -6
- package/docs/time/README.md +6 -0
- package/package.json +1 -1
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import { SimAthenaResolvedResult } from "../result/sim-athena-resolved-result.js";
|
|
3
3
|
import type { SimAthenaLoadedTable } from "./sim-athena-table-rows.js";
|
|
4
|
+
/** What one statement is run and read back with. */
|
|
5
|
+
export interface SimAthenaEngineResultRun {
|
|
6
|
+
readonly database: DatabaseSync;
|
|
7
|
+
readonly sql: string;
|
|
8
|
+
readonly loaded: readonly SimAthenaLoadedTable[];
|
|
9
|
+
/** The decimal places each result column was rounded to, by position. */
|
|
10
|
+
readonly scales: ReadonlyMap<number, number>;
|
|
11
|
+
}
|
|
4
12
|
/**
|
|
5
13
|
* Run one translated statement and read its answer back as a result set.
|
|
6
14
|
*
|
|
@@ -8,4 +16,4 @@ import type { SimAthenaLoadedTable } from "./sim-athena-table-rows.js";
|
|
|
8
16
|
* free to answer with two columns of one name and an object would keep one of
|
|
9
17
|
* them.
|
|
10
18
|
*/
|
|
11
|
-
export declare function simAthenaEngineResult(
|
|
19
|
+
export declare function simAthenaEngineResult(run: SimAthenaEngineResultRun): SimAthenaResolvedResult;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SimAthenaResolvedResult } from "../result/sim-athena-resolved-result.js";
|
|
2
2
|
import { simAthenaResultColumns } from "./sim-athena-result-columns.js";
|
|
3
|
+
import { simAthenaRoundedText } from "./sim-athena-round-scales.js";
|
|
3
4
|
/**
|
|
4
5
|
* Run one translated statement and read its answer back as a result set.
|
|
5
6
|
*
|
|
@@ -7,8 +8,8 @@ import { simAthenaResultColumns } from "./sim-athena-result-columns.js";
|
|
|
7
8
|
* free to answer with two columns of one name and an object would keep one of
|
|
8
9
|
* them.
|
|
9
10
|
*/
|
|
10
|
-
export function simAthenaEngineResult(
|
|
11
|
-
const statement = database.prepare(sql);
|
|
11
|
+
export function simAthenaEngineResult(run) {
|
|
12
|
+
const statement = run.database.prepare(run.sql);
|
|
12
13
|
statement.setReturnArrays(true);
|
|
13
14
|
// A whole number comes back as a BigInt, because SQLite holds a 64 bit
|
|
14
15
|
// integer and reading one past what a double represents exactly raises
|
|
@@ -16,12 +17,22 @@ export function simAthenaEngineResult(database, sql, loaded) {
|
|
|
16
17
|
// Every one of them renders the same either way.
|
|
17
18
|
statement.setReadBigInts(true);
|
|
18
19
|
const rows = statement.all();
|
|
19
|
-
const columns = simAthenaResultColumns(statement.columns(), rows, loaded);
|
|
20
|
+
const columns = simAthenaResultColumns(statement.columns(), rows, run.loaded, run.scales);
|
|
20
21
|
return new SimAthenaResolvedResult({
|
|
21
22
|
columns,
|
|
22
|
-
rows: rows.map((row) => columns.map((column, index) =>
|
|
23
|
+
rows: rows.map((row) => columns.map((column, index) => renderedCell(row[index], column, run.scales.get(index)))),
|
|
23
24
|
});
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* One value as `GetQueryResults` carries it, with the scale its column was
|
|
28
|
+
* rounded to where the statement asked for one.
|
|
29
|
+
*/
|
|
30
|
+
function renderedCell(value, column, scale) {
|
|
31
|
+
const text = rendered(value, column);
|
|
32
|
+
return scale === undefined || text === ""
|
|
33
|
+
? text
|
|
34
|
+
: simAthenaRoundedText(value, scale);
|
|
35
|
+
}
|
|
25
36
|
/**
|
|
26
37
|
* One value as `GetQueryResults` carries it.
|
|
27
38
|
*
|
|
@@ -14,6 +14,8 @@ export interface SimAthenaEngineRun {
|
|
|
14
14
|
readonly caller: SimAwsCaller | undefined;
|
|
15
15
|
readonly startedAt: Date;
|
|
16
16
|
readonly sql: string;
|
|
17
|
+
/** The decimal places each result column was rounded to, by position. */
|
|
18
|
+
readonly scales: ReadonlyMap<number, number>;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Load the tables, build the database and run the statement.
|
|
@@ -29,7 +29,12 @@ export async function simAthenaEngineRun(run) {
|
|
|
29
29
|
startedAt: run.startedAt,
|
|
30
30
|
});
|
|
31
31
|
try {
|
|
32
|
-
return simAthenaEngineAnswered(simAthenaEngineResult(
|
|
32
|
+
return simAthenaEngineAnswered(simAthenaEngineResult({
|
|
33
|
+
database,
|
|
34
|
+
sql: run.sql,
|
|
35
|
+
loaded,
|
|
36
|
+
scales: run.scales,
|
|
37
|
+
}));
|
|
33
38
|
}
|
|
34
39
|
finally {
|
|
35
40
|
database.close();
|
|
@@ -15,4 +15,4 @@ import type { SimAthenaLoadedTable } from "./sim-athena-table-rows.js";
|
|
|
15
15
|
* could not have been written as an identifier is how that is told apart from
|
|
16
16
|
* an alias a statement chose.
|
|
17
17
|
*/
|
|
18
|
-
export declare function simAthenaResultColumns(metadata: readonly StatementColumnMetadata[], rows: readonly (readonly SQLOutputValue[])[], loaded: readonly SimAthenaLoadedTable[]): readonly SimAthenaDeclaredColumn[];
|
|
18
|
+
export declare function simAthenaResultColumns(metadata: readonly StatementColumnMetadata[], rows: readonly (readonly SQLOutputValue[])[], loaded: readonly SimAthenaLoadedTable[], scales?: ReadonlyMap<number, number>): readonly SimAthenaDeclaredColumn[];
|
|
@@ -15,15 +15,22 @@ const identifier = /^[A-Za-z_][\dA-Za-z_]*$/u;
|
|
|
15
15
|
* could not have been written as an identifier is how that is told apart from
|
|
16
16
|
* an alias a statement chose.
|
|
17
17
|
*/
|
|
18
|
-
export function simAthenaResultColumns(metadata, rows, loaded) {
|
|
18
|
+
export function simAthenaResultColumns(metadata, rows, loaded, scales = new Map()) {
|
|
19
19
|
const types = glueColumnTypes(loaded);
|
|
20
20
|
return metadata.map((column, index) => ({
|
|
21
21
|
name: nameOf(column, index),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
// A column the statement rounded to a scale is a double whatever value it
|
|
23
|
+
// happens to hold, since a whole number rounded to one place is still one.
|
|
24
|
+
type: scales.has(index)
|
|
25
|
+
? "double"
|
|
26
|
+
: columnType({ column, rows, index, types }),
|
|
25
27
|
}));
|
|
26
28
|
}
|
|
29
|
+
function columnType(read) {
|
|
30
|
+
return read.column.column === null
|
|
31
|
+
? inferredType(read.rows, read.index)
|
|
32
|
+
: simAthenaResultType(read.types.get(originOf(read.column)));
|
|
33
|
+
}
|
|
27
34
|
function nameOf(column, index) {
|
|
28
35
|
const { name } = column;
|
|
29
36
|
if (column.column !== null || identifier.test(name)) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decimal places each result column was rounded to, by position.
|
|
3
|
+
*
|
|
4
|
+
* `round(x, 1)` answers `25.0` on Trino and `25` here, because SQLite hands
|
|
5
|
+
* back a double and nothing downstream knows how many places the statement
|
|
6
|
+
* asked to keep. The statement is the only thing that knows, so the scale is
|
|
7
|
+
* read off the syntax tree while it is still there and travels with the query.
|
|
8
|
+
*
|
|
9
|
+
* Only a literal scale is read. `round(x, n)` over a column is a scale that
|
|
10
|
+
* differs per row, and a result column has one rendering for every row in it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function simAthenaRoundScales(ast: unknown): ReadonlyMap<number, number>;
|
|
13
|
+
/**
|
|
14
|
+
* One rounded value as its column renders it.
|
|
15
|
+
*
|
|
16
|
+
* A whole number keeps the trailing zeros the scale asked for, which is the
|
|
17
|
+
* whole point of carrying the scale this far.
|
|
18
|
+
*/
|
|
19
|
+
export declare function simAthenaRoundedText(value: unknown, scale: number): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { isRecord } from "../../../util/type-guard/record.js";
|
|
2
|
+
/**
|
|
3
|
+
* The decimal places each result column was rounded to, by position.
|
|
4
|
+
*
|
|
5
|
+
* `round(x, 1)` answers `25.0` on Trino and `25` here, because SQLite hands
|
|
6
|
+
* back a double and nothing downstream knows how many places the statement
|
|
7
|
+
* asked to keep. The statement is the only thing that knows, so the scale is
|
|
8
|
+
* read off the syntax tree while it is still there and travels with the query.
|
|
9
|
+
*
|
|
10
|
+
* Only a literal scale is read. `round(x, n)` over a column is a scale that
|
|
11
|
+
* differs per row, and a result column has one rendering for every row in it.
|
|
12
|
+
*/
|
|
13
|
+
export function simAthenaRoundScales(ast) {
|
|
14
|
+
const scales = new Map();
|
|
15
|
+
for (const [index, item] of selectItems(ast).entries()) {
|
|
16
|
+
const scale = roundScaleOf(item);
|
|
17
|
+
if (scale !== undefined) {
|
|
18
|
+
scales.set(index, scale);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return scales;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One rounded value as its column renders it.
|
|
25
|
+
*
|
|
26
|
+
* A whole number keeps the trailing zeros the scale asked for, which is the
|
|
27
|
+
* whole point of carrying the scale this far.
|
|
28
|
+
*/
|
|
29
|
+
export function simAthenaRoundedText(value, scale) {
|
|
30
|
+
return typeof value === "number" ? value.toFixed(scale) : String(value);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The select list of the statement, which is the first one where a query
|
|
34
|
+
* carries several.
|
|
35
|
+
*/
|
|
36
|
+
function selectItems(ast) {
|
|
37
|
+
const statement = Array.isArray(ast) ? ast[0] : ast;
|
|
38
|
+
const columns = isRecord(statement) ? statement["columns"] : undefined;
|
|
39
|
+
return Array.isArray(columns) ? columns : [];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The scale one select item was rounded to, for an item that is a `round` call
|
|
43
|
+
* over a literal scale.
|
|
44
|
+
*/
|
|
45
|
+
function roundScaleOf(item) {
|
|
46
|
+
const expression = isRecord(item) ? item["expr"] : undefined;
|
|
47
|
+
if (!isRecord(expression) || functionNameOf(expression) !== "round") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const arguments_ = callArguments(expression);
|
|
51
|
+
return arguments_.length === 2 ? literalScale(arguments_[1]) : undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The name a function call node carries, which the parser holds as a list of
|
|
55
|
+
* the parts a qualified name would have.
|
|
56
|
+
*/
|
|
57
|
+
function functionNameOf(expression) {
|
|
58
|
+
const name = isRecord(expression["name"])
|
|
59
|
+
? expression["name"]["name"]
|
|
60
|
+
: undefined;
|
|
61
|
+
const first = Array.isArray(name) ? name[0] : undefined;
|
|
62
|
+
const value = isRecord(first) ? first["value"] : undefined;
|
|
63
|
+
return typeof value === "string" ? value.toLowerCase() : "";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The arguments a function call node carries.
|
|
67
|
+
*/
|
|
68
|
+
function callArguments(expression) {
|
|
69
|
+
const arguments_ = expression["args"];
|
|
70
|
+
const value = isRecord(arguments_) ? arguments_["value"] : undefined;
|
|
71
|
+
return Array.isArray(value) ? value : [];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A whole number written into the statement, or nothing for anything else.
|
|
75
|
+
*/
|
|
76
|
+
function literalScale(node) {
|
|
77
|
+
const value = isRecord(node) && node["type"] === "number" ? node["value"] : undefined;
|
|
78
|
+
const scale = Number(value);
|
|
79
|
+
return Number.isSafeInteger(scale) && scale >= 0 ? scale : undefined;
|
|
80
|
+
}
|
|
@@ -3,14 +3,23 @@ import type { SimAthenaSqlParser } from "./sim-athena-parser-module.js";
|
|
|
3
3
|
/** One statement translated for SQLite, or why it was not. */
|
|
4
4
|
export type SimAthenaTranslatedSql = {
|
|
5
5
|
readonly sql: string;
|
|
6
|
+
/**
|
|
7
|
+
* The decimal places each result column was rounded to, by position.
|
|
8
|
+
*
|
|
9
|
+
* Read here because the syntax tree is the only place that knows, and
|
|
10
|
+
* the tree is gone by the time the answer is rendered.
|
|
11
|
+
*/
|
|
12
|
+
readonly scales: ReadonlyMap<number, number>;
|
|
6
13
|
readonly turnedDown: undefined;
|
|
7
14
|
readonly rejected: undefined;
|
|
8
15
|
} | {
|
|
9
16
|
readonly sql: undefined;
|
|
17
|
+
readonly scales: undefined;
|
|
10
18
|
readonly turnedDown: string;
|
|
11
19
|
readonly rejected: undefined;
|
|
12
20
|
} | {
|
|
13
21
|
readonly sql: undefined;
|
|
22
|
+
readonly scales: undefined;
|
|
14
23
|
readonly turnedDown: undefined;
|
|
15
24
|
readonly rejected: string;
|
|
16
25
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { simAthenaSqlForParser, simAthenaSqlForSqlite, } from "./sim-athena-sql-rewrites.js";
|
|
2
2
|
import { simAthenaUnparsedStatement, simAthenaUnrewrittenUnnest, simAthenaUnwrittenStatement, } from "./sim-athena-turn-down.js";
|
|
3
3
|
import { simAthenaRejectedStatement } from "./sim-athena-rejected-statement.js";
|
|
4
|
+
import { simAthenaRoundScales } from "./sim-athena-round-scales.js";
|
|
4
5
|
import { simAthenaRewriteUnnest } from "./sim-athena-unnest-rewrite.js";
|
|
5
6
|
/**
|
|
6
7
|
* One Athena statement written back out for SQLite, or why it is not.
|
|
@@ -29,7 +30,12 @@ export function simAthenaSqliteSql(request) {
|
|
|
29
30
|
}
|
|
30
31
|
const rejected = simAthenaRejectedStatement(ast);
|
|
31
32
|
if (rejected !== undefined) {
|
|
32
|
-
return {
|
|
33
|
+
return {
|
|
34
|
+
sql: undefined,
|
|
35
|
+
scales: undefined,
|
|
36
|
+
turnedDown: undefined,
|
|
37
|
+
rejected,
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
try {
|
|
35
41
|
if (!simAthenaRewriteUnnest({
|
|
@@ -39,8 +45,12 @@ export function simAthenaSqliteSql(request) {
|
|
|
39
45
|
})) {
|
|
40
46
|
return turnedDown(simAthenaUnrewrittenUnnest);
|
|
41
47
|
}
|
|
48
|
+
// Read before the tree is written back out, since the rewrites above can
|
|
49
|
+
// move a select item and the scale travels by position.
|
|
50
|
+
const scales = simAthenaRoundScales(ast);
|
|
42
51
|
return {
|
|
43
52
|
sql: simAthenaSqlForSqlite(parser.sqlify(ast, { database: "sqlite" })),
|
|
53
|
+
scales,
|
|
44
54
|
turnedDown: undefined,
|
|
45
55
|
rejected: undefined,
|
|
46
56
|
};
|
|
@@ -50,5 +60,10 @@ export function simAthenaSqliteSql(request) {
|
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
function turnedDown(reason) {
|
|
53
|
-
return {
|
|
63
|
+
return {
|
|
64
|
+
sql: undefined,
|
|
65
|
+
scales: undefined,
|
|
66
|
+
turnedDown: reason,
|
|
67
|
+
rejected: undefined,
|
|
68
|
+
};
|
|
54
69
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { simLambdaFilterCriteriaInput } from "../../event-source/filter/sim-lambda-filter-criteria.js";
|
|
1
2
|
import { simLambdaStreamDestinationConfig } from "../../event-source/sim-lambda-stream-destination-config.js";
|
|
2
3
|
import { SimCfnLambdaPropertyParser } from "../function/sim-cfn-lambda-property-parser.js";
|
|
3
4
|
import { recordUnsimulatedEventSourceMappingProperties } from "./sim-cfn-lambda-event-source-mapping-property-rules.js";
|
|
@@ -46,6 +47,7 @@ export class SimCfnLambdaEventSourceMappingProperties {
|
|
|
46
47
|
StartingPosition: this.parser.optionalString(this.resource, this.properties["StartingPosition"], "StartingPosition"),
|
|
47
48
|
StartingPositionTimestamp: this.startingPositionTimestamp(),
|
|
48
49
|
FunctionResponseTypes: this.functionResponseTypes(),
|
|
50
|
+
FilterCriteria: simLambdaFilterCriteriaInput(this.properties["FilterCriteria"]),
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
@@ -44,13 +44,9 @@ const absentEventSource = "the mapping polls the source its EventSourceArn names
|
|
|
44
44
|
export const unsimulatedPropertyReasons = new Map([
|
|
45
45
|
["AmazonManagedKafkaEventSourceConfig", absentEventSource],
|
|
46
46
|
["DocumentDBEventSourceConfig", absentEventSource],
|
|
47
|
-
[
|
|
48
|
-
"FilterCriteria",
|
|
49
|
-
"every record on the event source is delivered to the function, unfiltered",
|
|
50
|
-
],
|
|
51
47
|
[
|
|
52
48
|
"KmsKeyArn",
|
|
53
|
-
"
|
|
49
|
+
"filter criteria are simulated in plaintext, and encrypting them is not",
|
|
54
50
|
],
|
|
55
51
|
["MetricsConfig", "the mapping publishes no CloudWatch metrics"],
|
|
56
52
|
[
|
package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type SimLambdaStreamDestinationConfiguration } from "../../event-source
|
|
|
2
2
|
import type { SimAwsAccountRegionScope } from "../../../aws/sim-aws-account-region-scope.js";
|
|
3
3
|
import { type SimLambdaEventSourceArn } from "../../event-source/sim-lambda-event-source-arn.js";
|
|
4
4
|
import type { SimLambdaFunctionResponseType } from "../../event-source/sim-lambda-event-source-mapping.js";
|
|
5
|
+
import { SimLambdaFilterCriteria } from "../../event-source/filter/sim-lambda-filter-criteria.js";
|
|
5
6
|
import type { SimLambdaStreamRetryLimits } from "../../event-source/sim-lambda-stream-retry-limits.js";
|
|
6
7
|
import type { SimLambdaEventSourceStart } from "../../event-source/sim-lambda-event-source-starting-position.js";
|
|
7
8
|
import type { SimCreateEventSourceMappingCommandInput } from "./event-source-mapping.command.js";
|
|
@@ -36,6 +37,10 @@ export declare class SimLambdaEventSourceMappingInput {
|
|
|
36
37
|
* the counting to the mapping.
|
|
37
38
|
*/
|
|
38
39
|
readonly streamRetryLimits: SimLambdaStreamRetryLimits | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The filters this mapping delivers through, if it was created with any.
|
|
42
|
+
*/
|
|
43
|
+
readonly filterCriteria: SimLambdaFilterCriteria | undefined;
|
|
39
44
|
private constructor();
|
|
40
45
|
/**
|
|
41
46
|
* Read a CreateEventSourceMapping request, refusing what this simulation
|
package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { simLambdaStreamDestinationConfig, } from "../../event-source/sim-lambda-stream-destination-config.js";
|
|
2
2
|
import { simLambdaEventSourceArnOf, } from "../../event-source/sim-lambda-event-source-arn.js";
|
|
3
|
+
import { SimLambdaFilterCriteria } from "../../event-source/filter/sim-lambda-filter-criteria.js";
|
|
3
4
|
import { SimLambdaInvalidParameterValueException } from "../../error/sim-lambda.error.js";
|
|
4
5
|
import { simLambdaFunctionReferenceOf } from "../../function/sim-lambda-function-reference.js";
|
|
5
6
|
import { functionResponseTypesIn, requiredString, } from "./create-event-source-mapping-values.js";
|
|
@@ -35,6 +36,10 @@ export class SimLambdaEventSourceMappingInput {
|
|
|
35
36
|
* the counting to the mapping.
|
|
36
37
|
*/
|
|
37
38
|
streamRetryLimits;
|
|
39
|
+
/**
|
|
40
|
+
* The filters this mapping delivers through, if it was created with any.
|
|
41
|
+
*/
|
|
42
|
+
filterCriteria;
|
|
38
43
|
constructor(properties) {
|
|
39
44
|
this.eventSourceArn = properties.eventSourceArn;
|
|
40
45
|
this.functionName = properties.functionName;
|
|
@@ -45,6 +50,7 @@ export class SimLambdaEventSourceMappingInput {
|
|
|
45
50
|
this.functionResponseTypes = properties.functionResponseTypes;
|
|
46
51
|
this.destinationConfig = properties.destinationConfig;
|
|
47
52
|
this.streamRetryLimits = properties.streamRetryLimits;
|
|
53
|
+
this.filterCriteria = properties.filterCriteria;
|
|
48
54
|
}
|
|
49
55
|
/**
|
|
50
56
|
* Read a CreateEventSourceMapping request, refusing what this simulation
|
|
@@ -65,6 +71,7 @@ export class SimLambdaEventSourceMappingInput {
|
|
|
65
71
|
}),
|
|
66
72
|
enabled: input.Enabled ?? true,
|
|
67
73
|
functionResponseTypes: functionResponseTypesIn(input),
|
|
74
|
+
filterCriteria: SimLambdaFilterCriteria.of(input.FilterCriteria, eventSourceArn.kind),
|
|
68
75
|
streamRetryLimits: eventSourceArn.retryLimitRules.limitsIn({
|
|
69
76
|
maximumRetryAttempts: input.MaximumRetryAttempts,
|
|
70
77
|
maximumRecordAgeInSeconds: input.MaximumRecordAgeInSeconds,
|
package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js
CHANGED
|
@@ -9,7 +9,6 @@ import { SimLambdaInvalidParameterValueException } from "../../error/sim-lambda.
|
|
|
9
9
|
* would not have.
|
|
10
10
|
*/
|
|
11
11
|
const unsimulatedInputs = new Map([
|
|
12
|
-
["FilterCriteria", "event filtering is not simulated"],
|
|
13
12
|
["ScalingConfig", "polling concurrency is not simulated"],
|
|
14
13
|
["ProvisionedPollerConfig", "polling concurrency is not simulated"],
|
|
15
14
|
[
|
package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js
CHANGED
|
@@ -81,6 +81,7 @@ export class CreateEventSourceMappingCommandHandler {
|
|
|
81
81
|
enabled: input.enabled,
|
|
82
82
|
functionResponseTypes: input.functionResponseTypes,
|
|
83
83
|
streamRetryLimits: input.streamRetryLimits,
|
|
84
|
+
filterCriteria: input.filterCriteria,
|
|
84
85
|
destinationConfig: input.destinationConfig,
|
|
85
86
|
destinations: this.properties.destinations,
|
|
86
87
|
createdAt: this.properties.background.now(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SimLambdaFilterCriteriaInput } from "../../event-source/filter/sim-lambda-filter-criteria.js";
|
|
1
2
|
/**
|
|
2
3
|
* https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/lambda/command/CreateEventSourceMappingCommand/
|
|
3
4
|
*
|
|
@@ -31,7 +32,7 @@ export interface SimCreateEventSourceMappingCommandInput {
|
|
|
31
32
|
readonly BatchSize?: number | undefined;
|
|
32
33
|
readonly MaximumBatchingWindowInSeconds?: number | undefined;
|
|
33
34
|
readonly FunctionResponseTypes?: readonly string[] | undefined;
|
|
34
|
-
readonly FilterCriteria?:
|
|
35
|
+
readonly FilterCriteria?: SimLambdaFilterCriteriaInput | undefined;
|
|
35
36
|
readonly ScalingConfig?: object | undefined;
|
|
36
37
|
readonly DestinationConfig?: object | undefined;
|
|
37
38
|
readonly SelfManagedEventSource?: object | undefined;
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/lambda/command/CreateEventSourceMappingCommand/
|
|
3
|
-
*
|
|
4
|
-
* The event source mapping commands share their output shape, because every one
|
|
5
|
-
* of them reports the mapping it acted on, so they share one module.
|
|
6
|
-
*/
|
|
7
1
|
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `FilterCriteria` a CreateEventSourceMapping request carries, as the
|
|
3
|
+
* command takes it.
|
|
4
|
+
*/
|
|
5
|
+
export interface SimLambdaFilterCriteriaInput {
|
|
6
|
+
readonly Filters?: readonly {
|
|
7
|
+
readonly Pattern?: string | undefined;
|
|
8
|
+
}[] | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Read a `FilterCriteria` value of unknown shape, as a template carries one.
|
|
12
|
+
*
|
|
13
|
+
* The same shape check for an SDK request and a CloudFormation Resource, so a
|
|
14
|
+
* mapping a template deployed filters the way an SDK caller's would.
|
|
15
|
+
*/
|
|
16
|
+
export declare function simLambdaFilterCriteriaInput(value: unknown): SimLambdaFilterCriteriaInput | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* The filters a mapping delivers through, read from what a request asked for.
|
|
19
|
+
*
|
|
20
|
+
* A record is delivered when any one of the filters matches it, which is how
|
|
21
|
+
* real Lambda ORs a list of patterns together. A mapping with no criteria has
|
|
22
|
+
* none of these, and delivers everything.
|
|
23
|
+
*
|
|
24
|
+
* The patterns are EventBridge event patterns. Lambda documents its filter
|
|
25
|
+
* rules as EventBridge's own syntax, so simulated EventBridge's matcher is what
|
|
26
|
+
* evaluates them, down to the operators it refuses.
|
|
27
|
+
*/
|
|
28
|
+
export declare class SimLambdaFilterCriteria {
|
|
29
|
+
private readonly patterns;
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Read the criteria a request carries, refusing a pattern this simulation
|
|
33
|
+
* cannot evaluate.
|
|
34
|
+
*
|
|
35
|
+
* The refusal comes before the mapping exists, because a mapping created
|
|
36
|
+
* with a filter it cannot apply would deliver the wrong records rather than
|
|
37
|
+
* none.
|
|
38
|
+
*/
|
|
39
|
+
static of(criteria: SimLambdaFilterCriteriaInput | undefined, eventSourceKind: string): SimLambdaFilterCriteria | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The criteria as Create, Get and List report them back.
|
|
42
|
+
*/
|
|
43
|
+
configuration(): SimLambdaFilterCriteriaInput;
|
|
44
|
+
/**
|
|
45
|
+
* Whether a record, as a pattern reads it, passes any of these filters.
|
|
46
|
+
*/
|
|
47
|
+
matches(document: Record<string, unknown>): boolean;
|
|
48
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { SimEventPattern } from "../../../eventbridge/pattern/sim-event-pattern.js";
|
|
2
|
+
import { SimLambdaInvalidParameterValueException } from "../../error/sim-lambda.error.js";
|
|
3
|
+
import { isRecord } from "../../../../util/type-guard/record.js";
|
|
4
|
+
/**
|
|
5
|
+
* The one key an SQS mapping may filter on.
|
|
6
|
+
*
|
|
7
|
+
* Real Lambda filters an SQS message on its body alone, where a stream mapping
|
|
8
|
+
* filters on the whole record. A pattern naming anything else is refused here,
|
|
9
|
+
* since accepting one would match nothing and read as a pattern that was
|
|
10
|
+
* simply too specific.
|
|
11
|
+
*/
|
|
12
|
+
const sqsFilterKey = "body";
|
|
13
|
+
/**
|
|
14
|
+
* Read a `FilterCriteria` value of unknown shape, as a template carries one.
|
|
15
|
+
*
|
|
16
|
+
* The same shape check for an SDK request and a CloudFormation Resource, so a
|
|
17
|
+
* mapping a template deployed filters the way an SDK caller's would.
|
|
18
|
+
*/
|
|
19
|
+
export function simLambdaFilterCriteriaInput(value) {
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const filters = isRecord(value) ? value["Filters"] : undefined;
|
|
24
|
+
if (!Array.isArray(filters)) {
|
|
25
|
+
throw new SimLambdaInvalidParameterValueException(`FilterCriteria carries a Filters list, and this one carries ${JSON.stringify(value)}`);
|
|
26
|
+
}
|
|
27
|
+
return { Filters: filters.map((filter) => ({ Pattern: patternIn(filter) })) };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The pattern one filter of a list carries.
|
|
31
|
+
*/
|
|
32
|
+
function patternIn(filter) {
|
|
33
|
+
const pattern = isRecord(filter) ? filter["Pattern"] : undefined;
|
|
34
|
+
if (pattern !== undefined && typeof pattern !== "string") {
|
|
35
|
+
throw new SimLambdaInvalidParameterValueException("A filter in FilterCriteria carries a Pattern string, and this one " +
|
|
36
|
+
`carries ${JSON.stringify(pattern)}`);
|
|
37
|
+
}
|
|
38
|
+
return pattern;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The filters a mapping delivers through, read from what a request asked for.
|
|
42
|
+
*
|
|
43
|
+
* A record is delivered when any one of the filters matches it, which is how
|
|
44
|
+
* real Lambda ORs a list of patterns together. A mapping with no criteria has
|
|
45
|
+
* none of these, and delivers everything.
|
|
46
|
+
*
|
|
47
|
+
* The patterns are EventBridge event patterns. Lambda documents its filter
|
|
48
|
+
* rules as EventBridge's own syntax, so simulated EventBridge's matcher is what
|
|
49
|
+
* evaluates them, down to the operators it refuses.
|
|
50
|
+
*/
|
|
51
|
+
export class SimLambdaFilterCriteria {
|
|
52
|
+
patterns;
|
|
53
|
+
constructor(patterns) {
|
|
54
|
+
this.patterns = patterns;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the criteria a request carries, refusing a pattern this simulation
|
|
58
|
+
* cannot evaluate.
|
|
59
|
+
*
|
|
60
|
+
* The refusal comes before the mapping exists, because a mapping created
|
|
61
|
+
* with a filter it cannot apply would deliver the wrong records rather than
|
|
62
|
+
* none.
|
|
63
|
+
*/
|
|
64
|
+
static of(criteria, eventSourceKind) {
|
|
65
|
+
const filters = criteria?.Filters ?? [];
|
|
66
|
+
if (filters.length === 0) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return new this(filters.map((filter) => patternOf(filter.Pattern, eventSourceKind)));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The criteria as Create, Get and List report them back.
|
|
73
|
+
*/
|
|
74
|
+
configuration() {
|
|
75
|
+
return {
|
|
76
|
+
Filters: this.patterns.map((pattern) => ({ Pattern: pattern.source })),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether a record, as a pattern reads it, passes any of these filters.
|
|
81
|
+
*/
|
|
82
|
+
matches(document) {
|
|
83
|
+
return this.patterns.some((pattern) => pattern.matches(document));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* One filter's pattern, refused as a Lambda parameter rather than as an
|
|
88
|
+
* EventBridge one.
|
|
89
|
+
*
|
|
90
|
+
* The matcher is EventBridge's and says so when it refuses a pattern, and the
|
|
91
|
+
* caller here sent a Lambda request. The reason survives and the exception type
|
|
92
|
+
* changes.
|
|
93
|
+
*/
|
|
94
|
+
function patternOf(source, eventSourceKind) {
|
|
95
|
+
if (source === undefined || source === "") {
|
|
96
|
+
throw new SimLambdaInvalidParameterValueException("A filter in FilterCriteria carries a Pattern, and this one carries " +
|
|
97
|
+
"none");
|
|
98
|
+
}
|
|
99
|
+
refuseUnfilterableKeys(source, eventSourceKind);
|
|
100
|
+
try {
|
|
101
|
+
return SimEventPattern.of(source);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
105
|
+
throw new SimLambdaInvalidParameterValueException(`FilterCriteria pattern ${source} cannot be evaluated. ${reason}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Refuse a pattern naming a key the event source does not filter on.
|
|
110
|
+
*/
|
|
111
|
+
function refuseUnfilterableKeys(source, eventSourceKind) {
|
|
112
|
+
if (eventSourceKind !== "sqs") {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let parsed;
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(source);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// An unparseable pattern is the matcher's refusal to make, in its own
|
|
121
|
+
// words, and it is about to make it.
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const named = isRecord(parsed) ? Object.keys(parsed) : [];
|
|
125
|
+
const unfilterable = named.filter((key) => key !== sqsFilterKey);
|
|
126
|
+
if (unfilterable.length > 0) {
|
|
127
|
+
throw new SimLambdaInvalidParameterValueException(`FilterCriteria on an SQS event source mapping filters on ${sqsFilterKey} alone, and this pattern names ${unfilterable.join(", ")}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SimLambdaFilterCriteria } from "./sim-lambda-filter-criteria.js";
|
|
2
|
+
interface SimLambdaFilteredDeliveryProperties<RecordType, Outcome> {
|
|
3
|
+
readonly criteria: SimLambdaFilterCriteria | undefined;
|
|
4
|
+
readonly records: readonly RecordType[];
|
|
5
|
+
/**
|
|
6
|
+
* What a pattern reads one record as, which is the source's translation.
|
|
7
|
+
*/
|
|
8
|
+
readonly documentOf: (record: RecordType) => Record<string, unknown>;
|
|
9
|
+
/**
|
|
10
|
+
* What becomes of a batch the filters emptied.
|
|
11
|
+
*/
|
|
12
|
+
readonly handled: () => Outcome;
|
|
13
|
+
/**
|
|
14
|
+
* Hand the records that matched to the function.
|
|
15
|
+
*/
|
|
16
|
+
readonly invoke: (delivered: readonly RecordType[]) => Promise<Outcome>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Deliver the records of a batch a mapping's filters let through.
|
|
20
|
+
*
|
|
21
|
+
* A batch the filters emptied is finished with and the function is never
|
|
22
|
+
* invoked, which is the one rule every source shares. What being finished with
|
|
23
|
+
* means is the source's own, and so is what a pattern reads a record as, so
|
|
24
|
+
* both are handed in.
|
|
25
|
+
*/
|
|
26
|
+
export declare function simLambdaFilteredDelivery<RecordType, Outcome>(properties: SimLambdaFilteredDeliveryProperties<RecordType, Outcome>): Promise<Outcome>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { simLambdaFilteredRecords } from "./sim-lambda-filtered-records.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deliver the records of a batch a mapping's filters let through.
|
|
4
|
+
*
|
|
5
|
+
* A batch the filters emptied is finished with and the function is never
|
|
6
|
+
* invoked, which is the one rule every source shares. What being finished with
|
|
7
|
+
* means is the source's own, and so is what a pattern reads a record as, so
|
|
8
|
+
* both are handed in.
|
|
9
|
+
*/
|
|
10
|
+
export async function simLambdaFilteredDelivery(properties) {
|
|
11
|
+
const delivered = simLambdaFilteredRecords(properties.criteria, properties.records, properties.documentOf);
|
|
12
|
+
return delivered.length === 0
|
|
13
|
+
? properties.handled()
|
|
14
|
+
: await properties.invoke(delivered);
|
|
15
|
+
}
|