Checkpoint to get cypress working - auth-type from backend, less hard-coded auth0, improvements on query screen (less redundant fetches)

This commit is contained in:
2022-11-15 16:42:54 -06:00
parent c5780df58e
commit 94002f0d16
21 changed files with 2236939 additions and 344 deletions

1
cypress/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
videos

View File

@ -0,0 +1,105 @@
/// <reference types="cypress-wait-for-stable-dom" />
describe("table query screen", () =>
{
before(() =>
{
cy.intercept("GET", "/metaData/authentication", {fixture: "metaData/authentication.json"}).as("authenticationMetaData");
cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData");
cy.intercept("GET", "/metaData/table/person", {fixture: "metaData/table/person.json"}).as("personMetaData");
cy.intercept("POST", "/data/person/query?*", {fixture: "data/person/index.json"}).as("personQuery");
cy.intercept("POST", "/data/person/count", {fixture: "data/person/count.json"}).as("personCount");
cy.intercept("POST", "/data/city/count", {fixture: "data/city/count.json"}).as("cityCount");
cy.intercept("GET", "/metaData/process/person.bulkEdit", {fixture: "metaData/process/person.bulkEdit.json"}).as("personBulkEditMetaData");
cy.intercept("POST", "/processes/person.bulkEdit/init?recordsParam=recordIds&recordIds=1,2,3,4,5", {fixture: "processes/person.bulkEdit/init.json"}).as("personBulkEditInit");
cy.intercept("POST", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/step/edit", {fixture: "processes/person.bulkEdit/step/edit.json"}).as("personBulkEditStepEdit");
cy.intercept("GET", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/records?skip=0&limit=10", {fixture: "processes/person.bulkEdit/records.json"}).as("personBulkEditRecords");
cy.intercept("GET", "/widget/* ", {fixture: "widget/empty.json"}).as("emptyWidget");
});
it("can be loaded from app screen", () =>
{
cy.visit("https://localhost:3000/peopleApp/greetingsApp/");
cy.contains("Person").click();
cy.location().should((loc) =>
{
expect(loc.pathname).to.eq("/peopleApp/greetingsApp/person");
});
});
it.only("can add query filters", () =>
{
/////////////////////////////////////////////////////////////
// go to table, wait for filter to run, and rows to appear //
/////////////////////////////////////////////////////////////
cy.visit("https://localhost:3000/peopleApp/greetingsApp/person");
cy.wait(["@personQuery", "@personCount"]);
cy.get(".MuiDataGrid-virtualScrollerRenderZone").children().should("have.length.greaterThan", 3);
/////////////////////////////////////////////////////////////////////
// open the filter window, enter a value, wait for query to re-run //
/////////////////////////////////////////////////////////////////////
cy.contains("Filters").click();
cy.get(".MuiDataGrid-filterForm input.MuiInput-input").should("be.focused").type("1");
cy.wait(["@personQuery", "@personCount"]);
///////////////////////////////////////
// click away from the filter window //
///////////////////////////////////////
cy.get("#root").click("topLeft", {force: true});
cy.contains(".MuiBadge-root", "1").should("be.visible");
///////////////////////////////////////////////////////////////////
// click the 'x' clear icon, then yes, then expect another query //
///////////////////////////////////////////////////////////////////
cy.waitForStableDOM();
cy.get("#clearFiltersButton").should("be.visible").click();
cy.contains("button", "Yes").click();
cy.wait(["@personQuery", "@personCount"]);
cy.contains(".MuiDataGrid-toolbarContainer .MuiBadge-root", "1").should("not.exist");
});
xit("todo delete", () =>
{
// cy.get(".MuiDataGrid-columnHeaders input[type='checkbox']").click();
// cy.contains("button", "Bulk Actions").click();
// cy.contains("li", "Bulk Edit").click();
// ////////////////////////////
// // bulk edit process init //
// ////////////////////////////
// cy.location().should((loc) =>
// {
// expect(loc.pathname).to.eq("/processes/person.bulkEdit");
// expect(loc.search).to.eq("?recordsParam=recordIds&recordIds=1,2,3,4,5");
// });
// cy.wait(["@personBulkEditMetaData"]);
// cy.wait(["@personBulkEditInit"]);
// cy.contains("p[variation='h5']", "Edit Values");
// cy.get("#bulkEditSwitch-firstName").click();
// cy.get("input[name='firstName']").click()
// .type("Kahhhhn");
// cy.contains("button", "next").click();
// ///////////////////////////
// // bulk edit review step //
// ///////////////////////////
// cy.contains("p[variation='h5']", "Review");
// cy.contains(".MuiDataGrid-cellContent", "Kahhhhn");
// cy.contains("button", "submit").click();
// cy.wait(["@personBulkEditStepEdit"]);
// cy.wait(["@personBulkEditRecords"]);
// ////////////////////////////
// // bulk edit result step //
// ////////////////////////////
// cy.contains("p[variation='h5']", "Results");
// cy.wait(["@personBulkEditRecords"]);
});
});

View File

@ -1,72 +0,0 @@
// noinspection ES6UnusedImports
import * as cypress from "cypress";
describe("empty spec", () =>
{
it("passes", () =>
{
cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData");
cy.intercept("GET", "/data/person?*", {fixture: "data/person/index.json"}).as("personQuery");
cy.intercept("GET", "/data/person/count?*", {fixture: "data/person/count.json"}).as("personCount")
cy.intercept("GET", "/metaData/process/person.bulkEdit", {fixture: "metaData/process/person.bulkEdit.json"}).as("personBulkEditMetaData")
cy.intercept("POST", "/processes/person.bulkEdit/init?recordsParam=recordIds&recordIds=1,2,3,4,5", {fixture: "processes/person.bulkEdit/init.json"}).as("personBulkEditInit")
cy.intercept("POST", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/step/edit", {fixture: "processes/person.bulkEdit/step/edit.json"}).as("personBulkEditStepEdit")
cy.intercept("GET", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/records?skip=0&limit=10", {fixture: "processes/person.bulkEdit/records.json"}).as("personBulkEditRecords")
/////////////////
// home screen //
/////////////////
cy.visit("http://localhost:3000/");
cy.wait(["@metaData"])
cy.contains(".MuiListItem-root", "Tables").click();
cy.contains(".MuiListItem-root", "Person").click();
/////////////////////////
// person query screen //
/////////////////////////
cy.location().should((loc) =>
{
expect(loc.pathname).to.eq("/person")
});
cy.wait(["@personQuery", "@personCount"])
cy.get(".MuiDataGrid-columnHeaders input[type='checkbox']").click();
cy.contains("button", "Bulk Actions").click();
cy.contains("li", "Bulk Edit").click();
////////////////////////////
// bulk edit process init //
////////////////////////////
cy.location().should((loc) =>
{
expect(loc.pathname).to.eq("/processes/person.bulkEdit");
expect(loc.search).to.eq("?recordsParam=recordIds&recordIds=1,2,3,4,5");
});
cy.wait(["@personBulkEditMetaData"])
cy.wait(["@personBulkEditInit"])
cy.contains("p[variation='h5']", "Edit Values");
cy.get("#bulkEditSwitch-firstName").click();
cy.get("input[name='firstName']").click()
.type("Kahhhhn");
cy.contains("button", "next").click();
///////////////////////////
// bulk edit review step //
///////////////////////////
cy.contains("p[variation='h5']", "Review");
cy.contains(".MuiDataGrid-cellContent", "Kahhhhn");
cy.contains("button", "submit").click();
cy.wait(["@personBulkEditStepEdit"])
cy.wait(["@personBulkEditRecords"])
////////////////////////////
// bulk edit result step //
////////////////////////////
cy.contains("p[variation='h5']", "Results");
cy.wait(["@personBulkEditRecords"])
});
});

View File

@ -0,0 +1,3 @@
{
"count": 101406
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
{
"count": 5
"count": 101406
}

View File

@ -0,0 +1,4 @@
{
"name": "mock",
"type": "MOCK"
}

View File

@ -1,102 +1,550 @@
{
"tables": {
"carrier": {
"name": "carrier",
"label": "Carrier",
"isHidden": false
},
"city": {
"name": "city",
"label": "Cities",
"isHidden": true
},
"person": {
"name": "person",
"label": "Person",
"isHidden": false
}
},
"processes": {
"person.bulkInsert": {
"name": "person.bulkInsert",
"label": "Person Bulk Insert",
"tableName": "person",
"isHidden": true
},
"carrier.bulkInsert": {
"name": "carrier.bulkInsert",
"label": "Carrier Bulk Insert",
"tableName": "carrier",
"isHidden": true
},
"simpleSleep": {
"name": "simpleSleep",
"label": "Simple Sleep",
"isHidden": true
},
"carrier.bulkDelete": {
"name": "carrier.bulkDelete",
"label": "Carrier Bulk Delete",
"tableName": "carrier",
"isHidden": true
},
"greet": {
"name": "greet",
"label": "Greet People",
"tableName": "person",
"isHidden": true
},
"city.bulkDelete": {
"name": "city.bulkDelete",
"label": "Cities Bulk Delete",
"tableName": "city",
"isHidden": true
},
"person.bulkDelete": {
"name": "person.bulkDelete",
"label": "Person Bulk Delete",
"tableName": "person",
"isHidden": true
},
"carrier.bulkEdit": {
"name": "carrier.bulkEdit",
"label": "Carrier Bulk Edit",
"tableName": "carrier",
"isHidden": true
},
"person.bulkEdit": {
"name": "person.bulkEdit",
"label": "Person Bulk Edit",
"tableName": "person",
"isHidden": true
},
"greetInteractive": {
"name": "greetInteractive",
"label": "Greet Interactive",
"tableName": "person",
"isHidden": false
},
"city.bulkEdit": {
"name": "city.bulkEdit",
"label": "Cities Bulk Edit",
"tableName": "city",
"isHidden": true
},
"simpleThrow": {
"name": "simpleThrow",
"label": "Simple Throw",
"isHidden": false
},
"sleepInteractive": {
"name": "sleepInteractive",
"label": "Sleep Interactive",
"isHidden": false
},
"city.bulkInsert": {
"name": "city.bulkInsert",
"label": "Cities Bulk Insert",
"tableName": "city",
"isHidden": true
}
}
"tables": {
"carrier": {
"name": "carrier",
"label": "Carrier",
"isHidden": false,
"iconName": "local_shipping",
"capabilities": [
"TABLE_COUNT",
"TABLE_GET",
"TABLE_QUERY",
"TABLE_UPDATE",
"TABLE_INSERT",
"TABLE_DELETE"
]
},
"person": {
"name": "person",
"label": "Person",
"isHidden": false,
"iconName": "person",
"capabilities": [
"TABLE_COUNT",
"TABLE_GET",
"TABLE_QUERY",
"TABLE_UPDATE",
"TABLE_INSERT",
"TABLE_DELETE"
]
},
"city": {
"name": "city",
"label": "Cities",
"isHidden": true,
"iconName": "location_city",
"capabilities": [
"TABLE_COUNT",
"TABLE_GET",
"TABLE_QUERY",
"TABLE_UPDATE",
"TABLE_INSERT",
"TABLE_DELETE"
]
}
},
"processes": {
"greet": {
"name": "greet",
"label": "Greet People",
"tableName": "person",
"isHidden": true,
"iconName": "emoji_people"
},
"greetInteractive": {
"name": "greetInteractive",
"label": "Greet Interactive",
"tableName": "person",
"isHidden": false,
"iconName": "waving_hand"
},
"clonePeople": {
"name": "clonePeople",
"label": "Clone People",
"tableName": "person",
"isHidden": false,
"iconName": "content_copy"
},
"simpleSleep": {
"name": "simpleSleep",
"label": "Simple Sleep",
"isHidden": true
},
"sleepInteractive": {
"name": "sleepInteractive",
"label": "Sleep Interactive",
"isHidden": false
},
"simpleThrow": {
"name": "simpleThrow",
"label": "Simple Throw",
"isHidden": false
},
"carrier.bulkInsert": {
"name": "carrier.bulkInsert",
"label": "Carrier Bulk Insert",
"tableName": "carrier",
"isHidden": true
},
"carrier.bulkEdit": {
"name": "carrier.bulkEdit",
"label": "Carrier Bulk Edit",
"tableName": "carrier",
"isHidden": true
},
"carrier.bulkDelete": {
"name": "carrier.bulkDelete",
"label": "Carrier Bulk Delete",
"tableName": "carrier",
"isHidden": true
},
"person.bulkInsert": {
"name": "person.bulkInsert",
"label": "Person Bulk Insert",
"tableName": "person",
"isHidden": true
},
"person.bulkEdit": {
"name": "person.bulkEdit",
"label": "Person Bulk Edit",
"tableName": "person",
"isHidden": true
},
"person.bulkDelete": {
"name": "person.bulkDelete",
"label": "Person Bulk Delete",
"tableName": "person",
"isHidden": true
},
"city.bulkInsert": {
"name": "city.bulkInsert",
"label": "Cities Bulk Insert",
"tableName": "city",
"isHidden": true
},
"city.bulkEdit": {
"name": "city.bulkEdit",
"label": "Cities Bulk Edit",
"tableName": "city",
"isHidden": true
},
"city.bulkDelete": {
"name": "city.bulkDelete",
"label": "Cities Bulk Delete",
"tableName": "city",
"isHidden": true
}
},
"apps": {
"greetingsApp": {
"name": "greetingsApp",
"label": "Greetings App",
"iconName": "emoji_people",
"widgets": [
"PersonsByCreateDateBarChart",
"QuickSightChartRenderer"
],
"children": [
{
"type": "PROCESS",
"name": "greet",
"label": "Greet People",
"iconName": "emoji_people"
},
{
"type": "TABLE",
"name": "person",
"label": "Person",
"iconName": "person"
},
{
"type": "TABLE",
"name": "city",
"label": "Cities",
"iconName": "location_city"
},
{
"type": "PROCESS",
"name": "greetInteractive",
"label": "Greet Interactive",
"iconName": "waving_hand"
}
],
"childMap": {
"greetInteractive": {
"type": "PROCESS",
"name": "greetInteractive",
"label": "Greet Interactive",
"iconName": "waving_hand"
},
"city": {
"type": "TABLE",
"name": "city",
"label": "Cities",
"iconName": "location_city"
},
"person": {
"type": "TABLE",
"name": "person",
"label": "Person",
"iconName": "person"
},
"greet": {
"type": "PROCESS",
"name": "greet",
"label": "Greet People",
"iconName": "emoji_people"
}
},
"sections": [
{
"name": "greetingsApp",
"label": "Greetings App",
"icon": {
"name": "badge"
},
"tables": [
"person",
"city"
],
"processes": [
"greet",
"greetInteractive"
]
}
]
},
"peopleApp": {
"name": "peopleApp",
"label": "People App",
"iconName": "person",
"widgets": [],
"children": [
{
"type": "APP",
"name": "greetingsApp",
"label": "Greetings App",
"iconName": "emoji_people"
},
{
"type": "PROCESS",
"name": "clonePeople",
"label": "Clone People",
"iconName": "content_copy"
}
],
"childMap": {
"greetingsApp": {
"type": "APP",
"name": "greetingsApp",
"label": "Greetings App",
"iconName": "emoji_people"
},
"clonePeople": {
"type": "PROCESS",
"name": "clonePeople",
"label": "Clone People",
"iconName": "content_copy"
}
},
"sections": [
{
"name": "peopleApp",
"label": "People App",
"icon": {
"name": "badge"
},
"processes": [
"clonePeople"
]
}
]
},
"miscellaneous": {
"name": "miscellaneous",
"label": "Miscellaneous",
"iconName": "stars",
"widgets": [],
"children": [
{
"type": "TABLE",
"name": "carrier",
"label": "Carrier",
"iconName": "local_shipping"
},
{
"type": "PROCESS",
"name": "simpleSleep",
"label": "Simple Sleep"
},
{
"type": "PROCESS",
"name": "sleepInteractive",
"label": "Sleep Interactive"
},
{
"type": "PROCESS",
"name": "simpleThrow",
"label": "Simple Throw"
}
],
"childMap": {
"carrier": {
"type": "TABLE",
"name": "carrier",
"label": "Carrier",
"iconName": "local_shipping"
},
"simpleSleep": {
"type": "PROCESS",
"name": "simpleSleep",
"label": "Simple Sleep"
},
"simpleThrow": {
"type": "PROCESS",
"name": "simpleThrow",
"label": "Simple Throw"
},
"sleepInteractive": {
"type": "PROCESS",
"name": "sleepInteractive",
"label": "Sleep Interactive"
}
},
"sections": [
{
"name": "miscellaneous",
"label": "Miscellaneous",
"icon": {
"name": "badge"
},
"tables": [
"carrier"
],
"processes": [
"simpleSleep",
"sleepInteractive",
"simpleThrow"
]
}
]
}
},
"appTree": [
{
"type": "APP",
"name": "peopleApp",
"label": "People App",
"children": [
{
"type": "APP",
"name": "greetingsApp",
"label": "Greetings App",
"children": [
{
"type": "PROCESS",
"name": "greet",
"label": "Greet People",
"iconName": "emoji_people"
},
{
"type": "TABLE",
"name": "person",
"label": "Person",
"iconName": "person"
},
{
"type": "TABLE",
"name": "city",
"label": "Cities",
"iconName": "location_city"
},
{
"type": "PROCESS",
"name": "greetInteractive",
"label": "Greet Interactive",
"iconName": "waving_hand"
}
],
"iconName": "emoji_people"
},
{
"type": "PROCESS",
"name": "clonePeople",
"label": "Clone People",
"iconName": "content_copy"
}
],
"iconName": "person"
},
{
"type": "APP",
"name": "miscellaneous",
"label": "Miscellaneous",
"children": [
{
"type": "TABLE",
"name": "carrier",
"label": "Carrier",
"iconName": "local_shipping"
},
{
"type": "PROCESS",
"name": "simpleSleep",
"label": "Simple Sleep"
},
{
"type": "PROCESS",
"name": "sleepInteractive",
"label": "Sleep Interactive"
},
{
"type": "PROCESS",
"name": "simpleThrow",
"label": "Simple Throw"
}
],
"iconName": "stars"
}
],
"branding": {
"logo": "/kr-logo.png",
"icon": "/kr-icon.png"
},
"widgets": {
"parcelTrackingDetails": {
"name": "parcelTrackingDetails",
"label": "Tracking Details",
"type": "childRecordList"
},
"deposcoSalesOrderLineItems": {
"name": "deposcoSalesOrderLineItems",
"label": "Line Items",
"type": "childRecordList"
},
"TotalShipmentsByDayBarChart": {
"name": "TotalShipmentsByDayBarChart",
"label": "Total Shipments By Day",
"type": "chart"
},
"TotalShipmentsByMonthLineChart": {
"name": "TotalShipmentsByMonthLineChart",
"label": "Total Shipments By Month",
"type": "chart"
},
"YTDShipmentsByCarrierPieChart": {
"name": "YTDShipmentsByCarrierPieChart",
"label": "Shipments By Carrier Year To Date",
"type": "chart"
},
"TodaysShipmentsStatisticsCard": {
"name": "TodaysShipmentsStatisticsCard",
"label": "Today's Shipments",
"type": "statistics"
},
"ShipmentsInTransitStatisticsCard": {
"name": "ShipmentsInTransitStatisticsCard",
"label": "Shipments In Transit",
"type": "statistics"
},
"OpenOrdersStatisticsCard": {
"name": "OpenOrdersStatisticsCard",
"label": "Open Orders",
"type": "statistics"
},
"ShippingExceptionsStatisticsCard": {
"name": "ShippingExceptionsStatisticsCard",
"label": "Shipping Exceptions",
"type": "statistics"
},
"WarehouseLocationCards": {
"name": "WarehouseLocationCards",
"type": "location"
},
"TotalShipmentsStatisticsCard": {
"name": "TotalShipmentsStatisticsCard",
"label": "Total Shipments",
"type": "statistics"
},
"SuccessfulDeliveriesStatisticsCard": {
"name": "SuccessfulDeliveriesStatisticsCard",
"label": "Successful Deliveries",
"type": "statistics"
},
"ServiceFailuresStatisticsCard": {
"name": "ServiceFailuresStatisticsCard",
"label": "Service Failures",
"type": "statistics"
},
"CarrierVolumeLineChart": {
"name": "CarrierVolumeLineChart",
"label": "Carrier Volume By Month",
"type": "lineChart"
},
"YTDSpendByCarrierTable": {
"name": "YTDSpendByCarrierTable",
"label": "Spend By Carrier Year To Date",
"type": "table"
},
"TimeInTransitBarChart": {
"name": "TimeInTransitBarChart",
"label": "Time In Transit Last 30 Days",
"type": "chart"
},
"OpenBillingWorksheetsTable": {
"name": "OpenBillingWorksheetsTable",
"label": "Open Billing Worksheets",
"type": "table"
},
"AssociatedParcelInvoicesTable": {
"name": "AssociatedParcelInvoicesTable",
"label": "Associated Parcel Invoices",
"type": "table",
"icon": "receipt"
},
"BillingWorksheetLinesTable": {
"name": "BillingWorksheetLinesTable",
"label": "Billing Worksheet Lines",
"type": "table"
},
"RatingIssuesWidget": {
"name": "RatingIssuesWidget",
"label": "Rating Issues",
"type": "html",
"icon": "warning",
"gridColumns": 6
},
"UnassignedParcelInvoicesTable": {
"name": "UnassignedParcelInvoicesTable",
"label": "Unassigned Parcel Invoices",
"type": "table"
},
"ParcelInvoiceSummaryWidget": {
"name": "ParcelInvoiceSummaryWidget",
"label": "Parcel Invoice Summary",
"type": "multiStatistics"
},
"ParcelInvoiceLineExceptionsSummaryWidget": {
"name": "ParcelInvoiceLineExceptionsSummaryWidget",
"label": "Parcel Invoice Line Exceptions",
"type": "multiStatistics"
},
"BillingWorksheetStatusStepper": {
"name": "BillingWorksheetStatusStepper",
"label": "Billing Worksheet Progress",
"type": "stepper",
"icon": "refresh",
"gridColumns": 6
},
"PersonsByCreateDateBarChart": {
"name": "PersonsByCreateDateBarChart",
"label": "Persons By Create Date",
"type": "barChart"
},
"QuickSightChartRenderer": {
"name": "QuickSightChartRenderer",
"label": "Quick Sight",
"type": "quickSightChart"
}
}
}

View File

@ -27,7 +27,8 @@
"backendName": "first_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "lastName",
@ -35,7 +36,8 @@
"backendName": "last_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "birthDate",
@ -43,14 +45,44 @@
"backendName": "birth_date",
"type": "DATE",
"isRequired": false,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "email",
"label": "Email",
"backendName": "email",
"type": "STRING",
"isRequired": false,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "isEmployed",
"label": "Is Employed",
"backendName": "is_employed",
"type": "BOOLEAN",
"isRequired": false,
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "annualSalary",
"label": "Annual Salary",
"backendName": "annual_salary",
"type": "DECIMAL",
"isRequired": false,
"isEditable": true,
"displayFormat": "$%,.2f"
},
{
"name": "daysWorked",
"label": "Days Worked",
"backendName": "days_worked",
"type": "INTEGER",
"isRequired": false,
"isEditable": true,
"displayFormat": "%,d"
}
]
},
@ -60,19 +92,7 @@
"stepType": "frontend",
"components": [
{
"type": "HELP_TEXT",
"values": {
"text": "The records below will be updated if you click Submit."
}
}
],
"viewFields": [
{
"name": "valuesBeingUpdated",
"label": "Values Being Updated",
"type": "STRING",
"isRequired": false,
"isEditable": true
"type": "VALIDATION_REVIEW_SCREEN"
}
],
"recordListFields": [
@ -82,7 +102,8 @@
"backendName": "first_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "lastName",
@ -90,7 +111,8 @@
"backendName": "last_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "birthDate",
@ -98,83 +120,54 @@
"backendName": "birth_date",
"type": "DATE",
"isRequired": false,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "email",
"label": "Email",
"backendName": "email",
"type": "STRING",
"isRequired": false,
"isEditable": true
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "isEmployed",
"label": "Is Employed",
"backendName": "is_employed",
"type": "BOOLEAN",
"isRequired": false,
"isEditable": true,
"displayFormat": "%s"
},
{
"name": "annualSalary",
"label": "Annual Salary",
"backendName": "annual_salary",
"type": "DECIMAL",
"isRequired": false,
"isEditable": true,
"displayFormat": "$%,.2f"
},
{
"name": "daysWorked",
"label": "Days Worked",
"backendName": "days_worked",
"type": "INTEGER",
"isRequired": false,
"isEditable": true,
"displayFormat": "%,d"
}
]
},
{
"name": "results",
"label": "Results",
"name": "result",
"label": "Result",
"stepType": "frontend",
"components": [
{
"type": "HELP_TEXT",
"values": {
"text": "The records below have been updated."
}
}
],
"recordListFields": [
{
"name": "id",
"label": "Id",
"type": "INTEGER",
"isRequired": false,
"isEditable": false
},
{
"name": "createDate",
"label": "Create Date",
"backendName": "create_date",
"type": "DATE_TIME",
"isRequired": false,
"isEditable": false
},
{
"name": "modifyDate",
"label": "Modify Date",
"backendName": "modify_date",
"type": "DATE_TIME",
"isRequired": false,
"isEditable": false
},
{
"name": "firstName",
"label": "First Name",
"backendName": "first_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
},
{
"name": "lastName",
"label": "Last Name",
"backendName": "last_name",
"type": "STRING",
"isRequired": true,
"isEditable": true
},
{
"name": "birthDate",
"label": "Birth Date",
"backendName": "birth_date",
"type": "DATE",
"isRequired": false,
"isEditable": true
},
{
"name": "email",
"label": "Email",
"type": "STRING",
"isRequired": false,
"isEditable": true
"type": "PROCESS_SUMMARY_RESULTS"
}
]
}

View File

@ -0,0 +1,155 @@
{
"table": {
"name": "person",
"label": "Person",
"isHidden": false,
"primaryKeyField": "id",
"iconName": "person",
"fields": {
"firstName": {
"name": "firstName",
"label": "First Name",
"type": "STRING",
"isRequired": true,
"isEditable": true,
"displayFormat": "%s"
},
"lastName": {
"name": "lastName",
"label": "Last Name",
"type": "STRING",
"isRequired": true,
"isEditable": true,
"displayFormat": "%s"
},
"annualSalary": {
"name": "annualSalary",
"label": "Annual Salary",
"type": "DECIMAL",
"isRequired": false,
"isEditable": true,
"displayFormat": "$%,.2f"
},
"modifyDate": {
"name": "modifyDate",
"label": "Modify Date",
"type": "DATE_TIME",
"isRequired": false,
"isEditable": false,
"displayFormat": "%s"
},
"daysWorked": {
"name": "daysWorked",
"label": "Days Worked",
"type": "INTEGER",
"isRequired": false,
"isEditable": true,
"displayFormat": "%,d"
},
"id": {
"name": "id",
"label": "Id",
"type": "INTEGER",
"isRequired": false,
"isEditable": false,
"displayFormat": "%s"
},
"birthDate": {
"name": "birthDate",
"label": "Birth Date",
"type": "DATE",
"isRequired": false,
"isEditable": true,
"displayFormat": "%s"
},
"isEmployed": {
"name": "isEmployed",
"label": "Is Employed",
"type": "BOOLEAN",
"isRequired": false,
"isEditable": true,
"displayFormat": "%s"
},
"email": {
"name": "email",
"label": "Email",
"type": "STRING",
"isRequired": false,
"isEditable": true,
"displayFormat": "%s"
},
"createDate": {
"name": "createDate",
"label": "Create Date",
"type": "DATE_TIME",
"isRequired": false,
"isEditable": false,
"displayFormat": "%s"
}
},
"sections": [
{
"name": "identity",
"label": "Identity",
"tier": "T1",
"fieldNames": [
"id",
"firstName",
"lastName"
],
"icon": {
"name": "badge"
},
"isHidden": false
},
{
"name": "basicInfo",
"label": "Basic Info",
"tier": "T2",
"fieldNames": [
"email",
"birthDate"
],
"icon": {
"name": "dataset"
},
"isHidden": false
},
{
"name": "employmentInfo",
"label": "Employment Info",
"tier": "T2",
"fieldNames": [
"isEmployed",
"annualSalary",
"daysWorked"
],
"icon": {
"name": "work"
},
"isHidden": false
},
{
"name": "dates",
"label": "Dates",
"tier": "T3",
"fieldNames": [
"createDate",
"modifyDate"
],
"icon": {
"name": "calendar_month"
},
"isHidden": false
}
],
"capabilities": [
"TABLE_COUNT",
"TABLE_GET",
"TABLE_QUERY",
"TABLE_DELETE",
"TABLE_INSERT",
"TABLE_UPDATE"
]
}
}

View File

@ -0,0 +1,3 @@
[
{}
]

View File

@ -0,0 +1 @@
{}

View File

@ -34,4 +34,8 @@
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
// }
import {registerCommand} from "cypress-wait-for-stable-dom";
registerCommand({pollInterval: 100, timeout: 3000});

Binary file not shown.

View File

@ -13,7 +13,7 @@
"@fullcalendar/interaction": "5.10.0",
"@fullcalendar/react": "5.10.0",
"@fullcalendar/timegrid": "5.10.0",
"@kingsrook/qqq-frontend-core": "1.0.35",
"@kingsrook/qqq-frontend-core": "1.0.36",
"@mui/icons-material": "5.4.1",
"@mui/material": "5.4.1",
"@mui/styled-engine": "5.4.1",
@ -105,7 +105,8 @@
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.10.2",
"@typescript-eslint/parser": "5.10.2",
"cypress": "10.3.1",
"cypress": "11.0.1",
"cypress-wait-for-stable-dom": "0.1.0",
"eslint": "8.8.0",
"eslint-config-airbnb": "19.0.4",
"eslint-import-resolver-typescript": "2.5.0",

View File

@ -23,6 +23,7 @@ import {useAuth0} from "@auth0/auth0-react";
import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException";
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
import {QAppTreeNode} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppTreeNode";
import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAuthenticationMetaData";
import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData";
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import CssBaseline from "@mui/material/CssBaseline";
@ -86,6 +87,7 @@ function getStaticRoutes()
];
}
const qController = QClient.getInstance();
export const SESSION_ID_COOKIE_NAME = "sessionId";
LicenseInfo.setLicenseKey(process.env.REACT_APP_MATERIAL_UI_LICENSE_KEY);
@ -105,24 +107,51 @@ export default function App()
return;
}
setLoadingToken(true);
(async () =>
{
try
const authenticationMetaData: QAuthenticationMetaData = await qController.getAuthenticationMetaData();
if (authenticationMetaData.type === "AUTH_0")
{
console.log("Loading token...");
await getAccessTokenSilently();
const idToken = await getIdTokenClaims();
setCookie(SESSION_ID_COOKIE_NAME, idToken.__raw, {path: "/"});
setIsFullyAuthenticated(true);
console.log("Token load complete.");
/////////////////////////////////////////
// use auth0 if auth type is ... auth0 //
/////////////////////////////////////////
try
{
console.log("Loading token...");
await getAccessTokenSilently();
const idToken = await getIdTokenClaims();
setCookie(SESSION_ID_COOKIE_NAME, idToken.__raw, {path: "/"});
setIsFullyAuthenticated(true);
console.log("Token load complete.");
}
catch (e)
{
console.log(`Error loading token: ${JSON.stringify(e)}`);
removeCookie(SESSION_ID_COOKIE_NAME);
qController.clearAuthenticationMetaDataLocalStorage();
logout();
return;
}
}
catch (e)
else if (authenticationMetaData.type === "FULLY_ANONYMOUS" || authenticationMetaData.type === "MOCK")
{
console.log(`Error loading token: ${JSON.stringify(e)}`);
removeCookie(SESSION_ID_COOKIE_NAME);
logout();
/////////////////////////////////////////////
// use a random token if anonymous or mock //
/////////////////////////////////////////////
console.log("Generating random token...");
setIsFullyAuthenticated(true);
setCookie(SESSION_ID_COOKIE_NAME, Md5.hashStr(`${new Date()}`), {path: "/"});
console.log("Token generation complete.");
return;
}
else
{
console.log(`Unrecognized authenticationMetaData.type: ${authenticationMetaData.type}`);
qController.clearAuthenticationMetaDataLocalStorage();
}
})();
}, [loadingToken]);
@ -265,7 +294,7 @@ export default function App()
routeList.push({
name: `${app.label}`,
key: `${app.name}.dev`,
key: `${app.name}.record.dev`,
route: `${path}/:id/dev`,
component: <EntityDeveloperView table={table} />,
});
@ -342,13 +371,13 @@ export default function App()
let profileRoutes = {};
const gravatarBase = "https://www.gravatar.com/avatar/";
const hash = Md5.hashStr(user.email);
const hash = Md5.hashStr(user?.email || "user");
const profilePicture = `${gravatarBase}${hash}`;
profileRoutes = {
type: "collapse",
name: user.name,
key: user.name,
icon: <MDAvatar src={profilePicture} alt="{user.name}" size="sm" />,
name: user?.name,
key: "username",
icon: <MDAvatar src={profilePicture} alt="{user?.name}" size="sm" />,
collapse: [
{
name: "My Profile",
@ -388,11 +417,16 @@ export default function App()
}
catch (e)
{
console.error(e);
if (e instanceof QException)
{
if ((e as QException).message.indexOf("status code 401") !== -1)
{
removeCookie(SESSION_ID_COOKIE_NAME);
//////////////////////////////////////////////////////
// todo - this is auth0 logout... make more generic //
//////////////////////////////////////////////////////
logout();
return;
}

View File

@ -19,11 +19,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Auth0Provider, useAuth0} from "@auth0/auth0-react";
import {useAuth0} from "@auth0/auth0-react";
import React, {useEffect} from "react";
import {useCookies} from "react-cookie";
import {SESSION_ID_COOKIE_NAME} from "App";
import {AUTH0_CLIENT_ID, AUTH0_DOMAIN} from "index";
interface Props
{
@ -44,11 +43,7 @@ function HandleAuthorizationError({errorMessage}: Props)
});
return (
<Auth0Provider domain={AUTH0_DOMAIN} clientId={AUTH0_CLIENT_ID}>
<div>
<div>{errorMessage}</div>
</div>
</Auth0Provider>
<div>{errorMessage}</div>
);
}

View File

@ -20,6 +20,7 @@
*/
import {Auth0Provider} from "@auth0/auth0-react";
import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAuthenticationMetaData";
import React from "react";
import {render} from "react-dom";
import {BrowserRouter, useNavigate, useSearchParams} from "react-router-dom";
@ -28,52 +29,73 @@ import "qqq/styles/qqq-override-styles.css";
import {MaterialUIControllerProvider} from "context";
import HandleAuthorizationError from "HandleAuthorizationError";
import ProtectedRoute from "qqq/auth0/protected-route";
import QClient from "qqq/utils/QClient";
export const AUTH0_DOMAIN = process.env.REACT_APP_AUTH0_DOMAIN;
export const AUTH0_CLIENT_ID = process.env.REACT_APP_AUTH0_CLIENT_ID;
const qController = QClient.getInstance();
const authenticationMetaDataPromise: Promise<QAuthenticationMetaData> = qController.getAuthenticationMetaData()
// @ts-ignore
function Auth0ProviderWithRedirectCallback({children, ...props})
authenticationMetaDataPromise.then((authenticationMetaData) =>
{
const navigate = useNavigate();
const [searchParams] = useSearchParams();
// @ts-ignore
const onRedirectCallback = (appState) =>
function Auth0ProviderWithRedirectCallback({children, ...props})
{
navigate((appState && appState.returnTo) || window.location.pathname);
};
if (searchParams.get("error"))
const navigate = useNavigate();
const [searchParams] = useSearchParams();
// @ts-ignore
const onRedirectCallback = (appState) =>
{
navigate((appState && appState.returnTo) || window.location.pathname);
};
if (searchParams.get("error"))
{
return (
// @ts-ignore
<Auth0Provider {...props}>
<HandleAuthorizationError errorMessage={searchParams.get("error_description")} />
</Auth0Provider>
);
}
else
{
return (
// @ts-ignore
<Auth0Provider onRedirectCallback={onRedirectCallback} {...props}>
{children}
</Auth0Provider>
);
}
}
if (authenticationMetaData.type === "AUTH_0")
{
return (
// @ts-ignore
<Auth0Provider {...props}>
<HandleAuthorizationError errorMessage={searchParams.get("error_description")} />
</Auth0Provider>
const domain = process.env.REACT_APP_AUTH0_DOMAIN;
const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
render(
<BrowserRouter>
<Auth0ProviderWithRedirectCallback
domain={domain}
clientId={clientId}
redirectUri={`${window.location.origin}/dashboards/overview`}
>
<MaterialUIControllerProvider>
<ProtectedRoute component={App} />
</MaterialUIControllerProvider>
</Auth0ProviderWithRedirectCallback>
</BrowserRouter>,
document.getElementById("root"),
);
}
else
{
return (
// @ts-ignore
<Auth0Provider onRedirectCallback={onRedirectCallback} {...props}>
{children}
</Auth0Provider>
);
render(
<BrowserRouter>
<MaterialUIControllerProvider>
<App />
</MaterialUIControllerProvider>
</BrowserRouter>
, document.getElementById("root"));
}
}
render(
<BrowserRouter>
<Auth0ProviderWithRedirectCallback
domain={AUTH0_DOMAIN}
clientId={AUTH0_CLIENT_ID}
redirectUri={`${window.location.origin}/dashboards/overview`}
>
<MaterialUIControllerProvider>
<ProtectedRoute component={App} />
</MaterialUIControllerProvider>
</Auth0ProviderWithRedirectCallback>
</BrowserRouter>,
document.getElementById("root"),
);
})

View File

@ -19,12 +19,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {AdornmentType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/AdornmentType";
import {Capability} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Capability";
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
import {QFilterOrderBy} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterOrderBy";
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
@ -44,10 +41,9 @@ import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import Modal from "@mui/material/Modal";
import Tooltip from "@mui/material/Tooltip";
import {DataGridPro, getGridDateOperators, GridCallbackDetails, GridColDef, GridColumnOrderChangeParams, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterModel, GridLinkOperator, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, MuiEvent, useGridApiContext, useGridApiEventHandler, useGridSelector} from "@mui/x-data-grid-pro";
import {GridFilterOperator} from "@mui/x-data-grid/models/gridFilterOperator";
import {DataGridPro, GridCallbackDetails, GridColDef, GridColumnOrderChangeParams, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterModel, GridLinkOperator, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, MuiEvent, useGridApiContext, useGridApiEventHandler, useGridSelector} from "@mui/x-data-grid-pro";
import React, {useContext, useEffect, useReducer, useRef, useState} from "react";
import {Link, useLocation, useNavigate, useSearchParams} from "react-router-dom";
import {useLocation, useNavigate, useSearchParams} from "react-router-dom";
import QContext from "QContext";
import DashboardLayout from "qqq/components/DashboardLayout";
import Footer from "qqq/components/Footer";
@ -55,7 +51,6 @@ import Navbar from "qqq/components/Navbar";
import {QActionsMenuButton, QCreateNewButton} from "qqq/components/QButtons";
import MDAlert from "qqq/components/Temporary/MDAlert";
import MDBox from "qqq/components/Temporary/MDBox";
import {buildQGridPvsOperators, QGridBooleanOperators, QGridNumericOperators, QGridStringOperators} from "qqq/pages/entity-list/QGridFilterOperators";
import ProcessRun from "qqq/pages/process-run";
import DataGridUtils from "qqq/utils/DataGridUtils";
import QClient from "qqq/utils/QClient";
@ -237,7 +232,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData);
const [launchingProcess, setLaunchingProcess] = useState(launchProcess);
const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter)
const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter);
const instance = useRef({timer: null});
@ -355,13 +350,26 @@ function EntityList({table, launchProcess}: Props): JSX.Element
return qFilter;
};
const getTableMetaData = async (): Promise<QTableMetaData> =>
{
if(tableMetaData !== null)
{
return(new Promise((resolve) =>
{
resolve(tableMetaData)
}));
}
return (qController.loadTableMetaData(tableName));
}
const updateTable = () =>
{
setLoading(true);
setRows([]);
(async () =>
{
const tableMetaData = await qController.loadTableMetaData(tableName);
const tableMetaData = await getTableMetaData();
setPageHeader(tableMetaData.label);
////////////////////////////////////////////////////////////////////////////////////////////////
@ -530,7 +538,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
const handlePinnedColumnsChange = (pinnedColumns: GridPinnedColumns) =>
{
setPinnedColumns(pinnedColumns)
setPinnedColumns(pinnedColumns);
localStorage.setItem(pinnedColumnsLocalStorageKey, JSON.stringify(pinnedColumns));
};
@ -1080,7 +1088,14 @@ function EntityList({table, launchProcess}: Props): JSX.Element
///////////////////////////////////////////////////////////////////////////////////////////
useEffect(() =>
{
updateTable();
if(latestQueryId > 0)
{
////////////////////////////////////////////////////////////////////////////////////////
// to avoid both this useEffect and the one below from both doing an "initial query", //
// only run this one if at least 1 query has already been ran //
////////////////////////////////////////////////////////////////////////////////////////
updateTable();
}
}, [ pageNumber, rowsPerPage, columnSortModel ]);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -32,6 +32,7 @@ class QClient
private static handleException(exception: QException)
{
// todo - check for 401 and clear cookie et al & logout?
console.log(`Caught Exception: ${JSON.stringify(exception)}`);
throw (exception);
}

View File

@ -32,30 +32,36 @@ class QProcessUtils
public static getProcessesForTable(metaData: QInstance, tableName: string, includeHidden = false): QProcessMetaData[]
{
const matchingProcesses: QProcessMetaData[] = [];
const processKeys = [...metaData.processes.keys()];
processKeys.forEach((key) =>
if (metaData.processes)
{
const process = metaData.processes.get(key);
if (process.tableName === tableName && (includeHidden || !process.isHidden))
const processKeys = [...metaData.processes.keys()];
processKeys.forEach((key) =>
{
matchingProcesses.push(process);
}
});
const process = metaData.processes.get(key);
if (process.tableName === tableName && (includeHidden || !process.isHidden))
{
matchingProcesses.push(process);
}
});
}
return matchingProcesses;
}
public static getReportsForTable(metaData: QInstance, tableName: string, includeHidden = false): QReportMetaData[]
{
const matchingReports: QReportMetaData[] = [];
const reportKeys = [...metaData.reports.keys()];
reportKeys.forEach((key) =>
if (metaData.reports)
{
const process = metaData.reports.get(key);
if (process.tableName === tableName)
const reportKeys = [...metaData.reports.keys()];
reportKeys.forEach((key) =>
{
matchingReports.push(process);
}
});
const process = metaData.reports.get(key);
if (process.tableName === tableName)
{
matchingReports.push(process);
}
});
}
return matchingReports;
}