0

Call Windmill API

by
Published today

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 4 hours 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