Merge pull request #32721 from appsmithorg/release
17/04 Daily promotion
This commit is contained in:
commit
b3f46b0373
|
|
@ -3,7 +3,6 @@ import {
|
|||
agHelper,
|
||||
locators,
|
||||
} from "../../../../support/Objects/ObjectsCore";
|
||||
import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags";
|
||||
|
||||
describe(
|
||||
"Entity explorer tests related to widgets and validation",
|
||||
|
|
@ -111,6 +110,11 @@ describe(
|
|||
// check that all widgets are present within their tags
|
||||
const widgetsInThisTag: string[] = [];
|
||||
|
||||
// click the see more button for building blocks first to show all widgets
|
||||
cy.wrap($widgetTag)
|
||||
.find(entityExplorer._widgetSeeMoreButton)
|
||||
.click({ force: true });
|
||||
|
||||
cy.wrap($widgetTag)
|
||||
.find(entityExplorer._widgetCardTitle)
|
||||
.each(($widgetName) => {
|
||||
|
|
@ -134,10 +138,10 @@ describe(
|
|||
});
|
||||
});
|
||||
|
||||
it("3. All widgets should be ordered alphabetically within their tags, except Essential widgets, which should be sorted by their static rank.", () => {
|
||||
it("3. All widgets other than building blocks should be ordered alphabetically within their tags, except Essential widgets, which should be sorted by their static rank.", () => {
|
||||
agHelper
|
||||
.GetElement(
|
||||
`${entityExplorer._widgetTagsList}:not(${entityExplorer._widgetTagSuggestedWidgets})`,
|
||||
`${entityExplorer._widgetTagsList}:not(${entityExplorer._widgetTagSuggestedWidgets}):not(${entityExplorer._widgetTagBuildingBlocks})`,
|
||||
)
|
||||
.each(($widgetTag) => {
|
||||
const widgetsInThisTag: string[] = [];
|
||||
|
|
@ -192,6 +196,12 @@ describe(
|
|||
agHelper.AssertElementExist(".t--widget-card-draggable-customwidget");
|
||||
|
||||
agHelper.ClearTextField(entityExplorer._widgetSearchInput);
|
||||
// click to show all building blocks
|
||||
agHelper.GetElement(entityExplorer._widgetTagsList).each(($widgetTag) => {
|
||||
cy.wrap($widgetTag)
|
||||
.find(entityExplorer._widgetSeeMoreButton)
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
agHelper.AssertElementLength(
|
||||
entityExplorer._widgetCards,
|
||||
|
|
|
|||
|
|
@ -95,12 +95,12 @@ describe("Admin settings page", { tags: ["@tag.Settings"] }, function () {
|
|||
cy.get("@pricingPage").should("be.called");
|
||||
cy.wait(2000);
|
||||
cy.go(-1);
|
||||
cy.stubPricingPage();
|
||||
cy.stubCustomerPortalPage();
|
||||
cy.get(adminsSettings.branding).click();
|
||||
cy.url().should("contain", adminSettingsHelper.routes.BRANDING);
|
||||
cy.get(adminsSettings.brandingSubmitButton).should("be.disabled");
|
||||
cy.xpath(adminsSettings.upgrade).click();
|
||||
cy.get("@pricingPage").should("be.called");
|
||||
cy.get("@customerPortalPage").should("be.called");
|
||||
cy.wait(2000);
|
||||
cy.go(-1);
|
||||
}
|
||||
|
|
@ -119,11 +119,14 @@ describe("Admin settings page", { tags: ["@tag.Settings"] }, function () {
|
|||
});
|
||||
cy.get(adminsSettings.accessControl).click();
|
||||
cy.url().should("contain", adminSettingsHelper.routes.ACCESS_CONTROL);
|
||||
cy.stubPricingPage();
|
||||
cy.stubCustomerPortalPage();
|
||||
cy.xpath(adminsSettings.upgrade).click();
|
||||
cy.get("@pricingPage").should("be.called");
|
||||
cy.get("@customerPortalPage").should("be.called");
|
||||
cy.wait(2000);
|
||||
cy.go(-1);
|
||||
agHelper.VisitNAssert(
|
||||
adminSettingsHelper.routes.GENERAL,
|
||||
"getEnvVariables",
|
||||
);
|
||||
cy.get(adminsSettings.auditLogs).within(() => {
|
||||
cy.get(adminsSettings.businessTag)
|
||||
.should("exist")
|
||||
|
|
@ -131,11 +134,14 @@ describe("Admin settings page", { tags: ["@tag.Settings"] }, function () {
|
|||
});
|
||||
cy.get(adminsSettings.auditLogs).click();
|
||||
cy.url().should("contain", adminSettingsHelper.routes.AUDIT_LOGS);
|
||||
cy.stubPricingPage();
|
||||
cy.stubCustomerPortalPage();
|
||||
cy.xpath(adminsSettings.upgrade).click();
|
||||
cy.get("@pricingPage").should("be.called");
|
||||
cy.get("@customerPortalPage").should("be.called");
|
||||
cy.wait(2000);
|
||||
cy.go(-1);
|
||||
agHelper.VisitNAssert(
|
||||
adminSettingsHelper.routes.GENERAL,
|
||||
"getEnvVariables",
|
||||
);
|
||||
cy.get(adminsSettings.provisioning).within(() => {
|
||||
cy.get(adminsSettings.enterpriseTag)
|
||||
.should("exist")
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ describe(
|
|||
agHelper.RenameWithInPane(dataSourceName, false);
|
||||
|
||||
dataSources.TestDatasource(false);
|
||||
agHelper.ValidateToastMessage("Missing endpoint.");
|
||||
agHelper.ValidateToastMessage("Missing username for authentication.");
|
||||
agHelper.ValidateToastMessage("Missing hostname.");
|
||||
agHelper.ClearTextField(dataSources._databaseName);
|
||||
dataSources.TestDatasource(false);
|
||||
agHelper.ValidateToastMessage("Missing database name.");
|
||||
|
|
@ -81,7 +81,7 @@ describe(
|
|||
agHelper.RenameWithInPane(dataSourceName, false);
|
||||
|
||||
dataSources.TestDatasource(false);
|
||||
agHelper.ValidateToastMessage("Missing endpoint and url");
|
||||
agHelper.ValidateToastMessage("Host value cannot be empty");
|
||||
agHelper.ValidateToastMessage("Missing username for authentication.");
|
||||
agHelper.ValidateToastMessage("Missing password for authentication.");
|
||||
agHelper.ClearTextField(dataSources._databaseName);
|
||||
|
|
@ -135,7 +135,9 @@ describe(
|
|||
agHelper.RenameWithInPane(dataSourceName, false);
|
||||
|
||||
dataSources.TestDatasource(false);
|
||||
agHelper.ValidateToastMessage("Missing endpoint(s)");
|
||||
agHelper.ValidateToastMessage(
|
||||
"Connection timed out. Please check if the datasource configuration fields have been filled correctly.",
|
||||
);
|
||||
dataSources.ValidateNSelectDropdown(
|
||||
"Use mongo connection string URI",
|
||||
"No",
|
||||
|
|
@ -166,7 +168,9 @@ describe(
|
|||
"Replica set",
|
||||
);
|
||||
dataSources.TestDatasource(false);
|
||||
agHelper.ValidateToastMessage("Missing endpoint(s)");
|
||||
agHelper.ValidateToastMessage(
|
||||
"REPLICA_SET connections should not be given a port. If you are trying to specify all the shards, please add more than one.",
|
||||
);
|
||||
agHelper.UpdateInputValue(
|
||||
dataSources._host(),
|
||||
dataManager.dsValues[dataManager.defaultEnviorment].mongo_host,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ describe("Validate Oracle DS", { tags: ["@tag.Datasource"] }, () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("1. Tc #2354, #2204 - Oracle placeholder & mandatory mark verification", () => {
|
||||
it("1. Tc #2354, #2204 - Oracle placeholder, port default value & mandatory mark verification", () => {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("Oracle");
|
||||
agHelper.GetNAssertContains(locators._dsName, "Untitled datasource");
|
||||
|
|
@ -45,6 +45,7 @@ describe("Validate Oracle DS", { tags: ["@tag.Datasource"] }, () => {
|
|||
"placeholder",
|
||||
"myapp.abcde.oracle.net",
|
||||
);
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "1521");
|
||||
agHelper.AssertAttribute(
|
||||
dataSources._databaseName,
|
||||
"placeholder",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ describe("Validate Redis DS", { tags: ["@tag.Datasource"] }, () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Create HAST set (Multiple key value pair under single key name) in redis DB, Read, Delete", () => {
|
||||
it("1. Create HAST set (Multiple key value pair under single key name) in redis DB, Read, Delete", () => {
|
||||
let hSetReceipe = `HSET recipe:1 name "Vegetable Stir Fry" ingredients "2 cups mixed vegetables (broccoli, carrots, bell peppers, mushrooms, snow peas), 2 cloves garlic, minced" instructions "1. Heat vegetable oil in a large skillet over medium-high heat. 2. Add mixed vegetables and garlic to the skillet and cook for 3-4 minutes. 3. In a small bowl, whisk together soy sauce and cornstarch. 4. Pour the soy sauce mixture over the vegetables and stir until the vegetables are coated. 5. Cook for an additional 1-2 minutes. 6. Serve hot." difficulty "easy"`;
|
||||
let hGetKeys = "HGET recipe:1 name";
|
||||
let hMGet = "HMGET recipe:1 difficulty name"; // getting multiple keys
|
||||
|
|
@ -90,20 +90,19 @@ describe("Validate Redis DS", { tags: ["@tag.Datasource"] }, () => {
|
|||
dataSources.EnterQuery(hGetKeys);
|
||||
dataSources.RunQueryNVerifyResponseViews(); //5 keys, 5 values
|
||||
dataSources.AssertQueryTableResponse(0, "null");
|
||||
});
|
||||
|
||||
after("Delete the query & datasource", () => {
|
||||
// Delete the query & datasource
|
||||
agHelper.ActionContextMenuWithInPane({
|
||||
action: "Delete",
|
||||
entityType: entityItems.Query,
|
||||
});
|
||||
dataSources.DeleteDatasourceFromWithinDS(dsName);
|
||||
//commenting below since after query delete, we run into risk of not seeing the datasource in EntityExplorer
|
||||
// EditorNavigation.SelectEntityByName(dsName, EntityType.Datasource);
|
||||
// entityExplorer.ActionContextMenuByEntityName({
|
||||
// entityNameinLeftSidebar: dsName,
|
||||
// action: "Delete",
|
||||
// entityType: entityItems.Datasource,
|
||||
// });
|
||||
});
|
||||
|
||||
it("2. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("Redis");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "6379");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ describe(
|
|||
dataSources.RunQuery({ toValidateResponse: false });
|
||||
cy.wait(500);
|
||||
cy.get("[data-testid=t--query-error]").contains(
|
||||
"[Missing endpoint., Missing username for authentication.]",
|
||||
"[Missing username for authentication., Missing hostname.]",
|
||||
);
|
||||
agHelper.ActionContextMenuWithInPane({
|
||||
action: "Delete",
|
||||
|
|
|
|||
|
|
@ -47,5 +47,12 @@ describe(
|
|||
.then(($dbName) => expect($dbName).to.eq("_system"));
|
||||
dataSources.SaveDSFromDialog(false);
|
||||
});
|
||||
|
||||
it("5. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("ArangoDB");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "8529");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const datasource = require("../../../locators/DatasourcesEditor.json");
|
||||
|
||||
import { dataSources } from "../../../support/Objects/ObjectsCore";
|
||||
import { agHelper, dataSources } from "../../../support/Objects/ObjectsCore";
|
||||
|
||||
let elasticSearchName;
|
||||
|
||||
|
|
@ -31,5 +31,12 @@ describe(
|
|||
|
||||
dataSources.SaveDSFromDialog(false);
|
||||
});
|
||||
|
||||
it("2. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("Elasticsearch");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "9200");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,5 +43,12 @@ describe(
|
|||
dataSources.DeleteDatasourceFromWithinDS(dsName);
|
||||
});
|
||||
});
|
||||
|
||||
it("4. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("MongoDB");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "27017");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -343,6 +343,13 @@ describe(
|
|||
table.WaitUntilTableLoad();
|
||||
});
|
||||
|
||||
it("7. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("Microsoft SQL Server");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "1433");
|
||||
});
|
||||
|
||||
after("Verify Deletion of the datasource", () => {
|
||||
cy.intercept("DELETE", "/api/v1/datasources/*").as("deleteDatasource"); //Since intercept from before is not working
|
||||
dataSources.DeleteDatasourceFromWithinDS(dsName, 409); //since CRUD pages are still active
|
||||
|
|
|
|||
|
|
@ -45,5 +45,12 @@ describe(
|
|||
cy.deleteQueryUsingContext();
|
||||
cy.deleteDatasource(datasourceName);
|
||||
});
|
||||
|
||||
it("4. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("MySQL");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "3306");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -42,5 +42,12 @@ describe(
|
|||
cy.deleteQueryUsingContext();
|
||||
cy.deleteDatasource(datasourceName);
|
||||
});
|
||||
|
||||
it("4. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("PostgreSQL");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "5432");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const datasource = require("../../../locators/DatasourcesEditor.json");
|
||||
let datasourceName;
|
||||
import { agHelper, dataSources } from "../../../support/Objects/ObjectsCore";
|
||||
import { ObjectsRegistry } from "../../../support/Objects/Registry";
|
||||
|
||||
describe(
|
||||
|
|
@ -49,5 +50,12 @@ describe(
|
|||
cy.deleteQueryUsingContext();
|
||||
cy.deleteDatasource(datasourceName);
|
||||
});
|
||||
|
||||
it("4. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("Redshift");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "5439");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -126,5 +126,12 @@ describe(
|
|||
expect(thisTestEmail.attachments.length).equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("4. Verify the default port for the datasource", function () {
|
||||
dataSources.NavigateToDSCreateNew();
|
||||
dataSources.CreatePlugIn("SMTP");
|
||||
|
||||
agHelper.AssertAttribute(dataSources._port, "value", "25");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"clientSchemaVersion":1,"serverSchemaVersion":7,"customJSLibList":[{"name":"jsonwebtoken","accessor":["jsonwebtoken"],"url":"/libraries/jsonwebtoken@8.5.1.js","version":"8.5.1","defs":"{\"!name\":\"LIB/jsonwebtoken\",\"jsonwebtoken\":{\"decode\":{\"!type\":\"fn()\",\"prototype\":{}},\"verify\":{\"!type\":\"fn()\",\"prototype\":{}},\"sign\":{\"!type\":\"fn()\",\"prototype\":{}},\"JsonWebTokenError\":{\"!type\":\"fn()\",\"prototype\":{\"message\":{\"!type\":\"string\"},\"toString\":{\"!type\":\"fn()\"}}},\"NotBeforeError\":{\"!type\":\"fn()\",\"prototype\":{}},\"TokenExpiredError\":{\"!type\":\"fn()\",\"prototype\":{}}}}","userPermissions":[],"uidString":"jsonwebtoken_/libraries/jsonwebtoken@8.5.1.js","new":true}],"widgets":""}
|
||||
{"artifactJsonType":"APPLICATION","clientSchemaVersion":1,"serverSchemaVersion":7,"customJSLibList":[{"name":"jsonwebtoken","accessor":["jsonwebtoken"],"url":"/libraries/jsonwebtoken@8.5.1.js","version":"8.5.1","defs":"{\"!name\":\"LIB/jsonwebtoken\",\"jsonwebtoken\":{\"decode\":{\"!type\":\"fn()\",\"prototype\":{}},\"verify\":{\"!type\":\"fn()\",\"prototype\":{}},\"sign\":{\"!type\":\"fn()\",\"prototype\":{}},\"JsonWebTokenError\":{\"!type\":\"fn()\",\"prototype\":{\"message\":{\"!type\":\"string\"},\"toString\":{\"!type\":\"fn()\"}}},\"NotBeforeError\":{\"!type\":\"fn()\",\"prototype\":{}},\"TokenExpiredError\":{\"!type\":\"fn()\",\"prototype\":{}}}}","userPermissions":[],"uidString":"jsonwebtoken_/libraries/jsonwebtoken@8.5.1.js","new":true}],"widgets":""}
|
||||
|
|
@ -1 +1 @@
|
|||
{"clientSchemaVersion":1,"serverSchemaVersion":7,"datasourceList":[{"datasourceConfiguration":{"connection":{"mode":"READ_WRITE","ssl":{"authType":"DEFAULT"}},"endpoints":[{"host":"mockdb.internal.appsmith.com"}]},"name":"Users","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"isMock":true,"isValid":true,"embedded":false,"new":true}],"widgets":""}
|
||||
{"artifactJsonType":"APPLICATION","clientSchemaVersion":1,"serverSchemaVersion":7,"datasourceList":[{"datasourceConfiguration":{"connection":{"mode":"READ_WRITE","ssl":{"authType":"DEFAULT"}},"endpoints":[{"host":"mockdb.internal.appsmith.com"}]},"name":"Users","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"isMock":true,"isValid":true,"new":true}],"widgets":""}
|
||||
|
|
@ -1 +1 @@
|
|||
{"clientSchemaVersion":1,"serverSchemaVersion":7,"actionList":[],"actionCollectionList":[{"id":"Home_JSObject1","unpublishedCollection":{"name":"JSObject1","pageId":"Home","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\taddNumbers (a, b) {\n\t\treturn a+b;\n\t},\n\tasync myFun2 () {\n\t\t//\tuse async-await or promises\n\t\t//\tawait storeValue('varName', 'hello world')\n\t}\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}],"userPermissions":[],"userExecutableName":"JSObject1"},"new":false}],"widgets":""}
|
||||
{"artifactJsonType":"APPLICATION","clientSchemaVersion":1,"serverSchemaVersion":7,"actionList":[],"actionCollectionList":[{"id":"Home_JSObject1","unpublishedCollection":{"name":"JSObject1","pageId":"Home","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\taddNumbers (a, b) {\n\t\treturn a+b;\n\t},\n\tasync myFun2 () {\n\t\t//\tuse async-await or promises\n\t\t//\tawait storeValue('varName', 'hello world')\n\t}\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}],"userPermissions":[]},"new":false}],"widgets":""}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -69,6 +69,8 @@ export class EntityExplorer {
|
|||
_widgetSearchInput = "#entity-explorer-search";
|
||||
_widgetCardTitle = ".t--widget-card-draggable span.ads-v2-text";
|
||||
_widgetTagSuggestedWidgets = ".widget-tag-collapisble-suggested";
|
||||
_widgetTagBuildingBlocks = ".widget-tag-collapisble-building-blocks";
|
||||
_widgetSeeMoreButton = "[data-testid='t--explorer-ui-entity-tag-see-more']";
|
||||
|
||||
public ActionContextMenuByEntityName({
|
||||
action = "Delete",
|
||||
|
|
|
|||
|
|
@ -2076,6 +2076,14 @@ Cypress.Commands.add("stubPricingPage", () => {
|
|||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("stubCustomerPortalPage", () => {
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, "open", (url) => {
|
||||
win.location.href = "https://customer.appsmith.com?";
|
||||
}).as("customerPortalPage");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param testID
|
||||
* @returns
|
||||
|
|
|
|||
|
|
@ -93,7 +93,9 @@ Cypress.Commands.add(
|
|||
: datasourceFormData["postgres-databaseName"];
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["postgres-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["postgres-port"]);
|
||||
cy.get(datasourceEditor.databaseName).clear().type(databaseName);
|
||||
cy.get(datasourceEditor.username).type(
|
||||
datasourceFormData["postgres-username"],
|
||||
|
|
@ -113,7 +115,9 @@ Cypress.Commands.add(
|
|||
const headerValue = "Bearer token";
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["postgres-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["postgres-port"]);
|
||||
cy.get(datasourceEditor.sectionAuthentication).click();
|
||||
cy.get(datasourceEditor.username).type(
|
||||
datasourceFormData["postgres-username"],
|
||||
|
|
@ -136,7 +140,9 @@ Cypress.Commands.add(
|
|||
: datasourceFormData["mysql-databaseName"];
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["mysql-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["mysql-port"]);
|
||||
cy.get(datasourceEditor.databaseName).clear().type(databaseName);
|
||||
cy.get(datasourceEditor.username).type(
|
||||
datasourceFormData["mysql-username"],
|
||||
|
|
@ -158,7 +164,9 @@ Cypress.Commands.add(
|
|||
: datasourceFormData["mssql-databaseName"];
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["mssql-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["mssql-port"]);
|
||||
cy.get(datasourceEditor.databaseName).clear().type(databaseName);
|
||||
cy.get(datasourceEditor.username).type(
|
||||
datasourceFormData["mssql-username"],
|
||||
|
|
@ -180,7 +188,9 @@ Cypress.Commands.add(
|
|||
: datasourceFormData["arango-databaseName"];
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["arango-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["arango-port"]);
|
||||
cy.get(datasourceEditor.databaseName).clear().type(databaseName);
|
||||
|
||||
cy.get(datasourceEditor.username).type(
|
||||
|
|
@ -203,7 +213,9 @@ Cypress.Commands.add(
|
|||
: datasourceFormData["redshift-databaseName"];
|
||||
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["redshift-port"]);
|
||||
cy.get(datasourceEditor.port)
|
||||
.clear()
|
||||
.type(datasourceFormData["redshift-port"]);
|
||||
cy.get(datasourceEditor.databaseName).clear().type(databaseName);
|
||||
cy.get(datasourceEditor.username).type(
|
||||
datasourceFormData["redshift-username"],
|
||||
|
|
@ -250,7 +262,7 @@ Cypress.Commands.add(
|
|||
? datasourceFormData["smtp-host"] + " "
|
||||
: datasourceFormData["smtp-host"];
|
||||
cy.get(datasourceEditor.host).type(hostAddress);
|
||||
cy.get(datasourceEditor.port).type(datasourceFormData["smtp-port"]);
|
||||
cy.get(datasourceEditor.port).clear().type(datasourceFormData["smtp-port"]);
|
||||
cy.get(datasourceEditor.sectionAuthentication).click();
|
||||
cy.get(datasourceEditor.username).type(datasourceFormData["smtp-username"]);
|
||||
cy.get(datasourceEditor.password).type(datasourceFormData["smtp-password"]);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ export interface Template {
|
|||
datasources: string[];
|
||||
pages: ApplicationPagePayload[];
|
||||
allowPageImport: boolean;
|
||||
templateGridColumnSize?: number;
|
||||
templateGridRowSize?: number;
|
||||
}
|
||||
|
||||
export type FetchTemplatesResponse = ApiResponse<Template[]>;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.5 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
<svg width="116" height="100" viewBox="0 0 116 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_369_78097)">
|
||||
<path d="M4.8535 65.131C4.8535 60.002 4.91123 60.002 4.91123 54.8812C4.91123 49.7604 4.63086 49.7521 4.63086 44.6231C4.63086 39.494 4.81227 39.494 4.81227 34.3732C4.81227 29.2524 4.81227 29.2442 4.81227 24.1151C4.81227 18.9861 4.71332 18.9861 4.71332 13.857C4.71332 9.89065 7.96227 6.79013 11.9286 6.79013C16.8845 6.79013 16.8845 6.76539 21.8404 6.76539C26.7963 6.76539 26.7963 6.86435 31.7522 6.86435C36.708 6.86435 36.708 6.82312 41.6639 6.82312C46.6198 6.82312 46.6198 6.82312 51.5757 6.82312C56.5316 6.82312 56.5316 6.79013 61.4875 6.79013C66.4433 6.79013 66.4433 6.77364 71.4075 6.77364C76.3716 6.77364 76.3633 6.50977 81.3275 6.50977C85.2938 6.50977 88.4438 9.88241 88.4438 13.8488C88.4438 18.9778 88.3531 18.9778 88.3531 24.0986C88.3531 29.2194 88.551 29.2277 88.551 34.3567C88.551 39.4858 88.551 39.4858 88.551 44.6066C88.551 49.7274 88.551 49.7356 88.551 54.8647C88.551 59.9937 88.6912 59.9937 88.6912 65.1228C88.6912 69.0891 85.3021 72.1649 81.3357 72.1649C76.3798 72.1649 76.3798 72.3958 71.424 72.3958C66.4681 72.3958 66.4681 72.1979 61.5122 72.1979C56.5563 72.1979 56.5563 72.1237 51.6004 72.1237C46.6445 72.1237 46.6445 72.1319 41.6887 72.1319C36.7328 72.1319 36.7328 72.437 31.7769 72.437C26.821 72.437 26.821 72.2309 21.8569 72.2309C16.8928 72.2309 16.901 72.1897 11.9369 72.1897C7.97052 72.1897 4.87 69.0891 4.87 65.1228L4.8535 65.131Z" fill="#EDEBFC"/>
|
||||
<path d="M18.3114 91.7905C18.3114 86.6615 18.0063 86.6615 18.0063 81.5407C18.0063 76.4199 17.998 76.4116 17.998 71.2826C17.998 66.1535 18.0558 66.1535 18.0558 61.0327C18.0558 55.9119 18.2619 55.9037 18.2619 50.7746C18.2619 45.6456 18.0888 45.6456 18.0888 40.5165C18.0888 36.5502 21.3707 33.2023 25.3453 33.2023C30.3012 33.2023 30.3012 33.4579 35.2571 33.4579C40.2129 33.4579 40.2129 33.4331 45.1688 33.4331C50.1247 33.4331 50.1247 33.5156 55.0806 33.5156C60.0365 33.5156 60.0365 33.1445 64.9924 33.1445C69.9482 33.1445 69.9482 33.5074 74.9041 33.5074C79.86 33.5074 79.86 33.3507 84.8241 33.3507C89.7883 33.3507 89.78 33.161 94.7441 33.161C98.7105 33.161 102.133 36.5502 102.133 40.5165C102.133 45.6456 102.025 45.6456 102.025 50.7664C102.025 55.8872 102.05 55.8954 102.05 61.0245C102.05 66.1535 101.976 66.1535 101.976 71.2743C101.976 76.3951 102.116 76.4034 102.116 81.5324C102.116 86.6615 101.959 86.6615 101.959 91.7905C101.959 95.7569 98.7105 99.0471 94.7441 99.0471C89.7883 99.0471 89.7883 98.8656 84.8324 98.8656C79.8765 98.8656 79.8765 98.9976 74.9206 98.9976C69.9647 98.9976 69.9647 99.0883 65.0089 99.0883C60.053 99.0883 60.053 98.8327 55.0971 98.8327C50.1412 98.8327 50.1412 98.8821 45.1853 98.8821C40.2294 98.8821 40.2294 98.9646 35.2653 98.9646C30.3012 98.9646 30.3094 98.8574 25.3453 98.8574C21.3789 98.8574 18.3196 95.7651 18.3196 91.7988L18.3114 91.7905Z" stroke="#602DED" stroke-width="1.33329" stroke-miterlimit="10" stroke-dasharray="6 6"/>
|
||||
<path d="M26.9788 64.2722C24.5132 64.2722 23.2763 64.2475 22.0394 64.231C20.7942 64.2063 19.5491 64.1898 17.0588 64.1898C14.5685 64.1898 13.4305 64.1238 12.1689 64.0496C10.932 63.9836 9.65381 63.9094 7.13876 63.9094C3.83209 63.9094 1.24283 61.3696 1.24283 58.1207C1.24283 55.5314 1.19335 54.2203 1.14387 52.9421C1.0944 51.697 1.04492 50.4024 1.04492 47.8626C1.04492 45.3228 1.11914 44.1106 1.2016 42.8077C1.27581 41.5296 1.35827 40.202 1.35827 37.6045C1.35827 35.007 1.28406 33.6876 1.21809 32.4095C1.14387 31.1066 1.07791 29.8779 1.07791 27.3546C1.07791 24.8313 1.13563 23.5284 1.19335 22.2833C1.25107 21.0051 1.3088 19.694 1.3088 17.0965C1.3088 14.499 1.28406 13.2291 1.25932 11.9427C1.23458 10.6646 1.20984 9.38645 1.20984 6.83841C1.20984 3.57297 3.87332 0.909495 7.15525 0.909495C9.62083 0.909495 10.8577 0.934234 12.0864 0.950726C13.3316 0.975464 14.5767 0.991956 17.067 0.991956C19.5573 0.991956 20.8025 0.967218 22.0476 0.94248C23.2846 0.917741 24.5132 0.893003 26.9788 0.893003C29.4444 0.893003 30.706 0.876511 31.9512 0.860019C33.1881 0.843527 34.425 0.827035 36.8906 0.827035C39.3561 0.827035 40.5271 0.884757 41.7887 0.94248C43.0256 1.0002 44.2955 1.06617 46.8023 1.06617C49.3091 1.06617 50.5708 1.02494 51.7994 0.991956C53.0116 0.950726 54.2568 0.917742 56.7141 0.917742C59.1714 0.917742 60.4908 0.868265 61.7195 0.827035C62.9234 0.785805 64.1768 0.736328 66.6341 0.736328C69.0914 0.736328 70.3531 0.761066 71.5652 0.794051C72.7939 0.818789 74.0556 0.851773 76.5541 0.851773C79.8361 0.851773 82.5078 3.53999 82.5078 6.83841C82.5078 9.38645 82.4748 10.6811 82.4501 11.9345C82.4171 13.2044 82.3923 14.5073 82.3923 17.0883C82.3923 19.6693 82.4666 21.0051 82.5408 22.2833C82.615 23.5862 82.6809 24.8148 82.6809 27.3381C82.6809 29.8614 82.6067 31.0901 82.5325 32.393C82.4583 33.6711 82.3841 34.9905 82.3841 37.588C82.3841 40.1855 82.3758 41.4306 82.3594 42.7088C82.3511 43.9952 82.3346 45.2816 82.3346 47.8461C82.3346 50.4106 82.3676 51.7382 82.3923 53.0081C82.4253 54.2615 82.4501 55.5562 82.4501 58.1124C82.4501 61.4603 79.8031 64.1815 76.5459 64.1815C74.0803 64.1815 72.8434 64.165 71.6065 64.1403C70.3613 64.1238 69.1162 64.0991 66.6341 64.0991C64.152 64.0991 62.9234 64.0991 61.6865 64.0908C60.4496 64.0908 59.2044 64.0826 56.7306 64.0826C54.2568 64.0826 53.0199 64.0826 51.7829 64.0743C50.546 64.0743 49.3009 64.0661 46.8188 64.0661C44.3368 64.0661 43.1163 64.0413 41.8877 64.0166C40.6425 63.9919 39.3974 63.9671 36.9071 63.9671C34.4167 63.9671 33.1139 64.0414 31.877 64.1238C30.6235 64.198 29.4361 64.2722 26.9953 64.2722H26.9788Z" fill="white"/>
|
||||
<path d="M66.6167 1.97382C69.0658 1.97382 70.3109 1.99856 71.5231 2.03154C72.7517 2.05628 74.0299 2.08926 76.5367 2.08926C79.0435 2.08926 81.2534 4.22499 81.2534 6.84724C81.2534 9.46949 81.2204 10.6734 81.1957 11.9186C81.1627 13.1885 81.138 14.5078 81.138 17.1053C81.138 19.7029 81.2122 21.0799 81.2864 22.3746C81.3606 23.661 81.4266 24.8731 81.4266 27.3634C81.4266 29.8538 81.3606 31.0659 81.2864 32.3441C81.2122 33.6387 81.138 34.9746 81.138 37.6133C81.138 40.252 81.1297 41.4477 81.1133 42.7259C81.105 44.0122 81.0885 45.2986 81.0885 47.8796C81.0885 50.4607 81.1215 51.7883 81.1462 53.0664C81.1792 54.3116 81.204 55.6062 81.204 58.1377C81.204 60.8012 79.1095 62.9699 76.5367 62.9699C73.9639 62.9699 72.826 62.9534 71.622 62.9287C70.3934 62.9122 69.1235 62.8875 66.6249 62.8875C64.1264 62.8875 62.9142 62.8875 61.6773 62.8792C60.4404 62.8792 59.1952 62.871 56.7132 62.871C54.2311 62.871 53.0024 62.871 51.7655 62.8627C50.5204 62.8627 49.2834 62.8545 46.8014 62.8545C44.3193 62.8545 43.0989 62.8298 41.895 62.805C40.6663 62.7803 39.3882 62.7555 36.8896 62.7555C34.3911 62.7555 33.0387 62.838 31.7853 62.9122C30.5484 62.9864 29.3857 63.0606 26.9778 63.0606C24.57 63.0606 23.2671 63.0359 22.0632 63.0194C20.8345 62.9947 19.5646 62.9782 17.0578 62.9782C14.551 62.9782 13.479 62.9122 12.2421 62.838C10.9887 62.7638 9.6941 62.6896 7.14607 62.6896C4.59804 62.6896 2.48704 60.7353 2.48704 58.1377C2.48704 55.5402 2.43757 54.1961 2.38809 52.918C2.33862 51.6234 2.28914 50.4029 2.28914 47.8879C2.28914 45.3728 2.36335 44.1937 2.43757 42.9073C2.52003 41.6126 2.60249 40.2685 2.60249 37.6298C2.60249 34.9911 2.52827 33.6634 2.45406 32.3688C2.37985 31.0824 2.31388 29.8702 2.31388 27.3799C2.31388 24.8896 2.3716 23.6527 2.42108 22.3581C2.4788 21.0717 2.53652 19.7358 2.53652 17.1218C2.53652 14.5078 2.51178 13.2132 2.48704 11.9433C2.46231 10.6982 2.43757 9.40352 2.43757 6.86373C2.43757 4.32395 4.54856 2.17172 7.14607 2.17172C9.6034 2.17172 10.8486 2.19646 12.0607 2.21295C13.2894 2.23769 14.5593 2.25418 17.0578 2.25418C19.5564 2.25418 20.8345 2.22945 22.0632 2.20471C23.2671 2.17997 24.5123 2.15523 26.9696 2.15523C29.4269 2.15523 30.7298 2.13874 31.9585 2.12225C33.1706 2.10576 34.4158 2.08926 36.8814 2.08926C39.3469 2.08926 40.4767 2.14699 41.7218 2.20471C42.967 2.26243 44.2616 2.3284 46.8014 2.3284C49.3412 2.3284 50.5946 2.28717 51.8315 2.25418C53.0849 2.21295 54.2723 2.17997 56.7132 2.17997C59.154 2.17997 60.5228 2.13049 61.768 2.08926C63.0214 2.03979 64.2006 1.99856 66.6332 1.99856M66.6332 -0.475262C61.6773 -0.475262 61.6773 -0.293848 56.7132 -0.293848C51.749 -0.293848 51.7573 -0.137173 46.8014 -0.137173C41.8455 -0.137173 41.8455 -0.376309 36.8896 -0.376309C31.9337 -0.376309 31.9337 -0.310341 26.9778 -0.310341C22.022 -0.310341 22.022 -0.211388 17.0661 -0.211388C12.1102 -0.211388 12.1102 -0.293848 7.15432 -0.293848C3.17147 -0.326833 -0.0362506 2.87264 -0.0362506 6.839C-0.0362506 11.968 0.0627021 11.968 0.0627021 17.0971C0.0627021 22.2261 -0.168188 22.2261 -0.168188 27.3634C-0.168188 32.5007 0.112179 32.4925 0.112179 37.6133C0.112179 42.7341 -0.201172 42.7424 -0.201172 47.8714C-0.201172 53.0005 0.00497968 53.0005 0.00497968 58.1213C0.00497968 62.0876 3.16322 65.1469 7.13782 65.1469C12.0937 65.1469 12.0937 65.4273 17.0578 65.4273C22.022 65.4273 22.0137 65.518 26.9778 65.518C31.942 65.518 31.9337 65.2129 36.8896 65.2129C41.8455 65.2129 41.8455 65.3118 46.8014 65.3118C51.7573 65.3118 51.7573 65.3283 56.7132 65.3283C61.669 65.3283 61.669 65.3448 66.6249 65.3448C71.5808 65.3448 71.5808 65.4273 76.5367 65.4273C80.503 65.4273 83.6778 62.0959 83.6778 58.1213C83.6778 52.9922 83.5623 52.9922 83.5623 47.8632C83.5623 42.7341 83.6036 42.7341 83.6036 37.6051C83.6036 32.476 83.9004 32.476 83.9004 27.3552C83.9004 22.2344 83.6118 22.2261 83.6118 17.0971C83.6118 11.968 83.7272 11.968 83.7272 6.84724C83.7272 2.88089 80.5113 -0.384555 76.5367 -0.384555C71.5808 -0.384555 71.5808 -0.5 66.6167 -0.5L66.6332 -0.475262Z" fill="#80949C"/>
|
||||
<path d="M9.56293 56.282C9.56293 56.282 9.57943 56.1831 9.57943 56.1418C9.57943 51.4086 9.69487 51.4086 9.69487 46.6754C9.69487 41.9421 9.71961 41.9421 9.71961 37.2089C9.71961 32.4756 9.67013 32.4756 9.67013 27.7424C9.67013 23.0092 9.55469 23.0092 9.55469 18.2759C9.55469 13.5427 9.74435 13.5427 9.74435 8.80944C9.74435 8.70224 9.72785 8.59504 9.82681 8.59504C15.162 8.59504 15.162 8.80119 20.4972 8.80119C25.8324 8.80119 25.8324 8.42188 31.1676 8.42188C36.5028 8.42188 36.5028 8.50434 41.8463 8.50434C45.4031 8.50434 48.9599 8.50434 52.5167 8.50434C57.8601 8.50434 57.8601 8.80119 63.1953 8.80119C68.5305 8.80119 68.5388 8.59504 73.874 8.59504C73.9729 8.59504 74.0554 8.65277 74.0719 8.68575C74.0884 8.71049 74.0142 8.75996 74.0142 8.79295C74.0142 13.5262 74.1791 13.5262 74.1791 18.2594C74.1791 22.9927 73.8822 22.9927 73.8822 27.7259C73.8822 32.4591 73.9482 32.4591 73.9482 37.1924C73.9482 41.9256 74.1791 41.9256 74.1791 46.6589C74.1791 51.3921 73.9647 51.3921 73.9647 56.1253C73.9647 56.2325 73.9647 56.4964 73.8657 56.4964C68.5305 56.4964 68.5305 56.2161 63.1953 56.2161C57.8601 56.2161 57.8601 56.414 52.5249 56.414C47.1897 56.414 47.1897 56.1501 41.8463 56.1418C36.5028 56.1336 36.5111 56.2161 31.1759 56.2161C25.8407 56.2161 25.8324 56.414 20.4972 56.414C15.162 56.414 15.1538 56.1996 9.81856 56.1996C9.71961 56.1996 9.58767 56.2903 9.56293 56.2573V56.282Z" stroke="#80949C" stroke-width="1.33329" stroke-miterlimit="10"/>
|
||||
<path d="M68.0914 95.0967C71.0682 91.0232 71.2414 91.1468 74.21 87.0733C77.1785 82.9997 77.2033 83.0162 80.1801 78.9427C80.6171 78.3407 81.3016 77.9037 82.0355 78.0191C87.0161 78.7942 86.9913 78.9757 91.972 79.7508C96.9526 80.5259 96.9608 80.4764 101.941 81.2598C103.846 81.5567 104.951 79.2725 103.582 77.9119C100.02 74.3496 99.9789 74.3826 96.4166 70.8203C92.8543 67.258 92.9862 67.1178 89.4239 63.5555C85.8616 59.9932 85.7957 60.0592 82.2251 56.4969C78.6546 52.9264 78.7288 52.8604 75.1582 49.2981C71.5877 45.7358 71.5712 45.744 68.0089 42.1817C66.7555 40.9283 64.5621 41.8189 64.5621 43.5918C64.5621 48.6302 64.5126 48.6301 64.5126 53.6767C64.5126 58.7233 64.595 58.7151 64.595 63.7617C64.595 68.8083 64.595 68.8 64.595 73.8466C64.595 78.8932 64.5703 78.8932 64.5703 83.9315C64.5703 88.9699 64.4219 88.9781 64.4219 94.0165C64.4219 95.9461 66.9452 96.6552 68.0749 95.0967H68.0914Z" fill="#253135"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_369_78097">
|
||||
<rect width="116" height="100" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -73,6 +73,7 @@ export function Routes() {
|
|||
const user = useSelector(getCurrentUserSelector);
|
||||
const tenantPermissions = useSelector(getTenantPermissions);
|
||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||
useFeatureFlagOverride();
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
|
|
@ -150,7 +151,6 @@ export default function AppRouter() {
|
|||
const safeCrashCode: ERROR_CODES | undefined = useSelector(getSafeCrashCode);
|
||||
const isConsolidatedPageLoading = useSelector(getIsConsolidatedPageLoading);
|
||||
const dispatch = useDispatch();
|
||||
useFeatureFlagOverride();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(initCurrentPage());
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import type {
|
|||
LayoutSystemTypeConfig,
|
||||
LayoutSystemTypes,
|
||||
} from "layoutSystems/types";
|
||||
import type { ActionViewMode } from "entities/Action";
|
||||
|
||||
export type EvaluationVersion = number;
|
||||
|
||||
|
|
@ -267,6 +268,11 @@ export interface ImportBuildingBlockToApplicationRequest {
|
|||
templateId: string;
|
||||
}
|
||||
|
||||
export interface ImportBuildingBlockToApplicationResponse {
|
||||
widgetDsl: string;
|
||||
onPageLoadActions: Omit<ActionViewMode, "pageId">[];
|
||||
}
|
||||
|
||||
export class ApplicationApi extends Api {
|
||||
static baseURL = "v1/applications";
|
||||
static publishURLPath = (applicationId: string) =>
|
||||
|
|
@ -489,7 +495,9 @@ export class ApplicationApi extends Api {
|
|||
|
||||
static async importBuildingBlockToApplication(
|
||||
request: ImportBuildingBlockToApplicationRequest,
|
||||
) {
|
||||
): Promise<
|
||||
AxiosPromise<ApiResponse<ImportBuildingBlockToApplicationResponse>>
|
||||
> {
|
||||
return Api.post(`${ApplicationApi.baseURL}/import/partial/block`, request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export enum API_STATUS_CODES {
|
|||
export const SERVER_ERROR_CODES = {
|
||||
INCORRECT_BINDING_LIST_OF_WIDGET: ["AE-JSN-4001", "AE-APP-4022"],
|
||||
RESOURCE_NOT_FOUND: [
|
||||
"AE-SCL-4004",
|
||||
"AE-ACL-4004",
|
||||
"AE-BAD-4000",
|
||||
"AE-APP-4028",
|
||||
"AE-APP-4013",
|
||||
|
|
|
|||
|
|
@ -700,9 +700,10 @@ const ActionTypes = {
|
|||
RESET_TEMPLATE_FILTERS: "RESET_TEMPLATE_FILTERS",
|
||||
SET_TEMPLATE_SEARCH_QUERY: "SET_TEMPLATE_SEARCH_QUERY",
|
||||
IMPORT_TEMPLATE_TO_APPLICATION_INIT: "IMPORT_TEMPLATE_TO_APPLICATION_INIT",
|
||||
DRAG_BUILDING_BLOCK_TO_CANVAS_INIT: "DRAG_BUILDING_BLOCK_TO_CANVAS_INIT",
|
||||
DRAG_BUILDING_BLOCK_TO_CANVAS_SUCCESS:
|
||||
"DRAG_BUILDING_BLOCK_TO_CANVAS_SUCCESS",
|
||||
DRAGGING_BUILDING_BLOCK_TO_CANVAS_INIT:
|
||||
"DRAGGING_BUILDING_BLOCK_TO_CANVAS_INIT",
|
||||
DRAGGING_BUILDING_BLOCK_TO_CANVAS_SUCCESS:
|
||||
"DRAGGING_BUILDING_BLOCK_TO_CANVAS_SUCCESS",
|
||||
IMPORT_STARTER_BUILDING_BLOCK_TO_APPLICATION_INIT:
|
||||
"IMPORT_STARTER_BUILDING_BLOCK_TO_APPLICATION_INIT",
|
||||
IMPORT_STARTER_TEMPLATE_TO_APPLICATION_SUCCESS:
|
||||
|
|
@ -1094,6 +1095,8 @@ export const ReduxActionErrorTypes = {
|
|||
IMPORT_TEMPLATE_TO_WORKSPACE_ERROR: "IMPORT_TEMPLATE_TO_WORKSPACE_ERROR",
|
||||
IMPORT_STARTER_BUILDING_BLOCK_TO_APPLICATION_ERROR:
|
||||
"IMPORT_STARTER_BUILDING_BLOCK_TO_APPLICATION_ERROR",
|
||||
DRAGGING_BUILDING_BLOCK_TO_CANVAS_ERROR:
|
||||
"DRAGGING_BUILDING_BLOCK_TO_CANVAS_ERROR",
|
||||
GET_DEFAULT_PLUGINS_ERROR: "GET_DEFAULT_PLUGINS_ERROR",
|
||||
GET_TEMPLATE_ERROR: "GET_TEMPLATE_ERROR",
|
||||
GET_TEMPLATE_FILTERS_ERROR: "GET_TEMPLATE_FILTERS_ERROR",
|
||||
|
|
|
|||
|
|
@ -2314,6 +2314,7 @@ export const EDITOR_PANE_TEXTS = {
|
|||
js_create_tab_title: () => "Create JS object",
|
||||
queries_create_from_existing: () => "From existing datasource",
|
||||
queries_create_new: () => "New API",
|
||||
loading_building_blocks: () => "Loading building blocks",
|
||||
};
|
||||
|
||||
export const PARTIAL_IMPORT_EXPORT = {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ import {
|
|||
import {
|
||||
AppIconCollection,
|
||||
Classes,
|
||||
EditableText,
|
||||
MenuItem as ListItem,
|
||||
Text,
|
||||
TextType,
|
||||
|
|
@ -450,9 +449,6 @@ export const WorkspaceNameWrapper = styled.div<{ disabled?: boolean }>`
|
|||
color: ${(props) => props.theme.colors.applications.iconColor};
|
||||
}
|
||||
`;
|
||||
export const WorkspaceRename = styled(EditableText)`
|
||||
padding: 0 2px;
|
||||
`;
|
||||
|
||||
export const NoSearchResultImg = styled.img`
|
||||
margin: 1em;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export function ProvisioningUpgradePage() {
|
|||
logEventData: { source: "Provisioning" },
|
||||
featureName: RampFeature.Provisioning,
|
||||
sectionName: RampSection.AdminSettings,
|
||||
isEnterprise: true,
|
||||
});
|
||||
|
||||
const header: Header = {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ import type { LayoutElementPositionsReduxState } from "layoutSystems/anvil/integ
|
|||
import type { ActiveField } from "reducers/uiReducers/activeFieldEditorReducer";
|
||||
import type { SelectedWorkspaceReduxState } from "@appsmith/reducers/uiReducers/selectedWorkspaceReducer";
|
||||
import type { ConsolidatedPageLoadState } from "reducers/uiReducers/consolidatedPageLoadReducer";
|
||||
import type { BuildingBlocksReduxState } from "reducers/uiReducers/buildingBlockReducer";
|
||||
|
||||
export const reducerObject = {
|
||||
entities: entityReducer,
|
||||
|
|
@ -104,6 +105,7 @@ export interface AppState {
|
|||
apiPane: ApiPaneReduxState;
|
||||
auth: AuthState;
|
||||
templates: TemplatesReduxState;
|
||||
buildingBlocks: BuildingBlocksReduxState;
|
||||
workspaces: WorkspaceReduxState;
|
||||
selectedWorkspace: SelectedWorkspaceReduxState;
|
||||
users: UsersReduxState;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import datasourcePaneReducer from "reducers/uiReducers/datasourcePaneReducer";
|
|||
import authReducer from "reducers/uiReducers/authReducer";
|
||||
import workspaceReducer from "@appsmith/reducers/uiReducers/workspaceReducer";
|
||||
import templateReducer from "reducers/uiReducers/templateReducer";
|
||||
import buildingBlockReducer from "reducers/uiReducers/buildingBlockReducer";
|
||||
import usersReducer from "reducers/uiReducers/usersReducer";
|
||||
import { widgetDraggingReducer } from "reducers/uiReducers/dragResizeReducer";
|
||||
import importReducer from "reducers/uiReducers/importReducer";
|
||||
|
|
@ -61,6 +62,7 @@ export const uiReducerObject = {
|
|||
apiPane: apiPaneReducer,
|
||||
auth: authReducer,
|
||||
templates: templateReducer,
|
||||
buildingBlocks: buildingBlockReducer,
|
||||
workspaces: workspaceReducer,
|
||||
selectedWorkspace: selectedWorkspaceReducer,
|
||||
users: usersReducer,
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ export function* saveWorkspaceSaga(action: ReduxAction<SaveWorkspaceRequest>) {
|
|||
yield put({
|
||||
type: ReduxActionErrorTypes.SAVE_WORKSPACE_ERROR,
|
||||
payload: {
|
||||
error: (error as Error).message,
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { removeSpecialChars } from "utils/helpers";
|
|||
import type { AppState } from "@appsmith/reducers";
|
||||
|
||||
import { saveActionName } from "actions/pluginActionActions";
|
||||
import { Flex, Spinner } from "design-system";
|
||||
import { Flex } from "design-system";
|
||||
import { getAction, getPlugin } from "@appsmith/selectors/entitiesSelector";
|
||||
import NameEditorComponent, {
|
||||
IconBox,
|
||||
|
|
@ -116,7 +116,6 @@ function ActionNameEditor(props: ActionNameEditorProps) {
|
|||
updating={saveStatus.isSaving}
|
||||
valueTransform={removeSpecialChars}
|
||||
/>
|
||||
{saveStatus.isSaving && <Spinner size="md" />}
|
||||
</Flex>
|
||||
</NameWrapper>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
|||
import { EntityIcon, JsFileIconV2 } from "pages/Editor/Explorer/ExplorerIcons";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import type { FeatureFlags } from "@appsmith/entities/FeatureFlag";
|
||||
import { Icon } from "design-system";
|
||||
import { Button, Icon } from "design-system";
|
||||
import { APPSMITH_AI } from "@appsmith/components/editorComponents/GPT/trigger";
|
||||
import { DatasourceCreateEntryPoints } from "constants/Datasource";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
|
|
@ -121,10 +121,10 @@ export function Command(props: {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="command-container relative group cursor-pointer w-full">
|
||||
<div className="command-container relative cursor-pointer w-full">
|
||||
<div className="command flex w-full">
|
||||
<div className="self-center shrink-0">{props.icon}</div>
|
||||
<div className="flex grow relative overflow-hidden">
|
||||
<div className="flex grow">
|
||||
<div className="flex flex-col gap-1 grow w-full">
|
||||
<div className="whitespace-nowrap flex flex-row items-center gap-2 text-[color:var(--ads-v2\-colors-content-label-default-fg)] relative">
|
||||
<span className="flex items-center overflow-hidden overflow-ellipsis slash-command-hint-text">
|
||||
|
|
@ -137,12 +137,14 @@ export function Command(props: {
|
|||
) : null}
|
||||
</div>
|
||||
{props.url ? (
|
||||
<span
|
||||
className="hidden group-hover:inline self-center h-full px-2 text-xs absolute right-0 command-suggestion-edit"
|
||||
<Button
|
||||
className="hidden group-hover:flex items-center self-center h-full px-2 text-xs !absolute command-suggestion-edit right-0 top-0"
|
||||
kind="tertiary"
|
||||
onClick={switchToAction}
|
||||
size="sm"
|
||||
>
|
||||
{createMessage(EDIT)}
|
||||
</span>
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -203,7 +205,7 @@ export const generateQuickCommands = (
|
|||
? `{{${name}.}}`
|
||||
: `{{${name}}}`,
|
||||
displayText: `${name}`,
|
||||
className: "CodeMirror-commands",
|
||||
className: "CodeMirror-commands group relative",
|
||||
data: suggestion,
|
||||
triggerCompletionsPostPick: suggestion.type !== ENTITY_TYPE.ACTION,
|
||||
render: (element: HTMLElement, _: unknown, data: CommandsCompletion) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from "@blueprintjs/core";
|
||||
import styled from "styled-components";
|
||||
import _ from "lodash";
|
||||
import { Button, toast, Tooltip } from "design-system";
|
||||
import { Button, Spinner, toast, Tooltip } from "design-system";
|
||||
|
||||
export enum EditInteractionKind {
|
||||
SINGLE,
|
||||
|
|
@ -221,13 +221,8 @@ export function EditableText(props: EditableTextProps) {
|
|||
[valueTransform, isInvalid],
|
||||
);
|
||||
|
||||
const showEditIcon = !(
|
||||
disabled ||
|
||||
minimal ||
|
||||
hideEditIcon ||
|
||||
updating ||
|
||||
isEditing
|
||||
);
|
||||
const showEditIcon = !(disabled || minimal || hideEditIcon || isEditing);
|
||||
|
||||
return (
|
||||
<EditableTextWrapper
|
||||
isEditing={isEditing}
|
||||
|
|
@ -265,15 +260,18 @@ export function EditableText(props: EditableTextProps) {
|
|||
selectAllOnFocus
|
||||
value={value}
|
||||
/>
|
||||
{showEditIcon && (
|
||||
<Button
|
||||
className="t--action-name-edit-icon"
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
size="md"
|
||||
startIcon="pencil-line"
|
||||
/>
|
||||
)}
|
||||
{showEditIcon &&
|
||||
(updating ? (
|
||||
<Spinner size="md" />
|
||||
) : (
|
||||
<Button
|
||||
className="t--action-name-edit-icon"
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
size="md"
|
||||
startIcon="pencil-line"
|
||||
/>
|
||||
))}
|
||||
</TextContainer>
|
||||
</Tooltip>
|
||||
</EditableTextWrapper>
|
||||
|
|
|
|||
|
|
@ -228,8 +228,8 @@ export const MAX_MODAL_WIDTH_FROM_MAIN_WIDTH = 0.95;
|
|||
export const FILE_SIZE_LIMIT_FOR_BLOBS = 5000 * 1024; // 5MB
|
||||
|
||||
export const WIDGET_TAGS = {
|
||||
SUGGESTED_WIDGETS: "Suggested",
|
||||
BUILDING_BLOCKS: "Building Blocks",
|
||||
SUGGESTED_WIDGETS: "Suggested",
|
||||
INPUTS: "Inputs",
|
||||
BUTTONS: "Buttons",
|
||||
SELECT: "Select",
|
||||
|
|
@ -265,6 +265,8 @@ export const SUGGESTED_WIDGETS_ORDER: Record<WidgetType, number> = {
|
|||
// Constant key to show walkthrough for a widget -> stores widget id
|
||||
export const WIDGET_ID_SHOW_WALKTHROUGH = "WIDGET_ID_SHOW_WALKTHROUGH";
|
||||
|
||||
export const DEFAULT_ROWS_FOR_EXPLORER_BUILDING_BLOCKS = 30;
|
||||
export const DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS = 5;
|
||||
export const DEFAULT_ROWS_FOR_EXPLORER_BUILDING_BLOCKS = 60;
|
||||
export const DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS = 62;
|
||||
export const BUILDING_BLOCK_MIN_HORIZONTAL_LIMIT = 2000;
|
||||
export const BUILDING_BLOCK_MIN_VERTICAL_LIMIT = 800;
|
||||
export const BUILDING_BLOCK_EXPLORER_TYPE = "BUILDING_BLOCK";
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
|||
color: var(--ads-v2-color-fg);
|
||||
}
|
||||
}
|
||||
.command-suggestion-edit {
|
||||
background: var(--ads-v2-color-bg-subtle);
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-command-header {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ import {
|
|||
} from "widgets/WidgetUtils";
|
||||
import DragLayerComponent from "./DragLayerComponent";
|
||||
import StarterBuildingBlocks from "./starterBuildingBlocks";
|
||||
import BuildingBlockExplorerDropTarget from "./buildingBlockExplorerDropTarget";
|
||||
import { isDraggingBuildingBlockToCanvas } from "selectors/buildingBlocksSelectors";
|
||||
|
||||
export type DropTargetComponentProps = PropsWithChildren<{
|
||||
snapColumnSpace: number;
|
||||
|
|
@ -72,40 +74,55 @@ const StyledDropTarget = styled.div`
|
|||
|
||||
function Onboarding() {
|
||||
const isMobileCanvas = useSelector(getIsMobileCanvasLayout);
|
||||
const isDraggingBuildingBlock = useSelector(isDraggingBuildingBlockToCanvas);
|
||||
const appState = useCurrentAppState();
|
||||
const isAirgappedInstance = isAirgapped();
|
||||
|
||||
const showStarterTemplatesInsteadofBlankCanvas = useFeatureFlag(
|
||||
const showStarterTemplatesInsteadOfBlankCanvas = useFeatureFlag(
|
||||
FEATURE_FLAG.ab_show_templates_instead_of_blank_canvas_enabled,
|
||||
);
|
||||
const releaseDragDropBuildingBlocks = useFeatureFlag(
|
||||
const releaseDragDropBuildingBlocksEnabled = useFeatureFlag(
|
||||
FEATURE_FLAG.release_drag_drop_building_blocks_enabled,
|
||||
);
|
||||
|
||||
const shouldShowStarterTemplates = useMemo(
|
||||
() =>
|
||||
showStarterTemplatesInsteadofBlankCanvas &&
|
||||
showStarterTemplatesInsteadOfBlankCanvas &&
|
||||
!isMobileCanvas &&
|
||||
!isAirgappedInstance &&
|
||||
// This is to hide starter building blocks once building blocks are available in the explorer
|
||||
!releaseDragDropBuildingBlocks,
|
||||
!releaseDragDropBuildingBlocksEnabled, // Hide starter templates when drag-drop building blocks are available
|
||||
[
|
||||
showStarterTemplatesInsteadofBlankCanvas,
|
||||
showStarterTemplatesInsteadOfBlankCanvas,
|
||||
isMobileCanvas,
|
||||
isAirgappedInstance,
|
||||
releaseDragDropBuildingBlocks,
|
||||
releaseDragDropBuildingBlocksEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
if (shouldShowStarterTemplates && appState === IDEAppState.EDITOR)
|
||||
const shouldShowBuildingBlocksDropTarget = useMemo(
|
||||
() => releaseDragDropBuildingBlocksEnabled && !isDraggingBuildingBlock,
|
||||
[releaseDragDropBuildingBlocksEnabled, isDraggingBuildingBlock],
|
||||
);
|
||||
|
||||
const isEditorState = appState === IDEAppState.EDITOR;
|
||||
|
||||
if (shouldShowStarterTemplates && isEditorState) {
|
||||
return <StarterBuildingBlocks />;
|
||||
else if (!shouldShowStarterTemplates && appState === IDEAppState.EDITOR)
|
||||
} else if (shouldShowBuildingBlocksDropTarget && isEditorState) {
|
||||
return <BuildingBlockExplorerDropTarget />;
|
||||
} else if (
|
||||
!shouldShowBuildingBlocksDropTarget &&
|
||||
!shouldShowStarterTemplates &&
|
||||
!isDraggingBuildingBlock
|
||||
) {
|
||||
return (
|
||||
<h2 className="absolute top-0 left-0 right-0 flex items-end h-108 justify-center text-2xl font-bold text-gray-300">
|
||||
Drag and drop a widget here
|
||||
</h2>
|
||||
);
|
||||
else return null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
import { importSvg } from "@design-system/widgets-old/src/utils/icon-loadables";
|
||||
import { Text } from "design-system";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const DROP_TARGET_CONTAINER_TOP_MARGIN = 20;
|
||||
const DROP_TARGET_CONTAINER_LEFT_MARGIN = 50;
|
||||
const DROP_TARGET_CONTAINER_PADDING = 16;
|
||||
|
||||
export const DropTargetContainer = styled.div`
|
||||
position: absolute;
|
||||
top: ${DROP_TARGET_CONTAINER_TOP_MARGIN}px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
const MainContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: ${DROP_TARGET_CONTAINER_PADDING}px;
|
||||
margin-left: ${DROP_TARGET_CONTAINER_LEFT_MARGIN}px;
|
||||
`;
|
||||
|
||||
const TextContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
max-width: 220px;
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
const BuildingBlockExplorerDropTarget = () => {
|
||||
return (
|
||||
<DropTargetContainer>
|
||||
<MainContainer>
|
||||
<ClickIcon />
|
||||
|
||||
<TextContainer>
|
||||
<Text kind="heading-s" style={{ marginBottom: "4px" }}>
|
||||
Drag & drop a building block
|
||||
</Text>
|
||||
|
||||
<Text kind="body-m" style={{ textAlign: "left" }}>
|
||||
Make a working app in seconds using functional blocks
|
||||
</Text>
|
||||
</TextContainer>
|
||||
</MainContainer>
|
||||
|
||||
<ArrowIcon height={100} width={100} />
|
||||
</DropTargetContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const ClickIcon = importSvg(
|
||||
async () =>
|
||||
import(
|
||||
"../../../../assets/icons/templates/building-block-explorer-drop-target-icon.svg"
|
||||
),
|
||||
);
|
||||
const ArrowIcon = importSvg(
|
||||
async () =>
|
||||
import(
|
||||
"../../../../assets/icons/templates/building-block-explorer-drop-target-arrow.svg"
|
||||
),
|
||||
);
|
||||
|
||||
export default BuildingBlockExplorerDropTarget;
|
||||
|
|
@ -465,10 +465,12 @@ export const modifyBlockDimension = (
|
|||
parentBottomRow: number,
|
||||
canExtend: boolean,
|
||||
modifyBlock: boolean,
|
||||
// this allows dynamic resize limit for building blocks
|
||||
horizontalMinResizeLimit: number = HORIZONTAL_RESIZE_MIN_LIMIT,
|
||||
verticalMinResizeLimit: number = VERTICAL_RESIZE_MIN_LIMIT,
|
||||
) => {
|
||||
const { columnWidth, fixedHeight, height, left, rowHeight, top, width } =
|
||||
draggingBlock;
|
||||
|
||||
//get left and top of widget on canvas grid
|
||||
const [leftColumn, topRow] = getDropZoneOffsets(
|
||||
snapColumnSpace,
|
||||
|
|
@ -490,23 +492,23 @@ export const modifyBlockDimension = (
|
|||
// calculate offsets based on collisions and limits
|
||||
if (leftColumn < 0) {
|
||||
leftOffset =
|
||||
leftColumn + columnWidth > HORIZONTAL_RESIZE_MIN_LIMIT
|
||||
leftColumn + columnWidth > horizontalMinResizeLimit
|
||||
? leftColumn
|
||||
: HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth;
|
||||
: horizontalMinResizeLimit - columnWidth;
|
||||
} else if (leftColumn + columnWidth > GridDefaults.DEFAULT_GRID_COLUMNS) {
|
||||
rightOffset =
|
||||
GridDefaults.DEFAULT_GRID_COLUMNS - leftColumn - columnWidth;
|
||||
rightOffset =
|
||||
columnWidth + rightOffset >= HORIZONTAL_RESIZE_MIN_LIMIT
|
||||
columnWidth + rightOffset >= horizontalMinResizeLimit
|
||||
? rightOffset
|
||||
: HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth;
|
||||
: horizontalMinResizeLimit - columnWidth;
|
||||
}
|
||||
|
||||
if (topRow < 0 && fixedHeight === undefined) {
|
||||
topOffset =
|
||||
topRow + rowHeight > VERTICAL_RESIZE_MIN_LIMIT
|
||||
topRow + rowHeight > verticalMinResizeLimit
|
||||
? topRow
|
||||
: VERTICAL_RESIZE_MIN_LIMIT - rowHeight;
|
||||
: verticalMinResizeLimit - rowHeight;
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -516,9 +518,9 @@ export const modifyBlockDimension = (
|
|||
) {
|
||||
bottomOffset = parentBottomRow - topRow - rowHeight;
|
||||
bottomOffset =
|
||||
rowHeight + bottomOffset >= VERTICAL_RESIZE_MIN_LIMIT
|
||||
rowHeight + bottomOffset >= verticalMinResizeLimit
|
||||
? bottomOffset
|
||||
: VERTICAL_RESIZE_MIN_LIMIT - rowHeight;
|
||||
: verticalMinResizeLimit - rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { OccupiedSpace } from "constants/CanvasEditorConstants";
|
||||
import {
|
||||
BUILDING_BLOCK_EXPLORER_TYPE,
|
||||
GridDefaults,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
} from "constants/WidgetConstants";
|
||||
|
|
@ -34,6 +35,8 @@ import { useBlocksToBeDraggedOnCanvas } from "./useBlocksToBeDraggedOnCanvas";
|
|||
import { useRenderBlocksOnCanvas } from "./useRenderBlocksOnCanvas";
|
||||
import { useCanvasDragToScroll } from "layoutSystems/common/canvasArenas/useCanvasDragToScroll";
|
||||
import type { FixedCanvasDraggingArenaProps } from "../FixedCanvasDraggingArena";
|
||||
import { useSelector } from "react-redux";
|
||||
import { getDragDetails } from "sagas/selectors";
|
||||
|
||||
/**
|
||||
* useCanvasDragging hook is utilized to handle all drag and drop related functions that are required to give user the sense of dragging and dropping while moving a widget on canvas
|
||||
|
|
@ -63,6 +66,7 @@ export const useCanvasDragging = (
|
|||
}: FixedCanvasDraggingArenaProps,
|
||||
) => {
|
||||
const currentDirection = useRef<ReflowDirection>(ReflowDirection.UNSET);
|
||||
const dragDetails = useSelector(getDragDetails);
|
||||
const { devicePixelRatio: scale = 1 } = window;
|
||||
const {
|
||||
blocksToDraw,
|
||||
|
|
@ -352,8 +356,14 @@ export const useCanvasDragging = (
|
|||
top: e.offsetY - startPoints.top - parentDiff.top,
|
||||
};
|
||||
|
||||
const drawingBlocks = blocksToDraw.map((each) =>
|
||||
modifyBlockDimension(
|
||||
const drawingBlocks = blocksToDraw.map((each) => {
|
||||
let buildingBlockRows;
|
||||
let buildingBlockColumns;
|
||||
if (each.type === BUILDING_BLOCK_EXPLORER_TYPE) {
|
||||
buildingBlockRows = dragDetails.newWidget.rows;
|
||||
buildingBlockColumns = dragDetails.newWidget.columns;
|
||||
}
|
||||
return modifyBlockDimension(
|
||||
{
|
||||
...each,
|
||||
left: each.left + delta.left,
|
||||
|
|
@ -364,8 +374,10 @@ export const useCanvasDragging = (
|
|||
rowRef.current - 1,
|
||||
canExtend,
|
||||
false,
|
||||
),
|
||||
);
|
||||
buildingBlockColumns,
|
||||
buildingBlockRows,
|
||||
);
|
||||
});
|
||||
const newRows = updateRelativeRows(drawingBlocks, rowRef.current);
|
||||
const rowDelta = newRows ? newRows - rowRef.current : 0;
|
||||
rowRef.current = newRows ? newRows : rowRef.current;
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ export function ActionButton({ method }: { method: AuthMethodType }) {
|
|||
logEventData: { method: method.label },
|
||||
featureName: RampFeature.Sso,
|
||||
sectionName: RampSection.AdminSettings,
|
||||
isEnterprise: true,
|
||||
});
|
||||
|
||||
const onClickHandler = (method: AuthMethodType) => {
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ export const StyledLabel = styled.div`
|
|||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ads-v2-spaces-3);
|
||||
|
||||
.admin-settings-form-group-label {
|
||||
font-weight: var(--ads-v2-h5-font-weight);
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
|
@ -66,11 +66,11 @@ export function FormGroup({ children, className, setting }: FieldHelperProps) {
|
|||
renderAs="label"
|
||||
>
|
||||
{setting.label || ""}
|
||||
{setting.isRequired && (
|
||||
<StyledAsterisk renderAs="span">*</StyledAsterisk>
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
{setting.isRequired && (
|
||||
<StyledAsterisk renderAs="span">*</StyledAsterisk>
|
||||
)}
|
||||
{setting.helpText && (
|
||||
<Tooltip content={createMessage(() => setting.helpText || "")}>
|
||||
<Icon
|
||||
|
|
@ -82,14 +82,8 @@ export function FormGroup({ children, className, setting }: FieldHelperProps) {
|
|||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<div className="ml-2">
|
||||
{setting.isFeatureEnabled === false &&
|
||||
(setting.isEnterprise === true ? (
|
||||
<EnterpriseTag />
|
||||
) : (
|
||||
<BusinessTag />
|
||||
))}
|
||||
</div>
|
||||
{setting.isFeatureEnabled === false &&
|
||||
(setting.isEnterprise === true ? <EnterpriseTag /> : <BusinessTag />)}
|
||||
</StyledLabel>
|
||||
{children}
|
||||
{setting.subText && (
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export const StyledLink = styled(Link)<{ $active: boolean }>`
|
|||
|
||||
.ads-v2-text {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: var(--ads-v2-spaces-3);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
saveDatasourceName,
|
||||
updateDatasourceName,
|
||||
} from "actions/datasourceActions";
|
||||
import { Spinner } from "@blueprintjs/core";
|
||||
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
|
|
@ -151,7 +150,6 @@ function FormTitle(props: FormTitleProps) {
|
|||
underline
|
||||
updating={saveStatus.isSaving}
|
||||
/>
|
||||
{saveStatus.isSaving && <Spinner size={16} />}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
import EditableText, {
|
||||
EditInteractionKind,
|
||||
} from "components/editorComponents/EditableText";
|
||||
import { Flex, Spinner } from "design-system";
|
||||
import { Flex } from "design-system";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import NameEditorComponent, {
|
||||
IconBox,
|
||||
|
|
@ -113,7 +113,6 @@ export function JSObjectNameEditor(props: JSObjectNameEditorProps) {
|
|||
updating={saveStatus.isSaving}
|
||||
valueTransform={removeSpecialChars}
|
||||
/>
|
||||
{saveStatus.isSaving && <Spinner size="md" />}
|
||||
</Flex>
|
||||
</NameWrapper>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ interface Props {
|
|||
|
||||
const SeeMoreButton = (props: Props) => {
|
||||
const SEE_MORE_LESS_TEXT = !props.showSeeLess ? "See more" : "See less";
|
||||
const SEE_MORE_ARROW = !props.showSeeLess
|
||||
? "arrow-down-s-line"
|
||||
: "arrow-up-s-line";
|
||||
const SEE_MORE_ARROW = !props.showSeeLess ? "down-arrow-2" : "arrow-up-line";
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -16,14 +16,21 @@ import React from "react";
|
|||
import type { WidgetCardProps } from "widgets/BaseWidget";
|
||||
import SeeMoreButton from "./SeeMoreButton";
|
||||
import styled from "styled-components";
|
||||
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||
import WidgetCard from "./WidgetCard";
|
||||
|
||||
const LoadingWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 70px;
|
||||
margin-bottom: 70px;
|
||||
margin: 8px 0px;
|
||||
`;
|
||||
|
||||
const LoadingContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 30px 0px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
|
|
@ -54,7 +61,16 @@ const UIEntityTagGroup = (props: Props) => {
|
|||
{props.tag}
|
||||
</Text>
|
||||
</CollapsibleHeader>
|
||||
<Spinner size={"lg"} />
|
||||
<LoadingContainer>
|
||||
<Spinner size="md" />
|
||||
<Text
|
||||
className="select-none"
|
||||
color="var(--ads-v2-color-gray-600)"
|
||||
kind="body-m"
|
||||
>
|
||||
{createMessage(EDITOR_PANE_TEXTS.loading_building_blocks)}
|
||||
</Text>
|
||||
</LoadingContainer>
|
||||
</LoadingWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,17 @@ import {
|
|||
} from "selectors/templatesSelectors";
|
||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { groupWidgetCardsByTags } from "../utils";
|
||||
import { isFixedLayoutSelector } from "selectors/layoutSystemSelectors";
|
||||
|
||||
/**
|
||||
* Custom hook for managing UI explorer items including widgets and building blocks.
|
||||
* @returns Object containing grouped cards and cards.
|
||||
* @returns Object containing cards, grouped cards and entity loading states.
|
||||
*/
|
||||
export const useUIExplorerItems = () => {
|
||||
const releaseDragDropBuildingBlocks = useFeatureFlag(
|
||||
FEATURE_FLAG.release_drag_drop_building_blocks_enabled,
|
||||
);
|
||||
const isFixedLayout = useSelector(isFixedLayoutSelector);
|
||||
const dispatch = useDispatch();
|
||||
// check if entities have loaded
|
||||
const isBuildingBlocksLoaded = useSelector(templatesCountSelector) > 0;
|
||||
|
|
@ -35,19 +37,30 @@ export const useUIExplorerItems = () => {
|
|||
|
||||
// handle loading async entities
|
||||
useEffect(() => {
|
||||
if (!isBuildingBlocksLoaded && releaseDragDropBuildingBlocks) {
|
||||
if (
|
||||
!isBuildingBlocksLoaded &&
|
||||
releaseDragDropBuildingBlocks &&
|
||||
isFixedLayout
|
||||
) {
|
||||
dispatch(getAllTemplates());
|
||||
} else {
|
||||
setEntityLoading((prev) => ({ ...prev, "Building Blocks": false }));
|
||||
}
|
||||
}, [isBuildingBlocksLoaded, releaseDragDropBuildingBlocks]);
|
||||
}, [isBuildingBlocksLoaded, releaseDragDropBuildingBlocks, isFixedLayout]);
|
||||
|
||||
const cards = useMemo(
|
||||
() => [
|
||||
...widgetCards,
|
||||
...(releaseDragDropBuildingBlocks ? buildingBlockCards : []),
|
||||
...(isFixedLayout && releaseDragDropBuildingBlocks
|
||||
? buildingBlockCards
|
||||
: []),
|
||||
],
|
||||
[
|
||||
widgetCards,
|
||||
buildingBlockCards,
|
||||
releaseDragDropBuildingBlocks,
|
||||
isFixedLayout,
|
||||
],
|
||||
[widgetCards, buildingBlockCards, releaseDragDropBuildingBlocks],
|
||||
);
|
||||
|
||||
const groupedCards = useMemo(() => groupWidgetCardsByTags(cards), [cards]);
|
||||
|
|
|
|||
42
app/client/src/reducers/uiReducers/buildingBlockReducer.ts
Normal file
42
app/client/src/reducers/uiReducers/buildingBlockReducer.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
import { createReducer } from "utils/ReducerUtils";
|
||||
|
||||
const initialState: BuildingBlocksReduxState = {
|
||||
isDraggingBuildingBlocksToCanvas: false,
|
||||
};
|
||||
|
||||
const buildingBlockReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_INIT]: (
|
||||
state: BuildingBlocksReduxState,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
isDraggingBuildingBlockToCanvas: true,
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_SUCCESS]: (
|
||||
state: BuildingBlocksReduxState,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
isDraggingBuildingBlockToCanvas: false,
|
||||
};
|
||||
},
|
||||
[ReduxActionErrorTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_ERROR]: (
|
||||
state: BuildingBlocksReduxState,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
isDraggingBuildingBlockToCanvas: false,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export interface BuildingBlocksReduxState {
|
||||
isDraggingBuildingBlocksToCanvas: boolean;
|
||||
}
|
||||
|
||||
export default buildingBlockReducer;
|
||||
|
|
@ -3,45 +3,56 @@ import {
|
|||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
import { BlueprintOperationTypes } from "WidgetProvider/constants";
|
||||
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
|
||||
import type { WidgetAddChild } from "actions/pageActions";
|
||||
import { updateAndSaveLayout } from "actions/pageActions";
|
||||
import { calculateDropTargetRows } from "layoutSystems/common/dropTarget/DropTargetUtils";
|
||||
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
|
||||
import type { OccupiedSpace } from "constants/CanvasEditorConstants";
|
||||
import {
|
||||
BUILDING_BLOCK_EXPLORER_TYPE,
|
||||
GridDefaults,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
} from "constants/WidgetConstants";
|
||||
import { toast } from "design-system";
|
||||
import { updateRelationships } from "layoutSystems/autolayout/utils/autoLayoutDraggingUtils";
|
||||
import type { WidgetDraggingUpdateParams } from "layoutSystems/common/canvasArenas/ArenaTypes";
|
||||
import { calculateDropTargetRows } from "layoutSystems/common/dropTarget/DropTargetUtils";
|
||||
import { LayoutSystemTypes } from "layoutSystems/types";
|
||||
import { cloneDeep } from "lodash";
|
||||
import log from "loglevel";
|
||||
import type {
|
||||
CanvasWidgetsReduxState,
|
||||
FlattenedWidgetProps,
|
||||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { LayoutSystemTypes } from "layoutSystems/types";
|
||||
import type { DragDetails } from "reducers/uiReducers/dragResizeReducer";
|
||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
||||
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
||||
import { getWidget, getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||
import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas";
|
||||
import {
|
||||
addBuildingBlockToApplication,
|
||||
getUpdateDslAfterCreatingChild,
|
||||
} from "sagas/WidgetAdditionSagas";
|
||||
import {
|
||||
executeWidgetBlueprintBeforeOperations,
|
||||
traverseTreeAndExecuteBlueprintChildOperations,
|
||||
} from "sagas/WidgetBlueprintSagas";
|
||||
import {
|
||||
getDragDetails,
|
||||
getWidget,
|
||||
getWidgetByName,
|
||||
getWidgets,
|
||||
getWidgetsMeta,
|
||||
} from "sagas/selectors";
|
||||
import {
|
||||
getCanvasWidth,
|
||||
getIsAutoLayoutMobileBreakPoint,
|
||||
getMainCanvasProps,
|
||||
getOccupiedSpacesSelectorForContainer,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { updateRelationships } from "layoutSystems/autolayout/utils/autoLayoutDraggingUtils";
|
||||
import { collisionCheckPostReflow } from "utils/reflowHookUtils";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { BlueprintOperationTypes } from "WidgetProvider/constants";
|
||||
import { toast } from "design-system";
|
||||
import type { WidgetDraggingUpdateParams } from "layoutSystems/common/canvasArenas/ArenaTypes";
|
||||
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
|
||||
|
||||
export interface WidgetMoveParams {
|
||||
widgetId: string;
|
||||
|
|
@ -139,12 +150,66 @@ const getBottomMostRowAfterMove = (
|
|||
return widgetBottomRow;
|
||||
};
|
||||
|
||||
function* addWidgetAndMoveWidgetsSaga(
|
||||
function* addBuildingBlockAndMoveWidgetsSaga(
|
||||
actionPayload: ReduxAction<{
|
||||
newWidget: WidgetAddChild;
|
||||
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
|
||||
canvasId: string;
|
||||
}>,
|
||||
) {
|
||||
const dragDetails: DragDetails = yield select(getDragDetails);
|
||||
const buildingblockName = dragDetails.newWidget.displayName;
|
||||
const skeletonWidgetName = `loading_${buildingblockName
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "_")}`;
|
||||
|
||||
yield call(addWidgetAndMoveWidgetsSaga, {
|
||||
...actionPayload,
|
||||
payload: {
|
||||
...actionPayload.payload,
|
||||
// so that the skeleton loader does not get included when the users uses the undo/redo
|
||||
shouldReplay: false,
|
||||
newWidget: {
|
||||
...actionPayload.payload.newWidget,
|
||||
type: "SKELETON_WIDGET",
|
||||
widgetName: skeletonWidgetName,
|
||||
widgetId: MAIN_CONTAINER_WIDGET_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const skeletonWidget: FlattenedWidgetProps = yield select(
|
||||
getWidgetByName,
|
||||
skeletonWidgetName,
|
||||
);
|
||||
yield call(
|
||||
addBuildingBlockToApplication,
|
||||
actionPayload.payload.newWidget,
|
||||
skeletonWidget.widgetId,
|
||||
);
|
||||
}
|
||||
|
||||
function* addAndMoveUIEntitySaga(
|
||||
actionPayload: ReduxAction<{
|
||||
newWidget: WidgetAddChild;
|
||||
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
|
||||
canvasId: string;
|
||||
}>,
|
||||
) {
|
||||
if (actionPayload.payload.newWidget.type === BUILDING_BLOCK_EXPLORER_TYPE) {
|
||||
yield call(addBuildingBlockAndMoveWidgetsSaga, actionPayload);
|
||||
} else {
|
||||
yield call(addWidgetAndMoveWidgetsSaga, actionPayload);
|
||||
}
|
||||
}
|
||||
|
||||
export function* addWidgetAndMoveWidgetsSaga(
|
||||
actionPayload: ReduxAction<{
|
||||
newWidget: WidgetAddChild;
|
||||
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
|
||||
canvasId: string;
|
||||
shouldReplay?: boolean;
|
||||
}>,
|
||||
) {
|
||||
const start = performance.now();
|
||||
|
||||
|
|
@ -165,7 +230,11 @@ function* addWidgetAndMoveWidgetsSaga(
|
|||
) {
|
||||
throw Error;
|
||||
}
|
||||
yield put(updateAndSaveLayout(updatedWidgetsOnAddAndMove));
|
||||
yield put(
|
||||
updateAndSaveLayout(updatedWidgetsOnAddAndMove, {
|
||||
shouldReplay: actionPayload.payload.shouldReplay,
|
||||
}),
|
||||
);
|
||||
yield put(generateAutoHeightLayoutTreeAction(true, true));
|
||||
yield put({
|
||||
type: ReduxActionTypes.RECORD_RECENTLY_ADDED_WIDGET,
|
||||
|
|
@ -183,8 +252,6 @@ function* addWidgetAndMoveWidgetsSaga(
|
|||
}
|
||||
}
|
||||
|
||||
// function* update
|
||||
|
||||
function* addWidgetAndMoveWidgets(
|
||||
newWidget: WidgetAddChild,
|
||||
draggedBlocksToUpdate: WidgetDraggingUpdateParams[],
|
||||
|
|
@ -471,7 +538,7 @@ export default function* draggingCanvasSagas() {
|
|||
takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga),
|
||||
takeLatest(
|
||||
ReduxActionTypes.WIDGETS_ADD_CHILD_AND_MOVE,
|
||||
addWidgetAndMoveWidgetsSaga,
|
||||
addAndMoveUIEntitySaga,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,96 @@
|
|||
import type { WidgetAddChild } from "actions/pageActions";
|
||||
import { updateAndSaveLayout } from "actions/pageActions";
|
||||
import type {
|
||||
ImportBuildingBlockToApplicationRequest,
|
||||
ImportBuildingBlockToApplicationResponse,
|
||||
} from "@appsmith/api/ApplicationApi";
|
||||
import ApplicationApi from "@appsmith/api/ApplicationApi";
|
||||
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
||||
import {
|
||||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
WidgetReduxActionTypes,
|
||||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils";
|
||||
import {
|
||||
getActions,
|
||||
getCanvasWidgets,
|
||||
} from "@appsmith/selectors/entitiesSelector";
|
||||
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
|
||||
import { flattenDSL } from "@shared/dsl";
|
||||
import type { WidgetBlueprint } from "WidgetProvider/constants";
|
||||
import {
|
||||
BlueprintOperationTypes,
|
||||
GRID_DENSITY_MIGRATION_V1,
|
||||
} from "WidgetProvider/constants";
|
||||
import WidgetFactory from "WidgetProvider/factory";
|
||||
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
|
||||
import type { WidgetAddChild } from "actions/pageActions";
|
||||
import { updateAndSaveLayout } from "actions/pageActions";
|
||||
import { runAction } from "actions/pluginActionActions";
|
||||
import { pasteWidget } from "actions/widgetActions";
|
||||
import type { ApiResponse } from "api/ApiResponses";
|
||||
import type { Template } from "api/TemplatesApi";
|
||||
import {
|
||||
BUILDING_BLOCK_EXPLORER_TYPE,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
RenderModes,
|
||||
} from "constants/WidgetConstants";
|
||||
import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils";
|
||||
import { toast } from "design-system";
|
||||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import produce from "immer";
|
||||
import { klona as clone } from "klona/full";
|
||||
import type { CopiedWidgetData } from "layoutSystems/anvil/utils/paste/types";
|
||||
import { getWidgetHierarchy } from "layoutSystems/anvil/utils/paste/utils";
|
||||
import { getWidgetMinMaxDimensionsInPixel } from "layoutSystems/autolayout/utils/flexWidgetUtils";
|
||||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
import { isFunction } from "lodash";
|
||||
import omit from "lodash/omit";
|
||||
import log from "loglevel";
|
||||
import type {
|
||||
CanvasWidgetsReduxState,
|
||||
FlattenedWidgetProps,
|
||||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import type { WidgetBlueprint } from "WidgetProvider/constants";
|
||||
import { all, call, put, select, takeEvery } from "redux-saga/effects";
|
||||
import type { DragDetails } from "reducers/uiReducers/dragResizeReducer";
|
||||
import { all, call, put, select, take, takeEvery } from "redux-saga/effects";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import {
|
||||
getCanvasWidth,
|
||||
getCurrentApplicationId,
|
||||
getCurrentPageId,
|
||||
getIsAutoLayout,
|
||||
getIsAutoLayoutMobileBreakPoint,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getTemplatesSelector } from "selectors/templatesSelectors";
|
||||
import AppsmithConsole from "utils/AppsmithConsole";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import { generateWidgetProps } from "utils/WidgetPropsUtils";
|
||||
import { getDragDetails, getWidget, getWidgets } from "./selectors";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { getCopiedWidgets, saveCopiedWidgets } from "utils/storage";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { isStack } from "../layoutSystems/autolayout/utils/AutoLayoutUtils";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
import { postPageAdditionSaga } from "./TemplatesSagas";
|
||||
import {
|
||||
buildWidgetBlueprint,
|
||||
executeWidgetBlueprintBeforeOperations,
|
||||
executeWidgetBlueprintOperations,
|
||||
traverseTreeAndExecuteBlueprintChildOperations,
|
||||
} from "./WidgetBlueprintSagas";
|
||||
import log from "loglevel";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import WidgetFactory from "WidgetProvider/factory";
|
||||
import omit from "lodash/omit";
|
||||
import produce from "immer";
|
||||
import {
|
||||
GRID_DENSITY_MIGRATION_V1,
|
||||
BlueprintOperationTypes,
|
||||
} from "WidgetProvider/constants";
|
||||
import { getPropertiesToUpdate } from "./WidgetOperationSagas";
|
||||
import { klona as clone } from "klona/full";
|
||||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
|
||||
import { toast } from "design-system";
|
||||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
import { isStack } from "../layoutSystems/autolayout/utils/AutoLayoutUtils";
|
||||
import {
|
||||
getCanvasWidth,
|
||||
getIsAutoLayout,
|
||||
getIsAutoLayoutMobileBreakPoint,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getWidgetMinMaxDimensionsInPixel } from "layoutSystems/autolayout/utils/flexWidgetUtils";
|
||||
import { isFunction } from "lodash";
|
||||
import type { DragDetails } from "reducers/uiReducers/dragResizeReducer";
|
||||
getDefaultCanvas,
|
||||
getMousePositionFromCanvasGridPosition,
|
||||
getSnappedGrid,
|
||||
} from "./WidgetOperationUtils";
|
||||
import {
|
||||
getDragDetails,
|
||||
getWidget,
|
||||
getWidgetByName,
|
||||
getWidgets,
|
||||
} from "./selectors";
|
||||
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
|
||||
import { SelectionRequestType } from "./WidgetSelectUtils";
|
||||
import type { ActionDataState } from "@appsmith/reducers/entityReducers/actionsReducer";
|
||||
import type { WidgetLayoutPositionInfo } from "layoutSystems/anvil/utils/layouts/widgetPositionUtils";
|
||||
|
||||
const WidgetTypes = WidgetFactory.widgetTypes;
|
||||
|
||||
|
|
@ -363,7 +401,13 @@ export function* getUpdateDslAfterCreatingChild(
|
|||
*
|
||||
* @param addChildAction
|
||||
*/
|
||||
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
|
||||
export function* addChildSaga(
|
||||
addChildAction: ReduxAction<
|
||||
WidgetAddChild & {
|
||||
shouldReplay?: boolean;
|
||||
}
|
||||
>,
|
||||
) {
|
||||
try {
|
||||
const start = performance.now();
|
||||
toast.dismiss();
|
||||
|
|
@ -384,8 +428,11 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
|
|||
const updatedWidgets: {
|
||||
[widgetId: string]: FlattenedWidgetProps;
|
||||
} = yield call(getUpdateDslAfterCreatingChild, addChildAction.payload);
|
||||
|
||||
yield put(updateAndSaveLayout(updatedWidgets));
|
||||
yield put(
|
||||
updateAndSaveLayout(updatedWidgets, {
|
||||
shouldReplay: addChildAction.payload.shouldReplay,
|
||||
}),
|
||||
);
|
||||
yield put({
|
||||
type: ReduxActionTypes.RECORD_RECENTLY_ADDED_WIDGET,
|
||||
payload: [addChildAction.payload.newWidgetId],
|
||||
|
|
@ -480,23 +527,239 @@ function* addNewTabChildSaga(
|
|||
yield put(updateAndSaveLayout(updatedWidgets));
|
||||
}
|
||||
|
||||
function* addBuildingBlockActionsToApp(dragDetails: DragDetails) {
|
||||
const applicationId: string = yield select(getCurrentApplicationId);
|
||||
const buildingblockName = dragDetails.newWidget.displayName;
|
||||
const buildingBlocks: Template[] = yield select(getTemplatesSelector);
|
||||
const currentPageId: string = yield select(getCurrentPageId);
|
||||
const workspaceId: string = yield select(getCurrentWorkspaceId);
|
||||
const selectedBuildingBlock = buildingBlocks.find(
|
||||
(buildingBlock) => buildingBlock.title === buildingblockName,
|
||||
) as Template;
|
||||
|
||||
const body: ImportBuildingBlockToApplicationRequest = {
|
||||
pageId: currentPageId,
|
||||
applicationId,
|
||||
workspaceId,
|
||||
templateId: selectedBuildingBlock.id,
|
||||
};
|
||||
|
||||
// api call adds DS, queries and JS to page and returns new page dsl with building block
|
||||
const response: ApiResponse<ImportBuildingBlockToApplicationResponse> =
|
||||
yield call(ApplicationApi.importBuildingBlockToApplication, body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function* saveBuildingBlockWidgetsToStore(
|
||||
response: ApiResponse<ImportBuildingBlockToApplicationResponse>,
|
||||
) {
|
||||
const buildingBlockDsl = JSON.parse(response.data.widgetDsl);
|
||||
const buildingBlockWidgets = buildingBlockDsl.children;
|
||||
const flattenedBlockWidgets = buildingBlockWidgets.map(
|
||||
(widget: WidgetProps) => flattenDSL(widget),
|
||||
);
|
||||
|
||||
const widgetsToPasteInCanvas: CopiedWidgetData[] = yield all(
|
||||
flattenedBlockWidgets.map((widget: FlattenedWidgetProps, index: number) => {
|
||||
const widgetPositionInfo: WidgetLayoutPositionInfo | null = null;
|
||||
return {
|
||||
hierarchy: getWidgetHierarchy(
|
||||
buildingBlockWidgets[index].type,
|
||||
buildingBlockWidgets[index].widgetId,
|
||||
),
|
||||
list: Object.values(widget)
|
||||
.map((obj) => ({ ...obj }))
|
||||
.reverse(),
|
||||
parentId: MAIN_CONTAINER_WIDGET_ID,
|
||||
widgetId: buildingBlockWidgets[index].widgetId,
|
||||
widgetPositionInfo,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
yield saveCopiedWidgets(
|
||||
JSON.stringify({
|
||||
widgets: widgetsToPasteInCanvas,
|
||||
flexLayers: [],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function* getBuildingBlocksDropMousePosition(
|
||||
topRow: number,
|
||||
leftColumn: number,
|
||||
) {
|
||||
const canvasWidgets: CanvasWidgetsReduxState = yield select(getCanvasWidgets);
|
||||
let mousePosition = { x: 0, y: 0 };
|
||||
|
||||
// convert grid position to mouse position for paste functionality
|
||||
const { canvasDOM, canvasId, containerWidget } =
|
||||
getDefaultCanvas(canvasWidgets);
|
||||
if (!canvasDOM || !containerWidget || !canvasId) {
|
||||
mousePosition = { x: 0, y: 0 };
|
||||
} else {
|
||||
const canvasRect = canvasDOM.getBoundingClientRect();
|
||||
const { padding, snapGrid } = getSnappedGrid(
|
||||
containerWidget,
|
||||
canvasRect.width,
|
||||
);
|
||||
mousePosition = getMousePositionFromCanvasGridPosition(
|
||||
topRow,
|
||||
leftColumn,
|
||||
snapGrid,
|
||||
padding,
|
||||
canvasId as string,
|
||||
);
|
||||
}
|
||||
|
||||
return mousePosition;
|
||||
}
|
||||
|
||||
function* runNewlyCreatedActions(
|
||||
actionsBeforeAddingBuildingBlock: ActionDataState,
|
||||
actionsAfterAddingBuildingBlocks: ActionDataState,
|
||||
) {
|
||||
// Extract unique ids from the actionsBeforeAddingBuildingBlocks array
|
||||
const actionIdsBeforeAddingBB = actionsBeforeAddingBuildingBlock.map(
|
||||
(obj) => obj.config.id,
|
||||
);
|
||||
|
||||
// Filter the after array to find new actions not present in actionsBeforeAddingBuildingBlocks array
|
||||
const newlyAddedActions = actionsAfterAddingBuildingBlocks.filter(
|
||||
(obj) => !actionIdsBeforeAddingBB.includes(obj.config.id),
|
||||
);
|
||||
|
||||
// run all newly created actions
|
||||
if (newlyAddedActions && newlyAddedActions.length > 0) {
|
||||
yield all(
|
||||
newlyAddedActions.map(function* (action) {
|
||||
yield put(runAction(action.config.id));
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* addBuildingBlockToApplication(
|
||||
buildingBlockWidget: WidgetAddChild,
|
||||
skeletonLoaderId: string,
|
||||
) {
|
||||
const { leftColumn, topRow } = buildingBlockWidget;
|
||||
try {
|
||||
const dragDetails: DragDetails = yield select(getDragDetails);
|
||||
const applicationId: string = yield select(getCurrentApplicationId);
|
||||
const actionsBeforeAddingBuildingBlock: ActionDataState =
|
||||
yield select(getActions);
|
||||
const existingCopiedWidgets: unknown = yield call(getCopiedWidgets);
|
||||
|
||||
// start loading for dragging building blocks
|
||||
yield put({
|
||||
type: ReduxActionTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_INIT,
|
||||
});
|
||||
|
||||
// makes sure updateAndSaveLayout completes first for skeletonWidget addition
|
||||
yield take(ReduxActionTypes.SAVE_PAGE_SUCCESS);
|
||||
|
||||
const response: ApiResponse<ImportBuildingBlockToApplicationResponse> =
|
||||
yield call(addBuildingBlockActionsToApp, dragDetails);
|
||||
const isValid: boolean = yield validateResponse(response);
|
||||
|
||||
if (isValid) {
|
||||
yield saveBuildingBlockWidgetsToStore(response);
|
||||
|
||||
const mousePosition: { x: number; y: number } = yield call(
|
||||
getBuildingBlocksDropMousePosition,
|
||||
topRow,
|
||||
leftColumn,
|
||||
);
|
||||
|
||||
// remove skeleton loader just before pasting the building block
|
||||
yield put({
|
||||
type: WidgetReduxActionTypes.WIDGET_SINGLE_DELETE,
|
||||
payload: {
|
||||
widgetId: skeletonLoaderId,
|
||||
parentId: MAIN_CONTAINER_WIDGET_ID,
|
||||
disallowUndo: true,
|
||||
isShortcut: false,
|
||||
},
|
||||
});
|
||||
|
||||
yield put(pasteWidget(false, mousePosition));
|
||||
yield call(postPageAdditionSaga, applicationId);
|
||||
// remove selecting of recently imported widgets
|
||||
yield put(selectWidgetInitAction(SelectionRequestType.Empty));
|
||||
|
||||
// stop loading after pasting process is complete
|
||||
yield put({
|
||||
type: ReduxActionTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_SUCCESS,
|
||||
});
|
||||
|
||||
const actionsAfterAddingBuildingBlocks: ActionDataState =
|
||||
yield select(getActions);
|
||||
|
||||
yield runNewlyCreatedActions(
|
||||
actionsBeforeAddingBuildingBlock,
|
||||
actionsAfterAddingBuildingBlocks,
|
||||
);
|
||||
|
||||
if (existingCopiedWidgets) {
|
||||
yield call(saveCopiedWidgets, JSON.stringify(existingCopiedWidgets));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: WidgetReduxActionTypes.WIDGET_SINGLE_DELETE,
|
||||
payload: {
|
||||
widgetId: skeletonLoaderId,
|
||||
parentId: MAIN_CONTAINER_WIDGET_ID,
|
||||
disallowUndo: true,
|
||||
isShortcut: false,
|
||||
},
|
||||
});
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.DRAGGING_BUILDING_BLOCK_TO_CANVAS_ERROR,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function* addBuildingBlockSaga(addEntityAction: ReduxAction<WidgetAddChild>) {
|
||||
const dragDetails: DragDetails = yield select(getDragDetails);
|
||||
const buildingblockName = dragDetails.newWidget.displayName;
|
||||
const skeletonWidgetName = `loading_${buildingblockName
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "_")}`;
|
||||
const addSkeletonWidgetAction: ReduxAction<
|
||||
WidgetAddChild & { shouldReplay: boolean }
|
||||
> = {
|
||||
...addEntityAction,
|
||||
payload: {
|
||||
...addEntityAction.payload,
|
||||
type: "SKELETON_WIDGET",
|
||||
widgetName: skeletonWidgetName,
|
||||
widgetId: MAIN_CONTAINER_WIDGET_ID,
|
||||
// so that the skeleton loader does not get included when the users uses the undo/redo
|
||||
shouldReplay: false,
|
||||
},
|
||||
};
|
||||
yield call(addChildSaga, addSkeletonWidgetAction);
|
||||
const skeletonWidget: FlattenedWidgetProps = yield select(
|
||||
getWidgetByName,
|
||||
skeletonWidgetName,
|
||||
);
|
||||
yield call(
|
||||
addBuildingBlockToApplication,
|
||||
addEntityAction.payload,
|
||||
skeletonWidget.widgetId,
|
||||
);
|
||||
}
|
||||
|
||||
function* addUIEntitySaga(addEntityAction: ReduxAction<WidgetAddChild>) {
|
||||
try {
|
||||
if (addEntityAction.payload.type === BUILDING_BLOCK_EXPLORER_TYPE) {
|
||||
const dragDetails: DragDetails = yield select(getDragDetails);
|
||||
const buildingblockName = dragDetails.newWidget.displayName;
|
||||
const skeletonWidgetName = `loading_${buildingblockName
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "_")}`;
|
||||
const addSkeletonWidgetAction: ReduxAction<WidgetAddChild> = {
|
||||
...addEntityAction,
|
||||
payload: {
|
||||
...addEntityAction.payload,
|
||||
type: "SKELETON_WIDGET",
|
||||
widgetName: skeletonWidgetName,
|
||||
},
|
||||
};
|
||||
yield call(addChildSaga, addSkeletonWidgetAction);
|
||||
const { payload } = addEntityAction;
|
||||
const { type } = payload;
|
||||
|
||||
if (type === BUILDING_BLOCK_EXPLORER_TYPE) {
|
||||
yield call(addBuildingBlockSaga, addEntityAction);
|
||||
} else {
|
||||
yield call(addChildSaga, addEntityAction);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import {
|
||||
getFocusedWidget,
|
||||
getSelectedWidget,
|
||||
getWidgetMetaProps,
|
||||
getWidgets,
|
||||
} from "./selectors";
|
||||
import _, { find, isString, reduce, remove } from "lodash";
|
||||
import type { WidgetEntity } from "@appsmith/entities/DataTree/types";
|
||||
import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import WidgetFactory from "WidgetProvider/factory";
|
||||
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
|
||||
import type {
|
||||
OccupiedSpace,
|
||||
WidgetSpace,
|
||||
} from "constants/CanvasEditorConstants";
|
||||
import type { WidgetType } from "constants/WidgetConstants";
|
||||
import { AUTO_LAYOUT_CONTAINER_PADDING } from "constants/WidgetConstants";
|
||||
import {
|
||||
AUTO_LAYOUT_CONTAINER_PADDING,
|
||||
CONTAINER_GRID_PADDING,
|
||||
FLEXBOX_PADDING,
|
||||
GridDefaults,
|
||||
|
|
@ -15,31 +16,29 @@ import {
|
|||
RenderModes,
|
||||
WIDGET_PADDING,
|
||||
} from "constants/WidgetConstants";
|
||||
import { all, call } from "redux-saga/effects";
|
||||
import {
|
||||
POSITIONED_WIDGET,
|
||||
getBaseWidgetClassName,
|
||||
getSlidingArenaName,
|
||||
getStickyCanvasName,
|
||||
} from "constants/componentClassNameConstants";
|
||||
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||
import { select } from "redux-saga/effects";
|
||||
import { getCopiedWidgets } from "utils/storage";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { getSelectedWidgets } from "selectors/ui";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import {
|
||||
getWidgetLayoutMetaInfo,
|
||||
type WidgetLayoutPositionInfo,
|
||||
} from "layoutSystems/anvil/utils/layouts/widgetPositionUtils";
|
||||
import type { CopiedWidgetData } from "layoutSystems/anvil/utils/paste/types";
|
||||
import { getWidgetHierarchy } from "layoutSystems/anvil/utils/paste/utils";
|
||||
import { Positioning } from "layoutSystems/common/utils/constants";
|
||||
import { LayoutSystemTypes } from "layoutSystems/types";
|
||||
import _, { find, isString, reduce, remove } from "lodash";
|
||||
import type {
|
||||
CanvasWidgetsReduxState,
|
||||
FlattenedWidgetProps,
|
||||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import type { DynamicPath } from "utils/DynamicBindingUtils";
|
||||
import {
|
||||
getDynamicBindings,
|
||||
combineDynamicBindings,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import WidgetFactory from "WidgetProvider/factory";
|
||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||
import type {
|
||||
OccupiedSpace,
|
||||
WidgetSpace,
|
||||
} from "constants/CanvasEditorConstants";
|
||||
import { areIntersecting } from "utils/boxHelpers";
|
||||
import type { MetaState } from "reducers/entityReducers/metaReducer";
|
||||
import { all, call, select } from "redux-saga/effects";
|
||||
import { reflow } from "reflow";
|
||||
import type {
|
||||
GridProps,
|
||||
PrevReflowState,
|
||||
|
|
@ -47,27 +46,27 @@ import type {
|
|||
SpaceMap,
|
||||
} from "reflow/reflowTypes";
|
||||
import { ReflowDirection } from "reflow/reflowTypes";
|
||||
import {
|
||||
getBaseWidgetClassName,
|
||||
getStickyCanvasName,
|
||||
getSlidingArenaName,
|
||||
POSITIONED_WIDGET,
|
||||
} from "constants/componentClassNameConstants";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import { getContainerWidgetSpacesSelector } from "selectors/editorSelectors";
|
||||
import { reflow } from "reflow";
|
||||
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
|
||||
import type { WidgetEntity } from "@appsmith/entities/DataTree/types";
|
||||
import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
|
||||
import type { MetaState } from "reducers/entityReducers/metaReducer";
|
||||
import { LayoutSystemTypes } from "layoutSystems/types";
|
||||
import { Positioning } from "layoutSystems/common/utils/constants";
|
||||
import { getWidgetHierarchy } from "layoutSystems/anvil/utils/paste/utils";
|
||||
import type { CopiedWidgetData } from "layoutSystems/anvil/utils/paste/types";
|
||||
import { getSelectedWidgets } from "selectors/ui";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import type { DynamicPath } from "utils/DynamicBindingUtils";
|
||||
import {
|
||||
getWidgetLayoutMetaInfo,
|
||||
type WidgetLayoutPositionInfo,
|
||||
} from "layoutSystems/anvil/utils/layouts/widgetPositionUtils";
|
||||
combineDynamicBindings,
|
||||
getDynamicBindings,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { areIntersecting } from "utils/boxHelpers";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
|
||||
import { getCopiedWidgets } from "utils/storage";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||
import {
|
||||
getFocusedWidget,
|
||||
getSelectedWidget,
|
||||
getWidgetMetaProps,
|
||||
getWidgets,
|
||||
} from "./selectors";
|
||||
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
|
||||
|
||||
export interface CopiedWidgetGroup {
|
||||
|
|
@ -693,6 +692,49 @@ export const getSelectedWidgetWhenPasting = function* () {
|
|||
return selectedWidget;
|
||||
};
|
||||
|
||||
function getStickyCanvasDOM(canvasId: string) {
|
||||
// get DOM of the overall canvas including it's total scroll height
|
||||
const stickyCanvasDOM = document.querySelector(
|
||||
`#${getSlidingArenaName(canvasId)}`,
|
||||
);
|
||||
return stickyCanvasDOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates mouse positions given canvas grid positions
|
||||
*
|
||||
* @param canvasRect canvas DOM rect
|
||||
* @param canvasId Id of the canvas widget
|
||||
* @param snapGrid grid parameters
|
||||
* @param padding padding inside of widget
|
||||
* @param canvasPosition position in canvas rows and columns
|
||||
* @returns
|
||||
*/
|
||||
export function getMousePositionFromCanvasGridPosition(
|
||||
top: number,
|
||||
left: number,
|
||||
snapGrid: { snapRowSpace: number; snapColumnSpace: number },
|
||||
padding: number,
|
||||
canvasId: string,
|
||||
) {
|
||||
// Get the canvas element
|
||||
const stickyCanvasDOM = getStickyCanvasDOM(canvasId);
|
||||
|
||||
if (!stickyCanvasDOM) return { x: 0, y: 0 };
|
||||
|
||||
const canvasRect = stickyCanvasDOM.getBoundingClientRect();
|
||||
|
||||
// Calculate actual mouse positions
|
||||
const x = left * snapGrid.snapColumnSpace + padding;
|
||||
const y = top * snapGrid.snapRowSpace + padding;
|
||||
|
||||
// Calculate actual mouse positions relative to the window
|
||||
const actualX = x + canvasRect.left;
|
||||
const actualY = y + canvasRect.top;
|
||||
|
||||
return { x: actualX, y: actualY };
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates mouse positions in terms of grid values
|
||||
*
|
||||
|
|
@ -722,10 +764,8 @@ export function getMousePositions(
|
|||
)
|
||||
return;
|
||||
|
||||
//get DOM of the overall canvas including it's total scroll height
|
||||
const stickyCanvasDOM = document.querySelector(
|
||||
`#${getSlidingArenaName(canvasId)}`,
|
||||
);
|
||||
const stickyCanvasDOM = getStickyCanvasDOM(canvasId);
|
||||
|
||||
if (!stickyCanvasDOM) return;
|
||||
|
||||
const rect = stickyCanvasDOM.getBoundingClientRect();
|
||||
|
|
|
|||
4
app/client/src/selectors/buildingBlocksSelectors.ts
Normal file
4
app/client/src/selectors/buildingBlocksSelectors.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import type { AppState } from "@appsmith/reducers";
|
||||
|
||||
export const isDraggingBuildingBlockToCanvas = (state: AppState) =>
|
||||
state.ui.buildingBlocks.isDraggingBuildingBlocksToCanvas;
|
||||
|
|
@ -47,3 +47,6 @@ export const getWidgetSelectorByWidgetId = (
|
|||
return widgetId;
|
||||
}
|
||||
};
|
||||
|
||||
export const isFixedLayoutSelector = (state: AppState) =>
|
||||
getLayoutSystemType(state) === LayoutSystemTypes.FIXED;
|
||||
|
|
|
|||
|
|
@ -74,8 +74,12 @@ export const getBuildingBlockExplorerCards = createSelector(
|
|||
(buildingBlocks) => {
|
||||
const adjustedBuildingBlocks: WidgetCardProps[] = buildingBlocks.map(
|
||||
(buildingBlock) => ({
|
||||
rows: DEFAULT_ROWS_FOR_EXPLORER_BUILDING_BLOCKS,
|
||||
columns: DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS,
|
||||
rows:
|
||||
buildingBlock.templateGridRowSize ||
|
||||
DEFAULT_ROWS_FOR_EXPLORER_BUILDING_BLOCKS,
|
||||
columns:
|
||||
buildingBlock.templateGridColumnSize ||
|
||||
DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS,
|
||||
type: "BUILDING_BLOCK",
|
||||
displayName: buildingBlock.title,
|
||||
icon:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { useSelector } from "react-redux";
|
||||
import { getInstanceId } from "@appsmith/selectors/tenantSelectors";
|
||||
import { CUSTOMER_PORTAL_URL_WITH_PARAMS } from "constants/ThirdPartyConstants";
|
||||
import {
|
||||
CUSTOMER_PORTAL_URL_WITH_PARAMS,
|
||||
PRICING_PAGE_URL,
|
||||
} from "constants/ThirdPartyConstants";
|
||||
import type { EventName } from "@appsmith/utils/analyticsUtilTypes";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||
|
|
@ -15,10 +18,12 @@ interface Props {
|
|||
logEventData?: any;
|
||||
featureName?: RampFeature;
|
||||
sectionName?: RampSection;
|
||||
isEnterprise?: boolean;
|
||||
}
|
||||
|
||||
const useOnUpgrade = (props: Props) => {
|
||||
const { featureName, logEventData, logEventName, sectionName } = props;
|
||||
const { featureName, isEnterprise, logEventData, logEventName, sectionName } =
|
||||
props;
|
||||
const instanceId = useSelector(getInstanceId);
|
||||
const appsmithConfigs = getAppsmithConfigs();
|
||||
|
||||
|
|
@ -27,16 +32,28 @@ const useOnUpgrade = (props: Props) => {
|
|||
logEventName || "ADMIN_SETTINGS_UPGRADE",
|
||||
logEventData,
|
||||
);
|
||||
window.open(
|
||||
CUSTOMER_PORTAL_URL_WITH_PARAMS(
|
||||
appsmithConfigs.customerPortalUrl,
|
||||
pricingPageUrlSource,
|
||||
instanceId,
|
||||
featureName,
|
||||
sectionName,
|
||||
),
|
||||
"_blank",
|
||||
);
|
||||
if (isEnterprise) {
|
||||
window.open(
|
||||
PRICING_PAGE_URL(
|
||||
appsmithConfigs.pricingUrl,
|
||||
pricingPageUrlSource,
|
||||
instanceId,
|
||||
featureName,
|
||||
sectionName,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
window.open(
|
||||
CUSTOMER_PORTAL_URL_WITH_PARAMS(
|
||||
appsmithConfigs.customerPortalUrl,
|
||||
pricingPageUrlSource,
|
||||
instanceId,
|
||||
featureName,
|
||||
sectionName,
|
||||
),
|
||||
"_blank",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return { onUpgrade };
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { AnvilConfig } from "WidgetProvider/constants";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import type { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import BaseWidget from "./BaseWidget";
|
||||
import type { AnvilConfig } from "WidgetProvider/constants";
|
||||
|
||||
const SkeletonWrapper = styled.div`
|
||||
height: 100%;
|
||||
|
|
@ -29,6 +29,19 @@ class SkeletonWidget extends BaseWidget<SkeletonWidgetProps, WidgetState> {
|
|||
};
|
||||
}
|
||||
|
||||
static getAutoLayoutConfig() {
|
||||
return {
|
||||
widgetSize: [
|
||||
{
|
||||
viewportMinWidth: 0,
|
||||
configuration: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
static getAnvilConfig(): AnvilConfig | null {
|
||||
return {
|
||||
isLargeWidget: false,
|
||||
|
|
|
|||
|
|
@ -5954,6 +5954,7 @@ export default {
|
|||
label: "Port",
|
||||
configProperty: "datasourceConfiguration.endpoints[*].port",
|
||||
dataType: "NUMBER",
|
||||
initialValue: ["5432"],
|
||||
controlType: "KEYVALUE_ARRAY",
|
||||
placeholderText: "5432",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
<artifactId>integrated</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.appsmith</groupId>
|
||||
<artifactId>appsmith-git</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,25 @@
|
|||
package com.appsmith.git.helpers.ce;
|
||||
package com.appsmith.git.files;
|
||||
|
||||
import com.appsmith.external.converters.ISOStringToInstantConverter;
|
||||
import com.appsmith.external.dtos.ModifiedResources;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.git.FileInterface;
|
||||
import com.appsmith.external.git.GitExecutor;
|
||||
import com.appsmith.external.git.constants.GitSpan;
|
||||
import com.appsmith.external.git.operations.FileOperations;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.external.helpers.Stopwatch;
|
||||
import com.appsmith.external.models.ApplicationGitReference;
|
||||
import com.appsmith.external.models.ArtifactGitReference;
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.constants.CommonConstants;
|
||||
import com.appsmith.git.converters.GsonDoubleToLongConverter;
|
||||
import com.appsmith.git.converters.GsonUnorderedToOrderedConverter;
|
||||
import com.appsmith.git.helpers.DSLTransformerHelper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import io.micrometer.tracing.Span;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -42,21 +32,15 @@ import reactor.core.scheduler.Schedulers;
|
|||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
|
@ -65,7 +49,6 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.appsmith.external.git.constants.GitConstants.ACTION_COLLECTION_LIST;
|
||||
import static com.appsmith.external.git.constants.GitConstants.ACTION_LIST;
|
||||
|
|
@ -73,10 +56,8 @@ import static com.appsmith.external.git.constants.GitConstants.CUSTOM_JS_LIB_LIS
|
|||
import static com.appsmith.external.git.constants.GitConstants.NAME_SEPARATOR;
|
||||
import static com.appsmith.external.git.constants.GitConstants.PAGE_LIST;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.ACTION_COLLECTION_BODY;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.METADATA;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.NEW_ACTION_BODY;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.RESOURCE_TYPE;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.WIDGETS;
|
||||
import static com.appsmith.git.constants.GitDirectories.ACTION_COLLECTION_DIRECTORY;
|
||||
import static com.appsmith.git.constants.GitDirectories.ACTION_DIRECTORY;
|
||||
import static com.appsmith.git.constants.GitDirectories.DATASOURCE_DIRECTORY;
|
||||
|
|
@ -91,7 +72,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
private final GitServiceConfig gitServiceConfig;
|
||||
private final GitExecutor gitExecutor;
|
||||
private final Gson gson;
|
||||
private final FileOperations fileOperations;
|
||||
private final ObservationHelper observationHelper;
|
||||
|
||||
private static final String EDIT_MODE_URL_TEMPLATE = "{{editModeUrl}}";
|
||||
|
|
@ -108,26 +89,11 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
public FileUtilsCEImpl(
|
||||
GitServiceConfig gitServiceConfig,
|
||||
GitExecutor gitExecutor,
|
||||
GsonBuilder gsonBuilder,
|
||||
FileOperations fileOperations,
|
||||
ObservationHelper observationHelper) {
|
||||
this.gitServiceConfig = gitServiceConfig;
|
||||
this.gitExecutor = gitExecutor;
|
||||
|
||||
// Gson to pretty format JSON file
|
||||
// Keep Long type as is by default GSON have behavior to convert to Double
|
||||
// Convert unordered set to ordered one
|
||||
this.gson = gsonBuilder
|
||||
.registerTypeAdapter(Double.class, new GsonDoubleToLongConverter())
|
||||
.registerTypeAdapter(Set.class, new GsonUnorderedToOrderedConverter())
|
||||
.registerTypeAdapter(Map.class, new GsonUnorderedToOrderedConverter())
|
||||
.registerTypeAdapter(Instant.class, new ISOStringToInstantConverter())
|
||||
// Instance creator is required while de-serialising using Gson as key instance can't be invoked
|
||||
// with no-args constructor
|
||||
.registerTypeAdapter(DatasourceStructure.Key.class, new DatasourceStructure.KeyInstanceCreator())
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
this.fileOperations = fileOperations;
|
||||
this.observationHelper = observationHelper;
|
||||
}
|
||||
|
||||
|
|
@ -255,8 +221,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
ModifiedResources modifiedResources = applicationGitReference.getModifiedResources();
|
||||
|
||||
// Remove unwanted directories which was present in v1 of the git file format version
|
||||
deleteDirectory(baseRepo.resolve(ACTION_DIRECTORY));
|
||||
deleteDirectory(baseRepo.resolve(ACTION_COLLECTION_DIRECTORY));
|
||||
fileOperations.deleteDirectory(baseRepo.resolve(ACTION_DIRECTORY));
|
||||
fileOperations.deleteDirectory(baseRepo.resolve(ACTION_COLLECTION_DIRECTORY));
|
||||
|
||||
// Save application
|
||||
saveResource(
|
||||
|
|
@ -264,9 +230,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
baseRepo.resolve(CommonConstants.APPLICATION + CommonConstants.JSON_EXTENSION));
|
||||
|
||||
// Save application metadata
|
||||
JsonObject metadata = gson.fromJson(gson.toJson(applicationGitReference.getMetadata()), JsonObject.class);
|
||||
metadata.addProperty(CommonConstants.FILE_FORMAT_VERSION, CommonConstants.fileFormatVersion);
|
||||
saveResource(metadata, baseRepo.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
fileOperations.saveMetadataResource(applicationGitReference, baseRepo);
|
||||
|
||||
// Save application theme
|
||||
saveResource(
|
||||
|
|
@ -307,19 +271,20 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
Path path = Paths.get(
|
||||
String.valueOf(pageSpecificDirectory.resolve(CommonConstants.WIDGETS)), childPath);
|
||||
validWidgetToParentMap.put(widgetName, path.toFile().toString());
|
||||
saveWidgets(jsonObject, widgetName, path);
|
||||
fileOperations.saveWidgets(jsonObject, widgetName, path);
|
||||
});
|
||||
// Remove deleted widgets from the file system
|
||||
deleteWidgets(
|
||||
pageSpecificDirectory.resolve(CommonConstants.WIDGETS).toFile(), validWidgetToParentMap);
|
||||
|
||||
// Remove the canvas.json from the file system since the value is stored in the page.json
|
||||
deleteFile(pageSpecificDirectory.resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION));
|
||||
fileOperations.deleteFile(
|
||||
pageSpecificDirectory.resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
validPages.add(pageName);
|
||||
}
|
||||
|
||||
scanAndDeleteDirectoryForDeletedResources(validPages, baseRepo.resolve(PAGE_DIRECTORY));
|
||||
fileOperations.scanAndDeleteDirectoryForDeletedResources(validPages, baseRepo.resolve(PAGE_DIRECTORY));
|
||||
|
||||
// Save JS Libs if there's at least one change
|
||||
if (modifiedResources != null
|
||||
|
|
@ -341,7 +306,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
}
|
||||
validJsLibs.add(fileNameWithExtension);
|
||||
});
|
||||
scanAndDeleteFileForDeletedResources(validJsLibs, jsLibDirectory);
|
||||
fileOperations.scanAndDeleteFileForDeletedResources(validJsLibs, jsLibDirectory);
|
||||
}
|
||||
|
||||
// Create HashMap for valid actions and actionCollections
|
||||
|
|
@ -381,7 +346,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
queryName,
|
||||
actionSpecificDirectory.resolve(queryName));
|
||||
// Delete the resource from the old file structure v2
|
||||
deleteFile(pageSpecificDirectory
|
||||
fileOperations.deleteFile(pageSpecificDirectory
|
||||
.resolve(ACTION_DIRECTORY)
|
||||
.resolve(queryName + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
|
|
@ -390,7 +355,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
validActionsMap.forEach((pageName, validActionNames) -> {
|
||||
Path pageSpecificDirectory = pageDirectory.resolve(pageName);
|
||||
scanAndDeleteDirectoryForDeletedResources(
|
||||
fileOperations.scanAndDeleteDirectoryForDeletedResources(
|
||||
validActionNames, pageSpecificDirectory.resolve(ACTION_DIRECTORY));
|
||||
});
|
||||
|
||||
|
|
@ -419,7 +384,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
actionCollectionName,
|
||||
actionCollectionSpecificDirectory.resolve(actionCollectionName));
|
||||
// Delete the resource from the old file structure v2
|
||||
deleteFile(actionCollectionSpecificDirectory.resolve(
|
||||
fileOperations.deleteFile(actionCollectionSpecificDirectory.resolve(
|
||||
actionCollectionName + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
}
|
||||
|
|
@ -428,7 +393,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
// Verify if the old files are deleted
|
||||
validActionCollectionsMap.forEach((pageName, validActionCollectionNames) -> {
|
||||
Path pageSpecificDirectory = pageDirectory.resolve(pageName);
|
||||
scanAndDeleteDirectoryForDeletedResources(
|
||||
fileOperations.scanAndDeleteDirectoryForDeletedResources(
|
||||
validActionCollectionNames, pageSpecificDirectory.resolve(ACTION_COLLECTION_DIRECTORY));
|
||||
});
|
||||
|
||||
|
|
@ -442,7 +407,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
}
|
||||
// Scan datasource directory and delete any unwanted files if present
|
||||
if (!applicationGitReference.getDatasources().isEmpty()) {
|
||||
scanAndDeleteFileForDeletedResources(validDatasourceFileNames, baseRepo.resolve(DATASOURCE_DIRECTORY));
|
||||
fileOperations.scanAndDeleteFileForDeletedResources(
|
||||
validDatasourceFileNames, baseRepo.resolve(DATASOURCE_DIRECTORY));
|
||||
}
|
||||
|
||||
return validPages;
|
||||
|
|
@ -458,7 +424,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
protected boolean saveResource(Object sourceEntity, Path path) {
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
return writeToFile(sourceEntity, path);
|
||||
return fileOperations.writeToFile(sourceEntity, path);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while writing resource to file {} with {}", path, e.getMessage());
|
||||
log.debug(e.getMessage());
|
||||
|
|
@ -466,22 +432,6 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void saveWidgets(JSONObject sourceEntity, String resourceName, Path path) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
String resourceType = WIDGETS;
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
writeStringToFile(sourceEntity.toString(4), path.resolve(resourceName + CommonConstants.JSON_EXTENSION));
|
||||
} catch (IOException e) {
|
||||
log.debug("Error while writings widgets data to file, {}", e.getMessage());
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to write actionCollection specific resource to file system. We write the data in two steps
|
||||
* 1. Actual js code
|
||||
|
|
@ -508,7 +458,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
// Write metadata for the jsObject
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION);
|
||||
return writeToFile(sourceEntity, metadataPath);
|
||||
return fileOperations.writeToFile(sourceEntity, metadataPath);
|
||||
} catch (IOException e) {
|
||||
log.debug(e.getMessage());
|
||||
} finally {
|
||||
|
|
@ -544,7 +494,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
// Write metadata for the actions
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION);
|
||||
return writeToFile(sourceEntity, metadataPath);
|
||||
return fileOperations.writeToFile(sourceEntity, metadataPath);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while reading file {} with message {} with cause", path, e.getMessage(), e.getCause());
|
||||
} finally {
|
||||
|
|
@ -553,101 +503,9 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean writeStringToFile(String data, Path path) throws IOException {
|
||||
private void writeStringToFile(String sourceEntity, Path path) throws IOException {
|
||||
try (BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
|
||||
fileWriter.write(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeToFile(Object sourceEntity, Path path) throws IOException {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
String resourceType = sourceEntity.getClass().getSimpleName();
|
||||
if (!(sourceEntity instanceof BaseDomain)) {
|
||||
resourceType = METADATA;
|
||||
}
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
try (BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
|
||||
gson.toJson(sourceEntity, fileWriter);
|
||||
return true;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the JSON resource available in local git directory on subsequent commit made after the
|
||||
* deletion of respective resource from DB
|
||||
*
|
||||
* @param validResources resources those are still available in DB
|
||||
* @param resourceDirectory directory which needs to be scanned for possible file deletion operations
|
||||
*/
|
||||
public void scanAndDeleteFileForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
// Scan resource directory and delete any unwanted file if present
|
||||
// unwanted file : corresponding resource from DB has been deleted
|
||||
if (resourceDirectory.toFile().exists()) {
|
||||
try (Stream<Path> paths = Files.walk(resourceDirectory)) {
|
||||
paths.filter(pathLocal -> Files.isRegularFile(pathLocal)
|
||||
&& !validResources.contains(
|
||||
pathLocal.getFileName().toString()))
|
||||
.forEach(this::deleteFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while scanning directory: {}, with error {}", resourceDirectory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the JSON resource directory available in local git directory on subsequent commit made after the
|
||||
* deletion of respective resource from DB
|
||||
*
|
||||
* @param validResources resources those are still available in DB
|
||||
* @param resourceDirectory directory which needs to be scanned for possible file deletion operations
|
||||
*/
|
||||
public void scanAndDeleteDirectoryForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
// Scan resource directory and delete any unwanted directory if present
|
||||
// unwanted directory : corresponding resource from DB has been deleted
|
||||
if (resourceDirectory.toFile().exists()) {
|
||||
try (Stream<Path> paths = Files.walk(resourceDirectory, 1)) {
|
||||
paths.filter(path -> Files.isDirectory(path)
|
||||
&& !path.equals(resourceDirectory)
|
||||
&& !validResources.contains(path.getFileName().toString()))
|
||||
.forEach(this::deleteDirectory);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while scanning directory {} with error {}", resourceDirectory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the directory and all its contents
|
||||
*
|
||||
* @param directory
|
||||
*/
|
||||
private void deleteDirectory(Path directory) {
|
||||
if (directory.toFile().exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(directory.toFile());
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to delete directory for path {} with message {}", directory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the file from local repo
|
||||
*
|
||||
* @param filePath file that needs to be deleted
|
||||
*/
|
||||
private void deleteFile(Path filePath) {
|
||||
try {
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (DirectoryNotEmptyException e) {
|
||||
log.error("Unable to delete non-empty directory at {} with cause", filePath, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to delete file {} with {}", filePath, e.getMessage());
|
||||
fileWriter.write(sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -747,73 +605,6 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json file present from the local git repo
|
||||
*
|
||||
* @param filePath file on which the read operation will be performed
|
||||
* @return resource stored in the JSON file
|
||||
*/
|
||||
public Object readFile(Path filePath) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_READ);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
Object file;
|
||||
try (JsonReader reader = new JsonReader(new FileReader(filePath.toFile()))) {
|
||||
file = gson.fromJson(reader, Object.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while reading file {} with message {} with cause", filePath, e.getMessage(), e.getCause());
|
||||
return null;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json files present from the local git repo
|
||||
*
|
||||
* @param directoryPath directory path for files on which read operation will be performed
|
||||
* @return resources stored in the directory
|
||||
*/
|
||||
protected Map<String, Object> readFiles(Path directoryPath, String keySuffix) {
|
||||
Map<String, Object> resource = new HashMap<>();
|
||||
File directory = directoryPath.toFile();
|
||||
if (directory.isDirectory()) {
|
||||
Arrays.stream(Objects.requireNonNull(directory.listFiles())).forEach(file -> {
|
||||
try (JsonReader reader = new JsonReader(new FileReader(file))) {
|
||||
resource.put(file.getName() + keySuffix, gson.fromJson(reader, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Error while reading file {} with message {} with cause",
|
||||
file.toPath(),
|
||||
e.getMessage(),
|
||||
e.getCause());
|
||||
}
|
||||
});
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will read the content of the file as a plain text and does not apply the gson to json transformation
|
||||
*
|
||||
* @param filePath file path for files on which read operation will be performed
|
||||
* @return content of the file in the path
|
||||
*/
|
||||
private String readFileAsString(Path filePath) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_READ);
|
||||
observationHelper.startSpan(span, true);
|
||||
String data = CommonConstants.EMPTY_STRING;
|
||||
try {
|
||||
data = FileUtils.readFileToString(filePath.toFile(), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
log.error("Error while reading the file from git repo {} ", e.getMessage());
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is to read the content for action and actionCollection or any nested resources which has the new structure - v3
|
||||
* Where the user written JS Object code and the metadata is split into to different files
|
||||
|
|
@ -832,9 +623,9 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
directoryPath.resolve(resourceName).resolve(resourceName + CommonConstants.JS_EXTENSION);
|
||||
String body = CommonConstants.EMPTY_STRING;
|
||||
if (resourcePath.toFile().exists()) {
|
||||
body = readFileAsString(resourcePath);
|
||||
body = fileOperations.readFileAsString(resourcePath);
|
||||
}
|
||||
Object file = readFile(directoryPath
|
||||
Object file = fileOperations.readFile(directoryPath
|
||||
.resolve(resourceName)
|
||||
.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
actionCollectionBodyMap.put(resourceName + keySuffix, body);
|
||||
|
|
@ -862,9 +653,9 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
Path queryPath =
|
||||
directoryPath.resolve(resourceName).resolve(resourceName + CommonConstants.TEXT_FILE_EXTENSION);
|
||||
if (queryPath.toFile().exists()) {
|
||||
body = readFileAsString(queryPath);
|
||||
body = fileOperations.readFileAsString(queryPath);
|
||||
}
|
||||
Object file = readFile(directoryPath
|
||||
Object file = fileOperations.readFile(directoryPath
|
||||
.resolve(resourceName)
|
||||
.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
actionCollectionBodyMap.put(resourceName + keySuffix, body);
|
||||
|
|
@ -875,38 +666,40 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
}
|
||||
|
||||
private Object readPageMetadata(Path directoryPath) {
|
||||
return readFile(directoryPath.resolve(directoryPath.toFile().getName() + CommonConstants.JSON_EXTENSION));
|
||||
return fileOperations.readFile(
|
||||
directoryPath.resolve(directoryPath.toFile().getName() + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
|
||||
private ApplicationGitReference fetchApplicationReference(Path baseRepoPath) {
|
||||
ApplicationGitReference applicationGitReference = new ApplicationGitReference();
|
||||
// Extract application metadata from the json
|
||||
Object metadata = readFile(baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Integer fileFormatVersion = getFileFormatVersion(metadata);
|
||||
Object metadata = fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Integer fileFormatVersion = fileOperations.getFileFormatVersion(metadata);
|
||||
// Check if fileFormat of the saved files in repo is compatible
|
||||
if (!isFileFormatCompatible(fileFormatVersion)) {
|
||||
throw new AppsmithPluginException(AppsmithPluginError.INCOMPATIBLE_FILE_FORMAT);
|
||||
}
|
||||
// Extract application data from the json
|
||||
applicationGitReference.setApplication(
|
||||
readFile(baseRepoPath.resolve(CommonConstants.APPLICATION + CommonConstants.JSON_EXTENSION)));
|
||||
applicationGitReference.setApplication(fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.APPLICATION + CommonConstants.JSON_EXTENSION)));
|
||||
applicationGitReference.setTheme(
|
||||
readFile(baseRepoPath.resolve(CommonConstants.THEME + CommonConstants.JSON_EXTENSION)));
|
||||
fileOperations.readFile(baseRepoPath.resolve(CommonConstants.THEME + CommonConstants.JSON_EXTENSION)));
|
||||
Path pageDirectory = baseRepoPath.resolve(PAGE_DIRECTORY);
|
||||
// Reconstruct application from given file format
|
||||
switch (fileFormatVersion) {
|
||||
case 1:
|
||||
// Extract actions
|
||||
applicationGitReference.setActions(
|
||||
readFiles(baseRepoPath.resolve(ACTION_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
fileOperations.readFiles(baseRepoPath.resolve(ACTION_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
// Extract actionCollections
|
||||
applicationGitReference.setActionCollections(
|
||||
readFiles(baseRepoPath.resolve(ACTION_COLLECTION_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
applicationGitReference.setActionCollections(fileOperations.readFiles(
|
||||
baseRepoPath.resolve(ACTION_COLLECTION_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
// Extract pages
|
||||
applicationGitReference.setPages(readFiles(pageDirectory, CommonConstants.EMPTY_STRING));
|
||||
applicationGitReference.setPages(fileOperations.readFiles(pageDirectory, CommonConstants.EMPTY_STRING));
|
||||
// Extract datasources
|
||||
applicationGitReference.setDatasources(
|
||||
readFiles(baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
applicationGitReference.setDatasources(fileOperations.readFiles(
|
||||
baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
|
@ -925,7 +718,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
applicationGitReference.setMetadata(metadata);
|
||||
|
||||
Path jsLibDirectory = baseRepoPath.resolve(JS_LIB_DIRECTORY);
|
||||
Map<String, Object> jsLibrariesMap = readFiles(jsLibDirectory, CommonConstants.EMPTY_STRING);
|
||||
Map<String, Object> jsLibrariesMap = fileOperations.readFiles(jsLibDirectory, CommonConstants.EMPTY_STRING);
|
||||
applicationGitReference.setJsLibraries(jsLibrariesMap);
|
||||
|
||||
return applicationGitReference;
|
||||
|
|
@ -950,13 +743,14 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
for (File page : Objects.requireNonNull(directory.listFiles())) {
|
||||
pageMap.put(
|
||||
page.getName(),
|
||||
readFile(page.toPath().resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION)));
|
||||
fileOperations.readFile(
|
||||
page.toPath().resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION)));
|
||||
|
||||
if (fileFormatVersion >= 4) {
|
||||
actionMap.putAll(
|
||||
readAction(page.toPath().resolve(ACTION_DIRECTORY), page.getName(), actionBodyMap));
|
||||
} else {
|
||||
actionMap.putAll(readFiles(page.toPath().resolve(ACTION_DIRECTORY), page.getName()));
|
||||
actionMap.putAll(fileOperations.readFiles(page.toPath().resolve(ACTION_DIRECTORY), page.getName()));
|
||||
}
|
||||
|
||||
if (fileFormatVersion >= 3) {
|
||||
|
|
@ -965,8 +759,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
page.getName(),
|
||||
actionCollectionBodyMap));
|
||||
} else {
|
||||
actionCollectionMap.putAll(
|
||||
readFiles(page.toPath().resolve(ACTION_COLLECTION_DIRECTORY), page.getName()));
|
||||
actionCollectionMap.putAll(fileOperations.readFiles(
|
||||
page.toPath().resolve(ACTION_COLLECTION_DIRECTORY), page.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -977,16 +771,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
applicationGitReference.setPages(pageMap);
|
||||
// Extract datasources
|
||||
applicationGitReference.setDatasources(
|
||||
readFiles(baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
}
|
||||
|
||||
private Integer getFileFormatVersion(Object metadata) {
|
||||
if (metadata == null) {
|
||||
return 1;
|
||||
}
|
||||
JsonObject json = gson.fromJson(gson.toJson(metadata), JsonObject.class);
|
||||
JsonElement fileFormatVersion = json.get(CommonConstants.FILE_FORMAT_VERSION);
|
||||
return fileFormatVersion.getAsInt();
|
||||
fileOperations.readFiles(baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
}
|
||||
|
||||
public static boolean isFileFormatCompatible(int savedFileFormat) {
|
||||
|
|
@ -1005,7 +790,6 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
Map<String, Object> actionMap = new HashMap<>();
|
||||
Map<String, String> actionBodyMap = new HashMap<>();
|
||||
Map<String, Object> actionCollectionMap = new HashMap<>();
|
||||
Map<String, Object> moduleInstanceMap = new HashMap<>();
|
||||
Map<String, String> actionCollectionBodyMap = new HashMap<>();
|
||||
if (directory.isDirectory()) {
|
||||
// Loop through all the directories and nested directories inside the pages directory to extract
|
||||
|
|
@ -1014,7 +798,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
if (page.isDirectory()) {
|
||||
pageMap.put(page.getName(), readPageMetadata(page.toPath()));
|
||||
|
||||
JSONObject mainContainer = getMainContainer(pageMap.get(page.getName()));
|
||||
JSONObject mainContainer = fileOperations.getMainContainer(pageMap.get(page.getName()));
|
||||
|
||||
// Read widgets data recursively from the widgets directory
|
||||
Map<String, JSONObject> widgetsData = readWidgetsData(
|
||||
|
|
@ -1042,7 +826,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
applicationGitReference.setPageDsl(pageDsl);
|
||||
// Extract datasources
|
||||
applicationGitReference.setDatasources(
|
||||
readFiles(baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
fileOperations.readFiles(baseRepoPath.resolve(DATASOURCE_DIRECTORY), CommonConstants.EMPTY_STRING));
|
||||
}
|
||||
|
||||
private Map<String, JSONObject> readWidgetsData(String directoryPath) {
|
||||
|
|
@ -1107,48 +891,34 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
// The check here is to validate if the parent is correct or not
|
||||
if (!validWidgetToParentMap.containsKey(name)) {
|
||||
if (file.isDirectory()) {
|
||||
deleteDirectory(file.toPath());
|
||||
fileOperations.deleteDirectory(file.toPath());
|
||||
} else {
|
||||
deleteFile(file.toPath());
|
||||
fileOperations.deleteFile(file.toPath());
|
||||
}
|
||||
} else if (!file.getParentFile().getPath().equals(validWidgetToParentMap.get(name))
|
||||
&& !file.getPath().equals(validWidgetToParentMap.get(name))) {
|
||||
if (file.isDirectory()) {
|
||||
deleteDirectory(file.toPath());
|
||||
fileOperations.deleteDirectory(file.toPath());
|
||||
} else {
|
||||
deleteFile(file.toPath());
|
||||
fileOperations.deleteFile(file.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject getMainContainer(Object pageJson) {
|
||||
JSONObject pageJSON = new JSONObject(gson.toJson(pageJson));
|
||||
JSONArray layouts = pageJSON.getJSONObject("unpublishedPage").getJSONArray("layouts");
|
||||
return layouts.getJSONObject(0).getJSONObject("dsl");
|
||||
@Override
|
||||
public Mono<Long> deleteIndexLockFile(Path path, int validTimeInSeconds) {
|
||||
return fileOperations.deleteIndexLockFile(path, validTimeInSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> deleteIndexLockFile(Path path, int validTimeInSeconds) {
|
||||
// Check the time created of the index.lock file
|
||||
// If the File is stale for more than validTime, then delete the file
|
||||
try {
|
||||
BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
FileTime fileTime = attr.creationTime();
|
||||
Instant now = Instant.now();
|
||||
Instant validCreateTime = now.minusSeconds(validTimeInSeconds);
|
||||
if (fileTime.toInstant().isBefore(validCreateTime)) {
|
||||
// Add base repo path
|
||||
path = Paths.get(path + ".lock");
|
||||
deleteFile(path);
|
||||
return Mono.just(now.minusMillis(fileTime.toMillis()).getEpochSecond());
|
||||
} else {
|
||||
return Mono.just(0L);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("Error reading index.lock file: {}", ex.getMessage());
|
||||
return Mono.just(0L);
|
||||
}
|
||||
public void scanAndDeleteFileForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
fileOperations.scanAndDeleteFileForDeletedResources(validResources, resourceDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanAndDeleteDirectoryForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
fileOperations.scanAndDeleteDirectoryForDeletedResources(validResources, resourceDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1193,7 +963,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
.map(isSwitched -> {
|
||||
Path baseRepoPath =
|
||||
Paths.get(gitServiceConfig.getGitRootPath()).resolve(baseRepoSuffix);
|
||||
Object metadata = readFile(
|
||||
Object metadata = fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
return metadata;
|
||||
});
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
package com.appsmith.git.helpers;
|
||||
package com.appsmith.git.files;
|
||||
|
||||
import com.appsmith.external.git.FileInterface;
|
||||
import com.appsmith.external.git.GitExecutor;
|
||||
import com.appsmith.external.git.operations.FileOperations;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.helpers.ce.FileUtilsCEImpl;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Component
|
||||
@Primary
|
||||
@Import({GitServiceConfig.class})
|
||||
public class FileUtilsImpl extends FileUtilsCEImpl implements FileInterface {
|
||||
|
||||
public FileUtilsImpl(
|
||||
GitServiceConfig gitServiceConfig,
|
||||
GitExecutor gitExecutor,
|
||||
GsonBuilder gsonBuilder,
|
||||
FileOperations fileOperations,
|
||||
ObservationHelper observationHelper) {
|
||||
super(gitServiceConfig, gitExecutor, gsonBuilder, observationHelper);
|
||||
super(gitServiceConfig, gitExecutor, fileOperations, observationHelper);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
package com.appsmith.git.files.operations;
|
||||
|
||||
import com.appsmith.external.converters.ISOStringToInstantConverter;
|
||||
import com.appsmith.external.git.GitExecutor;
|
||||
import com.appsmith.external.git.constants.GitSpan;
|
||||
import com.appsmith.external.git.operations.FileOperationsCE;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.external.models.ApplicationGitReference;
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.external.models.DatasourceStructure;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.constants.CommonConstants;
|
||||
import com.appsmith.git.converters.GsonDoubleToLongConverter;
|
||||
import com.appsmith.git.converters.GsonUnorderedToOrderedConverter;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.micrometer.tracing.Span;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.METADATA;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.RESOURCE_TYPE;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.WIDGETS;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Component
|
||||
@Import({GitServiceConfig.class})
|
||||
public class FileOperationsCEImpl implements FileOperationsCE {
|
||||
|
||||
private final GitServiceConfig gitServiceConfig;
|
||||
private final GitExecutor gitExecutor;
|
||||
private final Gson gson;
|
||||
|
||||
protected final ObservationHelper observationHelper;
|
||||
|
||||
private static final String EDIT_MODE_URL_TEMPLATE = "{{editModeUrl}}";
|
||||
|
||||
private static final String VIEW_MODE_URL_TEMPLATE = "{{viewModeUrl}}";
|
||||
|
||||
private static final Pattern ALLOWED_FILE_EXTENSION_PATTERN =
|
||||
Pattern.compile("(.*?)\\.(md|MD|git|gitignore|github|yml|yaml)$");
|
||||
|
||||
private final Scheduler scheduler = Schedulers.boundedElastic();
|
||||
|
||||
private static final String CANVAS_WIDGET = "(Canvas)[0-9]*.";
|
||||
|
||||
public FileOperationsCEImpl(
|
||||
GitServiceConfig gitServiceConfig,
|
||||
GitExecutor gitExecutor,
|
||||
GsonBuilder gsonBuilder,
|
||||
ObservationHelper observationHelper) {
|
||||
this.gitServiceConfig = gitServiceConfig;
|
||||
this.gitExecutor = gitExecutor;
|
||||
|
||||
// Gson to pretty format JSON file
|
||||
// Keep Long type as is by default GSON have behavior to convert to Double
|
||||
// Convert unordered set to ordered one
|
||||
this.gson = gsonBuilder
|
||||
.registerTypeAdapter(Double.class, new GsonDoubleToLongConverter())
|
||||
.registerTypeAdapter(Set.class, new GsonUnorderedToOrderedConverter())
|
||||
.registerTypeAdapter(Map.class, new GsonUnorderedToOrderedConverter())
|
||||
.registerTypeAdapter(Instant.class, new ISOStringToInstantConverter())
|
||||
// Instance creator is required while de-serialising using Gson as key instance can't be invoked
|
||||
// with no-args constructor
|
||||
.registerTypeAdapter(DatasourceStructure.Key.class, new DatasourceStructure.KeyInstanceCreator())
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
this.observationHelper = observationHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMetadataResource(ApplicationGitReference applicationGitReference, Path baseRepo) {
|
||||
JsonObject metadata = gson.fromJson(gson.toJson(applicationGitReference.getMetadata()), JsonObject.class);
|
||||
metadata.addProperty(CommonConstants.FILE_FORMAT_VERSION, CommonConstants.fileFormatVersion);
|
||||
saveResource(metadata, baseRepo.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to store the DB resource to JSON file
|
||||
*
|
||||
* @param sourceEntity resource extracted from DB to be stored in file
|
||||
* @param path file path where the resource to be stored
|
||||
* @return if the file operation is successful
|
||||
*/
|
||||
@Override
|
||||
public boolean saveResource(Object sourceEntity, Path path) {
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
return writeToFile(sourceEntity, path);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while writing resource to file {} with {}", path, e.getMessage());
|
||||
log.debug(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWidgets(JSONObject sourceEntity, String resourceName, Path path) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
String resourceType = WIDGETS;
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
writeStringToFile(sourceEntity.toString(4), path.resolve(resourceName + CommonConstants.JSON_EXTENSION));
|
||||
} catch (IOException e) {
|
||||
log.debug("Error while writings widgets data to file, {}", e.getMessage());
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStringToFile(String sourceEntity, Path path) throws IOException {
|
||||
try (BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
|
||||
fileWriter.write(sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeToFile(Object sourceEntity, Path path) throws IOException {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
String resourceType = sourceEntity.getClass().getSimpleName();
|
||||
if (!(sourceEntity instanceof BaseDomain)) {
|
||||
resourceType = METADATA;
|
||||
}
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
try (BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
|
||||
gson.toJson(sourceEntity, fileWriter);
|
||||
return true;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the JSON resource available in local git directory on subsequent commit made after the
|
||||
* deletion of respective resource from DB
|
||||
*
|
||||
* @param validResources resources those are still available in DB
|
||||
* @param resourceDirectory directory which needs to be scanned for possible file deletion operations
|
||||
*/
|
||||
@Override
|
||||
public void scanAndDeleteFileForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
// Scan resource directory and delete any unwanted file if present
|
||||
// unwanted file : corresponding resource from DB has been deleted
|
||||
if (resourceDirectory.toFile().exists()) {
|
||||
try (Stream<Path> paths = Files.walk(resourceDirectory)) {
|
||||
paths.filter(pathLocal -> Files.isRegularFile(pathLocal)
|
||||
&& !validResources.contains(
|
||||
pathLocal.getFileName().toString()))
|
||||
.forEach(this::deleteFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while scanning directory: {}, with error {}", resourceDirectory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the JSON resource directory available in local git directory on subsequent commit made after the
|
||||
* deletion of respective resource from DB
|
||||
*
|
||||
* @param validResources resources those are still available in DB
|
||||
* @param resourceDirectory directory which needs to be scanned for possible file deletion operations
|
||||
*/
|
||||
@Override
|
||||
public void scanAndDeleteDirectoryForDeletedResources(Set<String> validResources, Path resourceDirectory) {
|
||||
// Scan resource directory and delete any unwanted directory if present
|
||||
// unwanted directory : corresponding resource from DB has been deleted
|
||||
if (resourceDirectory.toFile().exists()) {
|
||||
try (Stream<Path> paths = Files.walk(resourceDirectory, 1)) {
|
||||
paths.filter(path -> Files.isDirectory(path)
|
||||
&& !path.equals(resourceDirectory)
|
||||
&& !validResources.contains(path.getFileName().toString()))
|
||||
.forEach(this::deleteDirectory);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while scanning directory {} with error {}", resourceDirectory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the directory and all its contents
|
||||
*
|
||||
* @param directory
|
||||
*/
|
||||
@Override
|
||||
public void deleteDirectory(Path directory) {
|
||||
if (directory.toFile().exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(directory.toFile());
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to delete directory for path {} with message {}", directory, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will delete the file from local repo
|
||||
*
|
||||
* @param filePath file that needs to be deleted
|
||||
*/
|
||||
@Override
|
||||
public void deleteFile(Path filePath) {
|
||||
try {
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (DirectoryNotEmptyException e) {
|
||||
log.error("Unable to delete non-empty directory at {} with cause", filePath, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to delete file {} with {}", filePath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json file present from the local git repo
|
||||
*
|
||||
* @param filePath file on which the read operation will be performed
|
||||
* @return resource stored in the JSON file
|
||||
*/
|
||||
@Override
|
||||
public Object readFile(Path filePath) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_READ);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
Object file;
|
||||
try (FileReader reader = new FileReader(filePath.toFile())) {
|
||||
file = gson.fromJson(reader, Object.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while reading file {} with message {} with cause", filePath, e.getMessage(), e.getCause());
|
||||
return null;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json files present from the local git repo
|
||||
*
|
||||
* @param directoryPath directory path for files on which read operation will be performed
|
||||
* @return resources stored in the directory
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> readFiles(Path directoryPath, String keySuffix) {
|
||||
Map<String, Object> resource = new HashMap<>();
|
||||
File directory = directoryPath.toFile();
|
||||
if (directory.isDirectory()) {
|
||||
Arrays.stream(Objects.requireNonNull(directory.listFiles())).forEach(file -> {
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
resource.put(file.getName() + keySuffix, gson.fromJson(reader, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Error while reading file {} with message {} with cause",
|
||||
file.toPath(),
|
||||
e.getMessage(),
|
||||
e.getCause());
|
||||
}
|
||||
});
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will read the content of the file as a plain text and does not apply the gson to json transformation
|
||||
*
|
||||
* @param filePath file path for files on which read operation will be performed
|
||||
* @return content of the file in the path
|
||||
*/
|
||||
@Override
|
||||
public String readFileAsString(Path filePath) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_READ);
|
||||
observationHelper.startSpan(span, true);
|
||||
String data = CommonConstants.EMPTY_STRING;
|
||||
try {
|
||||
data = FileUtils.readFileToString(filePath.toFile(), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
log.error("Error while reading the file from git repo {} ", e.getMessage());
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getFileFormatVersion(Object metadata) {
|
||||
if (metadata == null) {
|
||||
return 1;
|
||||
}
|
||||
JsonObject json = gson.fromJson(gson.toJson(metadata), JsonObject.class);
|
||||
JsonElement fileFormatVersion = json.get(CommonConstants.FILE_FORMAT_VERSION);
|
||||
return fileFormatVersion.getAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getMainContainer(Object pageJson) {
|
||||
JSONObject pageJSON = new JSONObject(gson.toJson(pageJson));
|
||||
JSONArray layouts = pageJSON.getJSONObject("unpublishedPage").getJSONArray("layouts");
|
||||
return layouts.getJSONObject(0).getJSONObject("dsl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> deleteIndexLockFile(Path path, int validTimeInSeconds) {
|
||||
// Check the time created of the index.lock file
|
||||
// If the File is stale for more than validTime, then delete the file
|
||||
try {
|
||||
BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
FileTime fileTime = attr.creationTime();
|
||||
Instant now = Instant.now();
|
||||
Instant validCreateTime = now.minusSeconds(validTimeInSeconds);
|
||||
if (fileTime.toInstant().isBefore(validCreateTime)) {
|
||||
// Add base repo path
|
||||
path = Paths.get(path + ".lock");
|
||||
deleteFile(path);
|
||||
return Mono.just(now.minusMillis(fileTime.toMillis()).getEpochSecond());
|
||||
} else {
|
||||
return Mono.just(0L);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("Error reading index.lock file: {}", ex.getMessage());
|
||||
return Mono.just(0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
package com.appsmith.git.files.operations;
|
||||
|
||||
import com.appsmith.external.annotations.FeatureFlagged;
|
||||
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||
import com.appsmith.external.git.GitExecutor;
|
||||
import com.appsmith.external.git.constants.GitSpan;
|
||||
import com.appsmith.external.git.operations.FileOperationsCE;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.external.models.ApplicationGitReference;
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.external.views.Git;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.constants.CommonConstants;
|
||||
import com.appsmith.util.SerializationUtils;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.PrettyPrinter;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.micrometer.tracing.Span;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.METADATA;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.RESOURCE_TYPE;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.WIDGETS;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Component
|
||||
@Import({GitServiceConfig.class})
|
||||
public class FileOperationsCEv2Impl extends FileOperationsCEImpl implements FileOperationsCE {
|
||||
|
||||
protected final ObjectMapper objectMapper;
|
||||
protected final ObjectReader objectReader;
|
||||
protected final ObjectWriter objectWriter;
|
||||
|
||||
private final ObservationHelper observationHelper;
|
||||
|
||||
public FileOperationsCEv2Impl(
|
||||
GitServiceConfig gitServiceConfig,
|
||||
GitExecutor gitExecutor,
|
||||
GsonBuilder gsonBuilder,
|
||||
PrettyPrinter prettyPrinter,
|
||||
ObservationHelper observationHelper) {
|
||||
super(gitServiceConfig, gitExecutor, gsonBuilder, observationHelper);
|
||||
|
||||
this.objectMapper = SerializationUtils.getBasicObjectMapper(prettyPrinter);
|
||||
this.objectReader = objectMapper.readerWithView(Git.class);
|
||||
this.objectWriter = objectMapper.writerWithView(Git.class);
|
||||
|
||||
this.observationHelper = observationHelper;
|
||||
}
|
||||
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public void saveMetadataResource(ApplicationGitReference applicationGitReference, Path baseRepo) {
|
||||
ObjectNode metadata = objectMapper.valueToTree(applicationGitReference.getMetadata());
|
||||
metadata.put(CommonConstants.FILE_FORMAT_VERSION, CommonConstants.fileFormatVersion);
|
||||
saveResource(metadata, baseRepo.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
}
|
||||
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public void saveWidgets(JSONObject sourceEntity, String resourceName, Path path) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
String resourceType = WIDGETS;
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
writeToFile(
|
||||
objectReader.readTree(sourceEntity.toString()),
|
||||
path.resolve(resourceName + CommonConstants.JSON_EXTENSION));
|
||||
} catch (IOException e) {
|
||||
log.debug("Error while writings widgets data to file, {}", e.getMessage());
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public boolean writeToFile(Object sourceEntity, Path path) throws IOException {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_WRITE);
|
||||
String resourceType = sourceEntity.getClass().getSimpleName();
|
||||
if (!(sourceEntity instanceof BaseDomain)) {
|
||||
resourceType = METADATA;
|
||||
}
|
||||
span.tag(RESOURCE_TYPE, resourceType);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
try (BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
|
||||
objectWriter.writeValue(fileWriter, sourceEntity);
|
||||
return true;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json file present from the local git repo
|
||||
*
|
||||
* @param filePath file on which the read operation will be performed
|
||||
* @return resource stored in the JSON file
|
||||
*/
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public Object readFile(Path filePath) {
|
||||
Span span = observationHelper.createSpan(GitSpan.FILE_READ);
|
||||
observationHelper.startSpan(span, true);
|
||||
|
||||
Object file;
|
||||
try (FileReader reader = new FileReader(filePath.toFile())) {
|
||||
file = objectReader.readValue(reader, Object.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while reading file {} with message {} with cause", filePath, e.getMessage(), e.getCause());
|
||||
return null;
|
||||
} finally {
|
||||
observationHelper.endSpan(span, true);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used to read and dehydrate the json files present from the local git repo
|
||||
*
|
||||
* @param directoryPath directory path for files on which read operation will be performed
|
||||
* @return resources stored in the directory
|
||||
*/
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public Map<String, Object> readFiles(Path directoryPath, String keySuffix) {
|
||||
Map<String, Object> resource = new HashMap<>();
|
||||
File directory = directoryPath.toFile();
|
||||
if (directory.isDirectory()) {
|
||||
Arrays.stream(Objects.requireNonNull(directory.listFiles())).forEach(file -> {
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
resource.put(file.getName() + keySuffix, objectReader.readValue(reader, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Error while reading file {} with message {} with cause",
|
||||
file.toPath(),
|
||||
e.getMessage(),
|
||||
e.getCause());
|
||||
}
|
||||
});
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public Integer getFileFormatVersion(Object metadata) {
|
||||
if (metadata == null) {
|
||||
return 1;
|
||||
}
|
||||
JsonNode json = objectMapper.valueToTree(metadata);
|
||||
int fileFormatVersion = json.get(CommonConstants.FILE_FORMAT_VERSION).asInt();
|
||||
return fileFormatVersion;
|
||||
}
|
||||
|
||||
@FeatureFlagged(featureFlagName = FeatureFlagEnum.release_git_cleanup_feature_enabled)
|
||||
@Override
|
||||
public JSONObject getMainContainer(Object pageJson) {
|
||||
JsonNode pageJSON = objectMapper.valueToTree(pageJson);
|
||||
try {
|
||||
return new JSONObject(objectMapper.writeValueAsString(
|
||||
pageJSON.get("unpublishedPage").get("layouts").get(0).get("dsl")));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.appsmith.git.files.operations;
|
||||
|
||||
import com.appsmith.external.git.GitExecutor;
|
||||
import com.appsmith.external.git.operations.FileOperations;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.fasterxml.jackson.core.PrettyPrinter;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Primary
|
||||
@Import({GitServiceConfig.class})
|
||||
public class FileOperationsImpl extends FileOperationsCEv2Impl implements FileOperations {
|
||||
public FileOperationsImpl(
|
||||
GitServiceConfig gitServiceConfig,
|
||||
GitExecutor gitExecutor,
|
||||
GsonBuilder gsonBuilder,
|
||||
PrettyPrinter prettyPrinter,
|
||||
ObservationHelper observationHelper) {
|
||||
super(gitServiceConfig, gitExecutor, gsonBuilder, prettyPrinter, observationHelper);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,11 @@ import com.appsmith.git.configurations.GitServiceConfig;
|
|||
import com.appsmith.git.service.ce.GitExecutorCEImpl;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Primary
|
||||
@Slf4j
|
||||
public class GitExecutorImpl extends GitExecutorCEImpl implements GitExecutor {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package com.appsmith.git.helpers;
|
||||
|
||||
import com.appsmith.external.git.operations.FileOperations;
|
||||
import com.appsmith.external.helpers.ObservationHelper;
|
||||
import com.appsmith.external.models.ApplicationGitReference;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.files.FileUtilsImpl;
|
||||
import com.appsmith.git.files.operations.FileOperationsImpl;
|
||||
import com.appsmith.git.service.GitExecutorImpl;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
|
@ -36,6 +39,8 @@ public class FileUtilsImplTest {
|
|||
@MockBean
|
||||
private GitExecutorImpl gitExecutor;
|
||||
|
||||
private FileOperations fileOperations;
|
||||
|
||||
private GitServiceConfig gitServiceConfig;
|
||||
private static final String localTestDirectory = "localTestDirectory";
|
||||
private static final Path localTestDirectoryPath = Path.of(localTestDirectory);
|
||||
|
|
@ -44,7 +49,9 @@ public class FileUtilsImplTest {
|
|||
public void setUp() {
|
||||
gitServiceConfig = new GitServiceConfig();
|
||||
gitServiceConfig.setGitRootPath(localTestDirectoryPath.toString());
|
||||
fileUtils = new FileUtilsImpl(gitServiceConfig, gitExecutor, new GsonBuilder(), ObservationHelper.NOOP);
|
||||
fileOperations =
|
||||
new FileOperationsImpl(gitServiceConfig, gitExecutor, new GsonBuilder(), null, ObservationHelper.NOOP);
|
||||
fileUtils = new FileUtilsImpl(gitServiceConfig, gitExecutor, fileOperations, ObservationHelper.NOOP);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
|||
|
|
@ -88,11 +88,13 @@
|
|||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -184,6 +186,12 @@
|
|||
<artifactId>jjwt-jackson</artifactId>
|
||||
<!-- or jjwt-gson if Gson is preferred -->
|
||||
<version>${jjwt.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Test dependencies-->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.annotations;
|
||||
package com.appsmith.external.annotations;
|
||||
|
||||
import com.appsmith.server.featureflags.FeatureFlagEnum;
|
||||
import com.appsmith.external.enums.FeatureFlagEnum;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package com.appsmith.external.constants;
|
||||
|
||||
public enum DatasourceQueryType {
|
||||
FETCH,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.minidev.json.JSONArray;
|
||||
import net.minidev.json.parser.JSONParser;
|
||||
|
|
@ -11,7 +12,8 @@ import reactor.core.Exceptions;
|
|||
|
||||
public class ArrayType implements AppsmithType {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.Exceptions;
|
||||
|
||||
|
|
@ -15,7 +16,8 @@ import java.util.regex.Matcher;
|
|||
|
||||
public class DateType implements AppsmithType {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
|
@ -21,7 +22,8 @@ import java.util.regex.Matcher;
|
|||
public class JsonObjectType implements AppsmithType {
|
||||
|
||||
private static final TypeAdapter<JsonObject> strictGsonObjectAdapter = new Gson().getAdapter(JsonObject.class);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.Exceptions;
|
||||
|
||||
|
|
@ -11,7 +12,8 @@ import java.util.regex.Matcher;
|
|||
|
||||
public class StringType implements AppsmithType {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.Exceptions;
|
||||
|
||||
|
|
@ -15,7 +16,8 @@ import java.util.regex.Matcher;
|
|||
|
||||
public class TimeType implements AppsmithType {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.constants.DataType;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import reactor.core.Exceptions;
|
||||
|
||||
|
|
@ -15,7 +16,8 @@ import java.util.regex.Matcher;
|
|||
|
||||
public class TimestampType implements AppsmithType {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.appsmith.server.featureflags;
|
||||
package com.appsmith.external.enums;
|
||||
|
||||
public enum FeatureFlagEnum {
|
||||
// ------------------- These features are only for JUnit testing. DO NOT use these features in your code path.--- //
|
||||
|
|
@ -15,5 +15,6 @@ public enum FeatureFlagEnum {
|
|||
release_embed_hide_share_settings_enabled,
|
||||
rollout_datasource_test_rate_limit_enabled,
|
||||
release_git_autocommit_feature_enabled,
|
||||
release_git_cleanup_feature_enabled,
|
||||
// Add EE flags below this line, to avoid conflicts.
|
||||
}
|
||||
|
|
@ -7,13 +7,14 @@ import reactor.core.publisher.Mono;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public interface FileInterface {
|
||||
/**
|
||||
* This method is use to store the serialised application to git repo, directory path structure we are going to follow :
|
||||
* ./container-volumes/git-repo/workspaceId/defaultApplicationId/repoName/{application_data}
|
||||
* @param baseRepoSuffix path suffix used to create a repo path
|
||||
* @param applicationGitReference application reference object from which entire application can be rehydrated
|
||||
* @param artifactGitReference application reference object from which entire application can be rehydrated
|
||||
* @return Path to where the application is stored
|
||||
*
|
||||
* Application will be stored in the following structure :
|
||||
|
|
@ -84,9 +85,13 @@ public interface FileInterface {
|
|||
* This will check if the cloned repo is empty. The check excludes files like Readme files
|
||||
*
|
||||
* @param baseRepoSuffix path suffix used to create a branch repo path as per worktree implementation
|
||||
* @return success if the clone repo doesnt contain any files
|
||||
* @return success if the clone repo doesn't contain any files
|
||||
*/
|
||||
Mono<Boolean> checkIfDirectoryIsEmpty(Path baseRepoSuffix) throws IOException;
|
||||
|
||||
Mono<Long> deleteIndexLockFile(Path path, int validTimeInSeconds);
|
||||
|
||||
void scanAndDeleteFileForDeletedResources(Set<String> validResources, Path resourceDirectory);
|
||||
|
||||
void scanAndDeleteDirectoryForDeletedResources(Set<String> validResources, Path resourceDirectory);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
package com.appsmith.external.git.operations;
|
||||
|
||||
public interface FileOperations extends FileOperationsCE {}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.appsmith.external.git.operations;
|
||||
|
||||
import com.appsmith.external.models.ApplicationGitReference;
|
||||
import org.json.JSONObject;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface FileOperationsCE {
|
||||
void saveMetadataResource(ApplicationGitReference applicationGitReference, Path baseRepo);
|
||||
|
||||
boolean saveResource(Object sourceEntity, Path path);
|
||||
|
||||
void saveWidgets(JSONObject sourceEntity, String resourceName, Path path);
|
||||
|
||||
void writeStringToFile(String sourceEntity, Path path) throws IOException;
|
||||
|
||||
boolean writeToFile(Object sourceEntity, Path path) throws IOException;
|
||||
|
||||
void scanAndDeleteFileForDeletedResources(Set<String> validResources, Path resourceDirectory);
|
||||
|
||||
void scanAndDeleteDirectoryForDeletedResources(Set<String> validResources, Path resourceDirectory);
|
||||
|
||||
void deleteDirectory(Path directory);
|
||||
|
||||
void deleteFile(Path filePath);
|
||||
|
||||
Object readFile(Path filePath);
|
||||
|
||||
Map<String, Object> readFiles(Path directoryPath, String keySuffix);
|
||||
|
||||
String readFileAsString(Path filePath);
|
||||
|
||||
Integer getFileFormatVersion(Object metadata);
|
||||
|
||||
JSONObject getMainContainer(Object pageJson);
|
||||
|
||||
Mono<Long> deleteIndexLockFile(Path path, int validTimeInSeconds);
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.appsmith.external.models.Param;
|
|||
import com.appsmith.external.models.ParsedDataType;
|
||||
import com.appsmith.external.plugins.SmartSubstitutionInterface;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
|
@ -53,7 +54,8 @@ public class DataTypeStringUtils {
|
|||
|
||||
public static Pattern placeholderPattern = Pattern.compile(APPSMITH_SUBSTITUTION_PLACEHOLDER);
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
private static final TypeAdapter<JsonObject> strictGsonObjectAdapter = new Gson().getAdapter(JsonObject.class);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.appsmith.external.models.Endpoint;
|
|||
import com.appsmith.external.models.Param;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
|
@ -46,7 +47,8 @@ import static com.appsmith.external.constants.CommonFieldName.VALUE;
|
|||
@Slf4j
|
||||
public class PluginUtils {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
public static final TypeReference<String> STRING_TYPE = new TypeReference<>() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.appsmith.external.models.ActionConfiguration;
|
|||
import com.appsmith.external.models.ApiContentType;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
|
@ -76,7 +77,7 @@ public class DataUtils {
|
|||
}
|
||||
|
||||
public DataUtils() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.objectMapper = new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
this.objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import com.appsmith.external.converters.HttpMethodConverter;
|
||||
import com.appsmith.external.views.FromRequest;
|
||||
import com.appsmith.external.views.Git;
|
||||
import com.appsmith.external.views.Views;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
|
|
@ -12,7 +16,6 @@ import lombok.experimental.FieldNameConstants;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import reactor.netty.http.HttpProtocol;
|
||||
|
||||
|
|
@ -28,7 +31,6 @@ import static com.appsmith.external.constants.ActionConstants.DEFAULT_ACTION_EXE
|
|||
@ToString
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
@Document
|
||||
@FieldNameConstants
|
||||
public class ActionConfiguration implements AppsmithDomain, ExecutableConfiguration {
|
||||
private static final int MIN_TIMEOUT_VALUE = 0; // in Milliseconds
|
||||
|
|
@ -45,32 +47,51 @@ public class ActionConfiguration implements AppsmithDomain, ExecutableConfigurat
|
|||
*/
|
||||
|
||||
@Range(min = MIN_TIMEOUT_VALUE, max = MAX_TIMEOUT_VALUE, message = TIMEOUT_OUT_OF_RANGE_MESSAGE)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Integer timeoutInMillisecond;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
PaginationType paginationType = PaginationType.NONE;
|
||||
|
||||
// API fields
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String path;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> headers;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> autoGeneratedHeaders;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Boolean encodeParamsToggle = true;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> queryParameters;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String body;
|
||||
// For form-data input instead of json use the following
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> bodyFormData;
|
||||
// For route parameters extracted from rapid-api
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> routeParameters;
|
||||
// All the following adapters are registered so that we can serialize between enum HttpMethod,
|
||||
// and what is now the class HttpMethod
|
||||
@JsonSerialize(using = HttpMethodConverter.HttpMethodSerializer.class)
|
||||
@JsonDeserialize(using = HttpMethodConverter.HttpMethodDeserializer.class)
|
||||
@JsonAdapter(HttpMethodConverter.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
HttpMethod httpMethod;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
HttpProtocol httpVersion;
|
||||
// Paginated API fields
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String next;
|
||||
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String prev;
|
||||
|
||||
/**
|
||||
|
|
@ -80,6 +101,7 @@ public class ActionConfiguration implements AppsmithDomain, ExecutableConfigurat
|
|||
* cyclic dependency errors.
|
||||
*/
|
||||
@Transient
|
||||
@JsonView({Views.Internal.class})
|
||||
Set<String> selfReferencingDataPaths = new HashSet<>();
|
||||
|
||||
// DB action fields
|
||||
|
|
@ -87,6 +109,7 @@ public class ActionConfiguration implements AppsmithDomain, ExecutableConfigurat
|
|||
// JS action fields
|
||||
// Body, the raw class data, is shared with API type actions
|
||||
// Represents the values that need to be
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<JSValue> jsArguments;
|
||||
// This property is being retained right now so that Git does not see commit changes, do not use
|
||||
@Deprecated(forRemoval = true)
|
||||
|
|
@ -99,12 +122,14 @@ public class ActionConfiguration implements AppsmithDomain, ExecutableConfigurat
|
|||
* They will have to represented in a key-value format where the plugin
|
||||
* understands what the keys stand for.
|
||||
*/
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> pluginSpecifiedTemplates;
|
||||
|
||||
/*
|
||||
* After porting plugins to UQI, we should be able to use a map for referring to form data
|
||||
* instead of a list of properties
|
||||
*/
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Map<String, Object> formData;
|
||||
|
||||
@Transient
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import com.appsmith.external.views.FromRequest;
|
||||
import com.appsmith.external.views.Views;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
|
@ -12,5 +15,6 @@ import java.util.Map;
|
|||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class AnalyticsInfo {
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
private Map<String, Object> analyticsData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import com.appsmith.external.helpers.Identifiable;
|
||||
import com.appsmith.external.views.FromRequest;
|
||||
import com.appsmith.external.views.Git;
|
||||
import com.appsmith.external.views.Views;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
|
|
@ -37,7 +39,7 @@ public abstract class BaseDomain implements Persistable<String>, AppsmithDomain,
|
|||
private static final long serialVersionUID = 7459916000501322517L;
|
||||
|
||||
@Id
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
private String id;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
|
|
@ -93,7 +95,7 @@ public abstract class BaseDomain implements Persistable<String>, AppsmithDomain,
|
|||
// This field will only be used for git related functionality to sync the action object across different instances.
|
||||
// This field will be deprecated once we move to the new git sync implementation.
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Internal.class)
|
||||
@JsonView({Views.Internal.class, Git.class})
|
||||
String gitSyncId;
|
||||
|
||||
public void sanitiseToExportDBObject() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import lombok.Getter;
|
|||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
|
@ -14,7 +13,6 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
|||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class Connection implements AppsmithDomain {
|
||||
|
||||
public enum Mode {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import com.appsmith.external.views.FromRequest;
|
||||
import com.appsmith.external.views.Git;
|
||||
import com.appsmith.external.views.Views;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
|
@ -29,38 +30,37 @@ public class Datasource extends BranchAwareDomain {
|
|||
@Transient
|
||||
public static final String DEFAULT_NAME_PREFIX = "Untitled datasource";
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String name;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String pluginId;
|
||||
|
||||
// name of the plugin. used to log analytics events where pluginName is a required attribute
|
||||
// It'll be null if not set
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String pluginName;
|
||||
|
||||
// Organizations migrated to workspaces, kept the field as deprecated to support the old migration
|
||||
@Deprecated
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String organizationId;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String workspaceId;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String templateName;
|
||||
|
||||
// This is only kept public for embedded datasource
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
DatasourceConfiguration datasourceConfiguration;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
Map<String, DatasourceStorageDTO> datasourceStorages = new HashMap<>();
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Set<String> invalids;
|
||||
|
||||
|
|
@ -69,7 +69,6 @@ public class Datasource extends BranchAwareDomain {
|
|||
* - These messages are generated by the API server based on the other datasource attributes.
|
||||
*/
|
||||
@Transient
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Set<String> messages = new HashSet<>();
|
||||
|
||||
|
|
@ -79,7 +78,7 @@ public class Datasource extends BranchAwareDomain {
|
|||
* while trying set createdAt and updatedAt properties on the null object
|
||||
*/
|
||||
@Transient
|
||||
@JsonView(Views.Internal.class)
|
||||
@JsonView({Views.Internal.class, Git.class})
|
||||
Boolean isAutoGenerated = false;
|
||||
|
||||
/*
|
||||
|
|
@ -90,7 +89,7 @@ public class Datasource extends BranchAwareDomain {
|
|||
Boolean isConfigured;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Boolean isRecentlyCreated;
|
||||
|
||||
/*
|
||||
|
|
@ -98,14 +97,14 @@ public class Datasource extends BranchAwareDomain {
|
|||
* The field is not used anywhere in the codebase because templates are created directly in the DB, and the field
|
||||
* serves only as a DTO property.
|
||||
*/
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Boolean isTemplate;
|
||||
|
||||
/*
|
||||
* This field is meant to indicate whether the datasource is part of a mock DB, or a copy of the same.
|
||||
* The field is set during the creation of the mock db
|
||||
*/
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Boolean isMock;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import lombok.NoArgsConstructor;
|
|||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -19,7 +18,6 @@ import java.util.List;
|
|||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
@FieldNameConstants
|
||||
public class DatasourceConfiguration implements AppsmithDomain {
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ public class DatasourceStorage extends BaseDomain {
|
|||
this.setIsRecentlyCreated(null);
|
||||
}
|
||||
|
||||
@JsonView({Views.Internal.class})
|
||||
public boolean isEmbedded() {
|
||||
/**
|
||||
* We cannot just rely on datasourceId == null check because it will always be true for all cases when the
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import lombok.Getter;
|
|||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
|
@ -14,7 +13,6 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
|||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@Document
|
||||
public class Endpoint {
|
||||
|
||||
String host;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import lombok.Getter;
|
|||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
|
@ -16,7 +15,6 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
|||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class PEMCertificate implements AppsmithDomain {
|
||||
|
||||
UploadedFile file;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.appsmith.external.models.ce;
|
||||
|
||||
import com.appsmith.external.constants.ActionCreationSourceTypeEnum;
|
||||
import com.appsmith.external.constants.DatasourceQueryType;
|
||||
import com.appsmith.external.dtos.DslExecutableDTO;
|
||||
import com.appsmith.external.dtos.LayoutExecutableUpdateDTO;
|
||||
import com.appsmith.external.exceptions.ErrorDTO;
|
||||
|
|
@ -17,9 +16,10 @@ import com.appsmith.external.models.Executable;
|
|||
import com.appsmith.external.models.PluginType;
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.views.FromRequest;
|
||||
import com.appsmith.external.views.Git;
|
||||
import com.appsmith.external.views.Views;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
|
@ -41,111 +41,106 @@ import java.util.Set;
|
|||
public class ActionCE_DTO implements Identifiable, Executable {
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
private String id;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String applicationId;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String workspaceId;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
PluginType pluginType;
|
||||
|
||||
// name of the plugin. used to log analytics events where pluginName is a required attribute
|
||||
// It'll be null if not set
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String pluginName;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
String pluginId;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String name;
|
||||
|
||||
// The FQN for an action will also include any collection it is a part of as collectionName.actionName
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String fullyQualifiedName;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Datasource datasource;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String pageId;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
CreatorContextType contextType;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
String collectionId;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
ActionConfiguration actionConfiguration;
|
||||
|
||||
// this attribute carries error messages while processing the actionCollection
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
List<ErrorDTO> errorReports;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Boolean executeOnLoad;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Boolean clientSideExecution;
|
||||
|
||||
/*
|
||||
* This is a list of fields specified by the client to signify which fields have dynamic bindings in them.
|
||||
* TODO: The server can use this field to simplify our Mustache substitutions in the future
|
||||
*/
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
List<Property> dynamicBindingPathList;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Boolean isValid;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Set<String> invalids;
|
||||
|
||||
@Transient
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Set<String> messages = new HashSet<>();
|
||||
|
||||
// This is a list of keys that the client whose values the client needs to send during action execution.
|
||||
// These are the Mustache keys that the server will replace before invoking the API
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
@JsonView(Views.Public.class)
|
||||
Set<String> jsonPathKeys;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
String cacheResponse;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
@JsonView({Views.Internal.class, Git.class})
|
||||
Boolean userSetOnLoad = false;
|
||||
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class, Git.class})
|
||||
Boolean confirmBeforeExecute = false;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Documentation documentation;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Instant deletedAt = null;
|
||||
|
||||
@Deprecated
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
Instant archivedAt = null;
|
||||
|
||||
@Transient
|
||||
|
|
@ -153,7 +148,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
protected Set<Policy> policies = new HashSet<>();
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
public Set<String> userPermissions = new HashSet<>();
|
||||
|
||||
// This field will be used to store the default/root actionId and applicationId for actions generated for git
|
||||
|
|
@ -163,28 +158,25 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
|
||||
// This field will be used to store analytics data related to this specific domain object. It's been introduced in
|
||||
// order to track success metrics of modules. Learn more on GitHub issue#24734
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
AnalyticsInfo eventData;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
protected Instant createdAt;
|
||||
|
||||
@JsonView(Views.Internal.class)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
|
||||
@JsonView(Views.Public.class)
|
||||
protected Instant updatedAt;
|
||||
|
||||
// Defines what triggered action creation, could be self (user explicitly created action) / generate crud / one
|
||||
// click binding etc
|
||||
// Used in logging create action event
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Public.class, FromRequest.class})
|
||||
ActionCreationSourceTypeEnum source;
|
||||
|
||||
@Transient
|
||||
@JsonView(Views.Public.class)
|
||||
DatasourceQueryType queryType;
|
||||
|
||||
@Override
|
||||
@JsonView(Views.Public.class)
|
||||
@JsonView({Views.Internal.class})
|
||||
public String getValidName() {
|
||||
if (this.fullyQualifiedName == null) {
|
||||
return this.name;
|
||||
|
|
@ -194,6 +186,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public Set<String> getExecutableNames() {
|
||||
String validName = this.getValidName();
|
||||
HashSet<String> validNames = new HashSet<>();
|
||||
|
|
@ -220,6 +213,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
this.resetTransientFields();
|
||||
this.setEventData(null);
|
||||
this.setDefaultResources(null);
|
||||
this.setUpdatedAt(null);
|
||||
this.setCacheResponse(null);
|
||||
if (this.getDatasource() != null) {
|
||||
this.getDatasource().setCreatedAt(null);
|
||||
|
|
@ -238,6 +232,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public Set<String> getSelfReferencingDataPaths() {
|
||||
if (this.getActionConfiguration() == null) {
|
||||
return new HashSet<>();
|
||||
|
|
@ -246,26 +241,31 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
|||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public ActionConfiguration getExecutableConfiguration() {
|
||||
return this.getActionConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public String getConfigurationPath() {
|
||||
return this.getUserExecutableName() + ".actionConfiguration";
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public String getCompleteDynamicBindingPath(String fieldPath) {
|
||||
return this.getConfigurationPath() + "." + fieldPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public boolean hasExtractableBinding() {
|
||||
return PluginType.JS.equals(this.getPluginType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonView({Views.Internal.class})
|
||||
public DslExecutableDTO getDslExecutable() {
|
||||
DslExecutableDTO dslExecutableDTO = new DslExecutableDTO();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package com.appsmith.external.plugins;
|
||||
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.pf4j.Plugin;
|
||||
import org.pf4j.PluginWrapper;
|
||||
|
||||
public abstract class BasePlugin extends Plugin {
|
||||
|
||||
protected static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
protected static final ObjectMapper objectMapper =
|
||||
new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
public BasePlugin(PluginWrapper wrapper) {
|
||||
super(wrapper);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.appsmith.external.plugins;
|
||||
|
||||
import com.appsmith.external.constants.DatasourceQueryType;
|
||||
import com.appsmith.external.dtos.ExecuteActionDTO;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
|
|
@ -376,13 +375,4 @@ public interface PluginExecutor<C> extends ExtensionPoint, CrudTemplateService {
|
|||
// wherever applicable.
|
||||
return Mono.just("");
|
||||
}
|
||||
|
||||
/*
|
||||
* This method returns query type, query type is used for
|
||||
* suggesting relevant actions to users when binding data
|
||||
*/
|
||||
default Mono<DatasourceQueryType> getQueryType(ActionConfiguration actionConfig) {
|
||||
// For all plugins where implementation is unclear right now, we will be returning its type as UNKNOWN
|
||||
return Mono.just(DatasourceQueryType.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
|||
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.models.Condition;
|
||||
import com.appsmith.external.models.UQIDataFilterParams;
|
||||
import com.fasterxml.jackson.core.StreamReadFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
|
|
@ -97,7 +98,7 @@ public class FilterDataServiceCE implements IFilterDataServiceCE {
|
|||
|
||||
public FilterDataServiceCE() {
|
||||
|
||||
objectMapper = new ObjectMapper();
|
||||
objectMapper = new ObjectMapper().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature());
|
||||
|
||||
try {
|
||||
connection = DriverManager.getConnection(URL);
|
||||
|
|
|
|||
7
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/views/FromRequest.java
vendored
Normal file
7
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/views/FromRequest.java
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package com.appsmith.external.views;
|
||||
|
||||
/**
|
||||
* Intended to annotate fields that can be set by HTTP request payloads, but should NOT be included
|
||||
* in HTTP responses sent back to the client.
|
||||
*/
|
||||
public interface FromRequest {}
|
||||
3
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/views/Git.java
vendored
Normal file
3
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/views/Git.java
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package com.appsmith.external.views;
|
||||
|
||||
public interface Git {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user