Skip to content

Commit

Permalink
feat(Pipelines): Add connection picker (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
cheikhgwane authored Nov 18, 2023
1 parent 2d27f76 commit 98dd463
Show file tree
Hide file tree
Showing 13 changed files with 425 additions and 5 deletions.
4 changes: 4 additions & 0 deletions public/images/cog.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
"Select a workspace": "",
"Select all": "",
"Select columns": "",
"Select connection": "",
"Select datasources": "",
"Select files": "",
"Select none": "",
Expand Down
1 change: 1 addition & 0 deletions public/locales/fr/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
"Select a workspace": "",
"Select all": "",
"Select columns": "",
"Select connection": "",
"Select datasources": "",
"Select files": "",
"Select none": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createGetServerSideProps } from "core/helpers/page";
import { formatDuration } from "core/helpers/time";
import { NextPageWithLayout } from "core/helpers/types";
import {
ConnectionType,
PipelineParameter,
PipelineRunStatus,
PipelineRunTrigger,
Expand All @@ -30,7 +31,10 @@ import {
WorkspacePipelineRunPageQuery,
WorkspacePipelineRunPageQueryVariables,
} from "workspaces/graphql/queries.generated";
import { getPipelineRunConfig } from "workspaces/helpers/pipelines";
import {
getPipelineRunConfig,
isConnectionParameter,
} from "workspaces/helpers/pipelines";
import WorkspaceLayout from "workspaces/layouts/WorkspaceLayout";
import Button from "core/components/Button";
import { ArrowPathIcon } from "@heroicons/react/24/outline";
Expand Down Expand Up @@ -95,6 +99,9 @@ const WorkspacePipelineRunPage: NextPageWithLayout = (props: Props) => {
) {
return entry.multiple ? entry.value.join(", ") : entry.value;
}
if (isConnectionParameter(entry.type) && entry.value) {
return entry.value;
}

return "-";
};
Expand Down
19 changes: 17 additions & 2 deletions src/workspaces/features/RunPipelineDialog/ParameterField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import Switch from "core/components/Switch/Switch";
import Input from "core/components/forms/Input/Input";
import Select from "core/components/forms/Select";
import Textarea from "core/components/forms/Textarea/Textarea";
import { ensureArray } from "core/helpers/array";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import WorkspaceConnectionPicker from "../WorkspaceConnectionPicker/WorkspaceConnectionPicker";
import { ConnectionType } from "graphql-types";
import { isConnectionParameter } from "workspaces/helpers/pipelines";

type ParameterFieldProps = {
parameter: any;
value: any;
onChange(value: any): void;
workspaceSlug?: string;
};

const ParameterField = (props: ParameterFieldProps) => {
const { t } = useTranslation();
const { parameter, value, onChange } = props;
const { parameter, value, onChange, workspaceSlug } = props;

const handleChange = useCallback(
(value: any) => {
Expand All @@ -39,6 +42,18 @@ const ParameterField = (props: ParameterFieldProps) => {
/>
);
}

if (isConnectionParameter(parameter.type)) {
return (
<WorkspaceConnectionPicker
workspaceSlug={workspaceSlug || ""}
value={value ?? []}
onChange={(option) => handleChange(option)}
withPortal
type={parameter.type}
/>
);
}
if (parameter.choices?.length) {
const choices =
parameter.type !== "str"
Expand Down
11 changes: 10 additions & 1 deletion src/workspaces/features/RunPipelineDialog/RunPipelineDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import Dialog from "core/components/Dialog";
import Field from "core/components/forms/Field";
import useCacheKey from "core/hooks/useCacheKey";
import useForm from "core/hooks/useForm";
import { PipelineVersion } from "graphql-types";
import { ConnectionType, PipelineVersion } from "graphql-types";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
convertParametersToPipelineInput,
isConnectionParameter,
runPipeline,
} from "workspaces/helpers/pipelines";
import PipelineVersionPicker from "../PipelineVersionPicker";
Expand Down Expand Up @@ -122,6 +123,13 @@ const RunPipelineDialog = (props: RunPipelineDialogProps) => {
) {
errors[parameter.code] = t("This field is required");
}
if (
isConnectionParameter(parameter.type) &&
parameter.required &&
!val
) {
errors[parameter.code] = t("This field is required");
}
}
return errors;
},
Expand Down Expand Up @@ -260,6 +268,7 @@ const RunPipelineDialog = (props: RunPipelineDialogProps) => {
onChange={(value: any) => {
form.setFieldValue(param.code, value);
}}
workspaceSlug={pipeline.workspace?.slug}
/>
</Field>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as Types from '../../../graphql-types';

import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type WorkspaceConnectionPickerQueryVariables = Types.Exact<{
slug: Types.Scalars['String']['input'];
}>;


export type WorkspaceConnectionPickerQuery = { __typename?: 'Query', workspace?: { __typename?: 'Workspace', slug: string, connections: Array<{ __typename?: 'Connection', id: string, name: string, slug: string, type: Types.ConnectionType }> } | null };

export type WorkspaceConnectionPicker_WorkspaceFragment = { __typename?: 'Workspace', slug: string, connections: Array<{ __typename?: 'Connection', id: string, name: string, slug: string, type: Types.ConnectionType }> };

export const WorkspaceConnectionPicker_WorkspaceFragmentDoc = gql`
fragment WorkspaceConnectionPicker_workspace on Workspace {
slug
connections {
id
name
slug
type
}
}
`;
export const WorkspaceConnectionPickerDocument = gql`
query WorkspaceConnectionPicker($slug: String!) {
workspace(slug: $slug) {
slug
...WorkspaceConnectionPicker_workspace
}
}
${WorkspaceConnectionPicker_WorkspaceFragmentDoc}`;

/**
* __useWorkspaceConnectionPickerQuery__
*
* To run a query within a React component, call `useWorkspaceConnectionPickerQuery` and pass it any options that fit your needs.
* When your component renders, `useWorkspaceConnectionPickerQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useWorkspaceConnectionPickerQuery({
* variables: {
* slug: // value for 'slug'
* },
* });
*/
export function useWorkspaceConnectionPickerQuery(baseOptions: Apollo.QueryHookOptions<WorkspaceConnectionPickerQuery, WorkspaceConnectionPickerQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<WorkspaceConnectionPickerQuery, WorkspaceConnectionPickerQueryVariables>(WorkspaceConnectionPickerDocument, options);
}
export function useWorkspaceConnectionPickerLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<WorkspaceConnectionPickerQuery, WorkspaceConnectionPickerQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<WorkspaceConnectionPickerQuery, WorkspaceConnectionPickerQueryVariables>(WorkspaceConnectionPickerDocument, options);
}
export type WorkspaceConnectionPickerQueryHookResult = ReturnType<typeof useWorkspaceConnectionPickerQuery>;
export type WorkspaceConnectionPickerLazyQueryHookResult = ReturnType<typeof useWorkspaceConnectionPickerLazyQuery>;
export type WorkspaceConnectionPickerQueryResult = Apollo.QueryResult<WorkspaceConnectionPickerQuery, WorkspaceConnectionPickerQueryVariables>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import WorkspaceConnectionPicker from "./WorkspaceConnectionPicker";
import { queryByRole, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MockedProvider } from "@apollo/client/testing";
import { faker } from "@faker-js/faker";
import { useQuery } from "@apollo/client";
import { ConnectionType } from "graphql-types";

jest.mock("@apollo/client", () => ({
__esModule: true,
useQuery: jest.fn(),
gql: jest.fn(() => "GQL"),
}));

const useQueryMock = useQuery as jest.Mock;

const WORKSPACE = {
slug: faker.datatype.uuid(),
connections: [
{
id: faker.datatype.uuid(),
name: "dhis2-dev",
slug: "dhis2-dev",
type: ConnectionType.Dhis2,
},
{
id: faker.datatype.uuid(),
name: "dhis2-staging",
slug: "dhis2-staging",
type: ConnectionType.Dhis2,
},
{
id: faker.datatype.uuid(),
name: "iaso-dev",
slug: "iaso-dev",
type: ConnectionType.Iaso,
},
],
};

describe("WorkspaceConnectionPicker", () => {
it("display all connections", async () => {
const user = userEvent.setup();

useQueryMock.mockReturnValue({
loading: true,
data: {
workspace: WORKSPACE,
},
});
const onChange = jest.fn();

const { container } = render(
<WorkspaceConnectionPicker
workspaceSlug={WORKSPACE.slug}
onChange={onChange}
value={[]}
/>,
);

await user.click(await screen.findByTestId("combobox-button"));
const option = await screen.queryAllByRole("option");
expect(option.length).toBe(WORKSPACE.connections.length);

await user.click(await screen.findByText(WORKSPACE.connections[0].name));
expect(onChange).toHaveBeenCalledWith(WORKSPACE.connections[0]);
expect(container).toMatchSnapshot();
});

it("display only connections with a given type", async () => {
const user = userEvent.setup();

useQueryMock.mockReturnValue({
loading: true,
data: {
workspace: WORKSPACE,
},
});
const onChange = jest.fn();

render(
<WorkspaceConnectionPicker
workspaceSlug={WORKSPACE.slug}
onChange={onChange}
value={[]}
type={ConnectionType.Dhis2}
/>,
);

await user.click(await screen.findByTestId("combobox-button"));
const option = await screen.queryAllByRole("option");
const dhis2Connections = WORKSPACE.connections.filter(
(c) => c.type === ConnectionType.Dhis2,
);

expect(option.length).toBe(dhis2Connections.length);
});
});
Loading

0 comments on commit 98dd463

Please sign in to comment.