From 867d407ca6f88e03880aafa347a2d28b44a83c6c Mon Sep 17 00:00:00 2001
From: Arne Bahlo <arne@axiom.co>
Date: Mon, 13 May 2024 16:14:50 +0200
Subject: [PATCH] fix: Add missing values, set id as output

* Send trace id on error
* Add Axiom API URL
---
 .github/workflows/ci.yml | 40 +++++++++++++++++-----
 action.yml               | 47 +++++++++++++++++---------
 dist/index.js            | 60 ++++++++++++++-------------------
 src/main.ts              | 73 +++++++++++++++++-----------------------
 4 files changed, 121 insertions(+), 99 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 90218a1..de1a903 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,13 +51,37 @@ jobs:
         id: checkout
         uses: actions/checkout@v4
 
-      - name: Test Local Action
-        id: test-action
+      - name: Test Local Action (Minimal)
+        id: test-action-minimal
         uses: ./
         with:
-          axiom_datasets: ${{ secrets.AXIOM_DATASETS }}
-          axiom_token: ${{ secrets.AXIOM_TOKEN }}
-          axiom_org_id: ${{ secrets.AXIOM_ORG_ID }}
-          title: ${{github.event.ref}}
-          description: ${{ github.event.head_commit.message }}
-          url: 'https://example.com'
+          token: ${{ secrets.AXIOM_TOKEN }}
+          axiomApiUrl: ${{ secrets.AXIOM_API_URL }}
+          datasets: annotations-action-ci
+          type: "minimal-example"
+      - uses: actions/github-script@v7
+        with:
+          script: |
+            const assert = require('node:assert/strict')
+
+            const res = await fetch('${{ secrets.AXIOM_API_URL }}/v2/annotations/${{ steps.test-action-minimal.outputs.id }}', {
+              headers: {
+                Authorization: `Bearer ${{ secrets.AXIOM_TOKEN }}`
+              }
+            });
+            const annotation = await res.json();
+
+            console.log(annotation);
+      - name: Test Local Action (Complete)
+        id: test-action-complete
+        uses: ./
+        with:
+          token: ${{ secrets.AXIOM_TOKEN }}
+          axiomApiUrl: ${{ secrets.AXIOM_API_URL }}
+          datasets: annotations-action-ci
+          time: "2024-01-01T00:00:00Z"
+          endTime: "2024-01-01T01:00:00Z"
+          title: "Complete example"
+          description: "This annotation has all the fields!"
+          url: "https://axiom.co"
+          type: "complete-example"
diff --git a/action.yml b/action.yml
index 1094d10..df48d06 100644
--- a/action.yml
+++ b/action.yml
@@ -1,32 +1,49 @@
-name: 'Axiom annotations'
+name: 'Axiom annotation'
+author: 'Axiom, Inc.'
 description: 'Create annotations in Axiom'
-author: 'axiomhq'
 
-# Add your action's branding here. This will appear on the GitHub Marketplace.
 branding:
-  icon: 'alert-triangle'
+  icon: 'message-square'
   color: 'black'
 
-# Define your inputs here.
 inputs:
-  axiom_datasets:
-    description: 'Comma separated list of datasets to associate the annotations with'
+  axiomApiUrl:
+    description: 'The Axiom API URL, defaults to https://api.axiom.co'
+    required: false
+    default: ''
+  datasets:
+    description: 'Comma separated list of datasets to associate the annotation with'
     required: true
-  axiom_token:
+  token:
     description: 'Axiom API token'
     required: true
-  axiom_org_id:
-    description: 'Axiom organization ID'
-    required: true
+  time:
+    description: 'The time of the annotation (defaults to now)'
+    required: false
+    default: ''
+  endTime:
+    description: 'The end time of the annotation (if set, will create a ranged annotation)'
+    required: false
+    default: ''
   title:
     description: 'The title of the annotation'
-    required: true
+    required: false
+    default: ''
   description:
-    description: 'The description of the annotation (markdown supported)'
-    required: true
+    description: 'The description of the annotation (supports Markdown)'
+    required: false
+    default: ''
   url:
-    description: 'The URL to link to (defaults to the GitHub action URL)'
+    description: 'The URL to link to (defaults to the GitHub Actions run URL)'
     required: false
+    default: ''
+  type:
+    description: 'The type of the annotation, (e.g. production-deploy)'
+    required: true
+
+outputs:
+  id:
+    description: 'The ID of the created integration'
 
 runs:
   using: node20
diff --git a/dist/index.js b/dist/index.js
index b928d53..fbc0095 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -24742,50 +24742,42 @@ const core = __importStar(__nccwpck_require__(2186));
  */
 async function run() {
     try {
-        const datasets = core
-            .getInput('axiom_datasets', { required: true })
-            .split(',');
-        const token = core.getInput('axiom_token', { required: true });
-        const orgId = core.getInput('axiom_org_id', { required: true });
-        const title = core.getInput('title', { required: true });
-        const description = core.getInput('description', { required: true });
-        const url = core.getInput('url', { required: false }) ??
-            `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
-        // Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true
-        core.debug('Sending annotation to Axiom');
-        const body = {
-            type: 'deploy',
-            time: new Date().toISOString(),
-            datasets,
-            title,
-            description,
-            url
+        const axiomApiUrl = core.getInput('axiomApiURL') || 'https://api.axiom.co';
+        const token = core.getInput('token', { required: true });
+        core.setSecret(token); // Mask token in output
+        const payload = {
+            datasets: core.getInput('datasets', { required: true }).split(','),
+            time: core.getInput('time') || new Date().toISOString(),
+            endTime: core.getInput('endTime') || undefined,
+            title: core.getInput('title') || undefined,
+            description: core.getInput('description') || undefined,
+            url: core.getInput('url') ||
+                `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
+            type: core.getInput('type', { required: true })
         };
-        // Log the annotation details
-        core.debug(`Annotation: ${JSON.stringify({
-            datasets,
-            title,
-            description,
-            url
-        })}`);
-        // Send the annotation to Axiom
-        const response = await fetch('https://api.axiom.co/v2/annotations', {
+        core.debug(`Sending annotation to Axiom: ${JSON.stringify(payload)}`);
+        const response = await fetch(`${axiomApiUrl}/v2/annotations`, {
             method: 'POST',
             headers: {
                 'Content-Type': 'application/json',
-                Authorization: `Bearer ${token}`,
-                'X-Axiom-Org-Id': orgId
+                Authorization: `Bearer ${token}`
             },
-            body: JSON.stringify(body)
+            body: JSON.stringify(payload)
         });
-        // Throw an error if the request fails
         if (!response.ok) {
-            throw new Error(`Failed to send annotation to Axiom: ${response.statusText}`);
+            let traceIdMsg = '';
+            const traceId = response.headers.get('x-axiom-trace-id');
+            if (traceId) {
+                traceIdMsg = ` (Trace ID: ${traceId})`;
+            }
+            throw new Error(`Failed to send annotation to Axiom${traceIdMsg}: ${response.statusText}`);
         }
-        core.debug('Annotation sent to Axiom');
+        core.debug('Decoding response');
+        const annotation = await response.json();
+        core.setOutput('id', annotation.id);
+        core.info(`Created annotation ${annotation.id}`);
     }
     catch (error) {
-        // Fail the workflow run if an error occurs
         if (error instanceof Error)
             core.setFailed(error.message);
     }
diff --git a/src/main.ts b/src/main.ts
index 59b8370..efc5e11 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,61 +6,50 @@ import * as core from '@actions/core'
  */
 export async function run(): Promise<void> {
   try {
-    const datasets = core
-      .getInput('axiom_datasets', { required: true })
-      .split(',')
-    const token = core.getInput('axiom_token', { required: true })
-    const orgId = core.getInput('axiom_org_id', { required: true })
-    const title = core.getInput('title', { required: true })
-    const description = core.getInput('description', { required: true })
-    const url =
-      core.getInput('url', { required: false }) ??
-      `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
-
-    // Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true
-    core.debug('Sending annotation to Axiom')
-
-    const body = {
-      type: 'deploy',
-      time: new Date().toISOString(),
-
-      datasets,
-      title,
-      description,
-      url
+    const axiomApiUrl = core.getInput('axiomApiURL') || 'https://api.axiom.co'
+
+    const token = core.getInput('token', { required: true })
+    core.setSecret(token) // Mask token in output
+
+    const payload = {
+      datasets: core.getInput('datasets', { required: true }).split(','),
+      time: core.getInput('time') || new Date().toISOString(),
+      endTime: core.getInput('endTime') || undefined,
+      title: core.getInput('title') || undefined,
+      description: core.getInput('description') || undefined,
+      url:
+        core.getInput('url') ||
+        `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
+      type: core.getInput('type', { required: true })
     }
 
-    // Log the annotation details
-    core.debug(
-      `Annotation: ${JSON.stringify({
-        datasets,
-        title,
-        description,
-        url
-      })}`
-    )
-
-    // Send the annotation to Axiom
-    const response = await fetch('https://api.axiom.co/v2/annotations', {
+    core.debug(`Sending annotation to Axiom: ${JSON.stringify(payload)}`)
+    const response = await fetch(`${axiomApiUrl}/v2/annotations`, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
-        Authorization: `Bearer ${token}`,
-        'X-Axiom-Org-Id': orgId
+        Authorization: `Bearer ${token}`
       },
-      body: JSON.stringify(body)
+      body: JSON.stringify(payload)
     })
-
-    // Throw an error if the request fails
     if (!response.ok) {
+      let traceIdMsg = ''
+      const traceId = response.headers.get('x-axiom-trace-id')
+      if (traceId) {
+        traceIdMsg = ` (Trace ID: ${traceId})`
+      }
+
       throw new Error(
-        `Failed to send annotation to Axiom: ${response.statusText}`
+        `Failed to send annotation to Axiom${traceIdMsg}: ${response.statusText}`
       )
     }
 
-    core.debug('Annotation sent to Axiom')
+    core.debug('Decoding response')
+    const annotation = await response.json()
+
+    core.setOutput('id', annotation.id)
+    core.info(`Created annotation ${annotation.id}`)
   } catch (error) {
-    // Fail the workflow run if an error occurs
     if (error instanceof Error) core.setFailed(error.message)
   }
 }