diff --git a/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/config.json b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/config.json new file mode 100644 index 0000000..f541a22 --- /dev/null +++ b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/config.json @@ -0,0 +1,5 @@ +{ + "schedule": "0 */1 * * * *", + "url": "https://postman-echo.com/headers", + "owner": "" +} diff --git a/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/index.ts b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/index.ts new file mode 100644 index 0000000..bd67bf1 --- /dev/null +++ b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/index.ts @@ -0,0 +1,89 @@ +import { + type ConfidentialHTTPSendRequester, + consensusIdenticalAggregation, + cre, + json, + ok, + Runner, + type Runtime, + safeJsonStringify, +} from '@chainlink/cre-sdk' +import { z } from 'zod' + +const configSchema = z.object({ + schedule: z.string(), + owner: z.string(), + url: z.string(), +}) + +type Config = z.infer + +type ResponseValues = { + result: { + headers: { + 'secret-header': string + } + } +} + +const fetchResult = (sendRequester: ConfidentialHTTPSendRequester, config: Config) => { + const { responses } = sendRequester + .sendRequests({ + input: { + requests: [ + { + url: config.url, + method: 'GET', + headers: ['secret-header: {{.SECRET_HEADER}}'], + }, + ], + }, + vaultDonSecrets: [ + { + key: 'SECRET_HEADER', + owner: config.owner, + }, + ], + }) + .result() + const response = responses[0] + + if (!ok(response)) { + throw new Error(`HTTP request failed with status: ${response.statusCode}`) + } + + return json(response) as ResponseValues +} + +const onCronTrigger = (runtime: Runtime) => { + runtime.log('Confidential HTTP workflow triggered.') + + const confHTTPClient = new cre.capabilities.ConfidentialHTTPClient() + const result = confHTTPClient + .sendRequests( + runtime, + fetchResult, + consensusIdenticalAggregation(), + )(runtime.config) + .result() + + runtime.log(`Successfully fetched result: ${safeJsonStringify(result)}`) + + return { + result, + } +} + +const initWorkflow = (config: Config) => { + const cron = new cre.capabilities.CronCapability() + + return [cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)] +} + +export async function main() { + const runner = await Runner.newRunner({ configSchema }) + + await runner.run(initWorkflow) +} + +main() diff --git a/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/workflow.yaml b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/workflow.yaml new file mode 100644 index 0000000..1097937 --- /dev/null +++ b/packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/workflow.yaml @@ -0,0 +1,38 @@ +# ========================================================================== +# CRE WORKFLOW SETTINGS FILE +# ========================================================================== +# This file defines environment-specific workflow settings used by the CRE CLI. +# +# Each top-level key is a target (e.g., `production`, `production-testnet`, etc.). +# You can also define your own custom targets, such as `my-target`, and +# point the CLI to it via an environment variable. +# +# Note: If any setting in this file conflicts with a setting in the CRE Project Settings File, +# the value defined here in the workflow settings file will take precedence. +# +# Below is an example `my-target`: +# +# my-target: +# user-workflow: +# # Optional: The address of the workflow owner (wallet or MSIG contract). +# # Used to establish ownership for encrypting the workflow's secrets. +# # If omitted, defaults to an empty string. +# workflow-owner-address: "0x1234567890abcdef1234567890abcdef12345678" +# +# # Required: The name of the workflow to register with the Workflow Registry contract. +# workflow-name: "MyExampleWorkflow" + +# ========================================================================== +local-simulation: + user-workflow: + workflow-owner-address: "(optional) Multi-signature contract address" + workflow-name: "http-confidential-fetch-with-secrets" + workflow-artifacts: + workflow-path: "./index.ts" + config-path: "./config.json" + +# ========================================================================== +production-testnet: + user-workflow: + workflow-owner-address: "(optional) Multi-signature contract address" + workflow-name: "http-confidential-fetch-with-secrets" diff --git a/packages/cre-sdk/src/sdk/utils/capabilities/http/http-helpers.ts b/packages/cre-sdk/src/sdk/utils/capabilities/http/http-helpers.ts index f9e9812..dbc6b3f 100644 --- a/packages/cre-sdk/src/sdk/utils/capabilities/http/http-helpers.ts +++ b/packages/cre-sdk/src/sdk/utils/capabilities/http/http-helpers.ts @@ -90,18 +90,18 @@ export function text( * @returns The parsed JSON * @throws Error if the body is not valid JSON */ -export function json(response: Response): unknown +export function json(response: Response | ResponseTemplate): unknown /** * Parses the response body as JSON * @param responseFn - Function that returns an object with result function that returns Response * @returns Object with result function that returns the parsed JSON * @throws Error if the body is not valid JSON */ -export function json(responseFn: () => { result: Response }): { +export function json(responseFn: () => { result: Response | ResponseTemplate }): { result: () => unknown } export function json( - responseOrFn: Response | (() => { result: Response }), + responseOrFn: Response | ResponseTemplate | (() => { result: Response | ResponseTemplate }), ): unknown | { result: () => unknown } { if (typeof responseOrFn === 'function') { return {