Skip to content

Commit 85e677d

Browse files
authored
Fix OpenAPI OAuth config compatibility (#848)
1 parent 0fd4d11 commit 85e677d

4 files changed

Lines changed: 78 additions & 3 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
UPDATE "plugin_storage"
2+
SET
3+
"data" = jsonb_set("data"::jsonb, '{config,oauth2,authorizationUrl}', 'null'::jsonb, true)::json,
4+
"updated_at" = now()
5+
WHERE
6+
"plugin_id" = 'openapi'
7+
AND "collection" = 'source'
8+
AND "data" #> '{config,oauth2}' IS NOT NULL
9+
AND NOT ("data"::jsonb #> '{config,oauth2}' ? 'authorizationUrl');

apps/cloud/drizzle/meta/_journal.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@
127127
"when": 1779087600000,
128128
"tag": "0017_plugin_storage_sources",
129129
"breakpoints": true
130+
},
131+
{
132+
"idx": 18,
133+
"version": "7",
134+
"when": 1779199200000,
135+
"tag": "0018_repair_openapi_oauth_authorization_url",
136+
"breakpoints": true
130137
}
131138
]
132139
}

packages/plugins/http-source/src/sdk/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Schema } from "effect";
1+
import { Effect, Schema } from "effect";
22

33
export const HttpCredentialInput = Schema.Union([
44
Schema.String,
@@ -37,7 +37,10 @@ export const OAuth2SourceConfig = Schema.Struct({
3737
securitySchemeName: Schema.String,
3838
flow: OAuth2Flow,
3939
tokenUrl: Schema.String,
40-
authorizationUrl: Schema.NullOr(Schema.String),
40+
authorizationUrl: Schema.NullOr(Schema.String).pipe(
41+
Schema.optional,
42+
Schema.withDecodingDefault(Effect.succeed(null)),
43+
),
4144
issuerUrl: Schema.optional(Schema.NullOr(Schema.String)),
4245
clientIdSlot: Schema.String,
4346
clientSecretSlot: Schema.NullOr(Schema.String),

packages/plugins/openapi/src/sdk/client-credentials-oauth.test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ describe("OpenAPI client_credentials OAuth", () => {
108108
openApiPlugin({ httpClientLayer: clientLayer }),
109109
memorySecretsPlugin(),
110110
] as const;
111-
const config = makeTestConfig({ plugins });
112111

113112
const now = new Date();
114113
const orgScope = Scope.make({
@@ -121,6 +120,7 @@ describe("OpenAPI client_credentials OAuth", () => {
121120
name: "alice",
122121
createdAt: now,
123122
});
123+
const config = makeTestConfig({ plugins, scopes: [userScope, orgScope] });
124124

125125
const adminExec = yield* createExecutor({
126126
...config,
@@ -214,6 +214,62 @@ describe("OpenAPI client_credentials OAuth", () => {
214214
namespace: "petstore",
215215
oauth2,
216216
});
217+
const storedSourceRow = yield* Effect.promise(() =>
218+
config.db.findFirst("plugin_storage", {
219+
where: (b) =>
220+
b.and(
221+
b("scope_id", "=", String(userScope.id)),
222+
b("plugin_id", "=", "openapi"),
223+
b("collection", "=", "source"),
224+
b("key", "=", "petstore"),
225+
),
226+
}),
227+
);
228+
const storedData = storedSourceRow?.data as
229+
| {
230+
readonly config?: Record<string, unknown>;
231+
}
232+
| undefined;
233+
const storedOAuth2 = storedData?.config?.oauth2;
234+
if (
235+
!storedSourceRow ||
236+
!storedData?.config ||
237+
typeof storedOAuth2 !== "object" ||
238+
storedOAuth2 === null ||
239+
Array.isArray(storedOAuth2)
240+
) {
241+
return yield* new OpenApiClientCredentialsTestSetupError({
242+
message: "Expected stored OpenAPI source OAuth config",
243+
});
244+
}
245+
const { authorizationUrl: _authorizationUrl, ...oauth2WithoutAuthorizationUrl } =
246+
storedOAuth2 as Record<string, unknown>;
247+
yield* Effect.promise(() =>
248+
config.db.updateMany("plugin_storage", {
249+
where: (b) =>
250+
b.and(
251+
b("scope_id", "=", String(userScope.id)),
252+
b("plugin_id", "=", "openapi"),
253+
b("collection", "=", "source"),
254+
b("key", "=", "petstore"),
255+
),
256+
set: {
257+
data: {
258+
...storedData,
259+
config: {
260+
...storedData.config,
261+
oauth2: oauth2WithoutAuthorizationUrl,
262+
},
263+
},
264+
},
265+
}),
266+
);
267+
const sourceAfterLegacyShape = yield* userExec.openapi.getSource(
268+
"petstore",
269+
String(userScope.id),
270+
);
271+
expect(sourceAfterLegacyShape?.config.oauth2?.authorizationUrl).toBeNull();
272+
217273
yield* userExec.sources.setBinding(
218274
SetSourceCredentialBindingInput.make({
219275
source: { id: "petstore", scope: userScope.id },

0 commit comments

Comments
 (0)