Integrations can be enabled just for admins, or turned off for everyone

This commit is contained in:
Matt Aitken
2023-01-06 12:05:02 +00:00
parent 7e0b79ff74
commit dfa3435037
9 changed files with 61 additions and 18 deletions
@@ -1,7 +1,7 @@
import * as fs from "node:fs";
import { getCatalog } from "internal-catalog";
export function getIntegrations() {
export function getIntegrations(showAdminOnly: boolean) {
const file = fs.readFileSync("./integrations.yml", "utf8");
return getCatalog(file);
return getCatalog(file, showAdminOnly);
}
@@ -30,7 +30,7 @@ export class WorkflowRunPresenter {
throw new Error(`Workflow run with id ${id} not found`);
}
const integrations = getIntegrations();
const integrations = getIntegrations(true);
const steps = await Promise.all(
workflowRun.tasks.map((step) => parseStep(step, integrations))
);
@@ -20,13 +20,13 @@ import { useCurrentOrganization } from "~/hooks/useOrganizations";
import type { OrgWorkflow } from "~/hooks/useWorkflows";
import { useWorkflows } from "~/hooks/useWorkflows";
import { getIntegrations } from "~/models/integrations.server";
import { requireUserId } from "~/services/session.server";
import { requireUser } from "~/services/session.server";
import { formatDateTime } from "~/utils";
import { getIntegration } from "~/utils/integrations";
export const loader = async ({ request, params }: LoaderArgs) => {
await requireUserId(request);
return typedjson({ integrations: getIntegrations() });
const user = await requireUser(request);
return typedjson({ integrations: getIntegrations(user.admin) });
};
export default function Page() {
@@ -18,12 +18,13 @@ import {
import { useCurrentOrganization } from "~/hooks/useOrganizations";
import { getConnectedApiConnectionsForOrganizationSlug } from "~/models/apiConnection.server";
import { getIntegrations } from "~/models/integrations.server";
import { requireUserId } from "~/services/session.server";
import { requireUser } from "~/services/session.server";
import { formatDateTime } from "~/utils";
import { getIntegration } from "~/utils/integrations";
export const loader = async ({ request, params }: LoaderArgs) => {
await requireUserId(request);
const user = await requireUser(request);
const { organizationSlug } = params;
invariant(organizationSlug, "organizationSlug not found");
@@ -31,7 +32,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
slug: organizationSlug,
});
return typedjson({ connections, integrations: getIntegrations() });
return typedjson({ connections, integrations: getIntegrations(user.admin) });
};
export default function Integrations() {
@@ -12,16 +12,16 @@ import { getConnectedApiConnectionsForOrganizationSlug } from "~/models/apiConne
import { getIntegrations } from "~/models/integrations.server";
import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server";
import { getWorkflowFromSlugs } from "~/models/workflow.server";
import { requireUserId } from "~/services/session.server";
import { requireUser } from "~/services/session.server";
export const loader = async ({ request, params }: LoaderArgs) => {
const userId = await requireUserId(request);
const user = await requireUser(request);
const { organizationSlug, workflowSlug } = params;
invariant(organizationSlug, "organizationSlug not found");
invariant(workflowSlug, "workflowSlug not found");
let workflow = await getWorkflowFromSlugs({
userId,
userId: user.id,
organizationSlug,
workflowSlug,
});
@@ -30,6 +30,8 @@ export const loader = async ({ request, params }: LoaderArgs) => {
throw new Response("Not Found", { status: 404 });
}
const integrations = getIntegrations(user.admin);
const rules = workflow.rules.map((r) => ({
...r,
trigger: TriggerMetadataSchema.parse(r.trigger),
@@ -43,7 +45,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
slug: organizationSlug,
});
const externalSourceIntegration = getIntegrations().find(
const externalSourceIntegration = integrations.find(
(i) => i.slug === workflow?.externalSource?.service
);
const externalSourceSlot =
@@ -60,7 +62,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
const connectionSlots = {
source: externalSourceSlot,
services: workflow.externalServices.flatMap((c) => {
const integration = getIntegrations().find((i) => i.slug === c.service);
const integration = integrations.find((i) => i.slug === c.service);
if (!integration) {
return [];
@@ -71,7 +71,7 @@ export const action = async ({ request }: ActionArgs) => {
}
const parsed = parsedResult.data;
const integrationInfo = getIntegrations().find(
const integrationInfo = getIntegrations(true).find(
(i) => i.slug === parsed.service
);
if (!integrationInfo) {
+20
View File
@@ -42,9 +42,29 @@
client_id: "09bd8b46-6b80-4343-9ffd-5750a8c3e2f1"
production:
client_id: abcdefg
- name: Shopify
slug: shopify
icon: /integrations/shopify.png
enabledFor: admins
authentication:
type: oauth
scopes:
- data.records:read
- data.records:write
- data.recordComments:read
- data.recordComments:write
- schema.bases:read
- schema.bases:write
- webook:manage
environments:
development:
client_id: "09bd8b46-6b80-4343-9ffd-5750a8c3e2f1"
production:
client_id: abcdefg
- name: Mailgun
slug: mailgun
icon: /integrations/mailgun.png
enabledFor: admins
authentication:
type: api_key
header_name: "Authorization"
+22 -2
View File
@@ -2,10 +2,19 @@ import { z } from "zod";
import { parseDocument } from "yaml";
import invariant from "tiny-invariant";
const enabledForSchema = z.union([
z.literal("all"),
z.literal("admins"),
z.literal("none"),
]);
type EnabledFor = z.infer<typeof enabledForSchema>;
const integrationMetadataSchema = z.object({
name: z.string(),
slug: z.string(),
icon: z.string(),
enabledFor: enabledForSchema.default("all"),
});
const oAuthIntegrationAuthenticationSchema = z.object({
@@ -37,9 +46,20 @@ const schema = z.array(integrationSchema);
type Catalog = z.infer<typeof schema>;
export type CatalogIntegration = Catalog[number];
export function getCatalog(raw: string) {
export function getCatalog(raw: string, isAdmin: boolean) {
const doc = parseDocument(raw);
invariant(doc, `Catalog doc is not defined: ${raw}`);
const jsObject = doc.toJS();
return schema.parse(jsObject);
const catalog = schema.parse(jsObject);
return catalog.filter((i) => {
switch (i.enabledFor) {
case "all":
return true;
case "admins":
return isAdmin;
case "none":
return false;
}
});
}
+1 -1
View File
@@ -44,7 +44,7 @@ program
const pizzly_host = options.pizzlyhost ?? "http://localhost:3004";
const file = fs.readFileSync(integration_file_path, "utf8");
const catalog = getCatalog(file);
const catalog = getCatalog(file, true);
const client = new SecretsManagerClient({
region: "us-east-1",