@knpkv/atlassian-common 1.3.0 → 1.4.1
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 +66 -0
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Hash.d.ts.map +1 -1
- package/dist/ast/Document.d.ts +3 -19
- package/dist/ast/Document.d.ts.map +1 -1
- package/dist/ast/Document.js +4 -3
- package/dist/ast/Document.js.map +1 -1
- package/dist/ast/MacroNode.d.ts +3 -3
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +2 -6
- package/dist/attachments.js.map +1 -1
- package/dist/auth/OAuthEndpoints.d.ts +13 -0
- package/dist/auth/OAuthEndpoints.d.ts.map +1 -1
- package/dist/auth/OAuthEndpoints.js +21 -0
- package/dist/auth/OAuthEndpoints.js.map +1 -1
- package/dist/auth/OAuthErrors.d.ts +22 -0
- package/dist/auth/OAuthErrors.d.ts.map +1 -1
- package/dist/auth/OAuthErrors.js +1 -1
- package/dist/auth/OAuthErrors.js.map +1 -1
- package/dist/auth/OAuthOperations.d.ts +7 -1
- package/dist/auth/OAuthOperations.d.ts.map +1 -1
- package/dist/auth/OAuthOperations.js +12 -2
- package/dist/auth/OAuthOperations.js.map +1 -1
- package/dist/auth/OAuthResponseSchemas.d.ts +11 -0
- package/dist/auth/OAuthResponseSchemas.d.ts.map +1 -1
- package/dist/auth/OAuthResponseSchemas.js +11 -0
- package/dist/auth/OAuthResponseSchemas.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/config/AuthProfiles.d.ts +140 -175
- package/dist/config/AuthProfiles.d.ts.map +1 -1
- package/dist/config/AuthProfiles.js +2 -1
- package/dist/config/AuthProfiles.js.map +1 -1
- package/dist/config/ConfigPaths.d.ts.map +1 -1
- package/dist/config/ProfileManager.d.ts +13 -2
- package/dist/config/ProfileManager.d.ts.map +1 -1
- package/dist/config/ProfileManager.js +30 -2
- package/dist/config/ProfileManager.js.map +1 -1
- package/dist/config/TokenStorage.d.ts.map +1 -1
- package/dist/config/TokenStorage.js +2 -1
- package/dist/config/TokenStorage.js.map +1 -1
- package/dist/serializers/MarkdownSerializer.d.ts.map +1 -1
- package/dist/serializers/MarkdownSerializer.js.map +1 -1
- package/package.json +7 -7
- package/src/ast/Document.ts +5 -4
- package/src/attachments.ts +2 -1
- package/src/auth/OAuthEndpoints.ts +22 -0
- package/src/auth/OAuthErrors.ts +23 -1
- package/src/auth/OAuthOperations.ts +26 -3
- package/src/auth/OAuthResponseSchemas.ts +12 -0
- package/src/auth/index.ts +1 -0
- package/src/bin.ts +3 -1
- package/src/config/AuthProfiles.ts +4 -2
- package/src/config/ProfileManager.ts +39 -3
- package/src/config/TokenStorage.ts +4 -2
- package/test/ApiUpdateWorkflows.test.ts +80 -0
- package/test/OAuthEndpoints.test.ts +23 -0
- package/test/ProfileManager.test.ts +61 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from "@effect/vitest"
|
|
2
2
|
import { ConfigProvider, Effect, Layer } from "effect"
|
|
3
|
+
import * as Clock from "effect/Clock"
|
|
4
|
+
import * as Deferred from "effect/Deferred"
|
|
5
|
+
import * as Fiber from "effect/Fiber"
|
|
3
6
|
import * as FileSystem from "effect/FileSystem"
|
|
4
7
|
import * as Path from "effect/Path"
|
|
5
8
|
import { SystemError } from "effect/PlatformError"
|
|
9
|
+
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
|
6
10
|
import {
|
|
7
11
|
type AtlassianToolDefinition,
|
|
8
12
|
HomeDirectoryLive,
|
|
@@ -13,6 +17,7 @@ import {
|
|
|
13
17
|
type OAuthToken,
|
|
14
18
|
ProfileNotFoundError,
|
|
15
19
|
refreshActiveProfiles,
|
|
20
|
+
saveOAuthConfig,
|
|
16
21
|
saveProfileToken,
|
|
17
22
|
useProfileForAllTools
|
|
18
23
|
} from "../src/config/index.js"
|
|
@@ -175,6 +180,62 @@ describe("ProfileManager", () => {
|
|
|
175
180
|
).rejects.toBeInstanceOf(ProfileNotFoundError)
|
|
176
181
|
})
|
|
177
182
|
|
|
183
|
+
// Atlassian rotates refresh tokens, so the grant consumes the stored one and
|
|
184
|
+
// the response carries its replacement. If an interrupt can land between the
|
|
185
|
+
// two, the credential is spent with nothing saved and the profile is silently
|
|
186
|
+
// logged out — hence the uninterruptible region around grant-and-persist.
|
|
187
|
+
it("persists the rotated token even when interrupted mid-rotation", async () => {
|
|
188
|
+
const mock = makeMockFS()
|
|
189
|
+
const program = Effect.gen(function*() {
|
|
190
|
+
yield* saveOAuthConfig("tool-a", { clientId: "client-1", clientSecret: "secret-1" })
|
|
191
|
+
const nowMs = yield* Clock.currentTimeMillis
|
|
192
|
+
yield* saveProfileToken("tool-a", makeToken(1, "read:me offline_access", nowMs - 1_000))
|
|
193
|
+
|
|
194
|
+
const issued = yield* Deferred.make<void>()
|
|
195
|
+
const release = yield* Deferred.make<void>()
|
|
196
|
+
const client = Layer.succeed(
|
|
197
|
+
HttpClient.HttpClient,
|
|
198
|
+
HttpClient.make((request) =>
|
|
199
|
+
Effect.gen(function*() {
|
|
200
|
+
yield* Deferred.succeed(issued, undefined)
|
|
201
|
+
yield* Deferred.await(release)
|
|
202
|
+
return HttpClientResponse.fromWeb(
|
|
203
|
+
request,
|
|
204
|
+
new Response(
|
|
205
|
+
JSON.stringify({
|
|
206
|
+
access_token: "fresh-access",
|
|
207
|
+
refresh_token: "rotated-refresh",
|
|
208
|
+
expires_in: 3600,
|
|
209
|
+
scope: "read:me offline_access",
|
|
210
|
+
token_type: "Bearer"
|
|
211
|
+
}),
|
|
212
|
+
{ status: 200, headers: { "content-type": "application/json" } }
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
})
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
const fiber = yield* refreshActiveProfiles([tools[0]!]).pipe(
|
|
220
|
+
Effect.provide(client),
|
|
221
|
+
Effect.exit,
|
|
222
|
+
Effect.forkChild({ startImmediately: true })
|
|
223
|
+
)
|
|
224
|
+
yield* Deferred.await(issued)
|
|
225
|
+
const interrupting = yield* Fiber.interrupt(fiber).pipe(Effect.forkChild({ startImmediately: true }))
|
|
226
|
+
yield* Deferred.succeed(release, undefined)
|
|
227
|
+
yield* Fiber.join(interrupting)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
await Effect.runPromise(
|
|
231
|
+
program.pipe(
|
|
232
|
+
Effect.provide(Layer.mergeAll(mock.layer, Path.layer, HomeDirectoryLive, ConfigProviderLive))
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
expect(JSON.stringify(mock.store)).toContain("rotated-refresh")
|
|
237
|
+
})
|
|
238
|
+
|
|
178
239
|
it("fails expired-token refresh when OAuth config is missing", async () => {
|
|
179
240
|
await expect(
|
|
180
241
|
run(
|