@devkong/cli-nx 0.0.67-alpha.23 → 0.0.67-alpha.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devkong/cli-nx",
3
- "version": "0.0.67-alpha.23",
3
+ "version": "0.0.67-alpha.25",
4
4
  "type": "commonjs",
5
5
  "main": "./src/index.js",
6
6
  "typings": "./src/index.d.ts",
@@ -4,7 +4,7 @@ kotlin.code.style=official
4
4
  javaVersion=21
5
5
 
6
6
  # kong artifacts
7
- kongVersion=0.0.40-SNAPSHOT
7
+ kongVersion=0.0.41-SNAPSHOT
8
8
 
9
9
  # https://mvnrepository.com/artifact/org.jlleitschuh.gradle/ktlint-gradle
10
10
  jlleitschuhGradlePluginVersion=14.0.1
@@ -1,2 +1,5 @@
1
1
  pydantic==2.13.4
2
- kong-sdk==0.0.46
2
+ kong-sdk==0.0.51
3
+ <% if (variant === "with-s3") { -%>
4
+ aiobotocore==2.15.2
5
+ <% } -%>
@@ -0,0 +1,6 @@
1
+ from kong.sdk.kong_frontend import KongFrontend, KongFrontendBuilder
2
+ from kong.sdk.s3 import S3Secret
3
+
4
+ frontend: KongFrontend = (
5
+ KongFrontendBuilder().with_secret_types("s3", {"s3": S3Secret}).with_form().build()
6
+ )
@@ -0,0 +1,17 @@
1
+ from kong.sdk.kong_function_request import KongFunctionRequest
2
+ from pydantic import BaseModel, Field
3
+
4
+
5
+ class ExecuteData(BaseModel):
6
+ key: str = Field(
7
+ examples=["test/example.json"],
8
+ description="Key (path) of the object to download from the S3 bucket",
9
+ )
10
+
11
+
12
+ class InputData(BaseModel):
13
+ request: KongFunctionRequest
14
+
15
+ execute: ExecuteData = Field(
16
+ description="Parameters for the 'execute' operation",
17
+ )
@@ -0,0 +1,54 @@
1
+ import sys
2
+ from datetime import timedelta
3
+
4
+ from frontend import frontend
5
+ from input_data import InputData
6
+ from kong.sdk.app import App
7
+ from kong.sdk.app_logger import app_logger
8
+ from kong.sdk.kong_function import KongFunction
9
+ from kong.sdk.kong_function_context import KongFunctionContext
10
+ from kong.sdk.s3 import S3Secret
11
+ from output_data import OutputData
12
+ from s3 import create_s3_client
13
+
14
+
15
+ class Main(KongFunction):
16
+ __logger = app_logger(__name__)
17
+
18
+ async def handle(self, data: InputData, context: KongFunctionContext) -> OutputData:
19
+ secret: S3Secret = context.secret.get("s3").data
20
+ client = await context.state.use(
21
+ key=f"{secret.endpoint}:{secret.port}:{secret.bucket}:{secret.access_key}",
22
+ create=lambda: create_s3_client(secret),
23
+ unused=(timedelta(minutes=5), lambda c: c.close()),
24
+ )
25
+
26
+ response = await client.get_object(
27
+ Bucket=secret.bucket,
28
+ Key=data.execute.key,
29
+ )
30
+
31
+ body = await response["Body"].read()
32
+ self.__logger.info(
33
+ "have response from S3",
34
+ lambda: {
35
+ "bucket": secret.bucket,
36
+ "key": data.execute.key,
37
+ "size": len(body),
38
+ },
39
+ )
40
+
41
+ return OutputData(
42
+ key=data.execute.key,
43
+ )
44
+
45
+
46
+ app = App(
47
+ InputData,
48
+ OutputData,
49
+ Main,
50
+ frontend,
51
+ )
52
+
53
+ if __name__ == "__main__":
54
+ app.run(sys.argv)
@@ -0,0 +1,40 @@
1
+ from datetime import timedelta
2
+ from types import SimpleNamespace
3
+
4
+ from frontend import frontend
5
+ from input_data import ExecuteData, InputData
6
+ from kong.sdk.conftest import ExtensionFixture, extension_test_context
7
+ from kong.sdk.kong_function_request import KongFunctionRequest
8
+ from kong.sdk.kong_state_context import KongStateContext
9
+ from kong.sdk.s3 import S3Secret
10
+ from main import Main, client_cache_key
11
+
12
+ S3_SECRET = S3Secret(
13
+ access_key="access",
14
+ secret_key="secret",
15
+ bucket="my-bucket",
16
+ region="us-east-1",
17
+ endpoint=None,
18
+ port=None,
19
+ use_ssl=True,
20
+ path_style=False,
21
+ )
22
+
23
+
24
+ async def test_should_download_object_from_s3(
25
+ create_extension: ExtensionFixture,
26
+ ):
27
+ extension = create_extension(Main, InputData)
28
+
29
+ input_data = InputData(
30
+ request=KongFunctionRequest(operation="execute"),
31
+ execute=ExecuteData(key="test/example.json"),
32
+ )
33
+
34
+ context = extension_test_context(
35
+ frontend,
36
+ secrets={"s3": S3_SECRET},
37
+ )
38
+
39
+ output = await extension.handle(input_data, context)
40
+ assert output.key == "test/example.json"
@@ -0,0 +1,21 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class OutputData(BaseModel):
7
+ key: str = Field(
8
+ examples=["test/example.json"],
9
+ description="Key of the object that was downloaded",
10
+ )
11
+
12
+ size: int = Field(
13
+ examples=[42],
14
+ description="Size of the downloaded object, in bytes",
15
+ )
16
+
17
+ content: Optional[str] = Field(
18
+ default=None,
19
+ examples=['{"hello": "world"}'],
20
+ description="UTF-8 decoded content of the downloaded object",
21
+ )
@@ -0,0 +1,27 @@
1
+ from aiobotocore.session import get_session
2
+ from botocore.config import Config
3
+ from kong.sdk.s3 import S3Secret
4
+
5
+
6
+ async def create_s3_client(secret: S3Secret):
7
+ # Standard AWS endpoints are derived by boto from the region; only S3-compatible
8
+ # stores (MinIO, Ceph, ...) need an explicit endpoint override.
9
+ endpoint_url = None
10
+ if secret.endpoint and "amazonaws.com" not in secret.endpoint:
11
+ scheme = "https" if secret.use_ssl else "http"
12
+ endpoint_url = (
13
+ f"{scheme}://{secret.endpoint}:{secret.port}"
14
+ if secret.port
15
+ else f"{scheme}://{secret.endpoint}"
16
+ )
17
+
18
+ client_context = get_session().create_client(
19
+ "s3",
20
+ region_name=secret.region,
21
+ endpoint_url=endpoint_url,
22
+ use_ssl=secret.use_ssl,
23
+ aws_access_key_id=secret.access_key,
24
+ aws_secret_access_key=secret.secret_key,
25
+ config=(Config(s3={"addressing_style": "path"}) if secret.path_style else None),
26
+ )
27
+ return await client_context.__aenter__()
@@ -16,11 +16,13 @@ function presetGenerator(tree, options) {
16
16
  options.os = process.platform;
17
17
  options.variant = (_a = options.variant) !== null && _a !== void 0 ? _a : "basic";
18
18
  (0, devkit_1.generateFiles)(tree, path.join(__dirname, `files/${options.platform}`), "", options);
19
- // The python preset ships a "basic" (single sum) and an "advanced" (multi-operation
20
- // + secret example) template. The advanced sources are layered on top of the shared
21
- // base, overwriting the basic src/*.py files.
22
- if (options.platform === "python" && options.variant === "advanced") {
23
- (0, devkit_1.generateFiles)(tree, path.join(__dirname, "files/python-advanced"), "", options);
19
+ const pythonVariantDirs = {
20
+ "with-secret": "files/python-with-secret",
21
+ "with-s3": "files/python-with-s3",
22
+ };
23
+ const variantDir = pythonVariantDirs[options.variant];
24
+ if (options.platform === "python" && variantDir) {
25
+ (0, devkit_1.generateFiles)(tree, path.join(__dirname, variantDir), "", options);
24
26
  }
25
27
  yield (0, devkit_1.formatFiles)(tree);
26
28
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../src/generators/preset/generator.ts"],"names":[],"mappings":";;AAUA,0CA6BC;;AAvCD,uCAMoB;AACpB,mDAA6B;AAG7B,SAAsB,eAAe,CAAC,IAAU,EAAE,OAA8B;;;QAC9E,MAAM,WAAW,GAAG,GAAG,CAAC;QACxB,MAAM,WAAW,GAAG,EAAE,CAAC;QACvB,IAAA,gCAAuB,EAAC,IAAI,EAAE,WAAW,EAAE;YACzC,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,aAAa;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC9B,OAAO,CAAC,OAAO,GAAG,MAAA,OAAO,CAAC,OAAO,mCAAI,OAAO,CAAC;QAC7C,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAEpF,oFAAoF;QACpF,oFAAoF;QACpF,8CAA8C;QAC9C,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACpE,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,uBAAuB,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAClF,CAAC;QAED,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,IAAA,0BAAiB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC9D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,YAAY;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;CAAA;AAED,kBAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../src/generators/preset/generator.ts"],"names":[],"mappings":";;AAUA,0CA+BC;;AAzCD,uCAMoB;AACpB,mDAA6B;AAG7B,SAAsB,eAAe,CAAC,IAAU,EAAE,OAA8B;;;QAC9E,MAAM,WAAW,GAAG,GAAG,CAAC;QACxB,MAAM,WAAW,GAAG,EAAE,CAAC;QACvB,IAAA,gCAAuB,EAAC,IAAI,EAAE,WAAW,EAAE;YACzC,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,aAAa;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC9B,OAAO,CAAC,OAAO,GAAG,MAAA,OAAO,CAAC,OAAO,mCAAI,OAAO,CAAC;QAC7C,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAEpF,MAAM,iBAAiB,GAA2B;YAChD,aAAa,EAAE,0BAA0B;YACzC,SAAS,EAAE,sBAAsB;SAClC,CAAC;QACF,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,UAAU,EAAE,CAAC;YAChD,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,IAAA,0BAAiB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC9D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,YAAY;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;CAAA;AAED,kBAAe,eAAe,CAAC"}
@@ -6,6 +6,5 @@ export interface PresetGeneratorSchema {
6
6
  os?: string;
7
7
  platform: "python" | "kotlin";
8
8
  // Which starter template to scaffold. Only the python preset currently ships more
9
- // than "basic"; "advanced" adds the multi-operation + secret example variant.
10
- variant?: "basic" | "advanced";
9
+ variant?: "basic" | "with-secret" | "with-s3";
11
10
  }
@@ -27,7 +27,8 @@
27
27
  "description": "Which starter template to scaffold",
28
28
  "enum": [
29
29
  "basic",
30
- "advanced"
30
+ "with-secret",
31
+ "with-s3"
31
32
  ],
32
33
  "default": "basic",
33
34
  "x-prompt": {
@@ -39,8 +40,12 @@
39
40
  "label": "basic - a single operation"
40
41
  },
41
42
  {
42
- "value": "advanced",
43
- "label": "advanced - multiple operations with a secret example"
43
+ "value": "with-secret",
44
+ "label": "with-secret - multiple operations with a secret example"
45
+ },
46
+ {
47
+ "value": "with-s3",
48
+ "label": "with-s3 - download a file from S3 using an s3 secret"
44
49
  }
45
50
  ]
46
51
  }