From bf5248d91898a2e5cc1a4805ff674eeeacd1a421 Mon Sep 17 00:00:00 2001 From: Dan Debrunner Date: Wed, 12 Nov 2025 10:00:43 -0500 Subject: [PATCH 1/3] iam-ibm example snippet --- rest/ibm-iam/README.md | 23 +++++++++++++++ rest/ibm-iam/config.yaml | 5 ++++ rest/ibm-iam/ibm-iam.graphql | 33 ++++++++++++++++++++++ rest/ibm-iam/index.graphql | 48 ++++++++++++++++++++++++++++++++ rest/ibm-iam/stepzen.config.json | 3 ++ rest/ibm-iam/tests/Test.js | 12 ++++++++ 6 files changed, 124 insertions(+) create mode 100644 rest/ibm-iam/README.md create mode 100644 rest/ibm-iam/config.yaml create mode 100644 rest/ibm-iam/ibm-iam.graphql create mode 100644 rest/ibm-iam/index.graphql create mode 100644 rest/ibm-iam/stepzen.config.json create mode 100644 rest/ibm-iam/tests/Test.js diff --git a/rest/ibm-iam/README.md b/rest/ibm-iam/README.md new file mode 100644 index 0000000..9fe82ff --- /dev/null +++ b/rest/ibm-iam/README.md @@ -0,0 +1,23 @@ +# IBM IAM Token + +## Overview + +Defines a field `Query.ibm_iam_token` that obtains an IBM Cloud IAM token. + +This can be used in as the first step of a sequence to obtain a token for a subsequent execution +of an `@rest` field in the sequence. + +An example (`Query.usage`) is provided of accessing the IBM Cloud billing usage endpoint using +a `@sequence` to fetch a token and then make the REST request. + +## Try it out + +The schema must be deployed with the environment variable `STEPZEN_IBM_IAM_APIKEY` set +to an IBM Cloud API key, typically by a CI/CD workflow. + +Then a request such as this can be executed, replacing `<>` with an IBM Cloud account ID +that matches the IBM Cloud API key: + +``` +stepzen request '{usage(account:"<>" month:"2025-01") }' +``` diff --git a/rest/ibm-iam/config.yaml b/rest/ibm-iam/config.yaml new file mode 100644 index 0000000..827dc28 --- /dev/null +++ b/rest/ibm-iam/config.yaml @@ -0,0 +1,5 @@ +configurationset: +- configuration: + endpoint: https://iam.cloud.ibm.com/identity/token + apikey: STEPZEN_IBM_IAM_APIKEY + name: ibm-iam diff --git a/rest/ibm-iam/ibm-iam.graphql b/rest/ibm-iam/ibm-iam.graphql new file mode 100644 index 0000000..3de1d0c --- /dev/null +++ b/rest/ibm-iam/ibm-iam.graphql @@ -0,0 +1,33 @@ +extend type Query { + """ + Obtain an IBM Cloud bearer token. + + Uses the [IBM Cloud API Key](https://cloud.ibm.com/docs/account?topic=account-userapikey&interface=ui#userapikey) + or [service ID's API Key](https://cloud.ibm.com/docs/account?topic=account-serviceidapikeys&interface=ui) + to [generate an IAM Token](https://cloud.ibm.com/docs/account?topic=account-iamtoken_from_apikey#iamtoken_from_apikey) + """ + ibm_iam_token: Secret + @rest( + endpoint: "$endpoint" + method: POST + contenttype: "x-www-form-urlencoded" + postbody: """ + grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={{ .Get "apikey"}} + """ + ecmascript: """ + function transformREST(body) { + switch (status) { + case 200: + return body + case 401: + case 400: // returned for apikey not found + throw new Error('unauthorized'); + default: + throw new Error('unknown error'); + } + } + """ + setters: { path: "access_token" } + configuration: "ibm-iam" + ) +} diff --git a/rest/ibm-iam/index.graphql b/rest/ibm-iam/index.graphql new file mode 100644 index 0000000..cc6c8a3 --- /dev/null +++ b/rest/ibm-iam/index.graphql @@ -0,0 +1,48 @@ +schema @sdl(files: ["ibm-iam.graphql"]) { + query: Query +} + +""" +Example use of `Query.ibm_iam_token`. + +First fetches a token and then executes a REST request using the token. +""" +type Query { + """ + IBM Cloud account usage. + """ + usage( + """ + IBM Cloud account ID. + """ + account: String! + """ + Billing month with format `yyyy-mm` + """ + month: String! + ): JSON + @sequence( + steps: [ + { query: "ibm_iam_token" } + { + query: "_usage" + arguments: [ + { name: "token", field: "ยง0" } + { name: "account", argument: "account" } + { name: "month", argument: "month" } + ] + } + ] + ) + + """ + Fetch requesst against the usage endpoint. + + https://cloud.ibm.com/apidocs/metering-reporting#get-account-usage + """ + _usage(token: Secret!, account: String!, month: String!): JSON + @rest( + endpoint: "https://billing.cloud.ibm.com/v4/accounts/$account/usage/$month" + headers: { name: "Authorization", value: "Bearer $token" } + ) +} diff --git a/rest/ibm-iam/stepzen.config.json b/rest/ibm-iam/stepzen.config.json new file mode 100644 index 0000000..abe76e9 --- /dev/null +++ b/rest/ibm-iam/stepzen.config.json @@ -0,0 +1,3 @@ +{ + "endpoint": "api/miscellaneous" +} \ No newline at end of file diff --git a/rest/ibm-iam/tests/Test.js b/rest/ibm-iam/tests/Test.js new file mode 100644 index 0000000..53b0db0 --- /dev/null +++ b/rest/ibm-iam/tests/Test.js @@ -0,0 +1,12 @@ +const { + deployAndRun, + getTestDescription, +} = require("../../../tests/gqltest.js"); + +testDescription = getTestDescription("snippets", __dirname); + +describe(testDescription, function () { + const tests = [ + ] + return deployAndRun(__dirname, tests); +}); \ No newline at end of file From d354dcde95f0a1bfa7441f3e106a91ca458d41a8 Mon Sep 17 00:00:00 2001 From: Dan Debrunner Date: Wed, 12 Nov 2025 10:02:33 -0500 Subject: [PATCH 2/3] chore: cleanup --- rest/ibm-iam/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rest/ibm-iam/README.md b/rest/ibm-iam/README.md index 9fe82ff..a19140c 100644 --- a/rest/ibm-iam/README.md +++ b/rest/ibm-iam/README.md @@ -10,6 +10,8 @@ of an `@rest` field in the sequence. An example (`Query.usage`) is provided of accessing the IBM Cloud billing usage endpoint using a `@sequence` to fetch a token and then make the REST request. +The same technique can be used with any IAM system. + ## Try it out The schema must be deployed with the environment variable `STEPZEN_IBM_IAM_APIKEY` set From 10f5035f1d20e64acc1fcbefcdeb0851d3619fd2 Mon Sep 17 00:00:00 2001 From: Dan Debrunner Date: Wed, 12 Nov 2025 10:05:24 -0500 Subject: [PATCH 3/3] chore: prettier --- rest/ibm-iam/stepzen.config.json | 2 +- rest/ibm-iam/tests/Test.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rest/ibm-iam/stepzen.config.json b/rest/ibm-iam/stepzen.config.json index abe76e9..af1c0ea 100644 --- a/rest/ibm-iam/stepzen.config.json +++ b/rest/ibm-iam/stepzen.config.json @@ -1,3 +1,3 @@ { "endpoint": "api/miscellaneous" -} \ No newline at end of file +} diff --git a/rest/ibm-iam/tests/Test.js b/rest/ibm-iam/tests/Test.js index 53b0db0..1596c11 100644 --- a/rest/ibm-iam/tests/Test.js +++ b/rest/ibm-iam/tests/Test.js @@ -6,7 +6,6 @@ const { testDescription = getTestDescription("snippets", __dirname); describe(testDescription, function () { - const tests = [ - ] + const tests = []; return deployAndRun(__dirname, tests); -}); \ No newline at end of file +});