@theholocron/sentry-client 1.53.1 → 1.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -5
- package/dist/index.d.mts +114 -1
- package/dist/index.mjs +72 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -40,6 +40,35 @@ const newProject = await sentry.projects.create("my-org", "my-team", {
|
|
|
40
40
|
// Retrieve client keys (DSNs) for a project
|
|
41
41
|
const keys = await sentry.projects.keys("my-org", newProject.slug);
|
|
42
42
|
const dsn = keys[0].dsn.public;
|
|
43
|
+
|
|
44
|
+
// Custom (internal) integrations, and the alert rules (workflows) that send to them
|
|
45
|
+
const apps = await sentry.sentryApps.list("my-org");
|
|
46
|
+
const app = await sentry.sentryApps.create("my-org", {
|
|
47
|
+
name: "Comlink",
|
|
48
|
+
scopes: ["event:read"],
|
|
49
|
+
webhookUrl: "https://comlink.example.dev/sentry",
|
|
50
|
+
isAlertable: true,
|
|
51
|
+
});
|
|
52
|
+
const installs = await sentry.sentryApps.installations("my-org"); // installs[0]?.app.sentryAppId is what an alert action points at
|
|
53
|
+
const detectors = await sentry.workflows.detectors("my-org"); // an `issue_stream` detector per project
|
|
54
|
+
await sentry.workflows.create("my-org", {
|
|
55
|
+
name: "Errors to Discord",
|
|
56
|
+
enabled: true,
|
|
57
|
+
environment: null,
|
|
58
|
+
config: { frequency: 5 },
|
|
59
|
+
detectorIds: detectors.filter((d) => d.type === "issue_stream").map((d) => d.id),
|
|
60
|
+
triggers: {
|
|
61
|
+
logicType: "any-short",
|
|
62
|
+
conditions: [] /* e.g. a "first seen" condition */,
|
|
63
|
+
},
|
|
64
|
+
actionFilters: [
|
|
65
|
+
{
|
|
66
|
+
logicType: "any-short",
|
|
67
|
+
conditions: [],
|
|
68
|
+
actions: [] /* a sentry_app action */,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
43
72
|
```
|
|
44
73
|
|
|
45
74
|
## Auth
|
|
@@ -47,11 +76,16 @@ const dsn = keys[0].dsn.public;
|
|
|
47
76
|
Generate an [auth token](https://sentry.io/settings/account/api/auth-tokens/)
|
|
48
77
|
with the required scopes:
|
|
49
78
|
|
|
50
|
-
| Operation | Required scopes
|
|
51
|
-
| ------------------------ |
|
|
52
|
-
| List orgs / verify token | `org:read`
|
|
53
|
-
| List / get projects | `project:read`
|
|
54
|
-
| Create projects | `project:write`
|
|
79
|
+
| Operation | Required scopes |
|
|
80
|
+
| ------------------------ | ---------------------------------------------------------------------------------------- |
|
|
81
|
+
| List orgs / verify token | `org:read` |
|
|
82
|
+
| List / get projects | `project:read` |
|
|
83
|
+
| Create projects | `project:write` |
|
|
84
|
+
| List / get integrations | `org:read` |
|
|
85
|
+
| Create / update one | `org:write`, and every scope you give the integration |
|
|
86
|
+
| Read its `clientSecret` | every scope the integration itself has (Sentry hides the secret from a token with fewer) |
|
|
87
|
+
| List / get alert rules | `alerts:read` |
|
|
88
|
+
| Create / update one | `alerts:write` |
|
|
55
89
|
|
|
56
90
|
The token is passed directly or read from `SENTRY_AUTH_TOKEN`.
|
|
57
91
|
|
package/dist/index.d.mts
CHANGED
|
@@ -45,6 +45,105 @@ interface CreateSentryProjectInput {
|
|
|
45
45
|
platform?: string;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
|
+
//#region ../../../../../src/sentry-apps/sentry-apps.d.ts
|
|
49
|
+
/**
|
|
50
|
+
* A custom (internal) integration. `clientSecret` is returned only to a caller whose token holds every scope the
|
|
51
|
+
* integration has, so it is absent for a token with fewer. It signs the webhooks Sentry sends to `webhookUrl`.
|
|
52
|
+
*/
|
|
53
|
+
interface SentryApp {
|
|
54
|
+
name: string;
|
|
55
|
+
slug: string;
|
|
56
|
+
uuid: string;
|
|
57
|
+
status: string;
|
|
58
|
+
scopes: string[];
|
|
59
|
+
webhookUrl: string | null;
|
|
60
|
+
isAlertable: boolean;
|
|
61
|
+
clientId?: string;
|
|
62
|
+
clientSecret?: string;
|
|
63
|
+
}
|
|
64
|
+
interface SentryAppInput {
|
|
65
|
+
name: string;
|
|
66
|
+
/** What the integration may read or write, e.g. `event:read`. The token making the call must hold each. */
|
|
67
|
+
scopes: string[];
|
|
68
|
+
webhookUrl?: string | null;
|
|
69
|
+
/** Offer the integration as an action in alert rules. */
|
|
70
|
+
isAlertable?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/** An integration installed on the organization. For an internal one this happens when it is created. */
|
|
73
|
+
interface SentryAppInstallation {
|
|
74
|
+
uuid: string;
|
|
75
|
+
status: string;
|
|
76
|
+
app: {
|
|
77
|
+
uuid: string;
|
|
78
|
+
slug: string;
|
|
79
|
+
sentryAppId: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region ../../../../../src/workflows/workflows.d.ts
|
|
84
|
+
interface SentryWorkflowCondition {
|
|
85
|
+
type: string;
|
|
86
|
+
comparison: unknown;
|
|
87
|
+
conditionResult: unknown;
|
|
88
|
+
}
|
|
89
|
+
interface SentryWorkflowAction {
|
|
90
|
+
type: string;
|
|
91
|
+
config: Record<string, unknown>;
|
|
92
|
+
data: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
interface SentryWorkflowActionFilter {
|
|
95
|
+
logicType: string;
|
|
96
|
+
conditions: SentryWorkflowCondition[];
|
|
97
|
+
actions: SentryWorkflowAction[];
|
|
98
|
+
}
|
|
99
|
+
/** What a workflow (an alert rule in Sentry's Monitors → Alerts) is made of, as sent when creating or updating one. */
|
|
100
|
+
interface SentryWorkflowInput {
|
|
101
|
+
name: string;
|
|
102
|
+
enabled: boolean;
|
|
103
|
+
environment: string | null;
|
|
104
|
+
/** `frequency` is minutes between notifications for one issue. */
|
|
105
|
+
config: {
|
|
106
|
+
frequency: number;
|
|
107
|
+
};
|
|
108
|
+
/** The detectors it is attached to; an issue stream detector per project covers that project's issues. */
|
|
109
|
+
detectorIds: string[];
|
|
110
|
+
triggers: {
|
|
111
|
+
logicType: string;
|
|
112
|
+
conditions: SentryWorkflowCondition[];
|
|
113
|
+
};
|
|
114
|
+
actionFilters: SentryWorkflowActionFilter[];
|
|
115
|
+
}
|
|
116
|
+
/** A condition or action as a read returns it: the same shape, with the id Sentry gave it. */
|
|
117
|
+
type SentryWorkflowConditionRead = SentryWorkflowCondition & {
|
|
118
|
+
id?: string;
|
|
119
|
+
};
|
|
120
|
+
type SentryWorkflowActionRead = SentryWorkflowAction & {
|
|
121
|
+
id?: string;
|
|
122
|
+
};
|
|
123
|
+
interface SentryWorkflow extends SentryWorkflowInput {
|
|
124
|
+
id: string;
|
|
125
|
+
triggers: {
|
|
126
|
+
id?: string;
|
|
127
|
+
logicType: string;
|
|
128
|
+
conditions: SentryWorkflowConditionRead[];
|
|
129
|
+
};
|
|
130
|
+
actionFilters: Array<{
|
|
131
|
+
id?: string;
|
|
132
|
+
logicType: string;
|
|
133
|
+
conditions: SentryWorkflowConditionRead[];
|
|
134
|
+
actions: SentryWorkflowActionRead[];
|
|
135
|
+
}>;
|
|
136
|
+
}
|
|
137
|
+
interface SentryDetector {
|
|
138
|
+
id: string;
|
|
139
|
+
/** Null for an "all projects" detector. */
|
|
140
|
+
projectId: string | null;
|
|
141
|
+
name: string;
|
|
142
|
+
/** `issue_stream` receives every issue of its project; `error` is the error monitor. */
|
|
143
|
+
type: string;
|
|
144
|
+
workflowIds: string[];
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
48
147
|
//#region ../../../../../src/index.d.ts
|
|
49
148
|
declare function createSentryClient(opts: SentryClientOptions): {
|
|
50
149
|
auth: {
|
|
@@ -57,7 +156,21 @@ declare function createSentryClient(opts: SentryClientOptions): {
|
|
|
57
156
|
create: (org: string, team: string, input: CreateSentryProjectInput) => Promise<SentryProject>;
|
|
58
157
|
keys: (org: string, slug: string) => Promise<SentryKey[]>;
|
|
59
158
|
};
|
|
159
|
+
sentryApps: {
|
|
160
|
+
list: (org: string) => Promise<SentryApp[]>;
|
|
161
|
+
get: (slug: string) => Promise<SentryApp>;
|
|
162
|
+
create: (org: string, input: SentryAppInput) => Promise<SentryApp>;
|
|
163
|
+
update: (slug: string, input: SentryAppInput) => Promise<SentryApp>;
|
|
164
|
+
installations: (org: string) => Promise<SentryAppInstallation[]>;
|
|
165
|
+
};
|
|
166
|
+
workflows: {
|
|
167
|
+
list: (org: string) => Promise<SentryWorkflow[]>;
|
|
168
|
+
get: (org: string, id: string) => Promise<SentryWorkflow>;
|
|
169
|
+
create: (org: string, input: SentryWorkflowInput) => Promise<SentryWorkflow>;
|
|
170
|
+
update: (org: string, id: string, input: SentryWorkflowInput) => Promise<SentryWorkflow>;
|
|
171
|
+
detectors: (org: string) => Promise<SentryDetector[]>;
|
|
172
|
+
};
|
|
60
173
|
};
|
|
61
174
|
type SentryClient = ReturnType<typeof createSentryClient>;
|
|
62
175
|
//#endregion
|
|
63
|
-
export { type CreateSentryProjectInput, SentryClient, type SentryClientOptions, type SentryKey, type SentryKeyDsn, type SentryOrganization, type SentryProject, createSentryClient };
|
|
176
|
+
export { type CreateSentryProjectInput, type SentryApp, type SentryAppInput, type SentryAppInstallation, SentryClient, type SentryClientOptions, type SentryDetector, type SentryKey, type SentryKeyDsn, type SentryOrganization, type SentryProject, type SentryWorkflow, type SentryWorkflowAction, type SentryWorkflowActionFilter, type SentryWorkflowCondition, type SentryWorkflowInput, createSentryClient };
|
package/dist/index.mjs
CHANGED
|
@@ -32,6 +32,51 @@ function projects(rest) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
//#endregion
|
|
35
|
+
//#region ../../../../../src/sentry-apps/sentry-apps.ts
|
|
36
|
+
const seg$1 = encodeURIComponent;
|
|
37
|
+
function sentryApps(rest) {
|
|
38
|
+
return {
|
|
39
|
+
/** The custom integrations an organization has made. */
|
|
40
|
+
list: (org) => rest.request(`/organizations/${seg$1(org)}/sentry-apps/`),
|
|
41
|
+
/** One integration by slug or id. */
|
|
42
|
+
get: (slug) => rest.request(`/sentry-apps/${seg$1(slug)}/`),
|
|
43
|
+
/** Create an internal integration. It is installed on the organization at once. */
|
|
44
|
+
create: (org, input) => rest.request("/sentry-apps/", {
|
|
45
|
+
method: "POST",
|
|
46
|
+
body: {
|
|
47
|
+
name: input.name,
|
|
48
|
+
organization: org,
|
|
49
|
+
scopes: input.scopes,
|
|
50
|
+
isInternal: true,
|
|
51
|
+
isAlertable: input.isAlertable ?? false,
|
|
52
|
+
webhookUrl: input.webhookUrl ?? null,
|
|
53
|
+
events: [],
|
|
54
|
+
schema: {},
|
|
55
|
+
verifyInstall: false
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
/**
|
|
59
|
+
* Update an integration (Sentry requires `name` and `scopes` on every update, and may reset a setting a PUT leaves
|
|
60
|
+
* out). A setting not given keeps its current value: it is read first, only when one is missing.
|
|
61
|
+
*/
|
|
62
|
+
update: async (slug, input) => {
|
|
63
|
+
const current = input.isAlertable === void 0 || input.webhookUrl === void 0 ? await rest.request(`/sentry-apps/${seg$1(slug)}/`) : void 0;
|
|
64
|
+
return rest.request(`/sentry-apps/${seg$1(slug)}/`, {
|
|
65
|
+
method: "PUT",
|
|
66
|
+
body: {
|
|
67
|
+
name: input.name,
|
|
68
|
+
scopes: input.scopes,
|
|
69
|
+
isInternal: true,
|
|
70
|
+
isAlertable: input.isAlertable ?? current?.isAlertable ?? false,
|
|
71
|
+
webhookUrl: input.webhookUrl !== void 0 ? input.webhookUrl : current?.webhookUrl ?? null
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
/** The integrations installed on an organization, with the numeric app id that alert actions point at. */
|
|
76
|
+
installations: (org) => rest.request(`/organizations/${seg$1(org)}/sentry-app-installations/`)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
35
80
|
//#region ../../../../../src/utils.ts
|
|
36
81
|
function createSentryRestClient(opts) {
|
|
37
82
|
return createRestClient({
|
|
@@ -44,12 +89,38 @@ function createSentryRestClient(opts) {
|
|
|
44
89
|
});
|
|
45
90
|
}
|
|
46
91
|
//#endregion
|
|
92
|
+
//#region ../../../../../src/workflows/workflows.ts
|
|
93
|
+
const seg = encodeURIComponent;
|
|
94
|
+
function workflows(rest) {
|
|
95
|
+
return {
|
|
96
|
+
/** The alert rules (workflows) of an organization. */
|
|
97
|
+
list: (org) => rest.request(`/organizations/${seg(org)}/workflows/`),
|
|
98
|
+
get: (org, id) => rest.request(`/organizations/${seg(org)}/workflows/${seg(id)}/`),
|
|
99
|
+
create: (org, input) => rest.request(`/organizations/${seg(org)}/workflows/`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: input
|
|
102
|
+
}),
|
|
103
|
+
/**
|
|
104
|
+
* Replace a workflow with the given body. Sentry offers no version or ETag to make this conditional, so a concurrent
|
|
105
|
+
* edit is overwritten: read, change and write back without another writer in between.
|
|
106
|
+
*/
|
|
107
|
+
update: (org, id, input) => rest.request(`/organizations/${seg(org)}/workflows/${seg(id)}/`, {
|
|
108
|
+
method: "PUT",
|
|
109
|
+
body: input
|
|
110
|
+
}),
|
|
111
|
+
/** The detectors of an organization: what a workflow is attached to. */
|
|
112
|
+
detectors: (org) => rest.request(`/organizations/${seg(org)}/detectors/`)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
47
116
|
//#region ../../../../../src/index.ts
|
|
48
117
|
function createSentryClient(opts) {
|
|
49
118
|
const rest = createSentryRestClient(opts);
|
|
50
119
|
return {
|
|
51
120
|
auth: auth(rest),
|
|
52
|
-
projects: projects(rest)
|
|
121
|
+
projects: projects(rest),
|
|
122
|
+
sentryApps: sentryApps(rest),
|
|
123
|
+
workflows: workflows(rest)
|
|
53
124
|
};
|
|
54
125
|
}
|
|
55
126
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/sentry-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.54.0",
|
|
4
4
|
"description": "A TypeScript client for the Sentry management API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sentry",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@theholocron/observability": "^0.3.0",
|
|
36
|
-
"@theholocron/http-client": "^1.
|
|
36
|
+
"@theholocron/http-client": "^1.54.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@theholocron/cli": "5.0.0-alpha.79",
|