0

Call Windmill API

by
Published 21 days ago

Call any operation of this instance's own API, picked from its OpenAPI document. Runs with the job's token, which only reaches the job's workspace; pass a user API token to call instance-level operations or another workspace.

Script windmill Verified

The script

Submitted by hugo989 Typescript (fetch-only)
Verified 22 days ago
1
//native
2

3
import * as wmill from "windmill-client"
4

5
export type DynSelect_operation = string
6

7
// Every operation of the instance's own OpenAPI document, so the list always matches its version.
8
async function listOperations() {
9
  const response = await fetch(`${wmill.OpenAPI.BASE}/openapi.json`)
10
  if (!response.ok) {
11
    throw new Error(`${response.status} ${await response.text()}`)
12
  }
13
  const spec = (await response.json()) as {
14
    paths: { [path: string]: { [method: string]: { operationId?: string } } }
15
  }
16
  return Object.entries(spec.paths).flatMap(([path, methods]) =>
17
    Object.entries(methods)
18
      .filter(([, op]) => op?.operationId)
19
      .map(([method, op]) => ({
20
        id: op.operationId!,
21
        value: `${method.toUpperCase()} ${path}`,
22
      }))
23
  )
24
}
25

26
export async function operation() {
27
  return (await listOperations())
28
    .map((op) => ({ value: op.value, label: `${op.id}: ${op.value}` }))
29
    .sort((a, b) => a.label.localeCompare(b.label))
30
}
31

32
/**
33
 * Call Windmill API
34
 * Call any operation of this instance's own API, picked from its OpenAPI document. Runs with the job's token, which only reaches the job's workspace; pass a user API token to call instance-level operations or another workspace.
35
 */
36
export async function main(
37
  operation: DynSelect_operation,
38
  path_params: { [key: string]: any } | undefined,
39
  query: { [key: string]: any } | undefined,
40
  body: { [key: string]: any } | undefined,
41
  token: string | undefined
42
) {
43
  // `operation` is "METHOD /path" (what the dropdown stores) or a bare operationId
44
  const resolved = operation.includes(" ")
45
    ? operation
46
    : (await listOperations()).find((op) => op.id === operation)?.value
47
  if (!resolved) {
48
    throw new Error(`Unknown operation ${operation}`)
49
  }
50
  const [method, path] = resolved.split(" ")
51

52
  const params: { [key: string]: any } = {
53
    workspace: wmill.getWorkspace(),
54
    ...path_params,
55
  }
56
  const url = new URL(
57
    wmill.OpenAPI.BASE +
58
      path.replace(/\{(\w+)\}/g, (_, name) => {
59
        if (params[name] == null || params[name] === "") {
60
          throw new Error(`Missing path parameter ${name} for ${resolved}`)
61
        }
62
        return encodeURI(String(params[name]))
63
      })
64
  )
65
  for (const [name, value] of Object.entries(query ?? {})) {
66
    if (value != null && value !== "") {
67
      url.searchParams.append(name, String(value))
68
    }
69
  }
70

71
  const hasBody = body != null && method !== "GET"
72
  const response = await fetch(url, {
73
    method,
74
    headers: {
75
      // the job's own token only reaches its workspace: pass an API token for anything else
76
      Authorization: `Bearer ${token || wmill.OpenAPI.TOKEN}`,
77
      ...(hasBody ? { "Content-Type": "application/json" } : {}),
78
    },
79
    body: hasBody ? JSON.stringify(body) : undefined,
80
  })
81

82
  if (!response.ok) {
83
    throw new Error(`${response.status} ${await response.text()}`)
84
  }
85

86
  const text = await response.text()
87
  return response.headers.get("Content-Type")?.includes("json")
88
    ? JSON.parse(text)
89
    : text
90
}
91