Skip to content

Commit

Permalink
Connector dataset dropdown (#2162)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisonking authored Jan 12, 2023
1 parent 303d054 commit f4bdefb
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 102 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ The types of changes are:

## [Unreleased](https://github.com/ethyca/fides/compare/2.4.0...main)

### Added

* Unified Fides Resources: Added a dataset dropdown selector when configuring a connector to link an existing dataset to the connector configuration. [#2162](https://github.com/ethyca/fides/pull/2162)

### Fixed

* Remove next-auth from privacy center to fix JS console error [#2090](https://github.com/ethyca/fides/pull/2090)
Expand Down
65 changes: 61 additions & 4 deletions clients/admin-ui/cypress/e2e/connectors.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ describe("Connectors", () => {
"/api/v1/connection/postgres_connector/datasetconfig",
{ body: {} }
).as("patchDatasetconfig");
cy.intercept("GET", "/api/v1/dataset", { fixture: "datasets.json" }).as(
"getDatasets"
);
});

it("Should show data store connections and view configuration", () => {
Expand All @@ -46,15 +49,51 @@ describe("Connectors", () => {
cy.getByTestId("input-name").should("have.value", "postgres_connector");
});

it("Should allow saving a dataset configuration", () => {
it("Should allow saving a dataset configuration via dropdown", () => {
cy.visit("/datastore-connection/postgres_connector");
cy.getByTestId("tab-Dataset configuration").click();
cy.wait("@getPostgresConnectorDatasetconfig");
// The monaco yaml editor takes a bit to load. Since this is likely going away,
// just wait for now and remove this once the yaml editor is no longer available

// The yaml editor will start off disabled
cy.getByTestId("save-yaml-btn").should("be.disabled");
// The dataset dropdown selector should have the value of the existing connected dataset
cy.getByTestId("save-dataset-link-btn").should("be.enabled");
cy.getByTestId("dataset-selector").should(
"have.value",
"postgres_example_test_dataset"
);

// Change the linked dataset
cy.getByTestId("dataset-selector").select("demo_users_dataset_2");
cy.getByTestId("save-dataset-link-btn").click();

cy.wait("@patchDatasetconfig").then((interception) => {
expect(interception.request.body).to.eql([
{
fides_key: "postgres_example_test_dataset",
ctl_dataset_fides_key: "demo_users_dataset_2",
},
]);
});
});

it("Should allow saving a dataset configuration via yaml", () => {
cy.visit("/datastore-connection/postgres_connector");
cy.getByTestId("tab-Dataset configuration").click();
cy.wait("@getPostgresConnectorDatasetconfig");

// Unset the linked dataset, which should switch the save button enable-ness
cy.getByTestId("dataset-selector").select("Select");
cy.getByTestId("save-dataset-link-btn").should("be.disabled");
// The monaco yaml editor takes a bit to load
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.getByTestId("save-btn").click();
cy.getByTestId("save-yaml-btn").click();

// Click past the confirmation modal
cy.getByTestId("confirmation-modal");
cy.getByTestId("continue-btn").click();

cy.wait("@upsertDataset").then((interception) => {
expect(interception.request.body.length).to.eql(1);
expect(interception.request.body[0].fides_key).to.eql(
Expand All @@ -70,5 +109,23 @@ describe("Connectors", () => {
]);
});
});

it("Should not show the dataset selector if no datasets exist", () => {
cy.intercept("GET", "/api/v1/dataset", { body: [] }).as("getDatasets");
cy.intercept(
"GET",
"/api/v1/connection/postgres_connector/datasetconfig",
{
body: {
items: [],
},
}
).as("getEmptyPostgresConnectorDatasetconfig");

cy.visit("/datastore-connection/postgres_connector");
cy.getByTestId("tab-Dataset configuration").click();
cy.wait("@getEmptyPostgresConnectorDatasetconfig");
cy.getByTestId("dataset-selector-section").should("not.exist");
});
});
});
15 changes: 15 additions & 0 deletions clients/admin-ui/cypress/fixtures/datasets.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,5 +290,20 @@
]
}
]
},
{
"fides_key": "postgres_example_test_dataset",
"organization_fides_key": "default_organization",
"tags": null,
"name": "Postgres Example Test Dataset",
"description": "Example of a Postgres dataset containing a variety of related tables like customers, products, addresses, etc.",
"meta": null,
"data_categories": null,
"data_qualifier": "aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified",
"fides_meta": null,
"joint_controller": null,
"retention": "No retention or erasure policy",
"third_country_transfers": null,
"collections": []
}
]
17 changes: 6 additions & 11 deletions clients/admin-ui/src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import {
connectionTypeApi,
reducer as connectionTypeReducer,
} from "connection-type/index";
import {
datastoreConnectionApi,
reducer as datastoreConnectionReducer,
} from "datastore-connections/index";
import { reducer as datastoreConnectionReducer } from "datastore-connections/index";
import {
privacyRequestApi,
reducer as privacyRequestsReducer,
Expand All @@ -34,6 +31,7 @@ import {
} from "user-management/index";

import { STORAGE_ROOT_KEY } from "~/constants";
import { baseApi } from "~/features/common/api.slice";
import { reducer as configWizardReducer } from "~/features/config-wizard/config-wizard.slice";
import { scannerApi } from "~/features/config-wizard/scanner.slice";
import {
Expand All @@ -48,7 +46,7 @@ import {
dataUseApi,
reducer as dataUseReducer,
} from "~/features/data-use/data-use.slice";
import { datasetApi, reducer as datasetReducer } from "~/features/dataset";
import { reducer as datasetReducer } from "~/features/dataset";
import {
organizationApi,
reducer as organizationReducer,
Expand Down Expand Up @@ -83,12 +81,11 @@ const storage =

const reducer = {
[authApi.reducerPath]: authApi.reducer,
[baseApi.reducerPath]: baseApi.reducer,
[connectionTypeApi.reducerPath]: connectionTypeApi.reducer,
[dataQualifierApi.reducerPath]: dataQualifierApi.reducer,
[dataSubjectsApi.reducerPath]: dataSubjectsApi.reducer,
[dataUseApi.reducerPath]: dataUseApi.reducer,
[datasetApi.reducerPath]: datasetApi.reducer,
[datastoreConnectionApi.reducerPath]: datastoreConnectionApi.reducer,
[organizationApi.reducerPath]: organizationApi.reducer,
[plusApi.reducerPath]: plusApi.reducer,
[privacyRequestApi.reducerPath]: privacyRequestApi.reducer,
Expand Down Expand Up @@ -135,12 +132,11 @@ const persistConfig = {
*/
blacklist: [
authApi.reducerPath,
baseApi.reducerPath,
connectionTypeApi.reducerPath,
dataQualifierApi.reducerPath,
dataSubjectsApi.reducerPath,
dataUseApi.reducerPath,
datasetApi.reducerPath,
datastoreConnectionApi.reducerPath,
organizationApi.reducerPath,
plusApi.reducerPath,
privacyRequestApi.reducerPath,
Expand All @@ -163,12 +159,11 @@ export const makeStore = (preloadedState?: Partial<RootState>) =>
},
}).concat(
authApi.middleware,
baseApi.middleware,
connectionTypeApi.middleware,
dataQualifierApi.middleware,
dataSubjectsApi.middleware,
dataUseApi.middleware,
datasetApi.middleware,
datastoreConnectionApi.middleware,
organizationApi.middleware,
plusApi.middleware,
privacyRequestApi.middleware,
Expand Down
22 changes: 22 additions & 0 deletions clients/admin-ui/src/features/common/api.slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

import type { RootState } from "~/app/store";
import { selectToken } from "~/features/auth";

import { addCommonHeaders } from "./CommonHeaders";

// Uses the code splitting pattern. New endpoints should be injected into this base API
// which itself has an empty endpoint object.
export const baseApi = createApi({
reducerPath: "baseApi",
baseQuery: fetchBaseQuery({
baseUrl: process.env.NEXT_PUBLIC_FIDESCTL_API,
prepareHeaders: (headers, { getState }) => {
const token: string | null = selectToken(getState() as RootState);
addCommonHeaders(headers, token);
return headers;
},
}),
tagTypes: ["DatastoreConnection", "Dataset", "Datasets"],
endpoints: () => ({}),
});
9 changes: 2 additions & 7 deletions clients/admin-ui/src/features/dataset/dataset.slice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

import type { RootState } from "~/app/store";
import { baseApi } from "~/features/common/api.slice";
import {
BulkPutDataset,
Dataset,
Expand All @@ -27,12 +27,7 @@ interface DatasetDeleteResponse {
resource: Dataset;
}

export const datasetApi = createApi({
reducerPath: "datasetApi",
baseQuery: fetchBaseQuery({
baseUrl: process.env.NEXT_PUBLIC_FIDESCTL_API,
}),
tagTypes: ["Dataset", "Datasets"],
const datasetApi = baseApi.injectEndpoints({
endpoints: (build) => ({
getAllDatasets: build.query<Dataset[], void>({
query: () => ({ url: `dataset/` }),
Expand Down
Loading

0 comments on commit f4bdefb

Please sign in to comment.