@forge/sql 4.0.5-next.1 → 4.0.5-next.1-experimental-e9e08bb
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/CHANGELOG.md +10 -0
- package/out/__test__/sql.test.js +54 -0
- package/out/sql.d.ts.map +1 -1
- package/out/sql.js +37 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @forge/sql
|
|
2
2
|
|
|
3
|
+
## 4.0.5-next.1-experimental-e9e08bb
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 067c20a: Add manifest-driven Docker local storage, seeding, reset, status, persistent lifecycle, and local
|
|
8
|
+
SDK routing for Forge tunnels. Simplify `forge storage entities indexes list` to
|
|
9
|
+
`forge storage indexes list`.
|
|
10
|
+
- 01e9f12: Include package changelogs in published artifacts.
|
|
11
|
+
- @forge/api@8.0.5-next.1-experimental-e9e08bb
|
|
12
|
+
|
|
3
13
|
## 4.0.5-next.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/out/__test__/sql.test.js
CHANGED
|
@@ -9,6 +9,9 @@ describe('SqlClient', () => {
|
|
|
9
9
|
let sqlClient;
|
|
10
10
|
let mockFetch;
|
|
11
11
|
beforeEach(() => {
|
|
12
|
+
for (const key of Object.keys(process.env).filter((key) => key.startsWith('FORGE_LOCAL_')))
|
|
13
|
+
delete process.env[key];
|
|
14
|
+
delete global.__forge_local_fetch__;
|
|
12
15
|
sqlClient = new sql_1.SqlClient();
|
|
13
16
|
mockFetch = jest.fn();
|
|
14
17
|
api_1.__fetchProduct.mockReturnValue(mockFetch);
|
|
@@ -107,6 +110,57 @@ describe('SqlClient', () => {
|
|
|
107
110
|
await expect(sqlClient['sendRequest'](path)).rejects.toThrow(mockError);
|
|
108
111
|
expect(mockFetch).toHaveBeenCalledWith(path, expect.any(Object));
|
|
109
112
|
});
|
|
113
|
+
it('routes local-storage requests directly to loopback without calling hosted Forge SQL', async () => {
|
|
114
|
+
Object.assign(process.env, {
|
|
115
|
+
FORGE_LOCAL_STORAGE: '1',
|
|
116
|
+
FORGE_LOCAL_SQL_URL: 'http://127.0.0.1:18091',
|
|
117
|
+
FORGE_LOCAL_STORAGE_APP_ID: 'local-app',
|
|
118
|
+
FORGE_LOCAL_STORAGE_ENVIRONMENT_ID: 'local-environment',
|
|
119
|
+
FORGE_LOCAL_STORAGE_INSTALLATION_ID: 'local-installation'
|
|
120
|
+
});
|
|
121
|
+
const localFetch = jest.fn().mockResolvedValue(new Response(JSON.stringify({ rows: [] }), { status: 200 }));
|
|
122
|
+
global.__forge_local_fetch__ = localFetch;
|
|
123
|
+
await sqlClient['sendRequest']('/api/v1/execute', { method: 'POST' });
|
|
124
|
+
expect(localFetch).toHaveBeenCalledWith(new URL('http://127.0.0.1:18091/api/v1/execute'), expect.objectContaining({
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: expect.objectContaining({
|
|
127
|
+
'x-forge-app-id': 'local-app',
|
|
128
|
+
'x-forge-environment-id': 'local-environment',
|
|
129
|
+
'x-forge-installation-id': 'local-installation',
|
|
130
|
+
'x-atlassian-forgeapp-app-id': 'local-app',
|
|
131
|
+
'x-atlassian-forgeapp-environment-id': 'local-environment',
|
|
132
|
+
'x-atlassian-forgeapp-installation-id': 'local-installation'
|
|
133
|
+
})
|
|
134
|
+
}));
|
|
135
|
+
expect(api_1.__fetchProduct).not.toHaveBeenCalled();
|
|
136
|
+
delete global.__forge_local_fetch__;
|
|
137
|
+
});
|
|
138
|
+
it('fails closed for incomplete or non-loopback local SQL routing', async () => {
|
|
139
|
+
process.env.FORGE_LOCAL_STORAGE = '1';
|
|
140
|
+
process.env.FORGE_LOCAL_SQL_URL = 'http://127.0.0.1:18091';
|
|
141
|
+
await expect(sqlClient['sendRequest']('/api/v1/execute')).rejects.toThrow('local namespace is incomplete');
|
|
142
|
+
expect(api_1.__fetchProduct).not.toHaveBeenCalled();
|
|
143
|
+
Object.assign(process.env, {
|
|
144
|
+
FORGE_LOCAL_STORAGE_APP_ID: 'local-app',
|
|
145
|
+
FORGE_LOCAL_STORAGE_ENVIRONMENT_ID: 'local-environment',
|
|
146
|
+
FORGE_LOCAL_STORAGE_INSTALLATION_ID: 'local-installation',
|
|
147
|
+
FORGE_LOCAL_SQL_URL: 'https://example.com'
|
|
148
|
+
});
|
|
149
|
+
await expect(sqlClient['sendRequest']('/api/v1/execute')).rejects.toThrow('must be http://127.0.0.1:18091');
|
|
150
|
+
expect(api_1.__fetchProduct).not.toHaveBeenCalled();
|
|
151
|
+
});
|
|
152
|
+
it('fails closed when local SQL routing has no tunnel local-fetch bridge', async () => {
|
|
153
|
+
Object.assign(process.env, {
|
|
154
|
+
FORGE_LOCAL_STORAGE: '1',
|
|
155
|
+
FORGE_LOCAL_SQL_URL: 'http://127.0.0.1:18091',
|
|
156
|
+
FORGE_LOCAL_STORAGE_APP_ID: 'local-app',
|
|
157
|
+
FORGE_LOCAL_STORAGE_ENVIRONMENT_ID: 'local-environment',
|
|
158
|
+
FORGE_LOCAL_STORAGE_INSTALLATION_ID: 'local-installation'
|
|
159
|
+
});
|
|
160
|
+
delete global.__forge_local_fetch__;
|
|
161
|
+
await expect(sqlClient['sendRequest']('/api/v1/execute')).rejects.toThrow('requires the tunnel local-fetch bridge');
|
|
162
|
+
expect(api_1.__fetchProduct).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
110
164
|
});
|
|
111
165
|
describe('prepare', () => {
|
|
112
166
|
it('should return a SqlStatement instance with query', () => {
|
package/out/sql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAgC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI7E,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,KAAK,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAElF,qBAAa,SAAS;YACN,WAAW;
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAgC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI7E,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,KAAK,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAElF,qBAAa,SAAS;YACN,WAAW;YAcX,UAAU;YAsBV,qBAAqB;IAYnC,OAAO,CAAC,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,eAA2C,GACpD,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAU3B,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAQ9D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;CAGrE;AAED,eAAO,MAAM,GAAG,WAAkB,CAAC"}
|
package/out/sql.js
CHANGED
|
@@ -11,15 +11,18 @@ exports.SQL_API_ENDPOINTS = {
|
|
|
11
11
|
};
|
|
12
12
|
class SqlClient {
|
|
13
13
|
async sendRequest(path, options) {
|
|
14
|
-
const
|
|
14
|
+
const requestOptions = {
|
|
15
15
|
...options,
|
|
16
16
|
redirect: 'follow',
|
|
17
17
|
headers: {
|
|
18
18
|
...options?.headers,
|
|
19
19
|
'Content-Type': 'application/json'
|
|
20
20
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
21
|
+
};
|
|
22
|
+
const localClient = getLocalSqlFetchClient();
|
|
23
|
+
if (localClient)
|
|
24
|
+
return localClient(path, requestOptions);
|
|
25
|
+
return (0, api_1.__fetchProduct)({ provider: 'app', remote: 'sql', type: 'sql' })(path, requestOptions);
|
|
23
26
|
}
|
|
24
27
|
async storageApi(query, params = [], method = 'all', endpoint = exports.SQL_API_ENDPOINTS.EXECUTE) {
|
|
25
28
|
const response = await this.sendRequest(endpoint, {
|
|
@@ -55,3 +58,34 @@ class SqlClient {
|
|
|
55
58
|
}
|
|
56
59
|
exports.SqlClient = SqlClient;
|
|
57
60
|
exports.sql = new SqlClient();
|
|
61
|
+
function getLocalSqlFetchClient() {
|
|
62
|
+
if (process.env.FORGE_LOCAL_STORAGE !== '1')
|
|
63
|
+
return undefined;
|
|
64
|
+
const baseUrl = process.env.FORGE_LOCAL_SQL_URL;
|
|
65
|
+
const appId = process.env.FORGE_LOCAL_STORAGE_APP_ID;
|
|
66
|
+
const environmentId = process.env.FORGE_LOCAL_STORAGE_ENVIRONMENT_ID;
|
|
67
|
+
const installationId = process.env.FORGE_LOCAL_STORAGE_INSTALLATION_ID;
|
|
68
|
+
if (!baseUrl || !appId || !environmentId || !installationId) {
|
|
69
|
+
throw new Error('Forge local SQL routing is enabled but its local namespace is incomplete.');
|
|
70
|
+
}
|
|
71
|
+
if (baseUrl !== 'http://127.0.0.1:18091') {
|
|
72
|
+
throw new Error('Forge local SQL endpoint must be http://127.0.0.1:18091.');
|
|
73
|
+
}
|
|
74
|
+
const fetchImplementation = globalThis.__forge_local_fetch__;
|
|
75
|
+
if (typeof fetchImplementation !== 'function') {
|
|
76
|
+
throw new Error('Forge local SQL routing requires the tunnel local-fetch bridge.');
|
|
77
|
+
}
|
|
78
|
+
const endpoint = new URL(baseUrl);
|
|
79
|
+
return async (requestPath, options) => (await fetchImplementation(new URL(requestPath, endpoint), {
|
|
80
|
+
...options,
|
|
81
|
+
headers: {
|
|
82
|
+
...options?.headers,
|
|
83
|
+
'x-forge-app-id': appId,
|
|
84
|
+
'x-forge-environment-id': environmentId,
|
|
85
|
+
'x-forge-installation-id': installationId,
|
|
86
|
+
'x-atlassian-forgeapp-app-id': appId,
|
|
87
|
+
'x-atlassian-forgeapp-environment-id': environmentId,
|
|
88
|
+
'x-atlassian-forgeapp-installation-id': installationId
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forge/sql",
|
|
3
|
-
"version": "4.0.5-next.1",
|
|
3
|
+
"version": "4.0.5-next.1-experimental-e9e08bb",
|
|
4
4
|
"description": "Forge SQL sdk",
|
|
5
5
|
"author": "Atlassian",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"typescript": "5.9.2"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@forge/api": "^8.0.5-next.
|
|
21
|
+
"@forge/api": "^8.0.5-next.1-experimental-e9e08bb"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"registry": "https://packages.atlassian.com/api/npm/npm-public/"
|