@taodigital/connectors 0.0.0-stage → 1.2.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/dist/azuredevops.d.ts +35 -0
- package/dist/azuredevops.js +116 -0
- package/dist/confluence.d.ts +22 -0
- package/dist/confluence.js +114 -0
- package/dist/connectors.test.d.ts +10 -0
- package/dist/connectors.test.js +407 -0
- package/dist/figma.d.ts +57 -0
- package/dist/figma.js +141 -0
- package/dist/github.d.ts +43 -0
- package/dist/github.js +120 -0
- package/dist/gitlab.d.ts +42 -0
- package/dist/gitlab.js +137 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +45 -0
- package/dist/jira.d.ts +93 -0
- package/dist/jira.js +354 -0
- package/dist/pagerduty.d.ts +25 -0
- package/dist/pagerduty.js +76 -0
- package/dist/salesforce.d.ts +35 -0
- package/dist/salesforce.js +93 -0
- package/dist/servicenow.d.ts +40 -0
- package/dist/servicenow.js +96 -0
- package/package.json +16 -4
- package/src/azuredevops.ts +141 -0
- package/src/confluence.ts +120 -0
- package/src/connectors.test.ts +424 -0
- package/src/figma.ts +194 -0
- package/src/github.ts +139 -0
- package/src/gitlab.ts +166 -0
- package/src/index.ts +9 -0
- package/src/jira.ts +372 -0
- package/src/pagerduty.ts +78 -0
- package/src/salesforce.ts +109 -0
- package/src/servicenow.ts +112 -0
- package/tsconfig.json +8 -0
- package/README.md +0 -3
package/src/pagerduty.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PagerDuty connector — alert on critical test failures.
|
|
3
|
+
* API key stored encrypted per project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface PagerDutyCredentials {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
serviceId?: string; // PD service to create incidents under
|
|
9
|
+
escalationPolicyId?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function headers(creds: PagerDutyCredentials, fromEmail = '') {
|
|
13
|
+
return {
|
|
14
|
+
Authorization: `Token token=${creds.apiKey}`,
|
|
15
|
+
Accept: 'application/vnd.pagerduty+json;version=2',
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
From: fromEmail,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function testConnection(creds: PagerDutyCredentials): Promise<{ ok: boolean; error?: string }> {
|
|
22
|
+
try {
|
|
23
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
24
|
+
const pdCfg = await getSystemConfig('connectors_pagerduty');
|
|
25
|
+
const fromEmail = String(pdCfg?.fromEmail ?? '');
|
|
26
|
+
const res = await fetch('https://api.pagerduty.com/users/me', { headers: headers(creds, fromEmail) });
|
|
27
|
+
if (!res.ok) return { ok: false, error: `PagerDuty returned ${res.status}` };
|
|
28
|
+
return { ok: true };
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return { ok: false, error: String(err) };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function createIncident(creds: PagerDutyCredentials, opts: {
|
|
35
|
+
title: string;
|
|
36
|
+
body: string;
|
|
37
|
+
severity: 'critical' | 'error' | 'warning' | 'info';
|
|
38
|
+
severityMap?: Partial<Record<'critical' | 'error' | 'warning' | 'info', 'high' | 'low'>>;
|
|
39
|
+
}) {
|
|
40
|
+
if (!creds.serviceId) throw new Error('PagerDuty serviceId is required to create incidents.');
|
|
41
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
42
|
+
const pdCfg = await getSystemConfig('connectors_pagerduty');
|
|
43
|
+
const fromEmail = String(pdCfg?.fromEmail ?? '');
|
|
44
|
+
const defaultSeverityMap: Partial<Record<'critical' | 'error' | 'warning' | 'info', 'high' | 'low'>> = (() => {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(String(pdCfg?.severityMap ?? '{"critical":"high","error":"high","warning":"medium","info":"low"}'));
|
|
47
|
+
} catch { return { critical: 'high', error: 'high' }; }
|
|
48
|
+
})();
|
|
49
|
+
const res = await fetch('https://api.pagerduty.com/incidents', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: headers(creds, fromEmail),
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
incident: {
|
|
54
|
+
type: 'incident',
|
|
55
|
+
title: opts.title,
|
|
56
|
+
service: { id: creds.serviceId, type: 'service_reference' },
|
|
57
|
+
urgency: (opts.severityMap ?? defaultSeverityMap)[opts.severity] ?? 'low',
|
|
58
|
+
body: { type: 'incident_body', details: opts.body },
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok) throw new Error(`PagerDuty error: ${res.status} ${await res.text()}`);
|
|
63
|
+
const data = (await res.json()) as any;
|
|
64
|
+
return { id: data.incident?.id, url: data.incident?.html_url };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function resolveIncident(creds: PagerDutyCredentials, incidentId: string) {
|
|
68
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
69
|
+
const pdCfg = await getSystemConfig('connectors_pagerduty');
|
|
70
|
+
const fromEmail = String(pdCfg?.fromEmail ?? '');
|
|
71
|
+
const res = await fetch(`https://api.pagerduty.com/incidents/${incidentId}`, {
|
|
72
|
+
method: 'PUT',
|
|
73
|
+
headers: headers(creds, fromEmail),
|
|
74
|
+
body: JSON.stringify({ incident: { type: 'incident', status: 'resolved' } }),
|
|
75
|
+
});
|
|
76
|
+
if (!res.ok) throw new Error(`PagerDuty error: ${res.status}`);
|
|
77
|
+
return { resolved: true };
|
|
78
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Salesforce connector — OAuth2 client credentials flow.
|
|
3
|
+
* clientId + encrypted clientSecret stored per project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assertResolvesToPublicAddress } from '@taodigital/core';
|
|
7
|
+
|
|
8
|
+
export interface SalesforceCredentials {
|
|
9
|
+
instanceUrl: string; // e.g. https://yourorg.my.salesforce.com
|
|
10
|
+
clientId: string; // Connected App Consumer Key
|
|
11
|
+
clientSecret: string; // Consumer Secret (stored encrypted)
|
|
12
|
+
username?: string; // Optional: for username-password flow
|
|
13
|
+
password?: string; // Optional: stored encrypted
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function getAccessToken(creds: SalesforceCredentials): Promise<string> {
|
|
17
|
+
const params = new URLSearchParams({
|
|
18
|
+
grant_type: 'client_credentials',
|
|
19
|
+
client_id: creds.clientId,
|
|
20
|
+
client_secret: creds.clientSecret,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const res = await fetch(`${creds.instanceUrl}/services/oauth2/token`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
26
|
+
body: params.toString(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!res.ok) throw new Error(`Salesforce OAuth error: ${res.status} ${await res.text()}`);
|
|
30
|
+
const data = (await res.json()) as any;
|
|
31
|
+
return data.access_token;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function testConnection(creds: SalesforceCredentials): Promise<{ ok: boolean; error?: string }> {
|
|
35
|
+
try {
|
|
36
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
37
|
+
await getAccessToken(creds);
|
|
38
|
+
return { ok: true };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return { ok: false, error: String(err) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function listObjects(creds: SalesforceCredentials) {
|
|
45
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
46
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
47
|
+
const sfCfg = await getSystemConfig('connectors_salesforce');
|
|
48
|
+
const apiVersion = String(sfCfg?.apiVersion ?? 'v59.0');
|
|
49
|
+
const token = await getAccessToken(creds);
|
|
50
|
+
const res = await fetch(`${creds.instanceUrl}/services/data/${apiVersion}/sobjects/`, {
|
|
51
|
+
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) throw new Error(`Salesforce error: ${res.status}`);
|
|
54
|
+
const data = (await res.json()) as any;
|
|
55
|
+
return (data.sobjects ?? []).map((o: any) => ({ name: o.name, label: o.label, custom: o.custom }));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function queryRecords(creds: SalesforceCredentials, soql: string) {
|
|
59
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
60
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
61
|
+
const sfCfg = await getSystemConfig('connectors_salesforce');
|
|
62
|
+
const apiVersion = String(sfCfg?.apiVersion ?? 'v59.0');
|
|
63
|
+
const token = await getAccessToken(creds);
|
|
64
|
+
const res = await fetch(
|
|
65
|
+
`${creds.instanceUrl}/services/data/${apiVersion}/query?q=${encodeURIComponent(soql)}`,
|
|
66
|
+
{ headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' } }
|
|
67
|
+
);
|
|
68
|
+
if (!res.ok) throw new Error(`Salesforce error: ${res.status}`);
|
|
69
|
+
const data = (await res.json()) as any;
|
|
70
|
+
return { totalSize: data.totalSize, records: data.records ?? [] };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function createCase(creds: SalesforceCredentials, opts: {
|
|
74
|
+
subject: string;
|
|
75
|
+
description: string;
|
|
76
|
+
priority?: 'High' | 'Medium' | 'Low';
|
|
77
|
+
origin?: string;
|
|
78
|
+
}) {
|
|
79
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
80
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
81
|
+
const sfCfg = await getSystemConfig('connectors_salesforce');
|
|
82
|
+
const apiVersion = String(sfCfg?.apiVersion ?? 'v59.0');
|
|
83
|
+
const token = await getAccessToken(creds);
|
|
84
|
+
const res = await fetch(`${creds.instanceUrl}/services/data/${apiVersion}/sobjects/Case/`, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
headers: {
|
|
87
|
+
Authorization: `Bearer ${token}`,
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
Accept: 'application/json',
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
Subject: opts.subject,
|
|
93
|
+
Description: opts.description,
|
|
94
|
+
Priority: opts.priority ?? 'High',
|
|
95
|
+
Origin: opts.origin ?? String(sfCfg?.caseOrigin ?? ''),
|
|
96
|
+
Status: 'New',
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok) throw new Error(`Salesforce error: ${res.status} ${await res.text()}`);
|
|
100
|
+
const data = (await res.json()) as any;
|
|
101
|
+
return { id: data.id, url: `${creds.instanceUrl}/lightning/r/Case/${data.id}/view` };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function listCases(creds: SalesforceCredentials, opts?: { limit?: number }) {
|
|
105
|
+
return queryRecords(
|
|
106
|
+
creds,
|
|
107
|
+
`SELECT Id, CaseNumber, Subject, Status, Priority, CreatedDate, Description FROM Case ORDER BY CreatedDate DESC LIMIT ${opts?.limit ?? 25}`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ServiceNow connector — incident and change management integration.
|
|
3
|
+
* Credentials stored encrypted per project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assertResolvesToPublicAddress } from '@taodigital/core';
|
|
7
|
+
|
|
8
|
+
export interface ServiceNowCredentials {
|
|
9
|
+
instanceUrl: string; // e.g. https://yourcompany.service-now.com
|
|
10
|
+
username: string;
|
|
11
|
+
password: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function authHeader(creds: ServiceNowCredentials): string {
|
|
15
|
+
return 'Basic ' + Buffer.from(`${creds.username}:${creds.password}`).toString('base64');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function headers(creds: ServiceNowCredentials) {
|
|
19
|
+
return {
|
|
20
|
+
Authorization: authHeader(creds),
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
Accept: 'application/json',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function testConnection(creds: ServiceNowCredentials): Promise<{ ok: boolean; error?: string }> {
|
|
27
|
+
try {
|
|
28
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
29
|
+
const res = await fetch(`${creds.instanceUrl}/api/now/table/sys_user?sysparm_limit=1`, {
|
|
30
|
+
headers: headers(creds),
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) return { ok: false, error: `ServiceNow returned ${res.status}` };
|
|
33
|
+
return { ok: true };
|
|
34
|
+
} catch (err) {
|
|
35
|
+
return { ok: false, error: String(err) };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function createIncident(creds: ServiceNowCredentials, opts: {
|
|
40
|
+
shortDescription: string;
|
|
41
|
+
description: string;
|
|
42
|
+
urgency?: 1 | 2 | 3;
|
|
43
|
+
impact?: 1 | 2 | 3;
|
|
44
|
+
category?: string;
|
|
45
|
+
defaultUrgency?: 1 | 2 | 3;
|
|
46
|
+
defaultImpact?: 1 | 2 | 3;
|
|
47
|
+
defaultCategory?: string;
|
|
48
|
+
}) {
|
|
49
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
50
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
51
|
+
const snCfg = await getSystemConfig('connectors_servicenow');
|
|
52
|
+
const res = await fetch(`${creds.instanceUrl}/api/now/table/incident`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: headers(creds),
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
short_description: opts.shortDescription,
|
|
57
|
+
description: opts.description,
|
|
58
|
+
urgency: String(opts.urgency ?? opts.defaultUrgency ?? 2),
|
|
59
|
+
impact: String(opts.impact ?? opts.defaultImpact ?? 2),
|
|
60
|
+
category: opts.category ?? opts.defaultCategory ?? String(snCfg?.defaultCategory ?? ''),
|
|
61
|
+
caller_id: creds.username,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) throw new Error(`ServiceNow error: ${res.status} ${await res.text()}`);
|
|
65
|
+
const data = (await res.json()) as any;
|
|
66
|
+
return {
|
|
67
|
+
sysId: data.result?.sys_id,
|
|
68
|
+
number: data.result?.number,
|
|
69
|
+
url: `${creds.instanceUrl}/nav_to.do?uri=incident.do?sys_id=${data.result?.sys_id}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function createChangeRequest(creds: ServiceNowCredentials, opts: {
|
|
74
|
+
shortDescription: string;
|
|
75
|
+
description: string;
|
|
76
|
+
type?: 'normal' | 'standard' | 'emergency';
|
|
77
|
+
defaultType?: 'normal' | 'standard' | 'emergency';
|
|
78
|
+
}) {
|
|
79
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
80
|
+
const res = await fetch(`${creds.instanceUrl}/api/now/table/change_request`, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: headers(creds),
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
short_description: opts.shortDescription,
|
|
85
|
+
description: opts.description,
|
|
86
|
+
type: opts.type ?? opts.defaultType ?? 'normal',
|
|
87
|
+
requested_by: creds.username,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok) throw new Error(`ServiceNow error: ${res.status}`);
|
|
91
|
+
const data = (await res.json()) as any;
|
|
92
|
+
return { sysId: data.result?.sys_id, number: data.result?.number };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function listIncidents(creds: ServiceNowCredentials, opts?: { limit?: number; state?: string }) {
|
|
96
|
+
await assertResolvesToPublicAddress(creds.instanceUrl);
|
|
97
|
+
const { getSystemConfig } = await import('@taodigital/core');
|
|
98
|
+
const snCfg = await getSystemConfig('connectors_servicenow');
|
|
99
|
+
const pageLimit = parseInt(String(snCfg?.pageLimit ?? '25'), 10);
|
|
100
|
+
const params = new URLSearchParams({
|
|
101
|
+
sysparm_limit: String(opts?.limit ?? pageLimit),
|
|
102
|
+
sysparm_fields: 'sys_id,number,short_description,state,urgency,priority,opened_at,caller_id',
|
|
103
|
+
sysparm_display_value: 'true',
|
|
104
|
+
...(opts?.state ? { sysparm_query: `state=${opts.state}` } : {}),
|
|
105
|
+
});
|
|
106
|
+
const res = await fetch(`${creds.instanceUrl}/api/now/table/incident?${params}`, {
|
|
107
|
+
headers: headers(creds),
|
|
108
|
+
});
|
|
109
|
+
if (!res.ok) throw new Error(`ServiceNow error: ${res.status}`);
|
|
110
|
+
const data = (await res.json()) as any;
|
|
111
|
+
return (data.result ?? []) as any[];
|
|
112
|
+
}
|
package/tsconfig.json
ADDED
package/README.md
DELETED