History

Keep fanned-out presets out of the custom Graph bundle

When presets are checked and custom scopes entered in the same submit, the workloads already fan out through addWorkloads, so the legacy addGraph call now carries only the custom scopes. Empty presetIds with custom scopes no longer falls back to the default workload selection.

Rhys Sullivan committed 4 months ago

Commit e0009531 parent 75d5c0c4 files · +76 −12Browse files

Unified · Split

4 files
  1. packages/plugins/microsoft/src/react/AddMicrosoftSource.test.ts +22 −0
  2. packages/plugins/microsoft/src/react/AddMicrosoftSource.tsx +20 −10
  3. packages/plugins/microsoft/src/sdk/graph.ts +7 −2
  4. packages/plugins/microsoft/src/sdk/plugin.test.ts +27 −0
packages/plugins/microsoft/src/react/AddMicrosoftSource.test.ts+22−0
@@ -7,6 +7,7 @@ import type { ReactElement, ReactNode } from "react"; · 6 unmodified lines
7import {7import {
8 MicrosoftWorkloadResultPanel,8 MicrosoftWorkloadResultPanel,
9 microsoftAddWorkloadsPayload,9 microsoftAddWorkloadsPayload,
10 microsoftCustomGraphPayload,
10 submitMicrosoftWorkloadsSelection,11 submitMicrosoftWorkloadsSelection,
11 type AddMicrosoftWorkloadsMutation,12 type AddMicrosoftWorkloadsMutation,
12} from "./AddMicrosoftSource";13} from "./AddMicrosoftSource";
@@ -169,4 +170,25 @@ describe("AddMicrosoftSource per-workload submit", () => { · 156 unmodified lines
169 ],170 ],
170 });171 });
171 });172 });
173
174 it("mixed flow: the custom Graph payload carries only custom scopes, never the fanned-out presets", () => {
175 // Checked workloads go through addWorkloads as their own integrations in
176 // the same submit; if the custom addGraph payload repeated their preset
177 // ids, the custom bundle would duplicate the same tools.
178 expect(
179 microsoftCustomGraphPayload({
180 customScopes: ["Sites.Read.All", "Custom.Scope"],
181 slug: "microsoft_graph_custom",
182 name: "Custom Microsoft Graph",
183 description: "Custom Microsoft Graph scopes.",
184 baseUrl: "",
185 }),
186 ).toEqual({
187 presetIds: [],
188 customScopes: ["Sites.Read.All", "Custom.Scope"],
189 slug: "microsoft_graph_custom",
190 name: "Custom Microsoft Graph",
191 description: "Custom Microsoft Graph scopes.",
192 });
193 });
172});194});
packages/plugins/microsoft/src/react/AddMicrosoftSource.tsx+20−10
@@ -273,14 +273,25 @@ type MicrosoftGraphAddPayload = MicrosoftGraphConfig & { · 272 unmodified lines
273 readonly presetIds: readonly string[];273 readonly presetIds: readonly string[];
274};274};
275 275
276const microsoftGraphPayload = (config: MicrosoftGraphAddPayload): MicrosoftGraphAddPayload => {276// The payload for the legacy addGraph call that carries the custom scopes.
277 const baseUrl = config.baseUrl?.trim() ?? "";277// The checked workloads already fan out as their own integrations through
278 const description = config.description?.trim() ?? "";278// addWorkloads in the same submit, so the custom bundle must carry only the
279// custom scopes (presetIds stays empty): folding the preset ids in again
280// would create a second integration containing the same tools.
281export const microsoftCustomGraphPayload = (input: {
282 readonly customScopes: readonly string[];
283 readonly slug: string;
284 readonly name: string;
285 readonly description: string;
286 readonly baseUrl: string;
287}): MicrosoftGraphAddPayload => {
288 const baseUrl = input.baseUrl.trim();
289 const description = input.description.trim();
279 return {290 return {
280 presetIds: config.presetIds,291 presetIds: [],
281 ...(config.customScopes !== undefined ? { customScopes: config.customScopes } : {}),292 customScopes: [...input.customScopes],
282 ...(config.slug !== undefined ? { slug: config.slug } : {}),293 slug: input.slug,
283 ...(config.name !== undefined ? { name: config.name } : {}),294 name: input.name,
284 ...(description.length > 0 ? { description } : {}),295 ...(description.length > 0 ? { description } : {}),
285 ...(baseUrl.length > 0 ? { baseUrl } : {}),296 ...(baseUrl.length > 0 ? { baseUrl } : {}),
286 };297 };
@@ -366,9 +377,8 @@ export default function AddMicrosoftSource(props: { · 79 unmodified lines
366 const addCustomGraphScopes = async (): Promise<boolean> => {377 const addCustomGraphScopes = async (): Promise<boolean> => {
367 if (customScopes.length === 0) return true;378 if (customScopes.length === 0) return true;
368 const exit = await doAddGraph({379 const exit = await doAddGraph({
369 payload: microsoftGraphPayload({380 payload: microsoftCustomGraphPayload({
370 presetIds: selectedIds,381 customScopes,
371 customScopes: [...customScopes],
372 slug: resolvedSourceId,382 slug: resolvedSourceId,
373 name: resolvedDisplayName,383 name: resolvedDisplayName,
374 description: resolvedDescription,384 description: resolvedDescription,
packages/plugins/microsoft/src/sdk/graph.ts+7−2
@@ -124,12 +124,17 @@ const uniqueStrings = (values: Iterable<string>): readonly string[] => { · 123 unmodified lines
124};124};
125 125
126const normalizeSelection = (input: MicrosoftGraphSelectionInput) => {126const normalizeSelection = (input: MicrosoftGraphSelectionInput) => {
127 const customScopes = uniqueStrings(input.customScopes ?? []);
128 // Explicitly empty presetIds alongside custom scopes means "custom scopes
129 // only": the add flow fans checked workloads out as their own integrations
130 // through addWorkloads, so folding the default presets back into the custom
131 // bundle would duplicate their tools. An empty selection without custom
132 // scopes still falls back to the default workloads.
127 const presetIds = uniqueStrings(133 const presetIds = uniqueStrings(
128 input.presetIds && input.presetIds.length > 0134 input.presetIds && (input.presetIds.length > 0 || customScopes.length > 0)
129 ? input.presetIds135 ? input.presetIds
130 : MICROSOFT_GRAPH_DEFAULT_PRESET_IDS,136 : MICROSOFT_GRAPH_DEFAULT_PRESET_IDS,
131 );137 );
132 const customScopes = uniqueStrings(input.customScopes ?? []);
133 const scopes = microsoftGraphScopesForPresetIds(presetIds, customScopes);138 const scopes = microsoftGraphScopesForPresetIds(presetIds, customScopes);
134 const exactPaths = microsoftGraphExactPathsForPresetIds(presetIds);139 const exactPaths = microsoftGraphExactPathsForPresetIds(presetIds);
135 const pathPrefixes = microsoftGraphPathPrefixesForPresetIds(presetIds);140 const pathPrefixes = microsoftGraphPathPrefixesForPresetIds(presetIds);
packages/plugins/microsoft/src/sdk/plugin.test.ts+27−0
@@ -407,6 +407,33 @@ describe("Microsoft Graph provider", () => { · 406 unmodified lines
407 ),407 ),
408 );408 );
409 409
410 it.effect("empty presetIds with custom scopes stays custom-only, no default workloads", () =>
411 Effect.scoped(
412 Effect.gen(function* () {
413 const executor = yield* createExecutor(makeTestConfig({ plugins: graphPlugins() }));
414
415 // The add flow fans checked workloads out through addWorkloads and
416 // sends the custom scopes here with presetIds: []. Folding the
417 // defaults back in would duplicate the fanned-out workloads' tools.
418 yield* executor.microsoft.addGraph({
419 slug: "microsoft_graph_custom",
420 presetIds: [],
421 customScopes: ["Custom.Scope"],
422 });
423
424 const config = yield* executor.microsoft.getConfig("microsoft_graph_custom");
425 expect(config?.microsoftGraphPresetIds).toEqual([]);
426 expect(config?.microsoftGraphCustomScopes).toEqual(["Custom.Scope"]);
427 expect(config?.microsoftGraphCoversFullGraph).toBe(false);
428 expect(config?.microsoftGraphScopes).toEqual([
429 "offline_access",
430 "User.Read",
431 "Custom.Scope",
432 ]);
433 }),
434 ),
435 );
436
410 it.effect("uses the app registration default scope when every Graph workload is selected", () =>437 it.effect("uses the app registration default scope when every Graph workload is selected", () =>
411 Effect.scoped(438 Effect.scoped(
412 Effect.gen(function* () {439 Effect.gen(function* () {