0

Get Record Schema

by
Published today

Retrieve the JSON schema of a record type from the metadata catalog (field names, types, read-only flags).

Script netsuite Verified

The script

Submitted by hugo989 Typescript (fetch-only)
Verified 3 hours ago
1
//native
2

3
const percentEncode = (s: string) =>
4
  encodeURIComponent(s).replace(
5
    /[!'()*]/g,
6
    (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
7
  )
8

9
function restBase(auth: RT.Netsuite) {
10
  return `https://${auth.account_id.trim().toLowerCase()}.suitetalk.api.netsuite.com/services/rest`
11
}
12

13
// Bearer when an OAuth 2.0 token is set, otherwise TBA (OAuth 1.0a HMAC-SHA256).
14
async function authHeader(auth: RT.Netsuite, method: string, url: URL) {
15
  if (auth.token !== undefined && auth.token !== "") {
16
    return `Bearer ${auth.token}`
17
  }
18
  // Re-serialize the query with %20 instead of '+' for spaces so the URL on
19
  // the wire matches the form the signature base string encodes
20
  url.search = [...url.searchParams.entries()]
21
    .map(([k, v]) => `${percentEncode(k)}=${percentEncode(v)}`)
22
    .join("&")
23
  const nonce = Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) =>
24
    b.toString(16).padStart(2, "0")
25
  ).join("")
26
  const timestamp = Math.floor(Date.now() / 1000).toString()
27
  const oauthParams: [string, string][] = [
28
    ["oauth_consumer_key", auth.consumer_key ?? ""],
29
    ["oauth_nonce", nonce],
30
    ["oauth_signature_method", "HMAC-SHA256"],
31
    ["oauth_timestamp", timestamp],
32
    ["oauth_token", auth.token_id ?? ""],
33
    ["oauth_version", "1.0"],
34
  ]
35
  const sortedParams = [...url.searchParams.entries(), ...oauthParams]
36
    .map(([k, v]) => [percentEncode(k), percentEncode(v)])
37
    .sort(([ak, av], [bk, bv]) =>
38
      ak === bk ? (av < bv ? -1 : 1) : ak < bk ? -1 : 1
39
    )
40
    .map(([k, v]) => `${k}=${v}`)
41
    .join("&")
42
  const baseString = [
43
    method.toUpperCase(),
44
    percentEncode(`${url.protocol}//${url.host}${url.pathname}`),
45
    percentEncode(sortedParams),
46
  ].join("&")
47
  const key = await crypto.subtle.importKey(
48
    "raw",
49
    new TextEncoder().encode(
50
      `${percentEncode(auth.consumer_secret ?? "")}&${percentEncode(auth.token_secret ?? "")}`
51
    ),
52
    { name: "HMAC", hash: "SHA-256" },
53
    false,
54
    ["sign"]
55
  )
56
  const signature = btoa(
57
    String.fromCharCode(
58
      ...new Uint8Array(
59
        await crypto.subtle.sign(
60
          "HMAC",
61
          key,
62
          new TextEncoder().encode(baseString)
63
        )
64
      )
65
    )
66
  )
67
  const realm = auth.account_id.trim().toUpperCase().replace(/-/g, "_")
68
  return `OAuth realm="${realm}", oauth_consumer_key="${percentEncode(auth.consumer_key ?? "")}", oauth_token="${percentEncode(auth.token_id ?? "")}", oauth_signature_method="HMAC-SHA256", oauth_timestamp="${timestamp}", oauth_nonce="${nonce}", oauth_version="1.0", oauth_signature="${percentEncode(signature)}"`
69
}
70

71
export type DynSelect_record_type = string
72

73
// Dropdown of the account's record types, from the REST metadata catalog.
74
export async function record_type(auth: RT.Netsuite) {
75
  const url = new URL(`${restBase(auth)}/record/v1/metadata-catalog`)
76
  const response = await fetch(url, {
77
    headers: {
78
      Authorization: await authHeader(auth, "GET", url),
79
      Accept: "application/json",
80
    },
81
  })
82
  if (!response.ok) {
83
    throw new Error(`${response.status} ${await response.text()}`)
84
  }
85
  const { items } = (await response.json()) as { items: { name: string }[] }
86
  return items.map((i) => ({ value: i.name, label: i.name }))
87
}
88

89
/**
90
 * Get Record Schema
91
 * Retrieve the JSON schema of a record type from the metadata catalog — every field, its type, and whether it is read-only. Useful to discover the exact field names create/update expect.
92
 */
93
export async function main(
94
  auth: RT.Netsuite,
95
  record_type: DynSelect_record_type
96
) {
97
  const url = new URL(
98
    `${restBase(auth)}/record/v1/metadata-catalog/${record_type}`
99
  )
100
  const response = await fetch(url, {
101
    headers: {
102
      Authorization: await authHeader(auth, "GET", url),
103
      Accept: "application/schema+json",
104
    },
105
  })
106
  if (!response.ok) {
107
    throw new Error(`${response.status} ${await response.text()}`)
108
  }
109
  return await response.json()
110
}
111