@forge/teamwork-graph 0.1.0-next.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/LICENSE.txt +7 -0
- package/README.md +198 -0
- package/out/__test__/error-handling.test.d.ts +2 -0
- package/out/__test__/error-handling.test.d.ts.map +1 -0
- package/out/__test__/error-handling.test.js +105 -0
- package/out/__test__/graph.test.d.ts +2 -0
- package/out/__test__/graph.test.d.ts.map +1 -0
- package/out/__test__/graph.test.js +59 -0
- package/out/error-handling.d.ts +3 -0
- package/out/error-handling.d.ts.map +1 -0
- package/out/error-handling.js +36 -0
- package/out/graph.d.ts +15 -0
- package/out/graph.d.ts.map +1 -0
- package/out/graph.js +60 -0
- package/out/index.d.ts +5 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +9 -0
- package/out/utils/errors.d.ts +18 -0
- package/out/utils/errors.d.ts.map +1 -0
- package/out/utils/errors.js +22 -0
- package/out/utils/types.d.ts +110 -0
- package/out/utils/types.d.ts.map +1 -0
- package/out/utils/types.js +2 -0
- package/package.json +27 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2025 Atlassian
|
|
2
|
+
Permission is hereby granted to use this software in accordance with the terms
|
|
3
|
+
and conditions outlined in the Atlassian Developer Terms, which can be found
|
|
4
|
+
at the following URL:
|
|
5
|
+
https://developer.atlassian.com/platform/marketplace/atlassian-developer-terms/
|
|
6
|
+
By using this software, you agree to comply with these terms and conditions.
|
|
7
|
+
If you do not agree with these terms, you are not permitted to use this software.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Forge Graph Client
|
|
2
|
+
|
|
3
|
+
A client for interacting with the Forge Graph API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @forge/teamwork-graph
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage Examples
|
|
12
|
+
|
|
13
|
+
### Document Entity
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { graph } from '@forge/teamwork-graph';
|
|
17
|
+
import type { EntityPayload, DocumentCategory } from '@forge/teamwork-graph';
|
|
18
|
+
|
|
19
|
+
const documentPayload: EntityPayload = {
|
|
20
|
+
// Required base fields
|
|
21
|
+
schemaVersion: '1.0',
|
|
22
|
+
id: 'doc-123',
|
|
23
|
+
updateSequenceNumber: Date.now(),
|
|
24
|
+
displayName: 'Project Requirements Doc',
|
|
25
|
+
url: 'https://example.com/docs/requirements',
|
|
26
|
+
|
|
27
|
+
// Optional base fields
|
|
28
|
+
description: 'Project requirements and specifications',
|
|
29
|
+
createdAt: new Date().toISOString(),
|
|
30
|
+
createdBy: {
|
|
31
|
+
accountId: 'acc-123',
|
|
32
|
+
email: 'creator@example.com'
|
|
33
|
+
},
|
|
34
|
+
lastUpdatedAt: new Date().toISOString(),
|
|
35
|
+
lastUpdatedBy: {
|
|
36
|
+
accountId: 'acc-456',
|
|
37
|
+
email: 'updater@example.com'
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Document ownership
|
|
41
|
+
owners: [
|
|
42
|
+
{
|
|
43
|
+
accountId: 'acc-123',
|
|
44
|
+
email: 'owner@example.com'
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
// Document preview
|
|
49
|
+
thumbnail: {
|
|
50
|
+
externalUrl: 'https://example.com/thumbnail.png'
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Hierarchical relationships
|
|
54
|
+
parentKey: {
|
|
55
|
+
type: 'atlassian:document',
|
|
56
|
+
value: {
|
|
57
|
+
entityId: 'parent-folder-id'
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
containerKey: {
|
|
61
|
+
type: 'atlassian:space',
|
|
62
|
+
value: {
|
|
63
|
+
entityId: 'space-123'
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// Access control
|
|
68
|
+
permissions: {
|
|
69
|
+
accessControls: [
|
|
70
|
+
{
|
|
71
|
+
principals: [
|
|
72
|
+
{ type: 'USER', id: 'user-123' },
|
|
73
|
+
{ type: 'GROUP', id: 'group-456' },
|
|
74
|
+
{ type: 'EVERYONE' }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
smartLinkViewedBy: [
|
|
79
|
+
{
|
|
80
|
+
principalUser: {
|
|
81
|
+
type: 'USER',
|
|
82
|
+
id: 'user-789'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Relationships
|
|
89
|
+
associations: {
|
|
90
|
+
set: [
|
|
91
|
+
{
|
|
92
|
+
associationType: 'issueIdOrKeys',
|
|
93
|
+
values: ['PROJ-123', 'PROJ-456']
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// Document-specific attributes
|
|
99
|
+
'atlassian:document': {
|
|
100
|
+
type: {
|
|
101
|
+
category: 'document',
|
|
102
|
+
mimeType: 'text/plain',
|
|
103
|
+
iconUrl: 'https://example.com/icon.png',
|
|
104
|
+
fileExtension: 'txt'
|
|
105
|
+
},
|
|
106
|
+
content: {
|
|
107
|
+
mimeType: 'text/plain',
|
|
108
|
+
text: 'Document content...',
|
|
109
|
+
// binary: 'base64EncodedContent...' // Alternative to text
|
|
110
|
+
},
|
|
111
|
+
byteSize: 1234,
|
|
112
|
+
exportLinks: [
|
|
113
|
+
{
|
|
114
|
+
mimeType: 'application/pdf',
|
|
115
|
+
url: 'https://example.com/export/pdf'
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
collaborators: [
|
|
119
|
+
{ email: 'collaborator1@example.com' },
|
|
120
|
+
{ email: 'collaborator2@example.com' }
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Send the document to the API
|
|
126
|
+
await graph.setEntity(documentPayload);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Delete Operations
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Delete an entity
|
|
133
|
+
await graph.deleteEntity('entity-123', {
|
|
134
|
+
target: 'TargetContainer',
|
|
135
|
+
token: 'auth-token'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Delete a user
|
|
139
|
+
await graph.deleteUser('user-123');
|
|
140
|
+
|
|
141
|
+
// Delete a group
|
|
142
|
+
await graph.deleteGroup('group-123');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Error Handling
|
|
146
|
+
|
|
147
|
+
The client throws `ForgeGraphAPIError` for API errors. Error types include:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { errorCodes } from '@forge/teamwork-graph';
|
|
151
|
+
|
|
152
|
+
// Available error codes
|
|
153
|
+
errorCodes.INVALID_REQUEST_BODY // 400 Bad Request
|
|
154
|
+
errorCodes.INSUFFICIENT_SCOPE // 403 Forbidden
|
|
155
|
+
errorCodes.TOO_MANY_REQUESTS // 429 Rate Limit
|
|
156
|
+
errorCodes.UNKNOWN_ERROR // Other errors
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await graph.setEntity(/* ... */);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error instanceof ForgeGraphAPIError) {
|
|
162
|
+
console.error(error.code, error.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Available Types
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import type {
|
|
171
|
+
// Entity-related
|
|
172
|
+
EntityPayload,
|
|
173
|
+
DocumentAttributes,
|
|
174
|
+
DocumentCategory,
|
|
175
|
+
DocumentType,
|
|
176
|
+
DocumentContent,
|
|
177
|
+
ExportLink,
|
|
178
|
+
|
|
179
|
+
// User and Group
|
|
180
|
+
UserObject,
|
|
181
|
+
GroupPayload,
|
|
182
|
+
|
|
183
|
+
// Common types
|
|
184
|
+
Permissions,
|
|
185
|
+
AccessControl,
|
|
186
|
+
Principal,
|
|
187
|
+
Thumbnail,
|
|
188
|
+
|
|
189
|
+
// Relationships
|
|
190
|
+
ParentKeyObject,
|
|
191
|
+
ContainerKeyObject,
|
|
192
|
+
Associations,
|
|
193
|
+
AssociationObject,
|
|
194
|
+
|
|
195
|
+
// Operations
|
|
196
|
+
DeleteContext
|
|
197
|
+
} from '@forge/teamwork-graph';
|
|
198
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.test.d.ts","sourceRoot":"","sources":["../../src/__test__/error-handling.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_fetch_1 = require("node-fetch");
|
|
4
|
+
const error_handling_1 = require("../error-handling");
|
|
5
|
+
const errors_1 = require("../utils/errors");
|
|
6
|
+
describe('checkResponseError in @forge/teamwork-graph', () => {
|
|
7
|
+
const traceId = 'trace-id';
|
|
8
|
+
describe('when response has JSON body', () => {
|
|
9
|
+
const baseErrorShape = {
|
|
10
|
+
responseDetails: {
|
|
11
|
+
status: 400,
|
|
12
|
+
statusText: 'Bad Request',
|
|
13
|
+
traceId
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
it('should use provided code and message', async () => {
|
|
17
|
+
const mockResponse = new node_fetch_1.Response(JSON.stringify({ code: 'CUSTOM_ERROR', message: 'Custom error message' }), {
|
|
18
|
+
status: 400,
|
|
19
|
+
statusText: 'Bad Request',
|
|
20
|
+
headers: { 'x-trace-id': traceId }
|
|
21
|
+
});
|
|
22
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse)).rejects.toMatchObject({
|
|
23
|
+
...baseErrorShape,
|
|
24
|
+
code: 'CUSTOM_ERROR',
|
|
25
|
+
message: 'Custom error message'
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it('should use status-based code when no code provided', async () => {
|
|
29
|
+
const mockResponse = new node_fetch_1.Response(JSON.stringify({ message: 'Error message' }), {
|
|
30
|
+
status: 400,
|
|
31
|
+
statusText: 'Bad Request',
|
|
32
|
+
headers: { 'x-trace-id': traceId }
|
|
33
|
+
});
|
|
34
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse)).rejects.toMatchObject({
|
|
35
|
+
...baseErrorShape,
|
|
36
|
+
code: errors_1.errorCodes.INVALID_REQUEST_BODY,
|
|
37
|
+
message: 'Error message'
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it('should use default message when no message provided', async () => {
|
|
41
|
+
const mockResponse = new node_fetch_1.Response(JSON.stringify({ code: 'CUSTOM_ERROR' }), {
|
|
42
|
+
status: 400,
|
|
43
|
+
statusText: 'Bad Request',
|
|
44
|
+
headers: { 'x-trace-id': traceId }
|
|
45
|
+
});
|
|
46
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse, 'Default message')).rejects.toMatchObject({
|
|
47
|
+
...baseErrorShape,
|
|
48
|
+
code: 'CUSTOM_ERROR',
|
|
49
|
+
message: 'Default message'
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('should include context if provided', async () => {
|
|
53
|
+
const mockResponse = new node_fetch_1.Response(JSON.stringify({
|
|
54
|
+
code: 'CUSTOM_ERROR',
|
|
55
|
+
message: 'Error message',
|
|
56
|
+
context: { details: 'Additional info' }
|
|
57
|
+
}), {
|
|
58
|
+
status: 400,
|
|
59
|
+
statusText: 'Bad Request',
|
|
60
|
+
headers: { 'x-trace-id': traceId }
|
|
61
|
+
});
|
|
62
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse)).rejects.toMatchObject({
|
|
63
|
+
...baseErrorShape,
|
|
64
|
+
code: 'CUSTOM_ERROR',
|
|
65
|
+
message: 'Error message',
|
|
66
|
+
context: { details: 'Additional info' }
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('when response has invalid or no body', () => {
|
|
71
|
+
const serverErrorShape = {
|
|
72
|
+
responseDetails: {
|
|
73
|
+
status: 500,
|
|
74
|
+
statusText: 'Internal Server Error',
|
|
75
|
+
traceId
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
it('should handle invalid JSON', async () => {
|
|
79
|
+
const mockResponse = new node_fetch_1.Response('Invalid JSON', {
|
|
80
|
+
status: 500,
|
|
81
|
+
statusText: 'Internal Server Error',
|
|
82
|
+
headers: { 'x-trace-id': traceId }
|
|
83
|
+
});
|
|
84
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse, 'Default message')).rejects.toMatchObject({
|
|
85
|
+
...serverErrorShape,
|
|
86
|
+
code: errors_1.errorCodes.UNKNOWN_ERROR,
|
|
87
|
+
message: 'Default message',
|
|
88
|
+
context: { responseText: 'Invalid JSON' }
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it('should handle empty response', async () => {
|
|
92
|
+
const mockResponse = new node_fetch_1.Response('', {
|
|
93
|
+
status: 500,
|
|
94
|
+
statusText: 'Internal Server Error',
|
|
95
|
+
headers: { 'x-trace-id': traceId }
|
|
96
|
+
});
|
|
97
|
+
await expect((0, error_handling_1.handleResponseError)(mockResponse)).rejects.toMatchObject({
|
|
98
|
+
...serverErrorShape,
|
|
99
|
+
code: errors_1.errorCodes.UNKNOWN_ERROR,
|
|
100
|
+
message: 'Failed to parse error response',
|
|
101
|
+
context: { responseText: '' }
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.test.d.ts","sourceRoot":"","sources":["../../src/__test__/graph.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_1 = require("@forge/api");
|
|
4
|
+
const graph_1 = require("../graph");
|
|
5
|
+
const errors_1 = require("../utils/errors");
|
|
6
|
+
jest.mock('@forge/api');
|
|
7
|
+
describe('Teamwork Graph Client', () => {
|
|
8
|
+
let graphClient;
|
|
9
|
+
let mockFetch;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
graphClient = new graph_1.TeamWorkGraphClient();
|
|
12
|
+
mockFetch = jest.fn();
|
|
13
|
+
api_1.__fetchProduct.mockReturnValue(mockFetch);
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
describe('setEntity', () => {
|
|
17
|
+
const entityPayload = {
|
|
18
|
+
id: 'test-entity-123',
|
|
19
|
+
type: 'test-type',
|
|
20
|
+
schemaVersion: '1',
|
|
21
|
+
updateSequenceNumber: 1,
|
|
22
|
+
displayName: 'Test Entity',
|
|
23
|
+
url: 'https://example.com/entity/123',
|
|
24
|
+
createdAt: new Date().toISOString(),
|
|
25
|
+
properties: { name: 'Test Entity' }
|
|
26
|
+
};
|
|
27
|
+
it('should successfully post entity data', async () => {
|
|
28
|
+
const expectedResponse = { success: true, entity: entityPayload };
|
|
29
|
+
mockFetch.mockResolvedValueOnce({
|
|
30
|
+
ok: true,
|
|
31
|
+
json: () => Promise.resolve(expectedResponse)
|
|
32
|
+
});
|
|
33
|
+
const result = await graphClient.setEntity(entityPayload);
|
|
34
|
+
expect(mockFetch).toHaveBeenCalledWith('/graph/connector/api/v1/entity', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: JSON.stringify(entityPayload),
|
|
37
|
+
redirect: 'follow',
|
|
38
|
+
headers: { 'Content-Type': 'application/json' }
|
|
39
|
+
});
|
|
40
|
+
expect(result).toEqual(expectedResponse);
|
|
41
|
+
});
|
|
42
|
+
it('should throw ForgeGraphAPIError when request fails', async () => {
|
|
43
|
+
const errorResponse = {
|
|
44
|
+
ok: false,
|
|
45
|
+
status: 400,
|
|
46
|
+
statusText: 'Bad Request',
|
|
47
|
+
headers: {
|
|
48
|
+
get: () => null
|
|
49
|
+
},
|
|
50
|
+
text: () => Promise.resolve(JSON.stringify({
|
|
51
|
+
code: errors_1.errorCodes.INVALID_REQUEST_BODY,
|
|
52
|
+
message: 'Invalid entity payload'
|
|
53
|
+
}))
|
|
54
|
+
};
|
|
55
|
+
mockFetch.mockResolvedValueOnce(errorResponse);
|
|
56
|
+
await expect(graphClient.setEntity(entityPayload)).rejects.toThrow(errors_1.ForgeGraphAPIError);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../src/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUzC,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBvG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleResponseError = void 0;
|
|
4
|
+
const errors_1 = require("./utils/errors");
|
|
5
|
+
async function handleResponseError(response, defaultMessage) {
|
|
6
|
+
const details = {
|
|
7
|
+
status: response.status,
|
|
8
|
+
statusText: response.statusText,
|
|
9
|
+
traceId: response.headers.get('x-b3-traceid') || response.headers.get('x-trace-id')
|
|
10
|
+
};
|
|
11
|
+
const responseText = await response.text();
|
|
12
|
+
try {
|
|
13
|
+
const body = JSON.parse(responseText);
|
|
14
|
+
const code = body.code || getErrorCodeFromStatus(response.status);
|
|
15
|
+
const message = body.message || defaultMessage || 'Unknown error occurred';
|
|
16
|
+
throw new errors_1.ForgeGraphAPIError(details, code, message, body.context);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error instanceof errors_1.ForgeGraphAPIError)
|
|
20
|
+
throw error;
|
|
21
|
+
throw new errors_1.ForgeGraphAPIError(details, errors_1.errorCodes.UNKNOWN_ERROR, defaultMessage || 'Failed to parse error response', { responseText });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.handleResponseError = handleResponseError;
|
|
25
|
+
function getErrorCodeFromStatus(status) {
|
|
26
|
+
switch (status) {
|
|
27
|
+
case 400:
|
|
28
|
+
return errors_1.errorCodes.INVALID_REQUEST_BODY;
|
|
29
|
+
case 403:
|
|
30
|
+
return errors_1.errorCodes.INSUFFICIENT_SCOPE;
|
|
31
|
+
case 429:
|
|
32
|
+
return errors_1.errorCodes.TOO_MANY_REQUESTS;
|
|
33
|
+
default:
|
|
34
|
+
return errors_1.errorCodes.UNKNOWN_ERROR;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/out/graph.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Result } from '@forge/api';
|
|
2
|
+
import { EntityPayload, UserObject, GroupObject, DeleteContext, TeamWorkGraph } from './utils/types';
|
|
3
|
+
export declare class TeamWorkGraphClient implements TeamWorkGraph {
|
|
4
|
+
setEntity: (entity: EntityPayload) => Promise<Result>;
|
|
5
|
+
deleteEntity: (entityId: string, context: DeleteContext) => Promise<Result>;
|
|
6
|
+
setUser: (user: UserObject) => Promise<Result>;
|
|
7
|
+
deleteUser: (userId: string) => Promise<Result>;
|
|
8
|
+
setGroup: (group: GroupObject) => Promise<Result>;
|
|
9
|
+
deleteGroup: (groupId: string) => Promise<Result>;
|
|
10
|
+
private sendPostRequest;
|
|
11
|
+
private sendDeleteRequest;
|
|
12
|
+
private sendRequest;
|
|
13
|
+
}
|
|
14
|
+
export declare const teamworkgraph: TeamWorkGraphClient;
|
|
15
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,MAAM,EAAe,MAAM,YAAY,CAAC;AAE9E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGrG,qBAAa,mBAAoB,YAAW,aAAa;IACvD,SAAS,WAAkB,aAAa,KAAG,QAAQ,MAAM,CAAC,CAExD;IAEF,YAAY,aAAoB,MAAM,WAAW,aAAa,KAAG,QAAQ,MAAM,CAAC,CAE9E;IAEF,OAAO,SAAgB,UAAU,KAAG,QAAQ,MAAM,CAAC,CAEjD;IAEF,UAAU,WAAkB,MAAM,KAAG,QAAQ,MAAM,CAAC,CAElD;IAEF,QAAQ,UAAiB,WAAW,KAAG,QAAQ,MAAM,CAAC,CAEpD;IAEF,WAAW,YAAmB,MAAM,KAAG,QAAQ,MAAM,CAAC,CAEpD;YAEY,eAAe;YAaf,iBAAiB;YAajB,WAAW;CAY1B;AAED,eAAO,MAAM,aAAa,qBAA4B,CAAC"}
|
package/out/graph.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.teamworkgraph = exports.TeamWorkGraphClient = void 0;
|
|
4
|
+
const api_1 = require("@forge/api");
|
|
5
|
+
const error_handling_1 = require("./error-handling");
|
|
6
|
+
const starGateBase = '/graph/connector';
|
|
7
|
+
class TeamWorkGraphClient {
|
|
8
|
+
setEntity = async (entity) => {
|
|
9
|
+
return this.sendPostRequest('/api/v1/entity', entity);
|
|
10
|
+
};
|
|
11
|
+
deleteEntity = async (entityId, context) => {
|
|
12
|
+
return this.sendDeleteRequest('/api/v1/entity', { id: entityId, context });
|
|
13
|
+
};
|
|
14
|
+
setUser = async (user) => {
|
|
15
|
+
return this.sendPostRequest('/api/v1/user', user);
|
|
16
|
+
};
|
|
17
|
+
deleteUser = async (userId) => {
|
|
18
|
+
return this.sendDeleteRequest('/api/v1/user', { id: userId });
|
|
19
|
+
};
|
|
20
|
+
setGroup = async (group) => {
|
|
21
|
+
return this.sendPostRequest('/api/v1/group', group);
|
|
22
|
+
};
|
|
23
|
+
deleteGroup = async (groupId) => {
|
|
24
|
+
return this.sendDeleteRequest('/api/v1/group', { id: groupId });
|
|
25
|
+
};
|
|
26
|
+
async sendPostRequest(path, body) {
|
|
27
|
+
const response = await this.sendRequest(path, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: JSON.stringify(body)
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
await (0, error_handling_1.handleResponseError)(response, `Error posting to ${path}`);
|
|
33
|
+
}
|
|
34
|
+
return response.json();
|
|
35
|
+
}
|
|
36
|
+
async sendDeleteRequest(path, body) {
|
|
37
|
+
const response = await this.sendRequest(path, {
|
|
38
|
+
method: 'DELETE',
|
|
39
|
+
body: JSON.stringify(body)
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
await (0, error_handling_1.handleResponseError)(response, `Error deleting from ${path}`);
|
|
43
|
+
}
|
|
44
|
+
return response.json();
|
|
45
|
+
}
|
|
46
|
+
async sendRequest(path, options) {
|
|
47
|
+
const reqPath = starGateBase + path;
|
|
48
|
+
const response = await (0, api_1.__fetchProduct)({ provider: 'app', remote: 'stargate' })(reqPath, {
|
|
49
|
+
...options,
|
|
50
|
+
redirect: 'follow',
|
|
51
|
+
headers: {
|
|
52
|
+
...options?.headers,
|
|
53
|
+
'Content-Type': 'application/json'
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.TeamWorkGraphClient = TeamWorkGraphClient;
|
|
60
|
+
exports.teamworkgraph = new TeamWorkGraphClient();
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAE,aAAa,IAAI,KAAK,EAAE,kBAAkB,EAAE,CAAC;AAElE,eAAe,aAAa,CAAC"}
|
package/out/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForgeGraphAPIError = exports.graph = exports.errorCodes = void 0;
|
|
4
|
+
const graph_1 = require("./graph");
|
|
5
|
+
Object.defineProperty(exports, "graph", { enumerable: true, get: function () { return graph_1.teamworkgraph; } });
|
|
6
|
+
const errors_1 = require("./utils/errors");
|
|
7
|
+
Object.defineProperty(exports, "errorCodes", { enumerable: true, get: function () { return errors_1.errorCodes; } });
|
|
8
|
+
Object.defineProperty(exports, "ForgeGraphAPIError", { enumerable: true, get: function () { return errors_1.ForgeGraphAPIError; } });
|
|
9
|
+
exports.default = graph_1.teamworkgraph;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const errorCodes: {
|
|
2
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
3
|
+
readonly INVALID_REQUEST_BODY: "INVALID_REQUEST_BODY";
|
|
4
|
+
readonly INSUFFICIENT_SCOPE: "INSUFFICIENT_SCOPE";
|
|
5
|
+
readonly TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS";
|
|
6
|
+
};
|
|
7
|
+
export interface APIErrorResponseDetails {
|
|
8
|
+
status: number;
|
|
9
|
+
statusText: string;
|
|
10
|
+
traceId?: string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare class ForgeGraphAPIError extends Error {
|
|
13
|
+
readonly responseDetails: APIErrorResponseDetails;
|
|
14
|
+
readonly code: string;
|
|
15
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
16
|
+
constructor(responseDetails: APIErrorResponseDetails, code: string, message: string, context?: Record<string, unknown> | undefined);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AASX,MAAM,WAAW,uBAAuB;IAEtC,MAAM,EAAE,MAAM,CAAC;IAGf,UAAU,EAAE,MAAM,CAAC;IAGnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAMD,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,eAAe,EAAE,uBAAuB;aACxC,IAAI,EAAE,MAAM;aAEZ,OAAO,CAAC;gBAHR,eAAe,EAAE,uBAAuB,EACxC,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,qCAAyB;CAKpD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForgeGraphAPIError = exports.errorCodes = void 0;
|
|
4
|
+
exports.errorCodes = {
|
|
5
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
|
|
6
|
+
INVALID_REQUEST_BODY: 'INVALID_REQUEST_BODY',
|
|
7
|
+
INSUFFICIENT_SCOPE: 'INSUFFICIENT_SCOPE',
|
|
8
|
+
TOO_MANY_REQUESTS: 'TOO_MANY_REQUESTS'
|
|
9
|
+
};
|
|
10
|
+
class ForgeGraphAPIError extends Error {
|
|
11
|
+
responseDetails;
|
|
12
|
+
code;
|
|
13
|
+
context;
|
|
14
|
+
constructor(responseDetails, code, message, context) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.responseDetails = responseDetails;
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.context = context;
|
|
19
|
+
this.name = 'ForgeGraphAPIError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.ForgeGraphAPIError = ForgeGraphAPIError;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Result } from '@forge/api';
|
|
2
|
+
export declare type UserObject = {
|
|
3
|
+
accountId?: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
email: string;
|
|
6
|
+
externalId?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare type Thumbnail = {
|
|
9
|
+
externalUrl: string;
|
|
10
|
+
};
|
|
11
|
+
export declare type Principal = {
|
|
12
|
+
type: 'USER' | 'GROUP' | 'EVERYONE' | 'ATLASSIAN_WORKSPACE' | 'CONTAINER';
|
|
13
|
+
id?: string | null;
|
|
14
|
+
};
|
|
15
|
+
export declare type AccessControl = {
|
|
16
|
+
principals: Principal[];
|
|
17
|
+
};
|
|
18
|
+
export declare type SmartLinkViewedByUser = {
|
|
19
|
+
principalUser: Principal;
|
|
20
|
+
};
|
|
21
|
+
export declare type Permissions = {
|
|
22
|
+
accessControls?: AccessControl[];
|
|
23
|
+
smartLinkViewedBy?: SmartLinkViewedByUser[];
|
|
24
|
+
};
|
|
25
|
+
export declare type DocumentCategory = 'folder' | 'document' | 'presentation' | 'spreadsheet' | 'image' | 'audio' | 'video' | 'pdf' | 'shortcut' | 'code' | 'archive' | 'form' | 'web-page' | 'other';
|
|
26
|
+
export declare type DocumentType = {
|
|
27
|
+
category?: DocumentCategory;
|
|
28
|
+
mimeType?: string;
|
|
29
|
+
iconUrl?: string;
|
|
30
|
+
fileExtension?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare type DocumentContent = {
|
|
33
|
+
mimeType?: string;
|
|
34
|
+
text?: string;
|
|
35
|
+
binary?: string;
|
|
36
|
+
};
|
|
37
|
+
export declare type ExportLink = {
|
|
38
|
+
mimeType: string;
|
|
39
|
+
url: string;
|
|
40
|
+
};
|
|
41
|
+
export declare type DocumentAttributes = {
|
|
42
|
+
collaborators?: Partial<UserObject>[];
|
|
43
|
+
type: DocumentType;
|
|
44
|
+
content: DocumentContent;
|
|
45
|
+
byteSize?: number;
|
|
46
|
+
exportLinks?: ExportLink[];
|
|
47
|
+
};
|
|
48
|
+
export declare type ContainerKeyObject = {
|
|
49
|
+
type: string;
|
|
50
|
+
value: {
|
|
51
|
+
entityId: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export declare type ParentKeyObject = {
|
|
55
|
+
type: string;
|
|
56
|
+
value: {
|
|
57
|
+
entityId: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export declare type EntityPayload = {
|
|
61
|
+
schemaVersion: string;
|
|
62
|
+
id: string;
|
|
63
|
+
updateSequenceNumber: number;
|
|
64
|
+
displayName: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
url: string;
|
|
67
|
+
createdAt: string;
|
|
68
|
+
createdBy?: Partial<UserObject>;
|
|
69
|
+
lastUpdatedAt?: string;
|
|
70
|
+
lastUpdatedBy?: Partial<UserObject>;
|
|
71
|
+
owners?: Partial<UserObject>[];
|
|
72
|
+
thumbnail?: Thumbnail;
|
|
73
|
+
parentKey?: ParentKeyObject | string;
|
|
74
|
+
containerKey?: ContainerKeyObject | string;
|
|
75
|
+
permissions?: Permissions;
|
|
76
|
+
associations?: Associations;
|
|
77
|
+
'atlassian:document'?: DocumentAttributes;
|
|
78
|
+
};
|
|
79
|
+
export declare type GroupObject = {
|
|
80
|
+
id?: string;
|
|
81
|
+
email?: string;
|
|
82
|
+
name?: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
adminCreated?: boolean;
|
|
85
|
+
directMembersCount?: string;
|
|
86
|
+
kind?: string;
|
|
87
|
+
etag?: string;
|
|
88
|
+
aliases?: string[];
|
|
89
|
+
nonEditableAliases?: string[];
|
|
90
|
+
};
|
|
91
|
+
export declare type DeleteContext = {
|
|
92
|
+
target: string;
|
|
93
|
+
token: string;
|
|
94
|
+
};
|
|
95
|
+
export declare type AssociationObject = {
|
|
96
|
+
associationType: string;
|
|
97
|
+
values: string[];
|
|
98
|
+
};
|
|
99
|
+
export declare type Associations = {
|
|
100
|
+
set: AssociationObject[];
|
|
101
|
+
};
|
|
102
|
+
export interface TeamWorkGraph {
|
|
103
|
+
setEntity(entity: EntityPayload): Promise<Result>;
|
|
104
|
+
deleteEntity(entityId: string, context: DeleteContext): Promise<Result>;
|
|
105
|
+
setUser(user: UserObject): Promise<Result>;
|
|
106
|
+
deleteUser(userId: string): Promise<Result>;
|
|
107
|
+
setGroup(group: GroupObject): Promise<Result>;
|
|
108
|
+
deleteGroup(groupId: string): Promise<Result>;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forge/teamwork-graph",
|
|
3
|
+
"version": "0.1.0-next.0",
|
|
4
|
+
"description": "Forge TeamworkGraph SDK",
|
|
5
|
+
"author": "Atlassian",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "out/index.js",
|
|
8
|
+
"types": "out/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"out"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "yarn run clean && yarn run compile",
|
|
14
|
+
"clean": "rm -rf ./out && rm -f tsconfig.tsbuildinfo",
|
|
15
|
+
"compile": "tsc -b -v"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "14.18.63",
|
|
19
|
+
"@types/node-fetch": "^2.6.11",
|
|
20
|
+
"expect-type": "^0.17.3",
|
|
21
|
+
"node-fetch": "2.7.0",
|
|
22
|
+
"jest-when": "^3.6.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@forge/api": "^5.1.0-next.2"
|
|
26
|
+
}
|
|
27
|
+
}
|