From 36163ee71008caf368b7000864a95c41f8d3c95e Mon Sep 17 00:00:00 2001 From: Aishwarya-U-R <91450662+Aishwarya-U-R@users.noreply.github.com> Date: Wed, 20 Sep 2023 14:28:22 +0530 Subject: [PATCH 01/48] test: Cypress | CodeScanner1_spec from js to ts (#27487) ## Description - This PR updates the CodeScanner1_spec from js to ts to comply rule --- .../{CodeScanner1_spec.js => CodeScanner1_spec.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/{CodeScanner1_spec.js => CodeScanner1_spec.ts} (96%) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.ts similarity index 96% rename from app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.js rename to app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.ts index 9ce99a7ab9..c39d2a2c17 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/CodeScanner/CodeScanner1_spec.ts @@ -1,7 +1,7 @@ -const explorer = require("../../../../../locators/explorerlocators.json"); -const widgetsPage = require("../../../../../locators/Widgets.json"); -const commonlocators = require("../../../../../locators/commonlocators.json"); -const publish = require("../../../../../locators/publishWidgetspage.json"); +import explorer from "../../../../../locators/explorerlocators.json"; +import publish from "../../../../../locators/publishWidgetspage.json"; +import commonlocators from "../../../../../locators/commonlocators.json"; +import widgetsPage from "../../../../../locators/Widgets.json"; import * as _ from "../../../../../support/Objects/ObjectsCore"; const widgetName = "codescannerwidget"; const codeScannerVideoOnPublishPage = `${publish.codescannerwidget} ${commonlocators.codeScannerVideo}`; From f2ae7695d84c2d707e7c3901e5fb8b70fa280437 Mon Sep 17 00:00:00 2001 From: Vemparala Surya Vamsi <121419957+vsvamsi1@users.noreply.github.com> Date: Wed, 20 Sep 2023 21:07:02 +0530 Subject: [PATCH 02/48] fix: revert skip klona (#27503) ## Description Reverting change for skip klona changes, this commit reverts the fix for https://github.com/appsmithorg/appsmith/issues/27048 #### PR fixes following issue(s) Fixes #27048 #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [ ] Manual - [ ] JUnit - [ ] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../workers/common/DataTreeEvaluator/index.ts | 18 ++++++++---------- .../workers/common/DataTreeEvaluator/test.ts | 7 +++---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/client/src/workers/common/DataTreeEvaluator/index.ts b/app/client/src/workers/common/DataTreeEvaluator/index.ts index cf1fb653d5..a87a3fb5da 100644 --- a/app/client/src/workers/common/DataTreeEvaluator/index.ts +++ b/app/client/src/workers/common/DataTreeEvaluator/index.ts @@ -344,8 +344,7 @@ export default class DataTreeEvaluator { const evaluationOrder = this.sortedDependencies; // Evaluate const { evalMetaUpdates, evaluatedTree, staleMetaIds } = this.evaluateTree( - //we need to deep clone oldUnEvalTree because evaluateTree will mutate it - klona(this.oldUnEvalTree), + this.oldUnEvalTree, evaluationOrder, undefined, this.oldConfigTree, @@ -782,7 +781,6 @@ export default class DataTreeEvaluator { evaluatedTree: newEvalTree, staleMetaIds, } = this.evaluateTree( - // should not clone evalTree unnessarily because it is anyways being overwritten in the subsequent statement this.evalTree, evaluationOrder, { @@ -924,7 +922,7 @@ export default class DataTreeEvaluator { } evaluateTree( - dataTree: DataTree, + oldUnevalTree: DataTree, evaluationOrder: Array, options: { isFirstTree: boolean; @@ -941,8 +939,10 @@ export default class DataTreeEvaluator { evalMetaUpdates: EvalMetaUpdates; staleMetaIds: string[]; } { + const tree = klona(oldUnevalTree); + errorModifier.updateAsyncFunctions( - dataTree, + tree, this.getConfigTree(), this.dependencyMap, ); @@ -1168,7 +1168,7 @@ export default class DataTreeEvaluator { return set(currentTree, fullPropertyPath, evalPropertyValue); } }, - dataTree, + tree, ); return { @@ -1181,7 +1181,7 @@ export default class DataTreeEvaluator { type: EvalErrorTypes.EVAL_TREE_ERROR, message: (error as Error).message, }); - return { evaluatedTree: dataTree, evalMetaUpdates, staleMetaIds: [] }; + return { evaluatedTree: tree, evalMetaUpdates, staleMetaIds: [] }; } } @@ -1472,9 +1472,7 @@ export default class DataTreeEvaluator { // setting parseValue in dataTree set(currentTree, fullPropertyPath, parsedValue); // setting evalPropertyValue in unParsedEvalTree - // cloning evalPropertyValue because parsedValue and evalPropertyValue could be equal, they both could share the same reference - //hence we are cloning evalPropertyValue to seperate them - set(this.getUnParsedEvalTree(), fullPropertyPath, klona(evalPropertyValue)); + set(this.getUnParsedEvalTree(), fullPropertyPath, evalPropertyValue); } reValidateWidgetDependentProperty({ diff --git a/app/client/src/workers/common/DataTreeEvaluator/test.ts b/app/client/src/workers/common/DataTreeEvaluator/test.ts index 1c4dc3ae3c..7b78bbe691 100644 --- a/app/client/src/workers/common/DataTreeEvaluator/test.ts +++ b/app/client/src/workers/common/DataTreeEvaluator/test.ts @@ -16,7 +16,6 @@ import { replaceThisDotParams } from "./utils"; import { isDataField } from "./utils"; import widgets from "widgets"; import type { WidgetConfiguration } from "WidgetProvider/constants"; -import { klona } from "klona"; const widgetConfigMap: Record< string, @@ -360,7 +359,7 @@ describe("DataTreeEvaluator", () => { nonDynamicFieldValidationOrder: nonDynamicFieldValidationOrder5, unEvalUpdates, } = dataTreeEvaluator.setupUpdateTree( - klona(nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree), + nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree, nestedArrayAccessorCyclicDependencyConfig.apiSuccessConfigTree, ); dataTreeEvaluator.evalAndValidateSubTree( @@ -424,7 +423,7 @@ describe("DataTreeEvaluator", () => { nonDynamicFieldValidationOrder, unEvalUpdates, } = dataTreeEvaluator.setupUpdateTree( - klona(nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree), + nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree, nestedArrayAccessorCyclicDependencyConfig.apiSuccessConfigTree, ); dataTreeEvaluator.evalAndValidateSubTree( @@ -471,7 +470,7 @@ describe("DataTreeEvaluator", () => { nonDynamicFieldValidationOrder: nonDynamicFieldValidationOrder2, unEvalUpdates, } = dataTreeEvaluator.setupUpdateTree( - klona(nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree), + nestedArrayAccessorCyclicDependency.apiSuccessUnEvalTree, nestedArrayAccessorCyclicDependencyConfig.apiSuccessConfigTree, ); dataTreeEvaluator.evalAndValidateSubTree( From 39c62826ee96a169302c4253e0ae63eb10dc4685 Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Fri, 22 Sep 2023 13:34:26 +0530 Subject: [PATCH 03/48] Revert "fix: fixed failing queries using aggregation pipeline (#26132)" (#27562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 66d5027126523fb73cb14ce1336b171a85f8db37. > Pull Request Template > > Use this template to quickly create a well written pull request. Delete all quotes before creating the pull request. > ## Description > Add a TL;DR when description is extra long (helps content team) > > Please include a summary of the changes and which issue has been fixed. Please also include relevant motivation > and context. List any dependencies that are required for this change > > Links to Notion, Figma or any other documents that might be relevant to the PR > > #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../repositories/BaseRepositoryImpl.java | 4 +- .../CustomNewActionRepositoryImpl.java | 6 +- .../CustomNewPageRepositoryImpl.java | 6 +- .../ce/BaseAppsmithRepositoryCEImpl.java | 6 +- .../ce/CustomNewActionRepositoryCE.java | 2 +- .../ce/CustomNewActionRepositoryCEImpl.java | 42 +++------- .../ce/CustomNewPageRepositoryCE.java | 6 +- .../ce/CustomNewPageRepositoryCEImpl.java | 77 +++---------------- .../ce/NewActionRepositoryCE.java | 5 -- .../ce/ApplicationPageServiceCEImpl.java | 5 +- .../services/ce/NewActionServiceCE.java | 4 +- .../services/ce/NewActionServiceCEImpl.java | 4 +- .../server/services/ce/NewPageServiceCE.java | 4 +- .../services/ce/NewPageServiceCEImpl.java | 4 +- .../services/ce/NewActionServiceUnitTest.java | 11 +-- 15 files changed, 45 insertions(+), 141 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java index a4090d8f70..7bae25049d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java @@ -161,9 +161,7 @@ public class BaseRepositoryImpl .flatMapMany(principal -> { Query query = new Query(notDeleted()); return mongoOperations.find( - query.cursorBatchSize(10000), - entityInformation.getJavaType(), - entityInformation.getCollectionName()); + query, entityInformation.getJavaType(), entityInformation.getCollectionName()); }); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java index b3eb05cb8d..e45c7fd224 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java @@ -2,7 +2,6 @@ package com.appsmith.server.repositories; import com.appsmith.server.repositories.ce.CustomNewActionRepositoryCEImpl; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.stereotype.Component; @@ -15,8 +14,7 @@ public class CustomNewActionRepositoryImpl extends CustomNewActionRepositoryCEIm public CustomNewActionRepositoryImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper, - MongoTemplate mongoTemplate) { - super(mongoOperations, mongoConverter, cacheableRepositoryHelper, mongoTemplate); + CacheableRepositoryHelper cacheableRepositoryHelper) { + super(mongoOperations, mongoConverter, cacheableRepositoryHelper); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java index 690b54d4c1..370e5202b2 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java @@ -2,7 +2,6 @@ package com.appsmith.server.repositories; import com.appsmith.server.repositories.ce.CustomNewPageRepositoryCEImpl; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.stereotype.Component; @@ -14,8 +13,7 @@ public class CustomNewPageRepositoryImpl extends CustomNewPageRepositoryCEImpl i public CustomNewPageRepositoryImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper, - MongoTemplate mongoTemplate) { - super(mongoOperations, mongoConverter, cacheableRepositoryHelper, mongoTemplate); + CacheableRepositoryHelper cacheableRepositoryHelper) { + super(mongoOperations, mongoConverter, cacheableRepositoryHelper); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java index 28d0012b85..c2d9b0634b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java @@ -22,7 +22,6 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.data.mongodb.repository.Meta; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; @@ -167,7 +166,7 @@ public abstract class BaseAppsmithRepositoryCEImpl { return mongoOperations .query(this.genericDomain) - .matching(query.cursorBatchSize(10000)) + .matching(query) .one() .flatMap(obj -> setUserPermissionsInObject(obj, permissionGroups)); }); @@ -332,7 +331,6 @@ public abstract class BaseAppsmithRepositoryCEImpl { }); } - @Meta(cursorBatchSize = 10000) protected Mono queryOne( List criterias, List projectionFieldNames, Optional permission) { Mono> permissionGroupsMono = getCurrentUserPermissionGroupsIfRequired(permission); @@ -541,7 +539,7 @@ public abstract class BaseAppsmithRepositoryCEImpl { sortOptional.ifPresent(sort -> query.with(sort)); return mongoOperations .query(this.genericDomain) - .matching(query.cursorBatchSize(10000)) + .matching(query) .all() .flatMap(obj -> setUserPermissionsInObject(obj, permissionGroups)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java index 11a6cb6af1..007ff409f8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java @@ -78,7 +78,7 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository> bulkUpdate(List newActions); - Mono> publishActions(String applicationId, AclPermission permission); + Mono publishActions(String applicationId, AclPermission permission); Mono archiveDeletedUnpublishedActions(String applicationId, AclPermission permission); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java index c45cf40bfa..5a90b20dce 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java @@ -20,12 +20,9 @@ import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.bson.types.ObjectId; import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.AggregationOperation; -import org.springframework.data.mongodb.core.aggregation.AggregationResults; -import org.springframework.data.mongodb.core.aggregation.Fields; +import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; @@ -55,15 +52,11 @@ import static org.springframework.data.mongodb.core.query.Criteria.where; public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl implements CustomNewActionRepositoryCE { - private final MongoTemplate mongoTemplate; - public CustomNewActionRepositoryCEImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper, - MongoTemplate mongoTemplate) { + CacheableRepositoryHelper cacheableRepositoryHelper) { super(mongoOperations, mongoConverter, cacheableRepositoryHelper); - this.mongoTemplate = mongoTemplate; } @Override @@ -578,33 +571,16 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl< } @Override - public Mono> publishActions(String applicationId, AclPermission permission) { + public Mono publishActions(String applicationId, AclPermission permission) { Criteria applicationIdCriteria = where(fieldName(QNewAction.newAction.applicationId)).is(applicationId); + // using aggregation update instead of regular update here + // it's required to set a field to a value of another field from the same domain + AggregationUpdate aggregationUpdate = AggregationUpdate.update() + .set(fieldName(QNewAction.newAction.publishedAction)) + .toValue("$" + fieldName(QNewAction.newAction.unpublishedAction)); - Mono> permissionGroupsMono = - getCurrentUserPermissionGroupsIfRequired(Optional.ofNullable(permission)); - - return permissionGroupsMono.flatMap(permissionGroups -> { - AggregationOperation matchAggregationWithPermission = null; - if (permission == null) { - matchAggregationWithPermission = Aggregation.match(new Criteria().andOperator(notDeleted())); - } else { - matchAggregationWithPermission = Aggregation.match( - new Criteria().andOperator(notDeleted(), userAcl(permissionGroups, permission))); - } - AggregationOperation matchAggregation = Aggregation.match(applicationIdCriteria); - AggregationOperation wholeProjection = Aggregation.project(NewAction.class); - AggregationOperation addFieldsOperation = Aggregation.addFields() - .addField(fieldName(QNewAction.newAction.publishedAction)) - .withValueOf(Fields.field(fieldName(QNewAction.newAction.unpublishedAction))) - .build(); - Aggregation combinedAggregation = Aggregation.newAggregation( - matchAggregation, matchAggregationWithPermission, wholeProjection, addFieldsOperation); - AggregationResults updatedResults = - mongoTemplate.aggregate(combinedAggregation, NewAction.class, NewAction.class); - return bulkUpdate(updatedResults.getMappedResults()); - }); + return updateByCriteria(List.of(applicationIdCriteria), aggregationUpdate, permission); } @Override diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java index ec6775c2cb..dc940ee25b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java @@ -3,7 +3,7 @@ package com.appsmith.server.repositories.ce; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.NewPage; import com.appsmith.server.repositories.AppsmithRepository; -import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.result.UpdateResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -43,7 +43,5 @@ public interface CustomNewPageRepositoryCE extends AppsmithRepository { Mono findByGitSyncIdAndDefaultApplicationId( String defaultApplicationId, String gitSyncId, Optional permission); - Mono> publishPages(Collection pageIds, AclPermission permission); - - Mono> bulkUpdate(List newPages); + Mono publishPages(Collection pageIds, AclPermission permission); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java index acd1a16e0f..92a93f963e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java @@ -9,32 +9,20 @@ import com.appsmith.server.domains.QNewPage; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl; import com.appsmith.server.repositories.CacheableRepositoryHelper; -import com.mongodb.bulk.BulkWriteResult; -import com.mongodb.client.model.UpdateOneModel; -import com.mongodb.client.model.WriteModel; +import com.mongodb.client.result.UpdateResult; import lombok.extern.slf4j.Slf4j; -import org.bson.Document; -import org.bson.types.ObjectId; -import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.AggregationOperation; -import org.springframework.data.mongodb.core.aggregation.AggregationResults; -import org.springframework.data.mongodb.core.aggregation.Fields; +import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import static org.springframework.data.mongodb.core.query.Criteria.where; @@ -42,15 +30,11 @@ import static org.springframework.data.mongodb.core.query.Criteria.where; public class CustomNewPageRepositoryCEImpl extends BaseAppsmithRepositoryImpl implements CustomNewPageRepositoryCE { - private final MongoTemplate mongoTemplate; - public CustomNewPageRepositoryCEImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper, - MongoTemplate mongoTemplate) { + CacheableRepositoryHelper cacheableRepositoryHelper) { super(mongoOperations, mongoConverter, cacheableRepositoryHelper); - this.mongoTemplate = mongoTemplate; } @Override @@ -267,55 +251,14 @@ public class CustomNewPageRepositoryCEImpl extends BaseAppsmithRepositoryImpl> publishPages(Collection pageIds, AclPermission permission) { + public Mono publishPages(Collection pageIds, AclPermission permission) { Criteria applicationIdCriteria = where(fieldName(QNewPage.newPage.id)).in(pageIds); + // using aggregation update instead of regular update here + // it's required to set a field to a value of another field from the same domain + AggregationUpdate aggregationUpdate = AggregationUpdate.update() + .set(fieldName(QNewPage.newPage.publishedPage)) + .toValue("$" + fieldName(QNewPage.newPage.unpublishedPage)); - Mono> permissionGroupsMono = - getCurrentUserPermissionGroupsIfRequired(Optional.ofNullable(permission)); - - return permissionGroupsMono.flatMap(permissionGroups -> { - AggregationOperation matchAggregationWithPermission = null; - if (permission == null) { - matchAggregationWithPermission = Aggregation.match(new Criteria().andOperator(notDeleted())); - } else { - matchAggregationWithPermission = Aggregation.match( - new Criteria().andOperator(notDeleted(), userAcl(permissionGroups, permission))); - } - AggregationOperation matchAggregation = Aggregation.match(applicationIdCriteria); - AggregationOperation wholeProjection = Aggregation.project(NewPage.class); - AggregationOperation addFieldsOperation = Aggregation.addFields() - .addField(fieldName(QNewPage.newPage.publishedPage)) - .withValueOf(Fields.field(fieldName(QNewPage.newPage.unpublishedPage))) - .build(); - Aggregation combinedAggregation = Aggregation.newAggregation( - matchAggregation, matchAggregationWithPermission, wholeProjection, addFieldsOperation); - AggregationResults updatedResults = - mongoTemplate.aggregate(combinedAggregation, NewPage.class, NewPage.class); - return bulkUpdate(updatedResults.getMappedResults()); - }); - } - - @Override - public Mono> bulkUpdate(List newPages) { - if (CollectionUtils.isEmpty(newPages)) { - return Mono.just(Collections.emptyList()); - } - - // convert the list of new pages to a list of DBObjects - List> dbObjects = newPages.stream() - .map(newPage -> { - assert newPage.getId() != null; - Document document = new Document(); - mongoOperations.getConverter().write(newPage, document); - document.remove("_id"); - return (WriteModel) new UpdateOneModel( - new Document("_id", new ObjectId(newPage.getId())), new Document("$set", document)); - }) - .collect(Collectors.toList()); - - return mongoOperations - .getCollection(mongoOperations.getCollectionName(NewPage.class)) - .flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects)) - .collectList(); + return updateByCriteria(List.of(applicationIdCriteria), aggregationUpdate, permission); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java index 505981ef1d..e74852d3b5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java @@ -3,17 +3,12 @@ package com.appsmith.server.repositories.ce; import com.appsmith.server.domains.NewAction; import com.appsmith.server.repositories.BaseRepository; import com.appsmith.server.repositories.CustomNewActionRepository; -import org.springframework.data.mongodb.repository.Meta; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface NewActionRepositoryCE extends BaseRepository, CustomNewActionRepository { - @Meta(cursorBatchSize = 10000) Flux findByApplicationId(String applicationId); - @Meta(cursorBatchSize = 10000) - Flux findAllByIdIn(Iterable ids); - Mono countByDeletedAtNull(); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java index 000b4bf90c..7269060239 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java @@ -50,7 +50,6 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.WorkspacePermission; import com.google.common.base.Strings; -import com.mongodb.bulk.BulkWriteResult; import com.mongodb.client.result.UpdateResult; import jakarta.annotation.Nullable; import lombok.RequiredArgsConstructor; @@ -1135,7 +1134,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE { if (isPublishedManually) { application.setLastDeployedAt(Instant.now()); } - Mono> publishPagesMono = + Mono publishPagesMono = newPageService.publishPages(editedPageIds, pagePermission.getEditPermission()); // Archive the deleted pages and save the application changes and then return the pages so that @@ -1145,7 +1144,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE { }) .cache(); // caching as we'll need this to send analytics attributes after publishing the app - Mono> publishActionsMono = + Mono publishActionsMono = newActionService.publishActions(applicationId, actionPermission.getEditPermission()); // this is a map of pluginType to count of actions for that pluginType, required for analytics diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java index 733b08c21d..9bf8e71f8e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java @@ -14,7 +14,7 @@ import com.appsmith.server.dtos.ce.ImportActionResultDTO; import com.appsmith.server.dtos.ce.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.helpers.ce.ImportApplicationPermissionProvider; import com.appsmith.server.services.CrudService; -import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.result.UpdateResult; import org.springframework.data.domain.Sort; import org.springframework.util.MultiValueMap; import reactor.core.publisher.Flux; @@ -134,7 +134,7 @@ public interface NewActionServiceCE extends CrudService { ImportActionCollectionResultDTO importActionCollectionResultDTO, ImportActionResultDTO importActionResultDTO); - Mono> publishActions(String applicationId, AclPermission permission); + Mono publishActions(String applicationId, AclPermission permission); Flux countActionsByPluginType(String applicationId); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java index 276d2ce24b..fb4dd3057b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java @@ -53,7 +53,7 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PolicySolution; -import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.result.UpdateResult; import io.micrometer.observation.ObservationRegistry; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; @@ -1965,7 +1965,7 @@ public class NewActionServiceCEImpl extends BaseService> publishActions(String applicationId, AclPermission permission) { + public Mono publishActions(String applicationId, AclPermission permission) { // delete the actions that were deleted in edit mode return repository .archiveDeletedUnpublishedActions(applicationId, permission) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java index b42c05c5fd..cb480d23f3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java @@ -7,7 +7,7 @@ import com.appsmith.server.domains.NewPage; import com.appsmith.server.dtos.ApplicationPagesDTO; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.services.CrudService; -import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.result.UpdateResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -93,5 +93,5 @@ public interface NewPageServiceCE extends CrudService { Flux findPageSlugsByApplicationIds(List applicationIds, AclPermission aclPermission); - Mono> publishPages(Collection pageIds, AclPermission permission); + Mono publishPages(Collection pageIds, AclPermission permission); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java index e6fa147a0f..9430d3bfa2 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java @@ -23,7 +23,7 @@ import com.appsmith.server.services.BaseService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.PagePermission; -import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.result.UpdateResult; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONObject; @@ -692,7 +692,7 @@ public class NewPageServiceCEImpl extends BaseService> publishPages(Collection pageIds, AclPermission permission) { + public Mono publishPages(Collection pageIds, AclPermission permission) { return repository.publishPages(pageIds, permission); } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java index 36f9d9aeef..21141119fb 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java @@ -23,6 +23,7 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PolicySolution; +import com.mongodb.client.result.UpdateResult; import io.micrometer.observation.ObservationRegistry; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; @@ -38,8 +39,6 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.test.StepVerifier; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; @@ -200,8 +199,9 @@ public class NewActionServiceUnitTest { @Test public void testPublishActionArchivesAndPublishesActions() { String applicationId = "dummy-application-id"; - List updateResult = Mockito.mock(List.class); - Mockito.when(updateResult.size()).thenReturn(10); + UpdateResult updateResult = Mockito.mock(UpdateResult.class); + Mockito.when(updateResult.getModifiedCount()).thenReturn(10L); + Mockito.when(updateResult.getMatchedCount()).thenReturn(5L); Mockito.when(newActionRepository.archiveDeletedUnpublishedActions( applicationId, actionPermission.getEditPermission())) @@ -212,7 +212,8 @@ public class NewActionServiceUnitTest { StepVerifier.create(newActionService.publishActions(applicationId, actionPermission.getEditPermission())) .assertNext(updateResult1 -> { - assertEquals(10, updateResult1.size()); + assertEquals(10L, updateResult1.getModifiedCount()); + assertEquals(5L, updateResult1.getMatchedCount()); }) .verifyComplete(); } From 7941ec25a65cef92265ac14c48c14af3c408496a Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Fri, 22 Sep 2023 14:55:52 +0530 Subject: [PATCH 04/48] Fixed compilation issue --- .../appsmith/server/services/ce/NewActionServiceCEImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java index fb4dd3057b..3088b833a0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java @@ -663,7 +663,7 @@ public class NewActionServiceCEImpl extends BaseService findAllById(Iterable id) { - return repository.findAllByIdIn(id).flatMap(this::sanitizeAction); + return repository.findAllById(id).flatMap(this::sanitizeAction); } @Override @@ -1907,7 +1907,7 @@ public class NewActionServiceCEImpl extends BaseService { // Update collectionId and defaultCollectionIds in actionDTOs ActionDTO unpublishedAction = newAction.getUnpublishedAction(); From 3c118af42c98e41431601144f54f07b9a900a995 Mon Sep 17 00:00:00 2001 From: Diljit Date: Wed, 20 Sep 2023 14:48:35 +0530 Subject: [PATCH 05/48] feat: Add placeholder component for AskAI Button in Code Editor (#27441) --- .../editorComponents/GPT/AskAIButton.tsx | 15 +++++++++++++++ .../editorComponents/CodeEditor/index.tsx | 11 +++++++++++ .../editorComponents/GPT/AskAIButton.tsx | 1 + 3 files changed, 27 insertions(+) create mode 100644 app/client/src/ce/components/editorComponents/GPT/AskAIButton.tsx create mode 100644 app/client/src/ee/components/editorComponents/GPT/AskAIButton.tsx diff --git a/app/client/src/ce/components/editorComponents/GPT/AskAIButton.tsx b/app/client/src/ce/components/editorComponents/GPT/AskAIButton.tsx new file mode 100644 index 0000000000..1bc3a41200 --- /dev/null +++ b/app/client/src/ce/components/editorComponents/GPT/AskAIButton.tsx @@ -0,0 +1,15 @@ +import type { + FieldEntityInformation, + TEditorModes, +} from "components/editorComponents/CodeEditor/EditorConfig"; + +type AskAIButtonProps = { + mode: TEditorModes; + onClick: () => void; + entity: FieldEntityInformation; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function AskAIButton(props: AskAIButtonProps) { + return null; +} diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx index 09291f81cf..5db148358e 100644 --- a/app/client/src/components/editorComponents/CodeEditor/index.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx @@ -144,6 +144,7 @@ import { import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; import { selectFeatureFlags } from "@appsmith/selectors/featureFlagsSelectors"; import { AIWindow } from "@appsmith/components/editorComponents/GPT"; +import { AskAIButton } from "@appsmith/components/editorComponents/GPT/AskAIButton"; import classNames from "classnames"; import { APPSMITH_AI, @@ -1594,6 +1595,16 @@ class CodeEditor extends Component { +
+ { + this.setState({ showAIWindow: true }); + }} + /> +
+ Date: Thu, 21 Sep 2023 15:37:41 +0530 Subject: [PATCH 06/48] fix: Active color of primary buttons broken after ADS 2.0 migration (#26958) ## Description When admin has configured the branding color, on click of primary button, still appsmith orange color was being rendered. This PR fixes that issue and also added a fallback to handle the old customers who already have a branding color. This fallback is required since the new fix work only for people who choose brand color after this PR is merged. Corresponding EE PR: https://github.com/appsmithorg/appsmith-ee/pull/2182 #### PR fixes following issue(s) Fixes #25446 Fixes #27501 #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [x] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Pawan Kumar --- .../theming/src/color/src/LightModeTheme.ts | 2 +- .../AdminSettings/Branding/SettingsForm.tsx | 2 +- .../AdminSettings/FormGroup/ColorInput.tsx | 11 ++++++++-- app/client/src/utils/BrandingUtils.ts | 20 +++++++++++++++---- .../src/utils/hooks/useBrandingTheme.ts | 18 +++++++++++++++++ 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts index 521a3ea6ba..fd63f74b4e 100644 --- a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts @@ -217,7 +217,7 @@ export class LightModeTheme implements ColorModeTheme { return color; } - private get bgAccentActive() { + public get bgAccentActive() { // Active state of bgAccent. Slightly darker than the resting state to produce the effect of moving further from the viewer / being pushed down. const color = this.bgAccent.clone(); diff --git a/app/client/src/pages/AdminSettings/Branding/SettingsForm.tsx b/app/client/src/pages/AdminSettings/Branding/SettingsForm.tsx index d911507cd7..bb3ef50180 100644 --- a/app/client/src/pages/AdminSettings/Branding/SettingsForm.tsx +++ b/app/client/src/pages/AdminSettings/Branding/SettingsForm.tsx @@ -155,7 +155,7 @@ function SettingsForm(props: SettingsFormProps) { !["disabled", "hover"].includes(key)} + filter={(key) => !["disabled", "hover", "active"].includes(key)} logEvent={(property: string) => { AnalyticsUtil.logEvent("BRANDING_PROPERTY_UPDATE", { propertyName: property, diff --git a/app/client/src/pages/AdminSettings/FormGroup/ColorInput.tsx b/app/client/src/pages/AdminSettings/FormGroup/ColorInput.tsx index 0e9128f520..1dd4ea0c68 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/ColorInput.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/ColorInput.tsx @@ -6,6 +6,7 @@ import tinycolor from "tinycolor2"; import styled from "styled-components"; import { Icon, Text, Tooltip } from "design-system"; import { InputGroup, Classes } from "@blueprintjs/core"; +import Color from "colorjs.io"; import type { SettingComponentProps } from "./Common"; import { FormGroup } from "./Common"; @@ -116,10 +117,16 @@ export const ColorInput = (props: ColorInputProps) => { shades[selectedIndex] = e.target.value; logEvent && logEvent(selectedIndex); - + let color = undefined; + try { + color = Color.parse(e.target.value); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } // if user is touching the primary color // then we need to update the shades - if (selectedIndex === "primary") { + if (selectedIndex === "primary" && color) { shades = createBrandColorsFromPrimaryColor(e.target.value); } diff --git a/app/client/src/utils/BrandingUtils.ts b/app/client/src/utils/BrandingUtils.ts index 9ce51f6ab2..eef24cdd4d 100644 --- a/app/client/src/utils/BrandingUtils.ts +++ b/app/client/src/utils/BrandingUtils.ts @@ -11,13 +11,15 @@ import { import { toast } from "design-system"; import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; +import { LightModeTheme } from "@design-system/theming"; const FAVICON_MAX_WIDTH = 32; const FAVICON_MAX_HEIGHT = 32; -const DEFAULT_BRANDING_PRIMARY_COLOR = "#D7D7D7"; -export const APPSMITH_BRAND_PRIMARY_COLOR = getComputedStyle( - document.documentElement, -).getPropertyValue("--ads-v2-color-bg-brand"); +const DEFAULT_BRANDING_PRIMARY_COLOR = "#E15615"; +export const APPSMITH_BRAND_PRIMARY_COLOR = + getComputedStyle(document.documentElement).getPropertyValue( + "--ads-v2-color-bg-brand", + ) || DEFAULT_BRANDING_PRIMARY_COLOR; export const APPSMITH_BRAND_BG_COLOR = "#F1F5F9"; export const APPSMITH_BRAND_FAVICON_URL = getAssetUrl( `${ASSETS_CDN_URL}/appsmith-favicon-orange.ico`, @@ -37,6 +39,8 @@ export function createBrandColorsFromPrimaryColor( const hsl = tinycolor(brand).toHsl(); const hue = hsl.h; const saturation = hsl.s; + // initialize light theme + const lightTheme = new LightModeTheme(brand); let textColor = "#000"; const isReadable = tinycolor.isReadable(brand, "#000", { @@ -66,10 +70,18 @@ export function createBrandColorsFromPrimaryColor( ) : darkenColor(brand); + // get active color from color algorithm + const activeColor = + brand === APPSMITH_BRAND_PRIMARY_COLOR + ? getComputedStyle(document.documentElement).getPropertyValue( + "--ads-v2-color-bg-brand-emphasis-plus", + ) + : lightTheme.bgAccentActive.toString({ format: "hex" }); return { primary: brand, background: bgColor, hover: hoverColor, + active: activeColor, font: textColor, disabled: disabledColor, }; diff --git a/app/client/src/utils/hooks/useBrandingTheme.ts b/app/client/src/utils/hooks/useBrandingTheme.ts index 220a9783d7..b763b00069 100644 --- a/app/client/src/utils/hooks/useBrandingTheme.ts +++ b/app/client/src/utils/hooks/useBrandingTheme.ts @@ -3,9 +3,25 @@ import { useSelector } from "react-redux"; import { getTenantConfig } from "@appsmith/selectors/tenantSelectors"; import { useLayoutEffect } from "react"; import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; +import { APPSMITH_BRAND_PRIMARY_COLOR } from "utils/BrandingUtils"; +import { LightModeTheme } from "@design-system/theming"; const useBrandingTheme = () => { const config = useSelector(getTenantConfig); + let activeColor: string | undefined = undefined; + if ( + config.brandColors.primary !== undefined && + (config.brandColors.active === undefined || + config.brandColors.active === "") + ) { + const lightTheme = new LightModeTheme(config.brandColors.primary); + activeColor = + config.brandColors.primary === APPSMITH_BRAND_PRIMARY_COLOR + ? getComputedStyle(document.documentElement).getPropertyValue( + "--ads-v2-color-bg-brand-emphasis-plus", + ) + : lightTheme.bgAccentActive.toString({ format: "hex" }); + } useLayoutEffect(() => { const cssVariables: Record = { @@ -15,8 +31,10 @@ const useBrandingTheme = () => { // TODO:(Albin) Remove this once branding and new DS is fully integrated "background-secondary": config.brandColors.background, "bg-brand-emphasis": config.brandColors.hover, + "bg-brand-emphasis-plus": activeColor || config.brandColors.active, "fg-brand-emphasis": config.brandColors.hover, "border-brand-emphasis": config.brandColors.hover, + "border-brand-emphasis-plus": activeColor || config.brandColors.active, "fg-on-brand": config.brandColors.font, }; From 228025ed6ecd0cbb3e7646bd5220e9e8425ea8fe Mon Sep 17 00:00:00 2001 From: Shubham Saxena <136057998+shubham7saxena7@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:43:39 +0530 Subject: [PATCH 07/48] fix: apply brand changes (#27536) ## Description apply branding changes to email verification template Fixes #27528 Fixes #27431 Fixes #27475 #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../server/constants/ce/EmailConstantsCE.java | 2 +- .../server/services/ce/EmailServiceCE.java | 2 +- .../services/ce/EmailServiceCEImpl.java | 17 +- .../server/services/ce/UserServiceCEImpl.java | 3 +- .../email/ce/emailVerificationTemplate.html | 540 +++++++++ .../email/emailVerificationTemplate.html | 1000 ----------------- 6 files changed, 555 insertions(+), 1009 deletions(-) create mode 100644 app/server/appsmith-server/src/main/resources/email/ce/emailVerificationTemplate.html delete mode 100644 app/server/appsmith-server/src/main/resources/email/emailVerificationTemplate.html diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/EmailConstantsCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/EmailConstantsCE.java index f0774c0ec0..6d2abde6d6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/EmailConstantsCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/EmailConstantsCE.java @@ -24,7 +24,7 @@ public class EmailConstantsCE { public static final String INVITE_WORKSPACE_TEMPLATE_NEW_USER_CE = "email/ce/inviteWorkspaceNewUserTemplate.html"; public static final String FORGOT_PASSWORD_TEMPLATE_CE = "email/ce/forgotPasswordTemplate.html"; public static final String INSTANCE_ADMIN_INVITE_EMAIL_TEMPLATE = "email/ce/instanceAdminInviteTemplate.html"; - public static final String EMAIL_VERIFICATION_EMAIL_TEMPLATE = "email/emailVerificationTemplate.html"; + public static final String EMAIL_VERIFICATION_EMAIL_TEMPLATE_CE = "email/ce/emailVerificationTemplate.html"; public static final String WORKSPACE_URL = "%s/applications#%s"; public static final String INVITER_FIRST_NAME = "inviterFirstName"; public static final String INVITER_WORKSPACE_NAME = "inviterWorkspaceName"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCE.java index 4bbbbf0d1d..31d37ae847 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCE.java @@ -16,7 +16,7 @@ public interface EmailServiceCE { String originHeader, boolean isNewUser); - Mono sendEmailVerificationEmail(User user, String verificationUrl); + Mono sendEmailVerificationEmail(User user, String verificationUrl, String originHeader); Mono sendInstanceAdminInviteEmail( User invitedUser, User invitingUser, String originHeader, boolean isNewUser); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCEImpl.java index 573faeb50a..bab662d65a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/EmailServiceCEImpl.java @@ -63,15 +63,16 @@ public class EmailServiceCEImpl implements EmailServiceCE { } @Override - public Mono sendEmailVerificationEmail(User user, String verificationURL) { + public Mono sendEmailVerificationEmail(User user, String verificationURL, String originHeader) { Map params = new HashMap<>(); params.put(EMAIL_VERIFICATION_URL, verificationURL); return this.enrichParams(params) - .flatMap(updatedParams -> emailSender.sendMail( - user.getEmail(), - EMAIL_VERIFICATION_EMAIL_SUBJECT, - EMAIL_VERIFICATION_EMAIL_TEMPLATE, - updatedParams)); + .flatMap(enrichedParams -> this.enrichWithBrandParams(enrichedParams, originHeader) + .flatMap(updatedParams -> emailSender.sendMail( + user.getEmail(), + EMAIL_VERIFICATION_EMAIL_SUBJECT, + getEmailVerificationTemplate(), + updatedParams))); } @Override @@ -123,6 +124,10 @@ public class EmailServiceCEImpl implements EmailServiceCE { return INVITE_WORKSPACE_TEMPLATE_EXISTING_USER_CE; } + protected String getEmailVerificationTemplate() { + return EMAIL_VERIFICATION_EMAIL_TEMPLATE_CE; + } + protected String getAdminInstanceInviteTemplate() { return INSTANCE_ADMIN_INVITE_EMAIL_TEMPLATE; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java index 1c8c21294d..36558c5363 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java @@ -850,7 +850,8 @@ public class UserServiceCEImpl extends BaseService URLEncoder.encode(emailVerificationToken.getEmail(), StandardCharsets.UTF_8), redirectUrlCopy); - return emailService.sendEmailVerificationEmail(user, verificationUrl); + return emailService.sendEmailVerificationEmail( + user, verificationUrl, resendEmailVerificationDTO.getBaseUrl()); }) .thenReturn(true); } diff --git a/app/server/appsmith-server/src/main/resources/email/ce/emailVerificationTemplate.html b/app/server/appsmith-server/src/main/resources/email/ce/emailVerificationTemplate.html new file mode 100644 index 0000000000..17530b1f16 --- /dev/null +++ b/app/server/appsmith-server/src/main/resources/email/ce/emailVerificationTemplate.html @@ -0,0 +1,540 @@ + + + + Verify your email for {{instanceName}} + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+
+
+

+ + Hey, + +

+
+
+
+

+ Thank you for signing up! Before we get started, we just need to confirm that this is you. Click below to verify your email address. +

+
+
+
+

+ The link expires in two days’ + time. Better not wait. +

+
+
+ + + + + + +
+ + Verify +
+
+
+

+ 1390, Market Street, Suite 200, San + Francisco, California 94102, United + States +

+
+
+
+
+ +
+
+
+ + diff --git a/app/server/appsmith-server/src/main/resources/email/emailVerificationTemplate.html b/app/server/appsmith-server/src/main/resources/email/emailVerificationTemplate.html deleted file mode 100644 index 17e9cc2a5c..0000000000 --- a/app/server/appsmith-server/src/main/resources/email/emailVerificationTemplate.html +++ /dev/null @@ -1,1000 +0,0 @@ - - - - Verify email for your Appsmith account - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - -
- -
- - - - - - -
-

- -
-
- -
- - - - - - -
- - -
-
-
- -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- -
-
-
-   -
-
-
-

- Verify your email address -

-
-
-
-

- Hello,
-
- Thank you for signing up! Before we get - started, we just need to confirm that - this is you. Click below to verify your - email address: -
-

-
-
-
-   -
-
- - - - - - -
- - Verify - -
-
-
-

- The link expires in two days’ time. - Better not wait. -
- P.S.: You will see a welcome e-mail - soon after you join to help get started - with Appsmith. -

-
-
-
-
- -
-
- -
- - - - - - -
- -
- - - - - - -
-

- -
-
- -
- - - - - - -
- - -
-
-
- -
- - - - - - -
- - - - - - - - - -
- - - - - - -
- -
-
-
-

- Appsmith is an open source framework - that helps developer and companies to - build powerful internal tools. -

-
-
-
-
- -
-
- -
- - From 6775323cbd9ead8feedb79e8d5630e992352cabb Mon Sep 17 00:00:00 2001 From: Anagh Hegde Date: Fri, 22 Sep 2023 13:16:04 +0530 Subject: [PATCH 08/48] chore: fix the jgit version updates in other places (#27558) Should resolve the following CVE reports: 1. https://github.com/appsmithorg/appsmith/security/dependabot/263 1. https://github.com/appsmithorg/appsmith/security/dependabot/264 --- app/server/appsmith-git/pom.xml | 10 +--------- .../git/helpers/SshTransportConfigCallback.java | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/server/appsmith-git/pom.xml b/app/server/appsmith-git/pom.xml index a0b89e9a40..60def1cd47 100644 --- a/app/server/appsmith-git/pom.xml +++ b/app/server/appsmith-git/pom.xml @@ -29,15 +29,7 @@ org.eclipse.jgit org.eclipse.jgit.ssh.jsch - 5.13.0.202109080827-r - - - - - org.eclipse.jgit - org.eclipse.jgit.junit.ssh - 6.4.0.202211300538-r - test + 6.6.1.202309021850-r diff --git a/app/server/appsmith-git/src/main/java/com/appsmith/git/helpers/SshTransportConfigCallback.java b/app/server/appsmith-git/src/main/java/com/appsmith/git/helpers/SshTransportConfigCallback.java index a210ddb5ce..bb74d5ad6e 100644 --- a/app/server/appsmith-git/src/main/java/com/appsmith/git/helpers/SshTransportConfigCallback.java +++ b/app/server/appsmith-git/src/main/java/com/appsmith/git/helpers/SshTransportConfigCallback.java @@ -4,11 +4,11 @@ import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import org.eclipse.jgit.api.TransportConfigCallback; -import org.eclipse.jgit.transport.JschConfigSessionFactory; -import org.eclipse.jgit.transport.OpenSshConfig; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.Transport; +import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory; +import org.eclipse.jgit.transport.ssh.jsch.OpenSshConfig; import org.eclipse.jgit.util.FS; /** From 4fcb639225612769050f1bbcbc10f7dbcf89dd3a Mon Sep 17 00:00:00 2001 From: Nayan Date: Mon, 25 Sep 2023 12:03:28 +0600 Subject: [PATCH 09/48] fix: Queries are missing from git after renaming a page (#27569) ## Description In git connected applications, if user renames a page, their queries are not pushed to git for that page. #### PR fixes following issue(s) Fixes #27508 --- .../server/helpers/ImportExportUtils.java | 16 +++ .../ImportExportApplicationServiceCEImpl.java | 12 ++ .../server/helpers/ImportExportUtilsTest.java | 25 ++++ .../ImportExportApplicationServiceTests.java | 124 ++++++++++++++++++ 4 files changed, 177 insertions(+) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ImportExportUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ImportExportUtils.java index 6de6563aa9..d3d2807556 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ImportExportUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ImportExportUtils.java @@ -2,13 +2,17 @@ package com.appsmith.server.helpers; import com.appsmith.external.models.ActionDTO; import com.appsmith.external.models.Datasource; +import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Application; +import com.appsmith.server.dtos.ApplicationJson; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.MongoTransactionException; import org.springframework.transaction.TransactionException; +import org.springframework.util.CollectionUtils; import java.util.Map; +import java.util.Set; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties; @@ -115,4 +119,16 @@ public class ImportExportUtils { importedApplication.setPublishedApplicationDetail(importedApplication.getUnpublishedApplicationDetail()); importedApplication.setPublishedAppLayout(importedApplication.getUnpublishedAppLayout()); } + + public static boolean isPageNameInUpdatedList(ApplicationJson applicationJson, String pageName) { + Map> updatedResources = applicationJson.getUpdatedResources(); + if (updatedResources == null) { + return false; + } + Set updatedPageNames = updatedResources.get(FieldName.PAGE_LIST); + if (CollectionUtils.isEmpty(updatedPageNames)) { + return false; + } + return pageName != null && updatedPageNames.contains(pageName); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ImportExportApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ImportExportApplicationServiceCEImpl.java index d32b737610..428a1da88f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ImportExportApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ImportExportApplicationServiceCEImpl.java @@ -477,6 +477,12 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica ActionCollectionDTO actionCollectionDTO = unpublishedActionCollectionDTO != null ? unpublishedActionCollectionDTO : publishedActionCollectionDTO; + + // TODO: check whether resource updated after last commit - move to a function + // we've replaced page id with page name in previous step + String pageName = actionCollectionDTO.getPageId(); + boolean isPageUpdated = + ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName); String actionCollectionName = actionCollectionDTO != null ? actionCollectionDTO.getName() + NAME_SEPARATOR @@ -485,6 +491,7 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica Instant actionCollectionUpdatedAt = actionCollection.getUpdatedAt(); boolean isActionCollectionUpdated = isClientSchemaMigrated || isServerSchemaMigrated + || isPageUpdated || applicationLastCommittedAt == null || actionCollectionUpdatedAt == null || applicationLastCommittedAt.isBefore(actionCollectionUpdatedAt); @@ -569,10 +576,15 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica String newActionName = actionDTO != null ? actionDTO.getValidName() + NAME_SEPARATOR + actionDTO.getPageId() : null; + // TODO: check whether resource updated after last commit - move to a function + String pageName = actionDTO.getPageId(); + boolean isPageUpdated = + ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName); Instant newActionUpdatedAt = newAction.getUpdatedAt(); boolean isNewActionUpdated = isClientSchemaMigrated || isServerSchemaMigrated || applicationLastCommittedAt == null + || isPageUpdated || newActionUpdatedAt == null || applicationLastCommittedAt.isBefore(newActionUpdatedAt); if (isNewActionUpdated && newActionName != null) { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/ImportExportUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/ImportExportUtilsTest.java index 45003e0d83..b2a7924917 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/ImportExportUtilsTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/ImportExportUtilsTest.java @@ -1,11 +1,16 @@ package com.appsmith.server.helpers; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.dtos.ApplicationJson; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.MongoTransactionException; +import java.util.Map; +import java.util.Set; + class ImportExportUtilsTest { @Test @@ -22,4 +27,24 @@ class ImportExportUtilsTest { String errorMessage = ImportExportUtils.getErrorMessage(throwable); Assertions.assertEquals(errorMessage, "Error: " + throwable.getMessage()); } + + @Test + void isPageNameInUpdatedList() { + String pageName = "Page1"; + Set updatedPageNames = Set.of("Page1", "Page2"); + ApplicationJson applicationJson = new ApplicationJson(); + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName)); + + applicationJson.setUpdatedResources(Map.of()); + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName)); + + Map> stringSetMap = Map.of(FieldName.PAGE_LIST, Set.of("Page1", "Page2")); + applicationJson.setUpdatedResources(stringSetMap); + Assertions.assertTrue(ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName)); + + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, pageName.toLowerCase())); + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, "test")); + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, "")); + Assertions.assertFalse(ImportExportUtils.isPageNameInUpdatedList(applicationJson, null)); + } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java index 338a62e224..07e25034da 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java @@ -108,6 +108,7 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import static com.appsmith.external.constants.GitConstants.NAME_SEPARATOR; import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS; import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS; import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES; @@ -4738,4 +4739,127 @@ public class ImportExportApplicationServiceTests { .expectError(AppsmithException.class) .verify(); } + + private Mono createActionToPage(String actionName, String pageId) { + ActionDTO action = new ActionDTO(); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(datasourceMap.get("DS1")); + action.setName(actionName); + action.setPageId(pageId); + return layoutActionService.createAction(action); + } + + private Mono createActionCollectionToPage(Application application, int pageIndex) { + ActionCollectionDTO actionCollectionDTO1 = new ActionCollectionDTO(); + actionCollectionDTO1.setName("TestJsObject"); + actionCollectionDTO1.setPageId(application.getPages().get(pageIndex).getId()); + actionCollectionDTO1.setApplicationId(application.getId()); + actionCollectionDTO1.setWorkspaceId(application.getWorkspaceId()); + actionCollectionDTO1.setPluginId(jsDatasource.getPluginId()); + ActionDTO action1 = new ActionDTO(); + action1.setName("testMethod"); + action1.setActionConfiguration(new ActionConfiguration()); + action1.getActionConfiguration().setBody("mockBody"); + actionCollectionDTO1.setActions(List.of(action1)); + actionCollectionDTO1.setPluginType(PluginType.JS); + return layoutCollectionService.createCollection(actionCollectionDTO1); + } + + @Test + @WithUserDetails("api_user") + public void exportApplicationByWhen_WhenGitConnectedAndPageRenamed_QueriesAreInUpdatedResources() { + String renamedPageName = "Renamed Page"; + // create an application + Application testApplication = new Application(); + final String appName = UUID.randomUUID().toString(); + testApplication.setName(appName); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setClientSchemaVersion(JsonSchemaVersions.clientVersion); + testApplication.setServerSchemaVersion(JsonSchemaVersions.serverVersion); + + Mono applicationJsonMono = applicationPageService + .createApplication(testApplication, workspaceId) + .flatMap(application -> { + // add another page to the application + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("second_page"); + pageDTO.setApplicationId(application.getId()); + return applicationPageService + .createPage(pageDTO) // get the updated application + .then(applicationService.findById(application.getId())); + }) + .flatMap(application -> { + assert application.getPages().size() == 2; + // add one action to each of the pages + return createActionToPage( + "first_page_action", + application.getPages().get(0).getId()) + .then(createActionToPage( + "second_page_action", + application.getPages().get(1).getId())) + .thenReturn(application); + }) + .flatMap(application -> { + // add one action collection to each of the pages + return createActionCollectionToPage(application, 0) + .then(createActionCollectionToPage(application, 1)) + .thenReturn(application); + }) + .flatMap(application -> { + // set git meta data for the application and set a last commit date + GitApplicationMetadata gitApplicationMetadata = new GitApplicationMetadata(); + // add buffer of 5 seconds so that the last commit date is definitely after the last updated date + gitApplicationMetadata.setLastCommittedAt(Instant.now()); + application.setGitApplicationMetadata(gitApplicationMetadata); + return applicationRepository.save(application); + }) + .delayElement(Duration.ofMillis( + 100)) // to make sure the last commit date is definitely after the last updated date + .flatMap(application -> { + // rename the page + ApplicationPage applicationPage = application.getPages().get(0); + PageDTO pageDTO = new PageDTO(); + pageDTO.setName(renamedPageName); + return newPageService + .updatePage(applicationPage.getId(), pageDTO) + // export the application + .then(importExportApplicationService.exportApplicationById( + application.getId(), SerialiseApplicationObjective.VERSION_CONTROL)); + }); + + // verify that the exported json has the updated page name, and the queries are in the updated resources + StepVerifier.create(applicationJsonMono) + .assertNext(applicationJson -> { + Map> updatedResources = applicationJson.getUpdatedResources(); + assertThat(updatedResources).isNotNull(); + Set updatedPageNames = updatedResources.get(FieldName.PAGE_LIST); + Set updatedActionNames = updatedResources.get(FieldName.ACTION_LIST); + Set updatedActionCollectionNames = updatedResources.get(FieldName.ACTION_COLLECTION_LIST); + + assertThat(updatedPageNames).isNotNull(); + assertThat(updatedActionNames).isNotNull(); + assertThat(updatedActionCollectionNames).isNotNull(); + + // only the first page should be present in the updated resources + assertThat(updatedPageNames.size()).isEqualTo(1); + assertThat(updatedPageNames).contains(renamedPageName); + + // only actions from first page should be present in the updated resources + // 1 query + 1 method from action collection + assertThat(updatedActionNames.size()).isEqualTo(2); + assertThat(updatedActionNames).contains("first_page_action" + NAME_SEPARATOR + renamedPageName); + assertThat(updatedActionNames) + .contains("TestJsObject.testMethod" + NAME_SEPARATOR + renamedPageName); + + // only action collections from first page should be present in the updated resources + assertThat(updatedActionCollectionNames.size()).isEqualTo(1); + assertThat(updatedActionCollectionNames) + .contains("TestJsObject" + NAME_SEPARATOR + renamedPageName); + }) + .verifyComplete(); + } } From 6ce2c82fd08a0fa9cbce98b8b9ccf87928b5a859 Mon Sep 17 00:00:00 2001 From: balajisoundar Date: Mon, 25 Sep 2023 13:31:23 +0530 Subject: [PATCH 10/48] fix: [Airgapped] use local assets url in one click binding (#27584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### PR fixes following issue(s) Fixes https://github.com/appsmithorg/appsmith/issues/27555 > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../DatasourceDropdown/useDatasource.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/DatasourceDropdown/useDatasource.tsx b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/DatasourceDropdown/useDatasource.tsx index edb2067462..73f0914ea8 100644 --- a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/DatasourceDropdown/useDatasource.tsx +++ b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/DatasourceDropdown/useDatasource.tsx @@ -43,6 +43,7 @@ import { import type { ActionDataState } from "reducers/entityReducers/actionsReducer"; import { getDatatype } from "utils/AppsmithUtils"; import { getCurrentEnvironmentId } from "@appsmith/selectors/environmentSelectors"; +import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; enum SortingWeights { alphabetical = 1, @@ -161,7 +162,7 @@ export function useDatasource(searchText: string) { ), @@ -253,13 +254,13 @@ export function useDatasource(searchText: string) { ), @@ -394,7 +395,7 @@ export function useDatasource(searchText: string) { ), From 97ad65054390f1df9e8a1d654df6ec1085c27020 Mon Sep 17 00:00:00 2001 From: Ankita Kinger Date: Mon, 25 Sep 2023 15:36:24 +0530 Subject: [PATCH 11/48] fix: Removing saas integrations section on datasources page for airgap (#27590) ## Description Removing saas integrations section on the datasources page for airgap, as it is not supported. #### PR fixes following issue(s) Fixes [#27544](https://github.com/appsmithorg/appsmith/issues/27544) #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../Editor/IntegrationEditor/IntegrationsHomeScreen.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx b/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx index 1367848ced..de30120d40 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx @@ -37,6 +37,7 @@ import Debugger, { import { showDebuggerFlag } from "selectors/debuggerSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { DatasourceCreateEntryPoints } from "constants/Datasource"; +import { isAirgapped } from "@appsmith/utils/airgapHelpers"; const HeaderFlex = styled.div` font-size: 20px; @@ -219,6 +220,7 @@ function CreateNewSaasIntegration({ }: any) { const newSaasAPIRef = useRef(null); const isMounted = useRef(false); + const isAirgappedInstance = isAirgapped(); useEffect(() => { if (active && newSaasAPIRef.current) { @@ -233,7 +235,7 @@ function CreateNewSaasIntegration({ isMounted.current = true; } }, [active]); - return ( + return !isAirgappedInstance ? (
Saas Integrations
- ); + ) : null; } function CreateNewDatasource({ From a9635812228bd4370daf062852f47885d65e6a1e Mon Sep 17 00:00:00 2001 From: sneha122 Date: Mon, 25 Sep 2023 15:47:17 +0530 Subject: [PATCH 12/48] fix: gsheet issue with import and redirection fixed (#27588) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR fixes issue with gsheet import and redirection. When an application containing a couple of datasources like mysql, postgres and gsheet are imported, we are shown reconnect datasource modal, suppose we first configure all datasources except gsheet, lastly we select gsheet datasource and save and authorise, we are not being redirected to authorisation. The issue occurred because after saving each datasource, that datasource was disappearing from the list of datasources shown on left panel in reconnect datasource modal, so if we configure gsheet datasource at last, it would remove datasource from the list and lose all the context. So the request sent to `/oauth` endpoint would be cancelled. This PR essentially solves 2 issues: - datasource disappearing from left panel in reconnect datasource modal - gsheet datasource not being redirected to authorisation #### PR fixes following issue(s) Fixes #27520 > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change - Bug fix (non-breaking change which fixes an issue) > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan Covered the following areas manually: - workspace import with app containing multiple DS - fork of above imported app into another workspace - app level import with multiple DS - importing an app without any datasources > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [x] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [x] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed Co-authored-by: “sneha122” <“sneha@appsmith.com”> --- .../pages/Editor/gitSync/ReconnectDatasourceModal.tsx | 5 ++++- app/client/src/sagas/DatasourcesSagas.ts | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx b/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx index 3021995d05..df4fb44d0b 100644 --- a/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx +++ b/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx @@ -516,7 +516,10 @@ function ReconnectDatasourceModal() { JSON.stringify(appInfo), ); } - } else if (appURL && unconfiguredDatasources.length === 0) { + } + // When datasources are present and pending datasources are 0, + // then only we want to update status as success + else if (appURL && pending.length === 0 && datasources.length > 0) { // open application import successfule localStorage.setItem("importApplicationSuccess", "true"); localStorage.setItem("importedAppPendingInfo", "null"); diff --git a/app/client/src/sagas/DatasourcesSagas.ts b/app/client/src/sagas/DatasourcesSagas.ts index a49453c836..bc7e5b854d 100644 --- a/app/client/src/sagas/DatasourcesSagas.ts +++ b/app/client/src/sagas/DatasourcesSagas.ts @@ -45,12 +45,10 @@ import { getPluginByPackageName, getDatasourcesUsedInApplicationByActions, getEntityExplorerDatasources, - getUnconfiguredDatasources, } from "@appsmith/selectors/entitiesSelector"; import { addMockDatasourceToWorkspace, setDatasourceViewModeFlag, - setUnconfiguredDatasourcesDuringImport, } from "actions/datasourceActions"; import type { UpdateDatasourceSuccessAction, @@ -525,14 +523,6 @@ function* updateDatasourceSaga( kind: "success", }); - const unconfiguredDSList: Datasource[] = yield select( - getUnconfiguredDatasources, - ); - const updatedList = unconfiguredDSList.filter( - (d: Datasource) => d.id !== datasourcePayload?.id, - ); - yield put(setUnconfiguredDatasourcesDuringImport(updatedList)); - const expandDatasourceId = state.ui.datasourcePane.expandDatasourceId; // Dont redirect if action payload has an onSuccess From f225758efff82315b9b4b062867409543b95e904 Mon Sep 17 00:00:00 2001 From: Rudraprasad Das Date: Tue, 26 Sep 2023 16:07:26 +0530 Subject: [PATCH 13/48] fix: adding offline error for git failure (#27597) ## Description Error message were ignored when offline. Fixed it! #### PR fixes following issue(s) Fixes #27589 #### Media ![image](https://github.com/appsmithorg/appsmith/assets/8724051/eaad3879-49f6-4c96-9281-2ab464d35a0a) #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../pages/Editor/gitSync/Tabs/GitConnectionV2/index.tsx | 8 +++++--- .../src/pages/Editor/gitSync/hooks/useGitConnect.ts | 3 ++- app/client/src/sagas/GitSyncSagas.ts | 9 ++++++--- app/client/src/selectors/gitSyncSelectors.tsx | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/Editor/gitSync/Tabs/GitConnectionV2/index.tsx b/app/client/src/pages/Editor/gitSync/Tabs/GitConnectionV2/index.tsx index 0a09c500b2..f6380536a4 100644 --- a/app/client/src/pages/Editor/gitSync/Tabs/GitConnectionV2/index.tsx +++ b/app/client/src/pages/Editor/gitSync/Tabs/GitConnectionV2/index.tsx @@ -170,12 +170,13 @@ function GitConnectionV2({ isImport = false }: GitConnectionV2Props) { isDefaultProfile: true, }, { - onErrorCallback: (err: Error, response?: any) => { + onErrorCallback: (error: any, response?: any) => { // AE-GIT-4033 is repo not empty error if (response?.responseMeta?.error?.code === "AE-GIT-4033") { setActiveStep(GIT_CONNECT_STEPS.GENERATE_SSH_KEY); } - setErrorData(response); + const errorResponse = response || error?.response?.data; + setErrorData(errorResponse); }, }, ); @@ -192,7 +193,8 @@ function GitConnectionV2({ isImport = false }: GitConnectionV2Props) { isDefaultProfile: true, }, onErrorCallback(error, response) { - setErrorData(response); + const errorResponse = response || error?.response?.data; + setErrorData(errorResponse); }, }), ); diff --git a/app/client/src/pages/Editor/gitSync/hooks/useGitConnect.ts b/app/client/src/pages/Editor/gitSync/hooks/useGitConnect.ts index e9db1ff138..bff9580ab9 100644 --- a/app/client/src/pages/Editor/gitSync/hooks/useGitConnect.ts +++ b/app/client/src/pages/Editor/gitSync/hooks/useGitConnect.ts @@ -27,7 +27,8 @@ export const useGitConnect = () => { }, onErrorCallback: (err, response) => { onErrorCallback(err, response); - setErrResponse(response); + const errorResponse = response || err?.response?.data; + setErrResponse(errorResponse); setIsConnectingToGit(false); }, }), diff --git a/app/client/src/sagas/GitSyncSagas.ts b/app/client/src/sagas/GitSyncSagas.ts index 076b5d6b4d..9408ddc60f 100644 --- a/app/client/src/sagas/GitSyncSagas.ts +++ b/app/client/src/sagas/GitSyncSagas.ts @@ -235,7 +235,7 @@ function* connectToGitSaga(action: ConnectToGitReduxAction) { } /* commit effect END */ } - } catch (error) { + } catch (error: any) { if (action.onErrorCallback) { action.onErrorCallback(error as Error, response); } @@ -248,11 +248,14 @@ function* connectToGitSaga(action: ConnectToGitReduxAction) { // Api error // Display on the UI - if (response && !response?.responseMeta?.success) { + + const errorResponse = response || error?.response?.data; + + if (errorResponse && !errorResponse?.responseMeta?.success) { yield put({ type: ReduxActionErrorTypes.CONNECT_TO_GIT_ERROR, payload: { - error: response?.responseMeta.error, + error: errorResponse?.responseMeta.error, show: false, }, }); diff --git a/app/client/src/selectors/gitSyncSelectors.tsx b/app/client/src/selectors/gitSyncSelectors.tsx index c5f3d88c84..4aa28df34b 100644 --- a/app/client/src/selectors/gitSyncSelectors.tsx +++ b/app/client/src/selectors/gitSyncSelectors.tsx @@ -184,7 +184,7 @@ export const getDisconnectDocUrl = () => "https://docs.appsmith.com/advanced-concepts/version-control-with-git/disconnect-the-git-repository"; export const getConnectingErrorDocUrl = (state: AppState) => - state.ui.gitSync.connectError?.error.referenceDoc || + state.ui.gitSync.connectError?.error?.referenceDoc || FALLBACK_GIT_SYNC_DOCS_URL; export const getUpstreamErrorDocUrl = (state: AppState) => From f763e75b9c8437bc3862354ccf37d5b35cd07374 Mon Sep 17 00:00:00 2001 From: Nidhi Date: Tue, 26 Sep 2023 18:21:33 +0530 Subject: [PATCH 14/48] chore: Added checks to only return plugins when new updates are present (#27641) --- .../server/constants/ce/FieldNameCE.java | 2 ++ .../ce/PluginScheduledTaskUtilsCEImpl.java | 4 ++- .../server/services/ConfigServiceImpl.java | 10 ++---- .../services/ce/ConfigServiceCEImpl.java | 15 +-------- .../services/ce/PluginServiceCEImpl.java | 5 +++ .../solutions/PluginScheduledTaskImpl.java | 7 ++-- .../ce/PluginScheduledTaskCEImpl.java | 32 ++++++++++++++++--- 7 files changed, 43 insertions(+), 32 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java index 6292e0b310..77e45838a9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java @@ -187,4 +187,6 @@ public class FieldNameCE { public static final String IS_MERGEABLE = "isMergeable"; public static final String FILE_LOCK_DURATION = "fileLockDuration"; + + public static final String REMOTE_PLUGINS = "remotePlugins"; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/PluginScheduledTaskUtilsCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/PluginScheduledTaskUtilsCEImpl.java index 3bf23bf3f6..6c379e42e4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/PluginScheduledTaskUtilsCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/PluginScheduledTaskUtilsCEImpl.java @@ -37,8 +37,10 @@ public class PluginScheduledTaskUtilsCEImpl implements PluginScheduledTaskUtilsC return Mono.empty(); } + String lastUpdatedAtParam = lastUpdatedAt != null ? "&lastUpdatedAt=" + lastUpdatedAt : ""; + return configService.getInstanceId().flatMap(instanceId -> WebClientUtils.create( - baseUrl + "/api/v1/plugins?instanceId=" + instanceId + "&lastUpdatedAt=" + lastUpdatedAt) + baseUrl + "/api/v1/plugins?instanceId=" + instanceId + lastUpdatedAtParam) .get() .exchangeToMono(clientResponse -> clientResponse.bodyToMono(new ParameterizedTypeReference>>() {})) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java index e19ccad98d..5f96581892 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java @@ -1,8 +1,6 @@ package com.appsmith.server.services; -import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ConfigRepository; -import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.services.ce.ConfigServiceCEImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,11 +9,7 @@ import org.springframework.stereotype.Service; @Service public class ConfigServiceImpl extends ConfigServiceCEImpl implements ConfigService { - public ConfigServiceImpl( - ConfigRepository repository, - ApplicationRepository applicationRepository, - DatasourceRepository datasourceRepository) { - - super(repository, applicationRepository, datasourceRepository); + public ConfigServiceImpl(ConfigRepository repository) { + super(repository); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java index e20f0d75a4..ba8f13e235 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java @@ -8,9 +8,7 @@ import com.appsmith.server.domains.Config; import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ConfigRepository; -import com.appsmith.server.repositories.DatasourceRepository; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONObject; import reactor.core.publisher.Flux; @@ -24,23 +22,12 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @Slf4j public class ConfigServiceCEImpl implements ConfigServiceCE { - - private static final String TEMPLATE_WORKSPACE_CONFIG_NAME = "template-workspace"; - - private final ApplicationRepository applicationRepository; - private final DatasourceRepository datasourceRepository; private final ConfigRepository repository; // This is permanently cached through the life of the JVM process as this is not intended to change at runtime ever. private String instanceId = null; - public ConfigServiceCEImpl( - ConfigRepository repository, - ApplicationRepository applicationRepository, - DatasourceRepository datasourceRepository) { - - this.applicationRepository = applicationRepository; - this.datasourceRepository = datasourceRepository; + public ConfigServiceCEImpl(ConfigRepository repository) { this.repository = repository; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/PluginServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/PluginServiceCEImpl.java index ff3390e385..8756c48f61 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/PluginServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/PluginServiceCEImpl.java @@ -196,6 +196,11 @@ public class PluginServiceCEImpl extends BaseService { // Only perform a DB op if plugins associated to this org have changed if (workspace.getPlugins().containsAll(newWorkspacePlugins)) { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PluginScheduledTaskImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PluginScheduledTaskImpl.java index 73a555b2a2..67ed080bc6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PluginScheduledTaskImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PluginScheduledTaskImpl.java @@ -1,6 +1,7 @@ package com.appsmith.server.solutions; import com.appsmith.server.helpers.PluginScheduledTaskUtils; +import com.appsmith.server.services.ConfigService; import com.appsmith.server.solutions.ce.PluginScheduledTaskCEImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -8,9 +9,7 @@ import org.springframework.stereotype.Component; @Slf4j @Component public class PluginScheduledTaskImpl extends PluginScheduledTaskCEImpl implements PluginScheduledTask { - - public PluginScheduledTaskImpl(PluginScheduledTaskUtils pluginScheduledTaskUtils) { - - super(pluginScheduledTaskUtils); + public PluginScheduledTaskImpl(PluginScheduledTaskUtils pluginScheduledTaskUtils, ConfigService configService) { + super(pluginScheduledTaskUtils, configService); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java index d699a4c1a7..b0ca1d5858 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java @@ -1,15 +1,21 @@ package com.appsmith.server.solutions.ce; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.domains.Config; import com.appsmith.server.helpers.PluginScheduledTaskUtils; +import com.appsmith.server.services.ConfigService; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.minidev.json.JSONObject; import org.springframework.scheduling.annotation.Scheduled; import reactor.core.scheduler.Schedulers; import java.time.Instant; +import java.util.Date; +import java.util.Map; /** * This class represents a scheduled task that pings cloud services for any updates in available plugins. @@ -19,18 +25,34 @@ import java.time.Instant; public class PluginScheduledTaskCEImpl implements PluginScheduledTaskCE { private final PluginScheduledTaskUtils pluginScheduledTaskUtils; - - private Instant lastUpdatedAt = null; + private final ConfigService configService; // Number of milliseconds between the start of each scheduled calls to this method. @Scheduled(initialDelay = 30 * 1000 /* 30 seconds */, fixedRate = 2 * 60 * 60 * 1000 /* two hours */) public void updateRemotePlugins() { // Moving the fetch and update remote plugins to helper classes to have custom implementation for business // edition - pluginScheduledTaskUtils - .fetchAndUpdateRemotePlugins(lastUpdatedAt) + configService + .getByName(FieldName.REMOTE_PLUGINS) + .onErrorReturn(new Config()) + .map(config -> { + JSONObject config1 = config.getConfig(); + Instant lastUpdatedAt = null; + + if (config1 != null) { + Object tempUpdatedAt = config1.getOrDefault(FieldName.UPDATED_AT, null); + if (tempUpdatedAt != null) { + lastUpdatedAt = ((Date) tempUpdatedAt).toInstant(); + } + } + return pluginScheduledTaskUtils.fetchAndUpdateRemotePlugins(lastUpdatedAt); + }) // Set new updated time - .doOnSuccess(success -> this.lastUpdatedAt = Instant.now()) + .flatMap(success -> { + Config config = new Config( + new JSONObject(Map.of(FieldName.UPDATED_AT, Instant.now())), FieldName.REMOTE_PLUGINS); + return configService.save(config); + }) .subscribeOn(Schedulers.single()) .subscribe(); } From e3d08140d4abc124d3f51556cf621ab197790bcd Mon Sep 17 00:00:00 2001 From: Nidhi Nair Date: Tue, 26 Sep 2023 18:43:16 +0530 Subject: [PATCH 15/48] fix: Added checks to only return plugins when new updates are present --- .../appsmith/server/services/ConfigServiceImpl.java | 10 ++++++++-- .../server/services/ce/ConfigServiceCEImpl.java | 13 ++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java index 5f96581892..e19ccad98d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ConfigServiceImpl.java @@ -1,6 +1,8 @@ package com.appsmith.server.services; +import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ConfigRepository; +import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.services.ce.ConfigServiceCEImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -9,7 +11,11 @@ import org.springframework.stereotype.Service; @Service public class ConfigServiceImpl extends ConfigServiceCEImpl implements ConfigService { - public ConfigServiceImpl(ConfigRepository repository) { - super(repository); + public ConfigServiceImpl( + ConfigRepository repository, + ApplicationRepository applicationRepository, + DatasourceRepository datasourceRepository) { + + super(repository, applicationRepository, datasourceRepository); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java index ba8f13e235..f28c96e663 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ConfigServiceCEImpl.java @@ -8,7 +8,9 @@ import com.appsmith.server.domains.Config; import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ConfigRepository; +import com.appsmith.server.repositories.DatasourceRepository; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONObject; import reactor.core.publisher.Flux; @@ -22,12 +24,21 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @Slf4j public class ConfigServiceCEImpl implements ConfigServiceCE { + private static final String TEMPLATE_WORKSPACE_CONFIG_NAME = "template-workspace"; private final ConfigRepository repository; + private final ApplicationRepository applicationRepository; + private final DatasourceRepository datasourceRepository; // This is permanently cached through the life of the JVM process as this is not intended to change at runtime ever. private String instanceId = null; - public ConfigServiceCEImpl(ConfigRepository repository) { + public ConfigServiceCEImpl( + ConfigRepository repository, + ApplicationRepository applicationRepository, + DatasourceRepository datasourceRepository) { + + this.applicationRepository = applicationRepository; + this.datasourceRepository = datasourceRepository; this.repository = repository; } From c85b454750fba566161dbc1a753ba60d9f27658c Mon Sep 17 00:00:00 2001 From: Nidhi Date: Wed, 27 Sep 2023 10:32:35 +0530 Subject: [PATCH 16/48] chore: Fixed flatmapping cs req (#27649) --- .../appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java index b0ca1d5858..d705705cdf 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PluginScheduledTaskCEImpl.java @@ -35,7 +35,7 @@ public class PluginScheduledTaskCEImpl implements PluginScheduledTaskCE { configService .getByName(FieldName.REMOTE_PLUGINS) .onErrorReturn(new Config()) - .map(config -> { + .flatMap(config -> { JSONObject config1 = config.getConfig(); Instant lastUpdatedAt = null; From 61ef8f7775e87b7aa8593bc608a3fcc4a9632013 Mon Sep 17 00:00:00 2001 From: tkAppsmith <131347120+tkAppsmith@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:22:52 +0530 Subject: [PATCH 17/48] fix: fixed failing queries using aggregation pipeline (#26132) ## Description > Queries using aggregation update failing. hence added a fallback. #### PR fixes following issue(s) Fixes #26090 Fixes https://github.com/appsmithorg/appsmith-ee/issues/1659 #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? - [x] Manual - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../repositories/BaseRepositoryImpl.java | 4 +- .../CustomNewActionRepositoryImpl.java | 6 +- .../CustomNewPageRepositoryImpl.java | 6 +- .../ce/BaseAppsmithRepositoryCEImpl.java | 6 +- .../ce/CustomNewActionRepositoryCE.java | 2 +- .../ce/CustomNewActionRepositoryCEImpl.java | 42 +++++++--- .../ce/CustomNewPageRepositoryCE.java | 6 +- .../ce/CustomNewPageRepositoryCEImpl.java | 77 ++++++++++++++++--- .../ce/NewActionRepositoryCE.java | 5 ++ .../ce/ApplicationPageServiceCEImpl.java | 5 +- .../services/ce/NewActionServiceCE.java | 4 +- .../services/ce/NewActionServiceCEImpl.java | 8 +- .../server/services/ce/NewPageServiceCE.java | 4 +- .../services/ce/NewPageServiceCEImpl.java | 4 +- .../services/ce/NewActionServiceUnitTest.java | 11 ++- 15 files changed, 143 insertions(+), 47 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java index 7bae25049d..a4090d8f70 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/BaseRepositoryImpl.java @@ -161,7 +161,9 @@ public class BaseRepositoryImpl .flatMapMany(principal -> { Query query = new Query(notDeleted()); return mongoOperations.find( - query, entityInformation.getJavaType(), entityInformation.getCollectionName()); + query.cursorBatchSize(10000), + entityInformation.getJavaType(), + entityInformation.getCollectionName()); }); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java index e45c7fd224..b3eb05cb8d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewActionRepositoryImpl.java @@ -2,6 +2,7 @@ package com.appsmith.server.repositories; import com.appsmith.server.repositories.ce.CustomNewActionRepositoryCEImpl; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.stereotype.Component; @@ -14,7 +15,8 @@ public class CustomNewActionRepositoryImpl extends CustomNewActionRepositoryCEIm public CustomNewActionRepositoryImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper) { - super(mongoOperations, mongoConverter, cacheableRepositoryHelper); + CacheableRepositoryHelper cacheableRepositoryHelper, + MongoTemplate mongoTemplate) { + super(mongoOperations, mongoConverter, cacheableRepositoryHelper, mongoTemplate); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java index 370e5202b2..690b54d4c1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/CustomNewPageRepositoryImpl.java @@ -2,6 +2,7 @@ package com.appsmith.server.repositories; import com.appsmith.server.repositories.ce.CustomNewPageRepositoryCEImpl; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.stereotype.Component; @@ -13,7 +14,8 @@ public class CustomNewPageRepositoryImpl extends CustomNewPageRepositoryCEImpl i public CustomNewPageRepositoryImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper) { - super(mongoOperations, mongoConverter, cacheableRepositoryHelper); + CacheableRepositoryHelper cacheableRepositoryHelper, + MongoTemplate mongoTemplate) { + super(mongoOperations, mongoConverter, cacheableRepositoryHelper, mongoTemplate); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java index c2d9b0634b..28d0012b85 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java @@ -22,6 +22,7 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; +import org.springframework.data.mongodb.repository.Meta; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; @@ -166,7 +167,7 @@ public abstract class BaseAppsmithRepositoryCEImpl { return mongoOperations .query(this.genericDomain) - .matching(query) + .matching(query.cursorBatchSize(10000)) .one() .flatMap(obj -> setUserPermissionsInObject(obj, permissionGroups)); }); @@ -331,6 +332,7 @@ public abstract class BaseAppsmithRepositoryCEImpl { }); } + @Meta(cursorBatchSize = 10000) protected Mono queryOne( List criterias, List projectionFieldNames, Optional permission) { Mono> permissionGroupsMono = getCurrentUserPermissionGroupsIfRequired(permission); @@ -539,7 +541,7 @@ public abstract class BaseAppsmithRepositoryCEImpl { sortOptional.ifPresent(sort -> query.with(sort)); return mongoOperations .query(this.genericDomain) - .matching(query) + .matching(query.cursorBatchSize(10000)) .all() .flatMap(obj -> setUserPermissionsInObject(obj, permissionGroups)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java index 007ff409f8..11a6cb6af1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCE.java @@ -78,7 +78,7 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository> bulkUpdate(List newActions); - Mono publishActions(String applicationId, AclPermission permission); + Mono> publishActions(String applicationId, AclPermission permission); Mono archiveDeletedUnpublishedActions(String applicationId, AclPermission permission); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java index 5a90b20dce..c45cf40bfa 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewActionRepositoryCEImpl.java @@ -20,9 +20,12 @@ import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.bson.types.ObjectId; import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; +import org.springframework.data.mongodb.core.aggregation.AggregationOperation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; +import org.springframework.data.mongodb.core.aggregation.Fields; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; @@ -52,11 +55,15 @@ import static org.springframework.data.mongodb.core.query.Criteria.where; public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl implements CustomNewActionRepositoryCE { + private final MongoTemplate mongoTemplate; + public CustomNewActionRepositoryCEImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper) { + CacheableRepositoryHelper cacheableRepositoryHelper, + MongoTemplate mongoTemplate) { super(mongoOperations, mongoConverter, cacheableRepositoryHelper); + this.mongoTemplate = mongoTemplate; } @Override @@ -571,16 +578,33 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl< } @Override - public Mono publishActions(String applicationId, AclPermission permission) { + public Mono> publishActions(String applicationId, AclPermission permission) { Criteria applicationIdCriteria = where(fieldName(QNewAction.newAction.applicationId)).is(applicationId); - // using aggregation update instead of regular update here - // it's required to set a field to a value of another field from the same domain - AggregationUpdate aggregationUpdate = AggregationUpdate.update() - .set(fieldName(QNewAction.newAction.publishedAction)) - .toValue("$" + fieldName(QNewAction.newAction.unpublishedAction)); - return updateByCriteria(List.of(applicationIdCriteria), aggregationUpdate, permission); + Mono> permissionGroupsMono = + getCurrentUserPermissionGroupsIfRequired(Optional.ofNullable(permission)); + + return permissionGroupsMono.flatMap(permissionGroups -> { + AggregationOperation matchAggregationWithPermission = null; + if (permission == null) { + matchAggregationWithPermission = Aggregation.match(new Criteria().andOperator(notDeleted())); + } else { + matchAggregationWithPermission = Aggregation.match( + new Criteria().andOperator(notDeleted(), userAcl(permissionGroups, permission))); + } + AggregationOperation matchAggregation = Aggregation.match(applicationIdCriteria); + AggregationOperation wholeProjection = Aggregation.project(NewAction.class); + AggregationOperation addFieldsOperation = Aggregation.addFields() + .addField(fieldName(QNewAction.newAction.publishedAction)) + .withValueOf(Fields.field(fieldName(QNewAction.newAction.unpublishedAction))) + .build(); + Aggregation combinedAggregation = Aggregation.newAggregation( + matchAggregation, matchAggregationWithPermission, wholeProjection, addFieldsOperation); + AggregationResults updatedResults = + mongoTemplate.aggregate(combinedAggregation, NewAction.class, NewAction.class); + return bulkUpdate(updatedResults.getMappedResults()); + }); } @Override diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java index dc940ee25b..ec6775c2cb 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCE.java @@ -3,7 +3,7 @@ package com.appsmith.server.repositories.ce; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.NewPage; import com.appsmith.server.repositories.AppsmithRepository; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -43,5 +43,7 @@ public interface CustomNewPageRepositoryCE extends AppsmithRepository { Mono findByGitSyncIdAndDefaultApplicationId( String defaultApplicationId, String gitSyncId, Optional permission); - Mono publishPages(Collection pageIds, AclPermission permission); + Mono> publishPages(Collection pageIds, AclPermission permission); + + Mono> bulkUpdate(List newPages); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java index 92a93f963e..acd1a16e0f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomNewPageRepositoryCEImpl.java @@ -9,20 +9,32 @@ import com.appsmith.server.domains.QNewPage; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl; import com.appsmith.server.repositories.CacheableRepositoryHelper; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.WriteModel; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; +import org.bson.types.ObjectId; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.AggregationOperation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; +import org.springframework.data.mongodb.core.aggregation.Fields; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import static org.springframework.data.mongodb.core.query.Criteria.where; @@ -30,11 +42,15 @@ import static org.springframework.data.mongodb.core.query.Criteria.where; public class CustomNewPageRepositoryCEImpl extends BaseAppsmithRepositoryImpl implements CustomNewPageRepositoryCE { + private final MongoTemplate mongoTemplate; + public CustomNewPageRepositoryCEImpl( ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter, - CacheableRepositoryHelper cacheableRepositoryHelper) { + CacheableRepositoryHelper cacheableRepositoryHelper, + MongoTemplate mongoTemplate) { super(mongoOperations, mongoConverter, cacheableRepositoryHelper); + this.mongoTemplate = mongoTemplate; } @Override @@ -251,14 +267,55 @@ public class CustomNewPageRepositoryCEImpl extends BaseAppsmithRepositoryImpl publishPages(Collection pageIds, AclPermission permission) { + public Mono> publishPages(Collection pageIds, AclPermission permission) { Criteria applicationIdCriteria = where(fieldName(QNewPage.newPage.id)).in(pageIds); - // using aggregation update instead of regular update here - // it's required to set a field to a value of another field from the same domain - AggregationUpdate aggregationUpdate = AggregationUpdate.update() - .set(fieldName(QNewPage.newPage.publishedPage)) - .toValue("$" + fieldName(QNewPage.newPage.unpublishedPage)); - return updateByCriteria(List.of(applicationIdCriteria), aggregationUpdate, permission); + Mono> permissionGroupsMono = + getCurrentUserPermissionGroupsIfRequired(Optional.ofNullable(permission)); + + return permissionGroupsMono.flatMap(permissionGroups -> { + AggregationOperation matchAggregationWithPermission = null; + if (permission == null) { + matchAggregationWithPermission = Aggregation.match(new Criteria().andOperator(notDeleted())); + } else { + matchAggregationWithPermission = Aggregation.match( + new Criteria().andOperator(notDeleted(), userAcl(permissionGroups, permission))); + } + AggregationOperation matchAggregation = Aggregation.match(applicationIdCriteria); + AggregationOperation wholeProjection = Aggregation.project(NewPage.class); + AggregationOperation addFieldsOperation = Aggregation.addFields() + .addField(fieldName(QNewPage.newPage.publishedPage)) + .withValueOf(Fields.field(fieldName(QNewPage.newPage.unpublishedPage))) + .build(); + Aggregation combinedAggregation = Aggregation.newAggregation( + matchAggregation, matchAggregationWithPermission, wholeProjection, addFieldsOperation); + AggregationResults updatedResults = + mongoTemplate.aggregate(combinedAggregation, NewPage.class, NewPage.class); + return bulkUpdate(updatedResults.getMappedResults()); + }); + } + + @Override + public Mono> bulkUpdate(List newPages) { + if (CollectionUtils.isEmpty(newPages)) { + return Mono.just(Collections.emptyList()); + } + + // convert the list of new pages to a list of DBObjects + List> dbObjects = newPages.stream() + .map(newPage -> { + assert newPage.getId() != null; + Document document = new Document(); + mongoOperations.getConverter().write(newPage, document); + document.remove("_id"); + return (WriteModel) new UpdateOneModel( + new Document("_id", new ObjectId(newPage.getId())), new Document("$set", document)); + }) + .collect(Collectors.toList()); + + return mongoOperations + .getCollection(mongoOperations.getCollectionName(NewPage.class)) + .flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects)) + .collectList(); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java index e74852d3b5..505981ef1d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/NewActionRepositoryCE.java @@ -3,12 +3,17 @@ package com.appsmith.server.repositories.ce; import com.appsmith.server.domains.NewAction; import com.appsmith.server.repositories.BaseRepository; import com.appsmith.server.repositories.CustomNewActionRepository; +import org.springframework.data.mongodb.repository.Meta; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface NewActionRepositoryCE extends BaseRepository, CustomNewActionRepository { + @Meta(cursorBatchSize = 10000) Flux findByApplicationId(String applicationId); + @Meta(cursorBatchSize = 10000) + Flux findAllByIdIn(Iterable ids); + Mono countByDeletedAtNull(); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java index 7269060239..000b4bf90c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationPageServiceCEImpl.java @@ -50,6 +50,7 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.WorkspacePermission; import com.google.common.base.Strings; +import com.mongodb.bulk.BulkWriteResult; import com.mongodb.client.result.UpdateResult; import jakarta.annotation.Nullable; import lombok.RequiredArgsConstructor; @@ -1134,7 +1135,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE { if (isPublishedManually) { application.setLastDeployedAt(Instant.now()); } - Mono publishPagesMono = + Mono> publishPagesMono = newPageService.publishPages(editedPageIds, pagePermission.getEditPermission()); // Archive the deleted pages and save the application changes and then return the pages so that @@ -1144,7 +1145,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE { }) .cache(); // caching as we'll need this to send analytics attributes after publishing the app - Mono publishActionsMono = + Mono> publishActionsMono = newActionService.publishActions(applicationId, actionPermission.getEditPermission()); // this is a map of pluginType to count of actions for that pluginType, required for analytics diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java index 9bf8e71f8e..733b08c21d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCE.java @@ -14,7 +14,7 @@ import com.appsmith.server.dtos.ce.ImportActionResultDTO; import com.appsmith.server.dtos.ce.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.helpers.ce.ImportApplicationPermissionProvider; import com.appsmith.server.services.CrudService; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; import org.springframework.data.domain.Sort; import org.springframework.util.MultiValueMap; import reactor.core.publisher.Flux; @@ -134,7 +134,7 @@ public interface NewActionServiceCE extends CrudService { ImportActionCollectionResultDTO importActionCollectionResultDTO, ImportActionResultDTO importActionResultDTO); - Mono publishActions(String applicationId, AclPermission permission); + Mono> publishActions(String applicationId, AclPermission permission); Flux countActionsByPluginType(String applicationId); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java index 3088b833a0..276d2ce24b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewActionServiceCEImpl.java @@ -53,7 +53,7 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PolicySolution; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; import io.micrometer.observation.ObservationRegistry; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; @@ -663,7 +663,7 @@ public class NewActionServiceCEImpl extends BaseService findAllById(Iterable id) { - return repository.findAllById(id).flatMap(this::sanitizeAction); + return repository.findAllByIdIn(id).flatMap(this::sanitizeAction); } @Override @@ -1907,7 +1907,7 @@ public class NewActionServiceCEImpl extends BaseService { // Update collectionId and defaultCollectionIds in actionDTOs ActionDTO unpublishedAction = newAction.getUnpublishedAction(); @@ -1965,7 +1965,7 @@ public class NewActionServiceCEImpl extends BaseService publishActions(String applicationId, AclPermission permission) { + public Mono> publishActions(String applicationId, AclPermission permission) { // delete the actions that were deleted in edit mode return repository .archiveDeletedUnpublishedActions(applicationId, permission) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java index cb480d23f3..b42c05c5fd 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java @@ -7,7 +7,7 @@ import com.appsmith.server.domains.NewPage; import com.appsmith.server.dtos.ApplicationPagesDTO; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.services.CrudService; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -93,5 +93,5 @@ public interface NewPageServiceCE extends CrudService { Flux findPageSlugsByApplicationIds(List applicationIds, AclPermission aclPermission); - Mono publishPages(Collection pageIds, AclPermission permission); + Mono> publishPages(Collection pageIds, AclPermission permission); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java index 9430d3bfa2..e6fa147a0f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java @@ -23,7 +23,7 @@ import com.appsmith.server.services.BaseService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.PagePermission; -import com.mongodb.client.result.UpdateResult; +import com.mongodb.bulk.BulkWriteResult; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONObject; @@ -692,7 +692,7 @@ public class NewPageServiceCEImpl extends BaseService publishPages(Collection pageIds, AclPermission permission) { + public Mono> publishPages(Collection pageIds, AclPermission permission) { return repository.publishPages(pageIds, permission); } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java index 21141119fb..36f9d9aeef 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceUnitTest.java @@ -23,7 +23,6 @@ import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PolicySolution; -import com.mongodb.client.result.UpdateResult; import io.micrometer.observation.ObservationRegistry; import jakarta.validation.Validator; import lombok.extern.slf4j.Slf4j; @@ -39,6 +38,8 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.test.StepVerifier; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; @@ -199,9 +200,8 @@ public class NewActionServiceUnitTest { @Test public void testPublishActionArchivesAndPublishesActions() { String applicationId = "dummy-application-id"; - UpdateResult updateResult = Mockito.mock(UpdateResult.class); - Mockito.when(updateResult.getModifiedCount()).thenReturn(10L); - Mockito.when(updateResult.getMatchedCount()).thenReturn(5L); + List updateResult = Mockito.mock(List.class); + Mockito.when(updateResult.size()).thenReturn(10); Mockito.when(newActionRepository.archiveDeletedUnpublishedActions( applicationId, actionPermission.getEditPermission())) @@ -212,8 +212,7 @@ public class NewActionServiceUnitTest { StepVerifier.create(newActionService.publishActions(applicationId, actionPermission.getEditPermission())) .assertNext(updateResult1 -> { - assertEquals(10L, updateResult1.getModifiedCount()); - assertEquals(5L, updateResult1.getMatchedCount()); + assertEquals(10, updateResult1.size()); }) .verifyComplete(); } From fc9f03d0175f3094eaccb93d724472d339c2b639 Mon Sep 17 00:00:00 2001 From: Aman Agarwal Date: Wed, 27 Sep 2023 17:01:14 +0530 Subject: [PATCH 18/48] fix: spacing issues on action page (#27656) --- app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx | 2 +- app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index 50260f72ba..4b893ee375 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -86,7 +86,7 @@ const Form = styled.form` const MainConfiguration = styled.div` z-index: 7; - padding-left: var(--ads-v2-spaces-7); + padding: 0 var(--ads-v2-spaces-7); .api-info-row { padding-top: var(--ads-v2-spaces-5); .ads-v2-select > .rc-select-selector { diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx index 26c698e7d4..e852b0d936 100644 --- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx +++ b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx @@ -315,7 +315,7 @@ const DocumentationButton = styled(Button)` const SidebarWrapper = styled.div<{ show: boolean }>` border-left: 1px solid var(--ads-v2-color-border); - padding: 0 var(--ads-v2-spaces-4) var(--ads-v2-spaces-4); + padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-4); overflow: hidden; border-bottom: 0; display: ${(props) => (props.show ? "flex" : "none")}; From a27576f74c142fcb863db078e8b163ebc4025389 Mon Sep 17 00:00:00 2001 From: Parthvi <80334441+Parthvi12@users.noreply.github.com> Date: Sun, 1 Oct 2023 16:35:28 +0530 Subject: [PATCH 19/48] test: Cypress| Fix omnibar test (#27728) ## Description Doc link got updated which was causing test to fail, fixed it. #### How Has This Been Tested? locally --- .../e2e/Regression/ClientSide/OtherUIFeatures/Omnibar_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Omnibar_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Omnibar_spec.js index 528a735422..cffad0e5ef 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Omnibar_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Omnibar_spec.js @@ -20,7 +20,7 @@ describe("Omnibar functionality test cases", () => { cy.dragAndDropToCanvas(draggableWidgets.AUDIO, { x: 300, y: 500 }); deployMode.StubWindowNAssert( '//span[text()="Learn more"]', - "connect-datasource", + "connect-to-a-database", "getWorkspace", ); }); From 86ccba382a2527710ec19d6bc7c9d813616a666e Mon Sep 17 00:00:00 2001 From: sharanya-appsmith <135708039+sharanya-appsmith@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:11:09 +0530 Subject: [PATCH 20/48] test: Cypress - fix (#27743) --- .../ClientSide/Widgets/Datepicker/DatePicker3_spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker3_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker3_spec.ts index 52b4da1de8..bda5ac446f 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker3_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker3_spec.ts @@ -53,7 +53,7 @@ describe("Date picker widget testcases", () => { .then((labelValue) => { const formattedDate = format( new Date(dateValueSet), - "dd MMMM, yyyy", + "d MMMM, yyyy", ); expect(labelValue).to.contain(formattedDate); }); From 645263e64180f953f402cb1a7bcae7de3344e4ba Mon Sep 17 00:00:00 2001 From: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:51:33 +0530 Subject: [PATCH 21/48] test: cypress - commenting a test in Tab spec (#27916) Skipping a test in this Tab regression spec --- .../e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts index eeca97b3f6..57240928a3 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts @@ -178,7 +178,8 @@ describe("Tabs widget Tests", function () { propPane.SelectPropertiesDropDown("height", "Auto Height"); }); - it("7. Verify colors, borders and shadows", () => { + // to work on redesign of the test, commenting for now + it.skip("7. Verify colors, borders and shadows", () => { // Verify font color picker opens up propPane.MoveToTab("Style"); agHelper.GetNClick(propPane._propertyControlColorPicker("accentcolor")); From bff76ca1bf05ad998bc05c3f9011102bd0d8a5fb Mon Sep 17 00:00:00 2001 From: albinAppsmith <87797149+albinAppsmith@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:46:04 +0530 Subject: [PATCH 22/48] fix: Branding hover color issue (#27964) ## Description Primary buttons when hovered, turns the background to white. This was happening due to some recent changes in the branding related APIs and API calls. #### PR fixes following issue(s) Fixes https://github.com/appsmithorg/appsmith/issues/27940 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Pawan Kumar (cherry picked from commit c218f9228fb18e0e866ae2447cefc0ef27d02ed3) --- app/client/src/ce/sagas/tenantSagas.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/client/src/ce/sagas/tenantSagas.tsx b/app/client/src/ce/sagas/tenantSagas.tsx index c197e3df13..4abb084117 100644 --- a/app/client/src/ce/sagas/tenantSagas.tsx +++ b/app/client/src/ce/sagas/tenantSagas.tsx @@ -22,12 +22,22 @@ export function* fetchCurrentTenantConfigSaga() { ); const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { - const data: any = response.data; + const payload: any = response.data; yield put({ type: ReduxActionTypes.FETCH_CURRENT_TENANT_CONFIG_SUCCESS, - payload: data, + payload: { + ...payload, + tenantConfiguration: { + ...CE_defaultBrandingConfig, + ...payload.tenantConfiguration, + brandColors: { + ...CE_defaultBrandingConfig.brandColors, + ...payload.tenantConfiguration.brandColors, + }, + }, + }, }); - AnalyticsUtil.initInstanceId(data.instanceId); + AnalyticsUtil.initInstanceId(payload.instanceId); } } catch (error) { yield put({ From 83970b9770baabad8cbad832907350ec25b34160 Mon Sep 17 00:00:00 2001 From: Dipyaman Biswas Date: Wed, 11 Oct 2023 23:19:19 +0530 Subject: [PATCH 23/48] feat: make features call a blocking API call for page load (#27974) ## Description Make features call a blocking API call for page load #### PR fixes following issue(s) Fixes #27973 #### Media #### Type of change > Please delete options that are not relevant. - Chore (housekeeping or task changes that don't impact user perception) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed (cherry picked from commit be7f6674f755b10a780e64280d4da1526482a021) --- app/client/src/ce/AppRouter.tsx | 16 ++++++++++++---- .../src/reducers/uiReducers/usersReducer.ts | 12 ++++++++++++ app/client/src/selectors/usersSelectors.tsx | 8 ++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/client/src/ce/AppRouter.tsx b/app/client/src/ce/AppRouter.tsx index cbfd14f1ef..1d95ef88c6 100644 --- a/app/client/src/ce/AppRouter.tsx +++ b/app/client/src/ce/AppRouter.tsx @@ -45,7 +45,10 @@ import * as Sentry from "@sentry/react"; import { getSafeCrash, getSafeCrashCode } from "selectors/errorSelectors"; import UserProfile from "pages/UserProfile"; import { getCurrentUser } from "actions/authActions"; -import { getCurrentUserLoading } from "selectors/usersSelectors"; +import { + getCurrentUserLoading, + getFeatureFlagsFetching, +} from "selectors/usersSelectors"; import Setup from "pages/setup"; import SettingsLoader from "pages/AdminSettings/loader"; import SignupSuccess from "pages/setup/SignupSuccess"; @@ -153,6 +156,7 @@ function AppRouter(props: { } = props; const tenantIsLoading = useSelector(isTenantLoading); const currentUserIsLoading = useSelector(getCurrentUserLoading); + const featuresIsLoading = useSelector(getFeatureFlagsFetching); useEffect(() => { getCurrentUser(); @@ -166,7 +170,11 @@ function AppRouter(props: { // hide the top loader once the tenant is loaded useEffect(() => { - if (tenantIsLoading === false && currentUserIsLoading === false) { + if ( + tenantIsLoading === false && + currentUserIsLoading === false && + featuresIsLoading === false + ) { const loader = document.getElementById("loader") as HTMLDivElement; if (loader) { @@ -177,9 +185,9 @@ function AppRouter(props: { }); } } - }, [tenantIsLoading, currentUserIsLoading]); + }, [tenantIsLoading, currentUserIsLoading, featuresIsLoading]); - if (tenantIsLoading || currentUserIsLoading) return null; + if (tenantIsLoading || currentUserIsLoading || featuresIsLoading) return null; return ( diff --git a/app/client/src/reducers/uiReducers/usersReducer.ts b/app/client/src/reducers/uiReducers/usersReducer.ts index cfef428966..52c0b5f96c 100644 --- a/app/client/src/reducers/uiReducers/usersReducer.ts +++ b/app/client/src/reducers/uiReducers/usersReducer.ts @@ -24,6 +24,7 @@ const initialState: UsersReduxState = { featureFlag: { data: DEFAULT_FEATURE_FLAG_VALUE, isFetched: false, + isFetching: true, }, productAlert: { config: { @@ -173,6 +174,14 @@ const usersReducer = createReducer(initialState, { photoId: action.payload.photoId, }, }), + [ReduxActionTypes.FETCH_FEATURE_FLAGS_INIT]: (state: UsersReduxState) => ({ + ...state, + featureFlag: { + ...state.featureFlag, + isFetched: false, + isFetching: true, + }, + }), [ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS]: ( state: UsersReduxState, action: ReduxAction, @@ -181,6 +190,7 @@ const usersReducer = createReducer(initialState, { featureFlag: { data: action.payload, isFetched: true, + isFetching: false, }, }), [ReduxActionErrorTypes.FETCH_FEATURE_FLAGS_ERROR]: ( @@ -190,6 +200,7 @@ const usersReducer = createReducer(initialState, { featureFlag: { data: {}, isFetched: true, + isFetching: false, }, }), [ReduxActionTypes.FETCH_PRODUCT_ALERT_SUCCESS]: ( @@ -252,6 +263,7 @@ export interface UsersReduxState { featureFlag: { isFetched: boolean; data: FeatureFlags; + isFetching: boolean; }; productAlert: ProductAlertState; } diff --git a/app/client/src/selectors/usersSelectors.tsx b/app/client/src/selectors/usersSelectors.tsx index 53fcc78c02..e693ec0aa1 100644 --- a/app/client/src/selectors/usersSelectors.tsx +++ b/app/client/src/selectors/usersSelectors.tsx @@ -5,16 +5,24 @@ import { ANONYMOUS_USERNAME } from "constants/userConstants"; export const getCurrentUser = (state: AppState): User | undefined => state.ui?.users?.currentUser; + export const getCurrentUserLoading = (state: AppState): boolean => state.ui.users.loadingStates.fetchingUser; + export const getUserAuthError = (state: AppState): string => state.ui.users.error; + export const getUsers = (state: AppState): User[] => state.ui.users.users; + export const getProppanePreference = ( state: AppState, ): PropertyPanePositionConfig | undefined => state.ui.users.propPanePreferences; + export const getFeatureFlagsFetched = (state: AppState) => state.ui.users.featureFlag.isFetched; +export const getFeatureFlagsFetching = (state: AppState) => + state.ui.users.featureFlag.isFetching; + export const getIsUserLoggedIn = (state: AppState): boolean => state.ui.users.currentUser?.email !== ANONYMOUS_USERNAME; From 6abd5e2c0a8c57cfa4e8a001b3e8ba26472f6f47 Mon Sep 17 00:00:00 2001 From: Valera Melnikov Date: Fri, 13 Oct 2023 12:08:01 +0300 Subject: [PATCH 24/48] fix: auto label position (#28022) ## Description Fix auto label position Fixes #27975 #### Media https://github.com/appsmithorg/appsmith/assets/11555074/bf6e4c8c-97db-4eab-8986-b4cd2de5bd0a #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed (cherry picked from commit b42caa030823f47625098305565b3c68d4725895) --- app/client/src/widgets/WidgetUtils.test.ts | 7 +++++++ app/client/src/widgets/WidgetUtils.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/WidgetUtils.test.ts b/app/client/src/widgets/WidgetUtils.test.ts index d6a90203a8..aeec7138e3 100644 --- a/app/client/src/widgets/WidgetUtils.test.ts +++ b/app/client/src/widgets/WidgetUtils.test.ts @@ -26,6 +26,7 @@ import { isAutoHeightEnabledForWidgetWithLimits, getWidgetMaxAutoHeight, getWidgetMinAutoHeight, + isCompactMode, } from "./WidgetUtils"; import { getCustomTextColor, @@ -696,4 +697,10 @@ describe("Should Update Widget Height Automatically?", () => { const result = shouldUpdateWidgetHeightAutomatically(input, props); expect(result).toStrictEqual(expected); }); + it("should return correct value for isCompactMode", () => { + const compactHeight = 40; + const unCompactHeight = 41; + expect(isCompactMode(compactHeight)).toBeTruthy(); + expect(isCompactMode(unCompactHeight)).toBeFalsy(); + }); }); diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index 31761b438a..8f60f05e20 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -921,7 +921,7 @@ const findReactInstanceProps = (domElement: any) => { export function isCompactMode(componentHeight: number) { return ( - componentHeight < + componentHeight <= COMPACT_MODE_MIN_ROWS * GridDefaults.DEFAULT_GRID_ROW_HEIGHT ); } From ee37b5272ad1e46b1dc9d77033e2cf450b35bcae Mon Sep 17 00:00:00 2001 From: arunvjn <32433245+arunvjn@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:32:27 +0530 Subject: [PATCH 25/48] fix: Race condition in JS object mutation (#28083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes race condition in JS Object mutation where evaluation overrides the variables state. Root Cause: Execution context is shared between every evaluation request and trigger execution request. A trigger execution request could be paused when an asynchronous task is awaited. In the mean time, worker might pick up the task to perform and evaluation. Evaluation would end up replacing the entities in the execution context, there by resetting the actions performed by the trigger execution before it was paused. What the fix does? We are now caching the JS object for reuse which means that every execution/evaluation request reuses the same JS Object as long the JS Object isn't modified by a user action. Fixes #27978 > > > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > - Bug fix (non-breaking change which fixes an issue) > > > - [x] Manual - [x] Jest - [x] Cypress > > > Add Testsmith test cases links that relate to this PR > > > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag - [x] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [x] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [x] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [x] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed (cherry picked from commit 1afd223e10a61db916c3c91ddd73daf10ada02d8) --- .../JSObject/JSObjectMutation_spec.ts | 48 ++++++++++++++ .../src/ce/actions/evaluationActions.ts | 2 - .../workers/Evaluation/JSObject/Collection.ts | 62 +++++++++++++++++++ .../Evaluation/JSObject/JSVariableFactory.ts | 52 ---------------- .../Evaluation/JSObject/JSVariableUpdates.ts | 13 +--- .../__test__/JSVariableFactory.test.ts | 23 +++++-- .../src/workers/Evaluation/JSObject/index.ts | 1 + .../Evaluation/fns/utils/TriggerEmitter.ts | 6 +- .../workers/Evaluation/getEntityForContext.ts | 4 +- 9 files changed, 136 insertions(+), 75 deletions(-) delete mode 100644 app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts diff --git a/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts index 3488d7a6e5..52b60ccbf0 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts @@ -90,4 +90,52 @@ describe("JSObject testing", () => { expect($label).contains("[]"); }); }); + + it("6. Bug 27978 Check assignment should not get overridden by evaluation", () => { + _.entityExplorer.DragNDropWidget(_.draggableWidgets.TEXT, 400, 400); + _.propPane.TypeTextIntoField( + "Text", + `{{JSObject1.data.length ? 'id-' + JSObject1.data[0].id : 'Not Set' }}`, + true, + false, + ); + _.entityExplorer.NavigateToSwitcher("Explorer"); + _.apiPage.CreateAndFillApi( + _.dataManager.dsValues[_.dataManager.defaultEnviorment].mockApiUrl, + ); + const JS_OBJECT_BODY = `export default { + data: [], + async getData() { + await Api1.run() + return Api1.data + }, + async myFun1() { + this.data = await this.getData(); + console.log(this.data); + }, + async myFun2() { + const data = await this.getData(); + data.push({ name: "test123" }) + this.data = data; + console.log(this.data); + }, + }`; + _.jsEditor.CreateJSObject(JS_OBJECT_BODY, { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + }); + _.jsEditor.SelectFunctionDropdown("myFun1"); + _.jsEditor.RunJSObj(); + _.entityExplorer.SelectEntityByName("Text2", "Widgets"); + _.agHelper.AssertContains("id-1"); + cy.reload(); + _.agHelper.AssertContains("Not Set"); + _.entityExplorer.SelectEntityByName("JSObject1", "Queries/JS"); + _.jsEditor.SelectFunctionDropdown("myFun2"); + _.jsEditor.RunJSObj(); + _.entityExplorer.SelectEntityByName("Text2", "Widgets"); + _.agHelper.AssertContains("id-1"); + }); }); diff --git a/app/client/src/ce/actions/evaluationActions.ts b/app/client/src/ce/actions/evaluationActions.ts index 2504d7b1be..8bb299de54 100644 --- a/app/client/src/ce/actions/evaluationActions.ts +++ b/app/client/src/ce/actions/evaluationActions.ts @@ -66,8 +66,6 @@ export const EVALUATE_REDUX_ACTIONS = [ ReduxActionTypes.MOVE_ACTION_SUCCESS, ReduxActionTypes.RUN_ACTION_SUCCESS, ReduxActionErrorTypes.RUN_ACTION_ERROR, - ReduxActionTypes.EXECUTE_PLUGIN_ACTION_SUCCESS, - ReduxActionErrorTypes.EXECUTE_PLUGIN_ACTION_ERROR, ReduxActionTypes.CLEAR_ACTION_RESPONSE, // JS Actions ReduxActionTypes.CREATE_JS_ACTION_SUCCESS, diff --git a/app/client/src/workers/Evaluation/JSObject/Collection.ts b/app/client/src/workers/Evaluation/JSObject/Collection.ts index df62aedeb5..d2874a2f0b 100644 --- a/app/client/src/workers/Evaluation/JSObject/Collection.ts +++ b/app/client/src/workers/Evaluation/JSObject/Collection.ts @@ -1,6 +1,20 @@ import { getEntityNameAndPropertyPath } from "@appsmith/workers/Evaluation/evaluationUtils"; import { klona } from "klona/full"; import { get, set } from "lodash"; +import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter"; +import ExecutionMetaData from "../fns/utils/ExecutionMetaData"; +import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; + +export enum PatchType { + "SET" = "SET", + "GET" = "GET", +} + +export interface Patch { + path: string; + method: PatchType; + value?: unknown; +} export type VariableState = Record>; @@ -61,6 +75,7 @@ export default class JSObjectCollection { const newVarState = { ...this.variableState[entityName] }; newVarState[propertyPath] = variableValue; this.variableState[entityName] = newVarState; + JSObjectCollection.clearCachedVariablesForEvaluationContext(entityName); } static getVariableState( @@ -77,6 +92,53 @@ export default class JSObjectCollection { delete jsObject[propertyPath]; } + /**Map */ + static cachedJSVariablesByEntityName: Record = {}; + + /**Computes Map with getters & setters to track JS mutations + * We cache and reuse this map. We recreate only when the JSObject's content changes or when any of the variables + * gets evaluated + */ + static getVariablesForEvaluationContext(entityName: string) { + if (JSObjectCollection.cachedJSVariablesByEntityName[entityName]) + return JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + const varState = JSObjectCollection.getVariableState(entityName); + const variables = Object.entries(varState); + const newJSObject = {} as JSActionEntity; + + for (const [varName, varValue] of variables) { + let variable = varValue; + Object.defineProperty(newJSObject, varName, { + enumerable: true, + configurable: true, + get() { + TriggerEmitter.emit(BatchKey.process_js_variable_updates, { + path: `${entityName}.${varName}`, + method: PatchType.GET, + }); + return variable; + }, + set(value) { + TriggerEmitter.emit(BatchKey.process_js_variable_updates, { + path: `${entityName}.${varName}`, + method: PatchType.SET, + value, + }); + variable = value; + }, + }); + } + ExecutionMetaData.setExecutionMetaData({ + enableJSVarUpdateTracking: true, + }); + JSObjectCollection.cachedJSVariablesByEntityName[entityName] = newJSObject; + return JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + } + + static clearCachedVariablesForEvaluationContext(entityName: string) { + delete JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + } + static clear() { this.variableState = {}; this.unEvalState = {}; diff --git a/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts b/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts deleted file mode 100644 index 1922f98a63..0000000000 --- a/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PatchType } from "./JSVariableUpdates"; -import ExecutionMetaData from "../fns/utils/ExecutionMetaData"; -import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; -import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter"; - -class JSFactory { - static create( - jsObjectName: string, - varState: Record = {}, - ): JSActionEntity { - const newJSObject: any = {}; - - const variables = Object.entries(varState); - - for (const [varName, varValue] of variables) { - let variable = varValue; - Object.defineProperty(newJSObject, varName, { - enumerable: true, - configurable: true, - get() { - TriggerEmitter.emit(BatchKey.process_js_variable_updates, { - path: `${jsObjectName}.${varName}`, - method: PatchType.GET, - }); - return variable; - }, - set(value) { - TriggerEmitter.emit(BatchKey.process_js_variable_updates, { - path: `${jsObjectName}.${varName}`, - method: PatchType.SET, - value, - }); - variable = value; - }, - }); - - ExecutionMetaData.setExecutionMetaData({ - enableJSVarUpdateTracking: false, - }); - - newJSObject[varName] = varValue; - - ExecutionMetaData.setExecutionMetaData({ - enableJSVarUpdateTracking: true, - }); - } - - return newJSObject; - } -} - -export default JSFactory; diff --git a/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts b/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts index a7b117d1f0..3d2638abba 100644 --- a/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts +++ b/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts @@ -5,17 +5,8 @@ import { evalTreeWithChanges } from "../evalTreeWithChanges"; import { get } from "lodash"; import { isJSObjectVariable } from "./utils"; import isDeepEqualES6 from "fast-deep-equal/es6"; - -export enum PatchType { - "SET" = "SET", - "GET" = "GET", -} - -export type Patch = { - path: string; - method: PatchType; - value?: unknown; -}; +import type { Patch } from "./Collection"; +import { PatchType } from "./Collection"; export type UpdatedPathsMap = Record; diff --git a/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts b/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts index f7e0355b70..80191e1c6d 100644 --- a/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts +++ b/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts @@ -1,9 +1,9 @@ -import JSFactory from "../JSVariableFactory"; import ExecutionMetaData from "workers/Evaluation/fns/utils/ExecutionMetaData"; import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; import TriggerEmitter, { jsVariableUpdatesHandlerWrapper, } from "workers/Evaluation/fns/utils/TriggerEmitter"; +import JSObjectCollection from "../Collection"; const applyJSVariableUpdatesToEvalTreeMock = jest.fn(); jest.mock("../JSVariableUpdates.ts", () => ({ @@ -36,7 +36,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: true, @@ -96,7 +101,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: false, @@ -125,7 +135,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: true, diff --git a/app/client/src/workers/Evaluation/JSObject/index.ts b/app/client/src/workers/Evaluation/JSObject/index.ts index 304b28009f..b4d5bceb4c 100644 --- a/app/client/src/workers/Evaluation/JSObject/index.ts +++ b/app/client/src/workers/Evaluation/JSObject/index.ts @@ -95,6 +95,7 @@ export function saveResolvedFunctionsAndJSUpdates( try { JSObjectCollection.deleteResolvedFunction(entityName); JSObjectCollection.deleteUnEvalState(entityName); + JSObjectCollection.clearCachedVariablesForEvaluationContext(entityName); const parseStartTime = performance.now(); const { parsedObject, success } = parseJSObject(entity.body); diff --git a/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts b/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts index 7d50754db0..95ba119c36 100644 --- a/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts +++ b/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts @@ -1,10 +1,7 @@ import { EventEmitter } from "events"; import { MAIN_THREAD_ACTION } from "@appsmith/workers/Evaluation/evalWorkerActions"; import { WorkerMessenger } from "workers/Evaluation/fns/utils/Messenger"; -import type { - Patch, - UpdatedPathsMap, -} from "workers/Evaluation/JSObject/JSVariableUpdates"; +import type { UpdatedPathsMap } from "workers/Evaluation/JSObject/JSVariableUpdates"; import { applyJSVariableUpdatesToEvalTree } from "workers/Evaluation/JSObject/JSVariableUpdates"; import ExecutionMetaData from "./ExecutionMetaData"; import { get } from "lodash"; @@ -18,6 +15,7 @@ import type { import type { UpdateActionProps } from "workers/Evaluation/handlers/updateActionData"; import { handleActionsDataUpdate } from "workers/Evaluation/handlers/updateActionData"; import { getEntityNameAndPropertyPath } from "@appsmith/workers/Evaluation/evaluationUtils"; +import type { Patch } from "workers/Evaluation/JSObject/Collection"; const _internalSetTimeout = self.setTimeout; const _internalClearTimeout = self.clearTimeout; diff --git a/app/client/src/workers/Evaluation/getEntityForContext.ts b/app/client/src/workers/Evaluation/getEntityForContext.ts index 06dd01c605..592d098b5a 100644 --- a/app/client/src/workers/Evaluation/getEntityForContext.ts +++ b/app/client/src/workers/Evaluation/getEntityForContext.ts @@ -4,7 +4,6 @@ import type { } from "@appsmith/entities/DataTree/types"; import { ENTITY_TYPE_VALUE } from "entities/DataTree/dataTreeFactory"; import JSObjectCollection from "./JSObject/Collection"; -import JSFactory from "./JSObject/JSVariableFactory"; import { jsObjectFunctionFactory } from "./fns/utils/jsObjectFnFactory"; import { isObject } from "lodash"; @@ -55,7 +54,8 @@ export function getEntityForEvalContext( return Object.assign({}, jsObject, fns); } - jsObjectForEval = JSFactory.create(entityName, jsObjectForEval); + jsObjectForEval = + JSObjectCollection.getVariablesForEvaluationContext(entityName); return Object.assign(jsObjectForEval, fns); } } From 1baa693f74d804b1229d85a4f8e5a9ed679813bc Mon Sep 17 00:00:00 2001 From: arunvjn <32433245+arunvjn@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:32:27 +0530 Subject: [PATCH 26/48] fix: Race condition in JS object mutation (#28083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Fixes race condition in JS Object mutation where evaluation overrides the variables state. Root Cause: Execution context is shared between every evaluation request and trigger execution request. A trigger execution request could be paused when an asynchronous task is awaited. In the mean time, worker might pick up the task to perform and evaluation. Evaluation would end up replacing the entities in the execution context, there by resetting the actions performed by the trigger execution before it was paused. What the fix does? We are now caching the JS object for reuse which means that every execution/evaluation request reuses the same JS Object as long the JS Object isn't modified by a user action. #### PR fixes following issue(s) Fixes #27978 > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change - Bug fix (non-breaking change which fixes an issue) > > ## Testing > #### How Has This Been Tested? - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [x] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [x] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [x] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [x] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed (cherry picked from commit 1afd223e10a61db916c3c91ddd73daf10ada02d8) --- .../JSObject/JSObjectMutation_spec.ts | 48 ++++++++++++++ .../src/ce/actions/evaluationActions.ts | 2 - .../workers/Evaluation/JSObject/Collection.ts | 62 +++++++++++++++++++ .../Evaluation/JSObject/JSVariableFactory.ts | 52 ---------------- .../Evaluation/JSObject/JSVariableUpdates.ts | 13 +--- .../__test__/JSVariableFactory.test.ts | 23 +++++-- .../src/workers/Evaluation/JSObject/index.ts | 1 + .../Evaluation/fns/utils/TriggerEmitter.ts | 6 +- .../workers/Evaluation/getEntityForContext.ts | 4 +- 9 files changed, 136 insertions(+), 75 deletions(-) delete mode 100644 app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts diff --git a/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts index 3488d7a6e5..52b60ccbf0 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/JSObject/JSObjectMutation_spec.ts @@ -90,4 +90,52 @@ describe("JSObject testing", () => { expect($label).contains("[]"); }); }); + + it("6. Bug 27978 Check assignment should not get overridden by evaluation", () => { + _.entityExplorer.DragNDropWidget(_.draggableWidgets.TEXT, 400, 400); + _.propPane.TypeTextIntoField( + "Text", + `{{JSObject1.data.length ? 'id-' + JSObject1.data[0].id : 'Not Set' }}`, + true, + false, + ); + _.entityExplorer.NavigateToSwitcher("Explorer"); + _.apiPage.CreateAndFillApi( + _.dataManager.dsValues[_.dataManager.defaultEnviorment].mockApiUrl, + ); + const JS_OBJECT_BODY = `export default { + data: [], + async getData() { + await Api1.run() + return Api1.data + }, + async myFun1() { + this.data = await this.getData(); + console.log(this.data); + }, + async myFun2() { + const data = await this.getData(); + data.push({ name: "test123" }) + this.data = data; + console.log(this.data); + }, + }`; + _.jsEditor.CreateJSObject(JS_OBJECT_BODY, { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + }); + _.jsEditor.SelectFunctionDropdown("myFun1"); + _.jsEditor.RunJSObj(); + _.entityExplorer.SelectEntityByName("Text2", "Widgets"); + _.agHelper.AssertContains("id-1"); + cy.reload(); + _.agHelper.AssertContains("Not Set"); + _.entityExplorer.SelectEntityByName("JSObject1", "Queries/JS"); + _.jsEditor.SelectFunctionDropdown("myFun2"); + _.jsEditor.RunJSObj(); + _.entityExplorer.SelectEntityByName("Text2", "Widgets"); + _.agHelper.AssertContains("id-1"); + }); }); diff --git a/app/client/src/ce/actions/evaluationActions.ts b/app/client/src/ce/actions/evaluationActions.ts index 2504d7b1be..8bb299de54 100644 --- a/app/client/src/ce/actions/evaluationActions.ts +++ b/app/client/src/ce/actions/evaluationActions.ts @@ -66,8 +66,6 @@ export const EVALUATE_REDUX_ACTIONS = [ ReduxActionTypes.MOVE_ACTION_SUCCESS, ReduxActionTypes.RUN_ACTION_SUCCESS, ReduxActionErrorTypes.RUN_ACTION_ERROR, - ReduxActionTypes.EXECUTE_PLUGIN_ACTION_SUCCESS, - ReduxActionErrorTypes.EXECUTE_PLUGIN_ACTION_ERROR, ReduxActionTypes.CLEAR_ACTION_RESPONSE, // JS Actions ReduxActionTypes.CREATE_JS_ACTION_SUCCESS, diff --git a/app/client/src/workers/Evaluation/JSObject/Collection.ts b/app/client/src/workers/Evaluation/JSObject/Collection.ts index df62aedeb5..d2874a2f0b 100644 --- a/app/client/src/workers/Evaluation/JSObject/Collection.ts +++ b/app/client/src/workers/Evaluation/JSObject/Collection.ts @@ -1,6 +1,20 @@ import { getEntityNameAndPropertyPath } from "@appsmith/workers/Evaluation/evaluationUtils"; import { klona } from "klona/full"; import { get, set } from "lodash"; +import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter"; +import ExecutionMetaData from "../fns/utils/ExecutionMetaData"; +import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; + +export enum PatchType { + "SET" = "SET", + "GET" = "GET", +} + +export interface Patch { + path: string; + method: PatchType; + value?: unknown; +} export type VariableState = Record>; @@ -61,6 +75,7 @@ export default class JSObjectCollection { const newVarState = { ...this.variableState[entityName] }; newVarState[propertyPath] = variableValue; this.variableState[entityName] = newVarState; + JSObjectCollection.clearCachedVariablesForEvaluationContext(entityName); } static getVariableState( @@ -77,6 +92,53 @@ export default class JSObjectCollection { delete jsObject[propertyPath]; } + /**Map */ + static cachedJSVariablesByEntityName: Record = {}; + + /**Computes Map with getters & setters to track JS mutations + * We cache and reuse this map. We recreate only when the JSObject's content changes or when any of the variables + * gets evaluated + */ + static getVariablesForEvaluationContext(entityName: string) { + if (JSObjectCollection.cachedJSVariablesByEntityName[entityName]) + return JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + const varState = JSObjectCollection.getVariableState(entityName); + const variables = Object.entries(varState); + const newJSObject = {} as JSActionEntity; + + for (const [varName, varValue] of variables) { + let variable = varValue; + Object.defineProperty(newJSObject, varName, { + enumerable: true, + configurable: true, + get() { + TriggerEmitter.emit(BatchKey.process_js_variable_updates, { + path: `${entityName}.${varName}`, + method: PatchType.GET, + }); + return variable; + }, + set(value) { + TriggerEmitter.emit(BatchKey.process_js_variable_updates, { + path: `${entityName}.${varName}`, + method: PatchType.SET, + value, + }); + variable = value; + }, + }); + } + ExecutionMetaData.setExecutionMetaData({ + enableJSVarUpdateTracking: true, + }); + JSObjectCollection.cachedJSVariablesByEntityName[entityName] = newJSObject; + return JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + } + + static clearCachedVariablesForEvaluationContext(entityName: string) { + delete JSObjectCollection.cachedJSVariablesByEntityName[entityName]; + } + static clear() { this.variableState = {}; this.unEvalState = {}; diff --git a/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts b/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts deleted file mode 100644 index 1922f98a63..0000000000 --- a/app/client/src/workers/Evaluation/JSObject/JSVariableFactory.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PatchType } from "./JSVariableUpdates"; -import ExecutionMetaData from "../fns/utils/ExecutionMetaData"; -import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; -import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter"; - -class JSFactory { - static create( - jsObjectName: string, - varState: Record = {}, - ): JSActionEntity { - const newJSObject: any = {}; - - const variables = Object.entries(varState); - - for (const [varName, varValue] of variables) { - let variable = varValue; - Object.defineProperty(newJSObject, varName, { - enumerable: true, - configurable: true, - get() { - TriggerEmitter.emit(BatchKey.process_js_variable_updates, { - path: `${jsObjectName}.${varName}`, - method: PatchType.GET, - }); - return variable; - }, - set(value) { - TriggerEmitter.emit(BatchKey.process_js_variable_updates, { - path: `${jsObjectName}.${varName}`, - method: PatchType.SET, - value, - }); - variable = value; - }, - }); - - ExecutionMetaData.setExecutionMetaData({ - enableJSVarUpdateTracking: false, - }); - - newJSObject[varName] = varValue; - - ExecutionMetaData.setExecutionMetaData({ - enableJSVarUpdateTracking: true, - }); - } - - return newJSObject; - } -} - -export default JSFactory; diff --git a/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts b/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts index d15265aeaf..3d2638abba 100644 --- a/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts +++ b/app/client/src/workers/Evaluation/JSObject/JSVariableUpdates.ts @@ -5,17 +5,8 @@ import { evalTreeWithChanges } from "../evalTreeWithChanges"; import { get } from "lodash"; import { isJSObjectVariable } from "./utils"; import isDeepEqualES6 from "fast-deep-equal/es6"; - -export enum PatchType { - "SET" = "SET", - "GET" = "GET", -} - -export interface Patch { - path: string; - method: PatchType; - value?: unknown; -} +import type { Patch } from "./Collection"; +import { PatchType } from "./Collection"; export type UpdatedPathsMap = Record; diff --git a/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts b/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts index f7e0355b70..80191e1c6d 100644 --- a/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts +++ b/app/client/src/workers/Evaluation/JSObject/__test__/JSVariableFactory.test.ts @@ -1,9 +1,9 @@ -import JSFactory from "../JSVariableFactory"; import ExecutionMetaData from "workers/Evaluation/fns/utils/ExecutionMetaData"; import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; import TriggerEmitter, { jsVariableUpdatesHandlerWrapper, } from "workers/Evaluation/fns/utils/TriggerEmitter"; +import JSObjectCollection from "../Collection"; const applyJSVariableUpdatesToEvalTreeMock = jest.fn(); jest.mock("../JSVariableUpdates.ts", () => ({ @@ -36,7 +36,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: true, @@ -96,7 +101,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: false, @@ -125,7 +135,12 @@ describe("JSVariableFactory", () => { weakSet: new WeakSet(), } as unknown as JSActionEntity; - const proxiedJSObject = JSFactory.create("JSObject1", jsObject); + Object.entries(jsObject).forEach(([k, v]) => + JSObjectCollection.setVariableValue(v, `JSObject1.${k}`), + ); + + const proxiedJSObject = + JSObjectCollection.getVariablesForEvaluationContext("JSObject1"); ExecutionMetaData.setExecutionMetaData({ enableJSVarUpdateTracking: true, diff --git a/app/client/src/workers/Evaluation/JSObject/index.ts b/app/client/src/workers/Evaluation/JSObject/index.ts index a1af115b2a..56ac11224f 100644 --- a/app/client/src/workers/Evaluation/JSObject/index.ts +++ b/app/client/src/workers/Evaluation/JSObject/index.ts @@ -92,6 +92,7 @@ export function saveResolvedFunctionsAndJSUpdates( try { JSObjectCollection.deleteResolvedFunction(entityName); JSObjectCollection.deleteUnEvalState(entityName); + JSObjectCollection.clearCachedVariablesForEvaluationContext(entityName); const parseStartTime = performance.now(); const { parsedObject, success } = parseJSObject(entity.body); diff --git a/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts b/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts index 7d50754db0..95ba119c36 100644 --- a/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts +++ b/app/client/src/workers/Evaluation/fns/utils/TriggerEmitter.ts @@ -1,10 +1,7 @@ import { EventEmitter } from "events"; import { MAIN_THREAD_ACTION } from "@appsmith/workers/Evaluation/evalWorkerActions"; import { WorkerMessenger } from "workers/Evaluation/fns/utils/Messenger"; -import type { - Patch, - UpdatedPathsMap, -} from "workers/Evaluation/JSObject/JSVariableUpdates"; +import type { UpdatedPathsMap } from "workers/Evaluation/JSObject/JSVariableUpdates"; import { applyJSVariableUpdatesToEvalTree } from "workers/Evaluation/JSObject/JSVariableUpdates"; import ExecutionMetaData from "./ExecutionMetaData"; import { get } from "lodash"; @@ -18,6 +15,7 @@ import type { import type { UpdateActionProps } from "workers/Evaluation/handlers/updateActionData"; import { handleActionsDataUpdate } from "workers/Evaluation/handlers/updateActionData"; import { getEntityNameAndPropertyPath } from "@appsmith/workers/Evaluation/evaluationUtils"; +import type { Patch } from "workers/Evaluation/JSObject/Collection"; const _internalSetTimeout = self.setTimeout; const _internalClearTimeout = self.clearTimeout; diff --git a/app/client/src/workers/Evaluation/getEntityForContext.ts b/app/client/src/workers/Evaluation/getEntityForContext.ts index 35b5bc46db..8037878d76 100644 --- a/app/client/src/workers/Evaluation/getEntityForContext.ts +++ b/app/client/src/workers/Evaluation/getEntityForContext.ts @@ -2,7 +2,6 @@ import type { JSActionEntity } from "@appsmith/entities/DataTree/types"; import type { DataTreeEntity } from "entities/DataTree/dataTreeTypes"; import { ENTITY_TYPE_VALUE } from "entities/DataTree/dataTreeFactory"; import JSObjectCollection from "./JSObject/Collection"; -import JSFactory from "./JSObject/JSVariableFactory"; import { jsObjectFunctionFactory } from "./fns/utils/jsObjectFnFactory"; import { isObject } from "lodash"; @@ -53,7 +52,8 @@ export function getEntityForEvalContext( return Object.assign({}, jsObject, fns); } - jsObjectForEval = JSFactory.create(entityName, jsObjectForEval); + jsObjectForEval = + JSObjectCollection.getVariablesForEvaluationContext(entityName); return Object.assign(jsObjectForEval, fns); } } From 97166581d383da149ebd85b0327134bb18a743f7 Mon Sep 17 00:00:00 2001 From: Nilesh Sarupriya Date: Mon, 16 Oct 2023 14:42:38 +0530 Subject: [PATCH 27/48] chore: populate user management roles with default domain (#26803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description > Populate user management roles with default domain type and id. #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change - Chore (housekeeping or task changes that don't impact user perception) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Nilesh Sarupriya <20905988+nsarupr@users.noreply.github.com> (cherry picked from commit 1305b1a2af3b4cae91e1f829c40bcba384faa8f5) --- .../server/exceptions/AppsmithError.java | 8 ++ .../server/exceptions/AppsmithErrorCode.java | 1 + ...entRolesWithoutDefaultDomainTypeAndId.java | 88 +++++++++++++ ...eDefaultDomainIdInUserManagementRoles.java | 117 ++++++++++++++++++ ...n030TagUsersWithNoUserManagementRoles.java | 70 +++++++++++ ...serManagementRolesForUsersTaggedIn030.java | 110 ++++++++++++++++ 6 files changed, 394 insertions(+) create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration029PopulateDefaultDomainIdInUserManagementRoles.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration030TagUsersWithNoUserManagementRoles.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration031CreateUserManagementRolesForUsersTaggedIn030.java diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java index 9de15ba27e..8985b52e39 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java @@ -969,6 +969,14 @@ public enum AppsmithError { "Invalid usage for custom annotation", ErrorType.CONFIGURATION_ERROR, null), + MIGRATION_FAILED( + 500, + AppsmithErrorCode.MIGRATION_FAILED.getCode(), + "Migration {0} failed. Reason: {1}. Note: {2}", + AppsmithErrorAction.DEFAULT, + "Migration failed", + ErrorType.INTERNAL_ERROR, + null), FeatureFlagMigrationFailure( 500, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java index e47cb2ad6a..9cd8a99b38 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java @@ -66,6 +66,7 @@ public enum AppsmithErrorCode { PLUGIN_EXECUTION_TIMEOUT("AE-APP-5040", "Plugin execution timeout"), MARKETPLACE_TIMEOUT("AE-APP-5041", "Marketplace timeout"), GOOGLE_RECAPTCHA_TIMEOUT("AE-APP-5042", "Google recaptcha timeout"), + MIGRATION_FAILED("AE-APP-5043", "Migration failed"), INVALID_PROPERTIES_CONFIGURATION("AE-APP-5044", "Property configuration is wrong or malformed"), NAME_CLASH_NOT_ALLOWED_IN_REFACTOR("AE-AST-4009", "Name clash not allowed in refactor"), GENERIC_BAD_REQUEST("AE-BAD-4000", "Generic bad request"), diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId.java new file mode 100644 index 0000000000..49d163b95f --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId.java @@ -0,0 +1,88 @@ +package com.appsmith.server.migrations.db.ce; + +import com.appsmith.external.models.Policy; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.QPermissionGroup; +import com.appsmith.server.domains.QUser; +import com.appsmith.server.domains.User; +import com.appsmith.server.helpers.CollectionUtils; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static com.appsmith.server.acl.AclPermission.RESET_PASSWORD_USERS; +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.fieldName; +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.notDeleted; + +@Slf4j +@RequiredArgsConstructor +@ChangeUnit(order = "028", id = "tag-user-management-roles-without-default-domain-type-and-id") +public class Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId { + private final MongoTemplate mongoTemplate; + + public static final String MIGRATION_FLAG_028_TAG_USER_MANAGEMENT_ROLE_WITHOUT_DEFAULT_DOMAIN_TYPE_AND_ID = + "tagUserManagementRoleWithoutDefaultDomainTypeAndId"; + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void tagUserManagementRolesWithoutDefaultDomainTypeAndId() { + Criteria resetPasswordPolicyExistsAndNotDeleted = Criteria.where("policies.permission") + .is(RESET_PASSWORD_USERS.getValue()) + .andOperator(notDeleted()); + Query queryExistingUsersWithResetPasswordPolicy = new Query(resetPasswordPolicyExistsAndNotDeleted); + queryExistingUsersWithResetPasswordPolicy.fields().include(fieldName(QUser.user.policies)); + + List existingUsers = mongoTemplate.find(queryExistingUsersWithResetPasswordPolicy, User.class); + + /* + * At the moment, RESET_PASSWORD_USERS policy for the user resource only contains 1 permission group ID, i.e. + * the ID of user management role for that user resource. Hence, we pick the ID present at the 0th index below. + */ + Set userManagementRoleIds = new HashSet<>(); + existingUsers.forEach(existingUser -> { + Optional resetPasswordPolicyOptional = existingUser.getPolicies().stream() + .filter(policy1 -> RESET_PASSWORD_USERS.getValue().equals(policy1.getPermission())) + .findFirst(); + resetPasswordPolicyOptional.ifPresent(resetPasswordPolicy -> { + List roleIdList = + resetPasswordPolicy.getPermissionGroups().stream().toList(); + if (!CollectionUtils.isNullOrEmpty(roleIdList)) { + userManagementRoleIds.add(roleIdList.get(0)); + } + }); + }); + + Criteria criteriaUserManagementRoleIds = + Criteria.where(fieldName(QPermissionGroup.permissionGroup.id)).in(userManagementRoleIds); + Criteria criteriaDefaultDomainIdDoesNotExist = new Criteria() + .orOperator( + Criteria.where(fieldName(QPermissionGroup.permissionGroup.defaultDomainId)) + .isNull(), + Criteria.where(fieldName(QPermissionGroup.permissionGroup.defaultDomainId)) + .exists(false)); + Criteria criteriaUserManagementRolesWithDefaultDomainIdDoesNotExist = + new Criteria().andOperator(criteriaUserManagementRoleIds, criteriaDefaultDomainIdDoesNotExist); + Query queryUserManagementRolesWithDefaultDomainIdDoesNotExist = + new Query(criteriaUserManagementRolesWithDefaultDomainIdDoesNotExist); + + Update updateSetMigrationFlag = new Update(); + updateSetMigrationFlag.set( + MIGRATION_FLAG_028_TAG_USER_MANAGEMENT_ROLE_WITHOUT_DEFAULT_DOMAIN_TYPE_AND_ID, Boolean.TRUE); + + mongoTemplate.updateMulti( + queryUserManagementRolesWithDefaultDomainIdDoesNotExist, updateSetMigrationFlag, PermissionGroup.class); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration029PopulateDefaultDomainIdInUserManagementRoles.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration029PopulateDefaultDomainIdInUserManagementRoles.java new file mode 100644 index 0000000000..332f76a529 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration029PopulateDefaultDomainIdInUserManagementRoles.java @@ -0,0 +1,117 @@ +package com.appsmith.server.migrations.db.ce; + +import com.appsmith.external.models.Policy; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.QPermissionGroup; +import com.appsmith.server.domains.QUser; +import com.appsmith.server.domains.User; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.appsmith.server.acl.AclPermission.RESET_PASSWORD_USERS; +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.fieldName; +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.notDeleted; + +@Slf4j +@RequiredArgsConstructor +@ChangeUnit(order = "029", id = "populate-default-domain-id-in-user-management-roles") +public class Migration029PopulateDefaultDomainIdInUserManagementRoles { + + private final MongoTemplate mongoTemplate; + + private static final int migrationRetries = 5; + private static final String migrationId = "populate-default-domain-id-in-user-management-roles"; + private static final String migrationNote = "This will not have any adverse effects on the Data. Restarting the " + + "server will begin the migration from where it left."; + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void populateDefaultDomainIdInUserManagementRoles() { + Criteria resetPasswordPolicyExistsAndNotDeleted = Criteria.where("policies.permission") + .is(RESET_PASSWORD_USERS.getValue()) + .andOperator(notDeleted()); + Query queryExistingUsersWithResetPasswordPolicy = new Query(resetPasswordPolicyExistsAndNotDeleted); + queryExistingUsersWithResetPasswordPolicy.fields().include(fieldName(QUser.user.policies)); + Map userManagementRoleIdToUserIdMap = new HashMap<>(); + mongoTemplate.stream(queryExistingUsersWithResetPasswordPolicy, User.class) + .forEach(existingUser -> { + Optional resetPasswordPolicyOptional = existingUser.getPolicies().stream() + .filter(policy1 -> RESET_PASSWORD_USERS.getValue().equals(policy1.getPermission())) + .findFirst(); + resetPasswordPolicyOptional.ifPresent(resetPasswordPolicy -> { + List roleIdList = resetPasswordPolicy.getPermissionGroups().stream() + .toList(); + roleIdList.forEach(roleId -> userManagementRoleIdToUserIdMap.put(roleId, existingUser.getId())); + }); + }); + + Criteria criteriaUserManagementRolesWithMigrationFlag028Set = Criteria.where( + Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId + .MIGRATION_FLAG_028_TAG_USER_MANAGEMENT_ROLE_WITHOUT_DEFAULT_DOMAIN_TYPE_AND_ID) + .exists(Boolean.TRUE); + Query queryUserManagementRolesWithWithMigrationFlag028Set = + new Query(criteriaUserManagementRolesWithMigrationFlag028Set); + + queryUserManagementRolesWithWithMigrationFlag028Set + .fields() + .include(fieldName(QPermissionGroup.permissionGroup.id)); + long countOfUserManagementRolesWithMigrationFlag028Set = + mongoTemplate.count(queryUserManagementRolesWithWithMigrationFlag028Set, PermissionGroup.class); + int attempt = 0; + + while (countOfUserManagementRolesWithMigrationFlag028Set > 0 && attempt < migrationRetries) { + List userManagementRolesWithMigrationFlag028Set = + mongoTemplate.find(queryUserManagementRolesWithWithMigrationFlag028Set, PermissionGroup.class); + userManagementRolesWithMigrationFlag028Set.parallelStream().forEach(userManagementRole -> { + if (userManagementRoleIdToUserIdMap.containsKey(userManagementRole.getId()) + && StringUtils.isNotEmpty(userManagementRoleIdToUserIdMap.get(userManagementRole.getId()))) { + Criteria criteriaUserManagementRoleById = Criteria.where( + fieldName(QPermissionGroup.permissionGroup.id)) + .is(userManagementRole.getId()); + Query queryUserManagementRoleById = new Query(criteriaUserManagementRoleById); + Update updateDefaultDomainIdOfUserManagementRole = new Update(); + + updateDefaultDomainIdOfUserManagementRole.set( + fieldName(QPermissionGroup.permissionGroup.defaultDomainId), + userManagementRoleIdToUserIdMap.get(userManagementRole.getId())); + + updateDefaultDomainIdOfUserManagementRole.set( + fieldName(QPermissionGroup.permissionGroup.defaultDomainType), User.class.getSimpleName()); + + updateDefaultDomainIdOfUserManagementRole.unset( + Migration028TagUserManagementRolesWithoutDefaultDomainTypeAndId + .MIGRATION_FLAG_028_TAG_USER_MANAGEMENT_ROLE_WITHOUT_DEFAULT_DOMAIN_TYPE_AND_ID); + mongoTemplate.updateFirst( + queryUserManagementRoleById, + updateDefaultDomainIdOfUserManagementRole, + PermissionGroup.class); + } + }); + countOfUserManagementRolesWithMigrationFlag028Set = + mongoTemplate.count(queryUserManagementRolesWithWithMigrationFlag028Set, PermissionGroup.class); + attempt += 1; + } + + if (countOfUserManagementRolesWithMigrationFlag028Set != 0) { + String reasonForFailure = "All user management roles were not tagged."; + throw new AppsmithException(AppsmithError.MIGRATION_FAILED, migrationId, reasonForFailure, migrationNote); + } + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration030TagUsersWithNoUserManagementRoles.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration030TagUsersWithNoUserManagementRoles.java new file mode 100644 index 0000000000..11555cc0bd --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration030TagUsersWithNoUserManagementRoles.java @@ -0,0 +1,70 @@ +package com.appsmith.server.migrations.db.ce; + +import com.appsmith.external.models.QBaseDomain; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.User; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import java.util.List; + +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.fieldName; + +@Slf4j +@RequiredArgsConstructor +@ChangeUnit(order = "030", id = "tag-users-with-no-user-management-roles") +public class Migration030TagUsersWithNoUserManagementRoles { + + private final MongoTemplate mongoTemplate; + + public static final String MIGRATION_FLAG_030_TAG_USER_WITHOUT_USER_MANAGEMENT_ROLE = + "tagUserWithoutUserManagementRole"; + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void tagUsersWithNoUserManagementRoles() { + Criteria criteriaDefaultDomainTypeExists = + Criteria.where("defaultDomainType").exists(true); + Criteria criteriaDefaultDomainTypeIsUser = + Criteria.where("defaultDomainType").is(User.class.getSimpleName()); + + Criteria criteriaUserManagementRoles = + new Criteria().andOperator(criteriaDefaultDomainTypeExists, criteriaDefaultDomainTypeIsUser); + Query queryUserManagementRoles = new Query(criteriaUserManagementRoles); + queryUserManagementRoles.fields().include("defaultDomainId"); + + List userManagementRoles = mongoTemplate.find(queryUserManagementRoles, PermissionGroup.class); + + List userIdsWithUserManagementRoles = userManagementRoles.stream() + .map(PermissionGroup::getDefaultDomainId) + .toList(); + + Criteria criteriaUsersWithNoUserManagementRoles = + Criteria.where(fieldName(QBaseDomain.baseDomain.id)).nin(userIdsWithUserManagementRoles); + Criteria criteriaUsersPoliciesExists = + Criteria.where(fieldName(QBaseDomain.baseDomain.policies)).exists(true); + Criteria criteriaUsersPoliciesNotEmpty = + Criteria.where(fieldName(QBaseDomain.baseDomain.policies)).not().size(0); + Criteria criteriaUsersWithNoUserManagementRolesAndUserPoliciesExists = new Criteria() + .andOperator( + criteriaUsersWithNoUserManagementRoles, + criteriaUsersPoliciesExists, + criteriaUsersPoliciesNotEmpty); + Query queryUsersWithNoUserManagementRoles = + new Query(criteriaUsersWithNoUserManagementRolesAndUserPoliciesExists); + + Update updateSetMigrationFlag = new Update(); + updateSetMigrationFlag.set(MIGRATION_FLAG_030_TAG_USER_WITHOUT_USER_MANAGEMENT_ROLE, Boolean.TRUE); + + mongoTemplate.updateMulti(queryUsersWithNoUserManagementRoles, updateSetMigrationFlag, User.class); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration031CreateUserManagementRolesForUsersTaggedIn030.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration031CreateUserManagementRolesForUsersTaggedIn030.java new file mode 100644 index 0000000000..b450af9daf --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration031CreateUserManagementRolesForUsersTaggedIn030.java @@ -0,0 +1,110 @@ +package com.appsmith.server.migrations.db.ce; + +import com.appsmith.external.models.Policy; +import com.appsmith.external.models.QBaseDomain; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.QUser; +import com.appsmith.server.domains.User; +import com.appsmith.server.dtos.Permission; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.migrations.utils.CompatibilityUtils; +import com.appsmith.server.solutions.PolicySolution; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import java.util.Map; +import java.util.Set; + +import static com.appsmith.server.acl.AclPermission.MANAGE_USERS; +import static com.appsmith.server.repositories.ce.BaseAppsmithRepositoryCEImpl.fieldName; +import static java.lang.Boolean.TRUE; + +@Slf4j +@RequiredArgsConstructor +@ChangeUnit(order = "031", id = "create-user-management-roles-for-users-tagged-in-migration-030") +public class Migration031CreateUserManagementRolesForUsersTaggedIn030 { + private final MongoTemplate mongoTemplate; + private final PolicySolution policySolution; + + private static final int migrationRetries = 5; + private static final String migrationNote = "This will not have any adverse effects on the Data. Restarting the " + + "server will begin the migration from where it left."; + private static final String migrationId = "create-user-management-roles-for-users-tagged-in-migration-030"; + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void createUserManagementRolesForUsersTaggedInMigration030() { + Criteria criteriaUsersTaggedInMigration030 = Criteria.where( + Migration030TagUsersWithNoUserManagementRoles + .MIGRATION_FLAG_030_TAG_USER_WITHOUT_USER_MANAGEMENT_ROLE) + .is(TRUE); + + Query queryUsersTaggedInMigration030 = new Query(criteriaUsersTaggedInMigration030); + queryUsersTaggedInMigration030.fields().include(fieldName(QUser.user.id)); + queryUsersTaggedInMigration030.fields().include(fieldName(QUser.user.policies)); + queryUsersTaggedInMigration030.fields().include(fieldName(QUser.user.email)); + + Query optimisedQueryUsersTaggedInMigration030 = CompatibilityUtils.optimizeQueryForNoCursorTimeout( + mongoTemplate, queryUsersTaggedInMigration030, User.class); + + int attempt = 0; + long countUsersTaggedInMigration030 = mongoTemplate.count(optimisedQueryUsersTaggedInMigration030, User.class); + + while (attempt < migrationRetries && countUsersTaggedInMigration030 > 0) { + mongoTemplate.stream(optimisedQueryUsersTaggedInMigration030, User.class) + .forEach(user -> { + User userWithUpdatedPolicies = createUserManagementRoleAndGetUserWithUpdatedPolicies(user); + Update updateMigrationFlagAndPoliciesForUser = new Update(); + updateMigrationFlagAndPoliciesForUser.unset( + Migration030TagUsersWithNoUserManagementRoles + .MIGRATION_FLAG_030_TAG_USER_WITHOUT_USER_MANAGEMENT_ROLE); + updateMigrationFlagAndPoliciesForUser.set( + fieldName(QUser.user.policies), userWithUpdatedPolicies.getPolicies()); + Criteria criteriaUserId = Criteria.where(fieldName(QBaseDomain.baseDomain.id)) + .is(user.getId()); + Query queryUserId = new Query(criteriaUserId); + mongoTemplate.updateFirst(queryUserId, updateMigrationFlagAndPoliciesForUser, User.class); + }); + attempt += 1; + countUsersTaggedInMigration030 = mongoTemplate.count(optimisedQueryUsersTaggedInMigration030, User.class); + } + + if (countUsersTaggedInMigration030 > 0) { + String reasonForFailure = "All user management roles were not created."; + throw new AppsmithException(AppsmithError.MIGRATION_FAILED, migrationId, reasonForFailure, migrationNote); + } + } + + private User createUserManagementRoleAndGetUserWithUpdatedPolicies(User user) { + // Create user management permission group + PermissionGroup userManagementRole = new PermissionGroup(); + userManagementRole.setName(user.getUsername() + FieldName.SUFFIX_USER_MANAGEMENT_ROLE); + // Add CRUD permissions for user to the group + userManagementRole.setPermissions(Set.of(new Permission(user.getId(), MANAGE_USERS))); + userManagementRole.setDefaultDomainId(user.getId()); + userManagementRole.setDefaultDomainType(User.class.getSimpleName()); + + // Assign the permission group to the user + userManagementRole.setAssignedToUserIds(Set.of(user.getId())); + + PermissionGroup savedUserManagementRole = mongoTemplate.save(userManagementRole); + + Map crudUserPolicies = + policySolution.generatePolicyFromPermissionGroupForObject(savedUserManagementRole, user.getId()); + + User updatedWithPolicies = policySolution.addPoliciesToExistingObject(crudUserPolicies, user); + + return updatedWithPolicies; + } +} From ab3ebc94f53ef1b824f690b7d009f66cb01ff731 Mon Sep 17 00:00:00 2001 From: Aishwarya-U-R <91450662+Aishwarya-U-R@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:35:05 +0530 Subject: [PATCH 28/48] test: Cypress | Skip MsSQL_Basic_Spec.ts spec in CI (#28074) - This PR skips MsSQL_Basic_Spe.ts from CI runs since its runing out of memory in multiple CI runs - Until this is added to TED or we find other ways - will be run locally during regression - Script fix (non-breaking change which fixes an issue) - [X] Added `Test Plan Approved` label after changes were reviewed (cherry picked from commit 2a302e234dfecb5751b3217878f6caf0f7a7c47a) --- app/client/cypress_ci_custom.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/cypress_ci_custom.config.ts b/app/client/cypress_ci_custom.config.ts index 025ee81137..9ffd3a80b2 100644 --- a/app/client/cypress_ci_custom.config.ts +++ b/app/client/cypress_ci_custom.config.ts @@ -58,6 +58,8 @@ export default defineConfig({ "cypress/e2e/EE/Enterprise/MultipleEnv/ME_airtable_spec.ts", "cypress/e2e/Regression/ServerSide/Datasources/ElasticSearch_Basic_Spec.ts", "cypress/e2e/Regression/ServerSide/Datasources/Oracle_Spec.ts", + "cypress/e2e/Regression/ClientSide/Widgets/Others/MapWidget_Spec.ts", + "cypress/e2e/Sanity/Datasources/MsSQL_Basic_Spec.ts", ], }, }); From 18553c6a8d6cb3b1cfa2443422ae23f97d825edc Mon Sep 17 00:00:00 2001 From: Ankita Kinger Date: Mon, 16 Oct 2023 23:13:38 +0530 Subject: [PATCH 29/48] fix: Refactoring the code for label wrapper on admin settings (#28123) ## Description Refactoring the code for label wrapper on admin settings #### PR fixes following issue(s) Fixes [#28099](https://github.com/appsmithorg/appsmith/issues/28099) #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../pages/AdminSettings/FormGroup/Button.tsx | 50 ++++++++--------- .../AdminSettings/FormGroup/Checkbox.tsx | 55 +++++++++---------- .../pages/AdminSettings/FormGroup/Common.tsx | 14 ++++- .../pages/AdminSettings/FormGroup/Radio.tsx | 26 +++++---- .../AdminSettings/FormGroup/TextInput.tsx | 27 ++------- 5 files changed, 81 insertions(+), 91 deletions(-) diff --git a/app/client/src/pages/AdminSettings/FormGroup/Button.tsx b/app/client/src/pages/AdminSettings/FormGroup/Button.tsx index e5c3dfbcd6..c1bef4609f 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/Button.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/Button.tsx @@ -1,14 +1,21 @@ import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import React from "react"; -import { Button, Text } from "design-system"; +import { Button } from "design-system"; import { useDispatch, useSelector } from "react-redux"; import { getFormValues } from "redux-form"; import styled from "styled-components"; -import type { SettingComponentProps } from "./Common"; +import { FormGroup, type SettingComponentProps } from "./Common"; const ButtonWrapper = styled.div` width: 357px; - margin-bottom: 8px; + + .styled-label { + padding: 0 0 0.5rem; + } + + .admin-settings-form-group-label { + font-weight: var(--ads-v2-h5-font-weight); + } `; export const StyledButton = styled(Button)` @@ -22,28 +29,21 @@ export default function ButtonComponent({ setting }: SettingComponentProps) { const settings = useSelector(formValuesSelector); return ( - - {setting.label} - - { - if (setting.action) { - setting.action(dispatch, settings); - } - }} - size="md" - > - {setting.text} - + + { + if (setting.action) { + setting.action(dispatch, settings); + } + }} + size="md" + > + {setting.text} + + ); } diff --git a/app/client/src/pages/AdminSettings/FormGroup/Checkbox.tsx b/app/client/src/pages/AdminSettings/FormGroup/Checkbox.tsx index 60c38c818b..5d9ad0ba7a 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/Checkbox.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/Checkbox.tsx @@ -2,13 +2,12 @@ import React, { memo } from "react"; import type { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form"; import { Field, getFormValues } from "redux-form"; import styled from "styled-components"; -import type { SettingComponentProps } from "./Common"; +import { FormGroup, type SettingComponentProps } from "./Common"; import type { FormTextFieldProps } from "components/utils/ReduxFormTextField"; -import { Checkbox, Text } from "design-system"; +import { Checkbox } from "design-system"; import { useSelector } from "react-redux"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { isTenantConfig } from "@appsmith/utils/adminSettingsHelpers"; -import BusinessTag from "components/BusinessTag"; const CheckboxWrapper = styled.div` display: grid; @@ -65,7 +64,13 @@ function FieldCheckboxWithCheckboxText(props: CheckboxProps) { } const StyledFieldCheckboxGroup = styled.div` - margin-bottom: 8px; + .styled-label { + padding: 0 0 0.5rem; + } + + .admin-settings-form-group-label { + font-weight: var(--ads-v2-h5-font-weight); + } `; const formValuesSelector = getFormValues(SETTINGS_FORM_NAME); @@ -75,32 +80,22 @@ export function CheckboxComponent({ setting }: SettingComponentProps) { return ( -
- - {setting.label} - - {setting.isFeatureEnabled === false && } -
- + + +
); } diff --git a/app/client/src/pages/AdminSettings/FormGroup/Common.tsx b/app/client/src/pages/AdminSettings/FormGroup/Common.tsx index 0198b5bc6b..254c3e03de 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/Common.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/Common.tsx @@ -3,6 +3,8 @@ import React from "react"; import styled from "styled-components"; import { Icon, Tooltip, Text } from "design-system"; import type { Setting } from "@appsmith/pages/AdminSettings/config/types"; +import EnterpriseTag from "components/EnterpriseTag"; +import BusinessTag from "components/BusinessTag"; interface FieldHelperProps { setting: Setting; @@ -28,6 +30,8 @@ export const StyledFormGroup = styled.div` export const StyledLabel = styled.div` margin-bottom: 4px; + display: flex; + align-items: center; `; export const StyledSubtext = styled(Text)` @@ -48,7 +52,7 @@ export function FormGroup({ children, className, setting }: FieldHelperProps) { className={`${className}`} data-testid="admin-settings-form-group" > - + {setting.label && ( )} +
+ {setting.isFeatureEnabled === false && + (setting.isEnterprise === true ? ( + + ) : ( + + ))} +
{children} {setting.subText && ( diff --git a/app/client/src/pages/AdminSettings/FormGroup/Radio.tsx b/app/client/src/pages/AdminSettings/FormGroup/Radio.tsx index 198d8e66d2..81e3624bbe 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/Radio.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/Radio.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import type { ReactElement } from "react"; import { FieldError } from "design-system-old"; import { Popover2 } from "@blueprintjs/popover2"; -import type { SettingComponentProps } from "./Common"; +import { FormGroup, type SettingComponentProps } from "./Common"; import type { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form"; import { Field } from "redux-form"; import styled, { createGlobalStyle } from "styled-components"; @@ -87,6 +87,16 @@ const PopoverStyles = createGlobalStyle` } `; +const StyledFormGroup = styled(FormGroup)` + .styled-label { + padding: 0 0 0.5rem; + } + + .admin-settings-form-group-label { + font-weight: var(--ads-v2-h5-font-weight); + } +`; + type RadioGroupProps = SettingComponentProps; function RadioFieldWrapper( @@ -201,20 +211,12 @@ export default function RadioField({ setting }: RadioGroupProps) { const controlTypeProps = setting.controlTypeProps as RadioOptionProps; return ( -
- - {setting.label} - -
+ ); } diff --git a/app/client/src/pages/AdminSettings/FormGroup/TextInput.tsx b/app/client/src/pages/AdminSettings/FormGroup/TextInput.tsx index ecbc2b57e7..2662119630 100644 --- a/app/client/src/pages/AdminSettings/FormGroup/TextInput.tsx +++ b/app/client/src/pages/AdminSettings/FormGroup/TextInput.tsx @@ -1,42 +1,23 @@ import FormTextField from "components/utils/ReduxFormTextField"; import { createMessage } from "@appsmith/constants/messages"; import React from "react"; -import type { SettingComponentProps } from "./Common"; -import BusinessTag from "components/BusinessTag"; -import EnterpriseTag from "components/EnterpriseTag"; -import styled from "styled-components"; - -const LabelWrapper = styled.div` - display: flex; - gap: 4px; - align-items: center; -`; +import { FormGroup, type SettingComponentProps } from "./Common"; export default function TextInput({ setting }: SettingComponentProps) { - const inputLabel = setting.label ? ( - -
{setting.label}
- {setting.isFeatureEnabled === false && - (setting.isEnterprise === true ? : )} -
- ) : ( - "" - ); return ( -
setting.placeholder || "")} type={setting.controlSubType} /> -
+ ); } From 0c9510704cb91eb100b295f1e10db6d3b588f206 Mon Sep 17 00:00:00 2001 From: Manish Kumar <107841575+sondermanish@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:11:12 +0530 Subject: [PATCH 30/48] chore: added ce support for KB changes (#28068) ## Description > TL;DR: CE support for PR: https://github.com/appsmithorg/appsmith-ee/pull/2645/ #### PR fixes following issue(s) Fixes https://github.com/appsmithorg/appsmith-ee/issues/2644 #### Type of change - Chore (housekeeping or task changes that don't impact user perception) #### How Has This Been Tested? - [ ] Manual ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../server/constants/SerialiseApplicationObjective.java | 3 ++- .../export/internal/ImportExportApplicationServiceCEImpl.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/SerialiseApplicationObjective.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/SerialiseApplicationObjective.java index 1889f901ff..d546144bf5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/SerialiseApplicationObjective.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/SerialiseApplicationObjective.java @@ -3,5 +3,6 @@ package com.appsmith.server.constants; public enum SerialiseApplicationObjective { // For which purpose we are serialising the application from DB VERSION_CONTROL, - SHARE + SHARE, + KNOWLEDGE_BASE_GENERATION } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/export/internal/ImportExportApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/export/internal/ImportExportApplicationServiceCEImpl.java index b7546cb9c5..4bf860f25f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/export/internal/ImportExportApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/export/internal/ImportExportApplicationServiceCEImpl.java @@ -173,7 +173,9 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATION_ID)); } - boolean isGitSync = SerialiseApplicationObjective.VERSION_CONTROL.equals(serialiseFor); + boolean isGitSync = SerialiseApplicationObjective.VERSION_CONTROL.equals(serialiseFor) + || SerialiseApplicationObjective.KNOWLEDGE_BASE_GENERATION.equals(serialiseFor); + exportingMetaDTO.setIsGitSync(isGitSync); exportingMetaDTO.setExportWithConfiguration(false); From bd6f403420c828ff36669a6887944b98bd3b8486 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Wed, 18 Oct 2023 07:58:56 +0530 Subject: [PATCH 31/48] fix: Fail startup when supervisor creds are missing --- deploy/docker/fs/opt/appsmith/entrypoint.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deploy/docker/fs/opt/appsmith/entrypoint.sh b/deploy/docker/fs/opt/appsmith/entrypoint.sh index 7eee9ea9dd..0a2957fcf7 100644 --- a/deploy/docker/fs/opt/appsmith/entrypoint.sh +++ b/deploy/docker/fs/opt/appsmith/entrypoint.sh @@ -143,6 +143,12 @@ unset_unused_variables() { unset APPSMITH_RECAPTCHA_SECRET_KEY unset APPSMITH_RECAPTCHA_ENABLED fi + + export APPSMITH_SUPERVISOR_USER="${APPSMITH_SUPERVISOR_USER:-appsmith}" + if [[ -z "${APPSMITH_SUPERVISOR_PASSWORD-}" ]]; then + APPSMITH_SUPERVISOR_PASSWORD="$(tr -dc A-Za-z0-9 Date: Wed, 18 Oct 2023 12:52:40 +0530 Subject: [PATCH 32/48] fix: removed datasourceContextCaching for RestAPI (#28160) ## Description > Rest Api used cached datasourceContext which was providing older tokens. Now when we look for datasourceContext we provide a new context. #### PR fixes following issue(s) Fixes #27699 #### Type of change - Bug fix (non-breaking change which fixes an issue) #### How Has This Been Tested? - [ ] Manual ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../external/constants/PluginConstants.java | 2 + .../ce/DatasourceContextServiceCEImpl.java | 70 +++++++----- .../DatasourceContextServiceTest.java | 102 +++++++++++++++--- 3 files changed, 137 insertions(+), 37 deletions(-) diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java index 3d12fee22c..883835c55a 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/PluginConstants.java @@ -10,6 +10,8 @@ public interface PluginConstants { String DYNAMO_PLUGIN = "dynamo-plugin"; String AMAZON_S3_PLUGIN = "amazons3-plugin"; String GOOGLE_SHEETS_PLUGIN = "google-sheets-plugin"; + String REST_API_PLUGIN = "restapi-plugin"; + String GRAPH_QL_PLUGIN = "graphql-plugin"; } public static final String DEFAULT_REST_DATASOURCE = "DEFAULT_REST_DATASOURCE"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java index 5594ad7cb6..1b24142b42 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/DatasourceContextServiceCEImpl.java @@ -1,5 +1,6 @@ package com.appsmith.server.services.ce; +import com.appsmith.external.constants.PluginConstants; import com.appsmith.external.dtos.ExecutePluginDTO; import com.appsmith.external.dtos.RemoteDatasourceDTO; import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException; @@ -70,6 +71,7 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC * value instead of creating a new connection for each subscription of the source publisher. * * @param datasourceStorage - datasource storage for which a new datasource context / connection needs to be created + * @param plugin * @param pluginExecutor - plugin executor associated with the datasource's plugin * @param monitor - unique monitor object per datasource id. Lock is acquired on this monitor object. * @param datasourceContextIdentifier - key for the datasourceContextMaps. @@ -78,6 +80,7 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC */ public Mono> getCachedDatasourceContextMono( DatasourceStorage datasourceStorage, + Plugin plugin, PluginExecutor pluginExecutor, Object monitor, DatasourceContextIdentifier datasourceContextIdentifier) { @@ -114,7 +117,7 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC /* Create a fresh datasource context */ DatasourceContext datasourceContext = new DatasourceContext<>(); - if (datasourceContextIdentifier.isKeyValid()) { + if (datasourceContextIdentifier.isKeyValid() && shouldCacheContextForThisPlugin(plugin)) { /* For this datasource, either the context doesn't exist, or the context is stale. Replace (or add) with the new connection in the context map. */ datasourceContextMap.put(datasourceContextIdentifier, datasourceContext); @@ -138,13 +141,25 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC datasourceContext) .cache(); /* Cache the value so that further evaluations don't result in new connections */ - if (datasourceContextIdentifier.isKeyValid()) { + if (datasourceContextIdentifier.isKeyValid() && shouldCacheContextForThisPlugin(plugin)) { datasourceContextMonoMap.put(datasourceContextIdentifier, datasourceContextMonoCache); } return datasourceContextMonoCache; } } + /** + * determines whether we should cache context for given plugin + * it gives false if plugin is rest-api or graph-ql + * @param plugin + * @return + */ + public boolean shouldCacheContextForThisPlugin(Plugin plugin) { + // !(a || b) => (!a) & (!b) + return !PluginConstants.PackageName.REST_API_PLUGIN.equals(plugin.getPackageName()) + && !PluginConstants.PackageName.GRAPH_QL_PLUGIN.equals(plugin.getPackageName()); + } + public Mono updateDatasourceAndSetAuthentication(Object connection, DatasourceStorage datasourceStorage) { Mono datasourceStorageMono = Mono.just(datasourceStorage); if (connection instanceof UpdatableConnection updatableConnection) { @@ -162,33 +177,38 @@ public class DatasourceContextServiceCEImpl implements DatasourceContextServiceC protected Mono> createNewDatasourceContext( DatasourceStorage datasourceStorage, DatasourceContextIdentifier datasourceContextIdentifier) { log.debug("Datasource context doesn't exist. Creating connection."); - Mono pluginMono = pluginService.findById(datasourceStorage.getPluginId()); + Mono pluginMono = + pluginService.findById(datasourceStorage.getPluginId()).cache(); - return pluginExecutorHelper.getPluginExecutor(pluginMono).flatMap(pluginExecutor -> { + return pluginMono + .zipWith(pluginExecutorHelper.getPluginExecutor(pluginMono)) + .flatMap(tuple2 -> { + Plugin plugin = tuple2.getT1(); + PluginExecutor pluginExecutor = tuple2.getT2(); - /** - * Keep one monitor object against each datasource id. The synchronized method - * `getCachedDatasourceContextMono` would then acquire lock on the monitor object which is unique - * for each datasourceId hence ensuring that if competing threads want to create datasource context - * on different datasource id then they are not blocked on each other and can run concurrently. - * Only threads that want to create a new datasource context on the same datasource id would be - * synchronized. - */ - Object monitor = new Object(); - if (datasourceContextIdentifier.isKeyValid()) { - if (datasourceContextSynchronizationMonitorMap.get(datasourceContextIdentifier) == null) { - synchronized (this) { - datasourceContextSynchronizationMonitorMap.computeIfAbsent( - datasourceContextIdentifier, k -> new Object()); + /** + * Keep one monitor object against each datasource id. The synchronized method + * `getCachedDatasourceContextMono` would then acquire lock on the monitor object which is unique + * for each datasourceId hence ensuring that if competing threads want to create datasource context + * on different datasource id then they are not blocked on each other and can run concurrently. + * Only threads that want to create a new datasource context on the same datasource id would be + * synchronized. + */ + Object monitor = new Object(); + if (datasourceContextIdentifier.isKeyValid()) { + if (datasourceContextSynchronizationMonitorMap.get(datasourceContextIdentifier) == null) { + synchronized (this) { + datasourceContextSynchronizationMonitorMap.computeIfAbsent( + datasourceContextIdentifier, k -> new Object()); + } + } + + monitor = datasourceContextSynchronizationMonitorMap.get(datasourceContextIdentifier); } - } - monitor = datasourceContextSynchronizationMonitorMap.get(datasourceContextIdentifier); - } - - return getCachedDatasourceContextMono( - datasourceStorage, pluginExecutor, monitor, datasourceContextIdentifier); - }); + return getCachedDatasourceContextMono( + datasourceStorage, plugin, pluginExecutor, monitor, datasourceContextIdentifier); + }); } public boolean getIsStale( diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java index e5e93b5e64..717a795037 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java @@ -1,9 +1,14 @@ package com.appsmith.server.services; +import com.appsmith.external.constants.PluginConstants; +import com.appsmith.external.helpers.restApiUtils.connections.APIConnection; +import com.appsmith.external.helpers.restApiUtils.connections.APIConnectionFactory; +import com.appsmith.external.helpers.restApiUtils.connections.BearerTokenAuthentication; import com.appsmith.external.helpers.restApiUtils.connections.OAuth2ClientCredentials; import com.appsmith.external.models.ApiKeyAuth; import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.external.models.BasicAuth; +import com.appsmith.external.models.BearerTokenAuth; import com.appsmith.external.models.DBAuth; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.DatasourceConfiguration; @@ -146,6 +151,7 @@ public class DatasourceContextServiceTest { @WithUserDetails(value = "api_user") public void testDatasourceCache_afterDatasourceDeleted_doesNotReturnOldConnection() { // Never require the datasource connection to be stale + Plugin emptyPlugin = new Plugin(); doReturn(false).doReturn(false).when(datasourceContextService).getIsStale(any(), any()); MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); @@ -169,7 +175,7 @@ public class DatasourceContextServiceTest { Object monitor = new Object(); // Create one instance of datasource connection Mono> dsContextMono1 = datasourceContextService.getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier); + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier); Datasource datasource = new Datasource(); datasource.setId("id1"); @@ -197,7 +203,7 @@ public class DatasourceContextServiceTest { Mono> dsContextMono2 = datasourceService .archiveById("id1") .flatMap(deleted -> datasourceContextService.getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier)); + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier)); StepVerifier.create(dsContextMono1) .assertNext(dsContext1 -> { @@ -349,7 +355,7 @@ public class DatasourceContextServiceTest { @WithUserDetails(value = "api_user") public void testCachedDatasourceCreate() { doReturn(false).doReturn(false).when(datasourceContextService).getIsStale(any(), any()); - + Plugin emptyPlugin = new Plugin(); MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); MockPluginExecutor spyMockPluginExecutor = spy(mockPluginExecutor); /* Return two different connection objects if `datasourceCreate` method is called twice */ @@ -369,11 +375,11 @@ public class DatasourceContextServiceTest { Object monitor = new Object(); DatasourceContext dsContext1 = (DatasourceContext) datasourceContextService .getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier) + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier) .block(); DatasourceContext dsContext2 = (DatasourceContext) datasourceContextService .getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier) + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier) .block(); /* They can only be equal if the `datasourceCreate` method was called only once */ @@ -388,6 +394,7 @@ public class DatasourceContextServiceTest { @Test @WithUserDetails(value = "api_user") public void testDatasourceCreate_withUpdatableConnection_recreatesConnectionAlways() { + Plugin emptyPlugin = new Plugin(); MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(mockPluginExecutor)); @@ -452,7 +459,11 @@ public class DatasourceContextServiceTest { Object monitor = new Object(); final DatasourceContext dsc1 = (DatasourceContext) datasourceContextService .getCachedDatasourceContextMono( - createdDatasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier) + createdDatasourceStorage, + emptyPlugin, + spyMockPluginExecutor, + monitor, + datasourceContextIdentifier) .block(); assertNotNull(dsc1); assertTrue(dsc1.getConnection() instanceof UpdatableConnection); @@ -461,7 +472,11 @@ public class DatasourceContextServiceTest { final DatasourceContext dsc2 = (DatasourceContext) datasourceContextService .getCachedDatasourceContextMono( - createdDatasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier) + createdDatasourceStorage, + emptyPlugin, + spyMockPluginExecutor, + monitor, + datasourceContextIdentifier) .block(); assertNotNull(dsc2); assertTrue(dsc2.getConnection() instanceof UpdatableConnection); @@ -479,6 +494,7 @@ public class DatasourceContextServiceTest { public void testDatasourceContextIsInvalid_whenCachedDatasourceContextMono_isInErrorState() { doReturn(false).when(datasourceContextService).getIsStale(any(), any()); + Plugin emptyPlugin = new Plugin(); MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); MockPluginExecutor spyMockPluginExecutor = spy(mockPluginExecutor); @@ -499,7 +515,7 @@ public class DatasourceContextServiceTest { Mono> failedDatasourceContextMono = datasourceContextService.getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier); + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier); StepVerifier.create(failedDatasourceContextMono) .expectError(RuntimeException.class) @@ -512,14 +528,14 @@ public class DatasourceContextServiceTest { /** * This test verifies that if a cached datasource context Mono goes to an error state, then that Mono is invalidated * and a new datasource context mono is created on calling - * {@link com.appsmith.server.services.ce.DatasourceContextServiceCEImpl#getCachedDatasourceContextMono(DatasourceStorage, PluginExecutor, Object, DatasourceContextIdentifier)} + * {@link com.appsmith.server.services.ce.DatasourceContextServiceCEImpl#getCachedDatasourceContextMono(DatasourceStorage, Plugin, PluginExecutor, Object, DatasourceContextIdentifier)} * and not fetched from the cache. */ @Test @WithUserDetails(value = "api_user") public void testNewDatasourceContextCreate_whenCachedDatasourceContextMono_isInErrorState() { doReturn(false).doReturn(false).when(datasourceContextService).getIsStale(any(), any()); - + Plugin emptyPlugin = new Plugin(); MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); MockPluginExecutor spyMockPluginExecutor = spy(mockPluginExecutor); @@ -541,13 +557,13 @@ public class DatasourceContextServiceTest { Mono> failedDatasourceContextMono = datasourceContextService.getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier); + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier); StepVerifier.create(failedDatasourceContextMono) .expectError(RuntimeException.class) .verify(); Mono> validDatasourceContextMono = datasourceContextService.getCachedDatasourceContextMono( - datasourceStorage, spyMockPluginExecutor, monitor, datasourceContextIdentifier); + datasourceStorage, emptyPlugin, spyMockPluginExecutor, monitor, datasourceContextIdentifier); StepVerifier.create(validDatasourceContextMono) .assertNext(validDatasourceContext -> @@ -645,4 +661,66 @@ public class DatasourceContextServiceTest { }) .verifyComplete(); } + + @Test + @WithUserDetails(value = "api_user") + public void verifyDatasourceContextHasRightCredentialsAfterVariableReplacement() { + String datasourceId = "datasourceId"; + String environmentId = "environmentId"; + + Plugin restApiPlugin = pluginService + .findByPackageName(PluginConstants.PackageName.REST_API_PLUGIN) + .block(); + String primaryBearerToken = "bearerToken1"; + BearerTokenAuth authenticationDTO = new BearerTokenAuth(); + authenticationDTO.setBearerToken(primaryBearerToken); + DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setAuthentication(authenticationDTO); + + DatasourceStorage datasourceStorage = new DatasourceStorage(); + datasourceStorage.setDatasourceId(datasourceId); + datasourceStorage.setEnvironmentId(environmentId); + datasourceStorage.setDatasourceConfiguration(datasourceConfiguration); + datasourceStorage.setPluginId(restApiPlugin.getId()); + datasourceStorage.setPluginName(restApiPlugin.getPluginName()); + + MockPluginExecutor mockPluginExecutor = new MockPluginExecutor(); + MockPluginExecutor spyMockPluginExecutor = spy(mockPluginExecutor); + + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())) + .thenReturn(Mono.just(spyMockPluginExecutor)); + + doReturn(APIConnectionFactory.createConnection(datasourceStorage.getDatasourceConfiguration())) + .when(spyMockPluginExecutor) + .datasourceCreate(any()); + + Mono> datasourceContextMono = + datasourceContextService.getDatasourceContext(datasourceStorage, restApiPlugin); + StepVerifier.create(datasourceContextMono) + .assertNext(datasourceContext -> { + assertThat(datasourceContext.getConnection()).isInstanceOf(APIConnection.class); + assertThat(((BearerTokenAuthentication) datasourceContext.getConnection()).getBearerToken()) + .isEqualTo(primaryBearerToken); + }) + .verifyComplete(); + + String updatedBearerToken = "bearerToken2"; + BearerTokenAuth bearerTokenAuth = new BearerTokenAuth(); + bearerTokenAuth.setBearerToken(updatedBearerToken); + datasourceStorage.getDatasourceConfiguration().setAuthentication(bearerTokenAuth); + + doReturn(APIConnectionFactory.createConnection(datasourceStorage.getDatasourceConfiguration())) + .when(spyMockPluginExecutor) + .datasourceCreate(any()); + + Mono> datasourceContextMono2 = + datasourceContextService.getDatasourceContext(datasourceStorage, restApiPlugin); + StepVerifier.create(datasourceContextMono) + .assertNext(datasourceContext -> { + assertThat(datasourceContext.getConnection()).isInstanceOf(APIConnection.class); + assertThat(((BearerTokenAuthentication) datasourceContext.getConnection()).getBearerToken()) + .isEqualTo(updatedBearerToken); + }) + .verifyComplete(); + } } From 15f7580d87bb799f734d6276af42770cb5630596 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:41:40 +0530 Subject: [PATCH 33/48] fix: Check for parent widget existence to render drop target (#28275) In the legacy architecture, List Widget meta first item was rendering in edit mode allowing DnD, resize, etc while the rest of the items were rendering in view mode restricting any editing experience. The first item was being used as a template to decide what the template was going to be for rest of the items. However after the overhaul of BaseWidget and CanvasWidget render mode is always EDIT or VIEW for all widgets so DropTarget had to render a meta canvas widget which it was not written to handle. so have added checks to make sure DropTarget does not render and wrap widgets that do not have a parent. Ideally this should have been caught in the CI, there are tests already but the checks were happening to check if List widget was allowed inside another List widget but the other items that were rendering in view mode were not being asserted. I have added tests to check if all nested widgets are properly rendered and there is no "Oops, something went wrong" error. This should make sure this issue does not get past CI in the future. (cherry picked from commit 6355e7a697da9b0eaebe583c47dc752c5449b718) --- .../Widgets/ListV2/ListV2_NestedList_spec.ts | 9 +++++++++ app/client/cypress/fixtures/listV2NestedDsl.json | 2 +- .../common/dropTarget/DropTargetComponentWrapper.tsx | 10 +++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts index 0bd956001d..5b0d9269cf 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts @@ -1,3 +1,4 @@ +import { WIDGET } from "../../../../../locators/WidgetLocators"; import { agHelper, entityExplorer, @@ -13,6 +14,14 @@ describe("Nested List widget V2 ", () => { }); it("1. Verify only 3 levels of nesting is allowed", () => { + agHelper.AssertContains( + "Oops, Something went wrong.", + "not.exist", + locators._widgetInCanvas(WIDGET.LIST_V2), + ); + agHelper + .GetElement(locators._widgetInCanvas(WIDGET.LIST_V2)) + .should("have.length", 5); entityExplorer.SelectEntityByName("List1", "Widgets"); entityExplorer.SelectEntityByName("Container1", "List1"); entityExplorer.SelectEntityByName("List2", "Container1"); diff --git a/app/client/cypress/fixtures/listV2NestedDsl.json b/app/client/cypress/fixtures/listV2NestedDsl.json index 8d8948cea5..d2f772e7c9 100644 --- a/app/client/cypress/fixtures/listV2NestedDsl.json +++ b/app/client/cypress/fixtures/listV2NestedDsl.json @@ -1238,7 +1238,7 @@ } ], "displayName": "List", - "bottomRow": 48, + "bottomRow": 88, "parentRowSpace": 10, "hideCard": false, "templateBottomRow": 16, diff --git a/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx b/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx index 45ec5947a6..ecc048b180 100644 --- a/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx +++ b/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx @@ -3,6 +3,10 @@ import type { DropTargetComponentProps } from "layoutSystems/common/dropTarget/D import type { ReactNode } from "react"; import { memo } from "react"; import React from "react"; +import { useSelector } from "react-redux"; +import { getWidget } from "sagas/selectors"; +import type { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; interface DropTargetComponentWrapperProps { dropTargetProps: DropTargetComponentProps; @@ -22,7 +26,11 @@ export const DropTargetComponentWrapper = memo( dropDisabled, dropTargetProps, }: DropTargetComponentWrapperProps) => { - if (dropDisabled) { + // This code block is added exclusively to handle List Widget Meta Canvas Widget which is generated via template. + const widget = useSelector((state: AppState) => + getWidget(state, dropTargetProps.parentId || MAIN_CONTAINER_WIDGET_ID), + ); + if ((dropTargetProps.parentId && !widget) || dropDisabled) { //eslint-disable-next-line return <>{children}; } From 1c2e7915107e6ca90c6d3a2aea0a17f34d1134a6 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Sat, 21 Oct 2023 09:58:40 +0530 Subject: [PATCH 34/48] chore: Remove RTS Health check in Docker container --- deploy/docker/fs/opt/appsmith/healthcheck.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/deploy/docker/fs/opt/appsmith/healthcheck.sh b/deploy/docker/fs/opt/appsmith/healthcheck.sh index 7c6dc058d8..52028f05ee 100644 --- a/deploy/docker/fs/opt/appsmith/healthcheck.sh +++ b/deploy/docker/fs/opt/appsmith/healthcheck.sh @@ -20,11 +20,6 @@ while read -r line echo 'ERROR: Server is down'; healthy=false fi - elif [[ "$process" == "rts" ]]; then - if [[ $(curl -s -w "%{http_code}\n" http://localhost:8091/ -o /dev/null) -ne 302 ]]; then - echo 'ERROR: RTS is down'; - healthy=false - fi elif [[ "$process" == "mongo" ]]; then if [[ $(mongo --eval 'db.runCommand("ping").ok') -ne 1 ]]; then echo 'ERROR: Mongo is down'; From ccc8ad32d68800b986640b827c746ceb19b4951a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:41:40 +0530 Subject: [PATCH 35/48] fix: Check for parent widget existence to render drop target (#28275) In the legacy architecture, List Widget meta first item was rendering in edit mode allowing DnD, resize, etc while the rest of the items were rendering in view mode restricting any editing experience. The first item was being used as a template to decide what the template was going to be for rest of the items. However after the overhaul of BaseWidget and CanvasWidget render mode is always EDIT or VIEW for all widgets so DropTarget had to render a meta canvas widget which it was not written to handle. so have added checks to make sure DropTarget does not render and wrap widgets that do not have a parent. Ideally this should have been caught in the CI, there are tests already but the checks were happening to check if List widget was allowed inside another List widget but the other items that were rendering in view mode were not being asserted. I have added tests to check if all nested widgets are properly rendered and there is no "Oops, something went wrong" error. This should make sure this issue does not get past CI in the future. (cherry picked from commit 6355e7a697da9b0eaebe583c47dc752c5449b718) --- .../Widgets/ListV2/ListV2_NestedList_spec.ts | 9 +++++++++ app/client/cypress/fixtures/listV2NestedDsl.json | 2 +- .../common/dropTarget/DropTargetComponentWrapper.tsx | 10 +++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts index 0bd956001d..5b0d9269cf 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/ListV2_NestedList_spec.ts @@ -1,3 +1,4 @@ +import { WIDGET } from "../../../../../locators/WidgetLocators"; import { agHelper, entityExplorer, @@ -13,6 +14,14 @@ describe("Nested List widget V2 ", () => { }); it("1. Verify only 3 levels of nesting is allowed", () => { + agHelper.AssertContains( + "Oops, Something went wrong.", + "not.exist", + locators._widgetInCanvas(WIDGET.LIST_V2), + ); + agHelper + .GetElement(locators._widgetInCanvas(WIDGET.LIST_V2)) + .should("have.length", 5); entityExplorer.SelectEntityByName("List1", "Widgets"); entityExplorer.SelectEntityByName("Container1", "List1"); entityExplorer.SelectEntityByName("List2", "Container1"); diff --git a/app/client/cypress/fixtures/listV2NestedDsl.json b/app/client/cypress/fixtures/listV2NestedDsl.json index 8d8948cea5..d2f772e7c9 100644 --- a/app/client/cypress/fixtures/listV2NestedDsl.json +++ b/app/client/cypress/fixtures/listV2NestedDsl.json @@ -1238,7 +1238,7 @@ } ], "displayName": "List", - "bottomRow": 48, + "bottomRow": 88, "parentRowSpace": 10, "hideCard": false, "templateBottomRow": 16, diff --git a/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx b/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx index 45ec5947a6..ecc048b180 100644 --- a/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx +++ b/app/client/src/layoutSystems/common/dropTarget/DropTargetComponentWrapper.tsx @@ -3,6 +3,10 @@ import type { DropTargetComponentProps } from "layoutSystems/common/dropTarget/D import type { ReactNode } from "react"; import { memo } from "react"; import React from "react"; +import { useSelector } from "react-redux"; +import { getWidget } from "sagas/selectors"; +import type { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; interface DropTargetComponentWrapperProps { dropTargetProps: DropTargetComponentProps; @@ -22,7 +26,11 @@ export const DropTargetComponentWrapper = memo( dropDisabled, dropTargetProps, }: DropTargetComponentWrapperProps) => { - if (dropDisabled) { + // This code block is added exclusively to handle List Widget Meta Canvas Widget which is generated via template. + const widget = useSelector((state: AppState) => + getWidget(state, dropTargetProps.parentId || MAIN_CONTAINER_WIDGET_ID), + ); + if ((dropTargetProps.parentId && !widget) || dropDisabled) { //eslint-disable-next-line return <>{children}; } From 10386e375933e819267c12609981566a3a88f0e9 Mon Sep 17 00:00:00 2001 From: Anagh Hegde Date: Wed, 25 Oct 2023 19:09:18 +0530 Subject: [PATCH 36/48] fix: Add support for jackson with java time modules (#28347) ## Description Add support for java Instant new Java8 Date API datatype modules in Jackson. #### PR fixes following issue(s) Fixes https://github.com/appsmithorg/appsmith/issues/28350 #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [ ] Manual - [ ] JUnit ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed (cherry picked from commit ecf0b9bd2aab1efa2d787f9f8ecc5c8c9c30c677) --- .../server/services/ApplicationTemplateServiceImpl.java | 7 +++++-- .../services/ce/ApplicationTemplateServiceCEImpl.java | 8 ++++++-- .../services/ApplicationTemplateServiceUnitTest.java | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationTemplateServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationTemplateServiceImpl.java index b215c32a87..edee6d4ca8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationTemplateServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationTemplateServiceImpl.java @@ -7,6 +7,7 @@ import com.appsmith.server.imports.internal.ImportApplicationService; import com.appsmith.server.services.ce.ApplicationTemplateServiceCEImpl; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.ReleaseNotesService; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -24,7 +25,8 @@ public class ApplicationTemplateServiceImpl extends ApplicationTemplateServiceCE UserDataService userDataService, ApplicationService applicationService, ResponseUtils responseUtils, - ApplicationPermission applicationPermission) { + ApplicationPermission applicationPermission, + ObjectMapper objectMapper) { super( cloudServicesConfig, releaseNotesService, @@ -34,6 +36,7 @@ public class ApplicationTemplateServiceImpl extends ApplicationTemplateServiceCE userDataService, applicationService, responseUtils, - applicationPermission); + applicationPermission, + objectMapper); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java index 28f81aeffb..011d927956 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java @@ -56,6 +56,8 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ private final ResponseUtils responseUtils; private final ApplicationPermission applicationPermission; + private final ObjectMapper objectMapper; + public ApplicationTemplateServiceCEImpl( CloudServicesConfig cloudServicesConfig, ReleaseNotesService releaseNotesService, @@ -65,7 +67,8 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ UserDataService userDataService, ApplicationService applicationService, ResponseUtils responseUtils, - ApplicationPermission applicationPermission) { + ApplicationPermission applicationPermission, + ObjectMapper objectMapper) { this.cloudServicesConfig = cloudServicesConfig; this.releaseNotesService = releaseNotesService; this.importApplicationService = importApplicationService; @@ -75,6 +78,7 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ this.applicationService = applicationService; this.responseUtils = responseUtils; this.applicationPermission = applicationPermission; + this.objectMapper = objectMapper; } @Override @@ -337,7 +341,7 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ String authHeader = "Authorization"; String payload; try { - ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + ObjectWriter ow = objectMapper.writer().withDefaultPrettyPrinter(); payload = ow.writeValueAsString(communityTemplate); } catch (Exception e) { return Mono.error(e); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceUnitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceUnitTest.java index 543dab3787..6b9f492f79 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceUnitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceUnitTest.java @@ -95,7 +95,8 @@ public class ApplicationTemplateServiceUnitTest { userDataService, applicationService, responseUtils, - applicationPermission); + applicationPermission, + objectMapper); } private ApplicationTemplate create(String id, String title) { From 22a6144e082bd6219b9138cc08b0d9da39580160 Mon Sep 17 00:00:00 2001 From: Manish Kumar <107841575+sondermanish@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:02:13 +0530 Subject: [PATCH 37/48] chore: modified server schema version (#28358) ## Description > fixes xml parser Fixes: https://github.com/appsmithorg/appsmith/issues/28364 --- .../CustomJSLibImportableServiceCEImpl.java | 29 --------------- .../migrations/JsonSchemaMigration.java | 3 ++ .../server/migrations/JsonSchemaVersions.java | 2 +- .../migrations/MigrationHelperMethods.java | 35 +++++++++++++++++++ 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/jslibs/imports/CustomJSLibImportableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/jslibs/imports/CustomJSLibImportableServiceCEImpl.java index a8140fde10..526d30c3d0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/jslibs/imports/CustomJSLibImportableServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/jslibs/imports/CustomJSLibImportableServiceCEImpl.java @@ -1,6 +1,5 @@ package com.appsmith.server.jslibs.imports; -import com.appsmith.server.constants.ApplicationConstants; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.CustomJSLib; import com.appsmith.server.domains.Workspace; @@ -36,8 +35,6 @@ public class CustomJSLibImportableServiceCEImpl implements ImportableServiceCE(); } - ensureXmlParserPresenceInCustomJsLibList(customJSLibs); - return Flux.fromIterable(customJSLibs) .flatMap(customJSLib -> { customJSLib.setId(null); @@ -60,30 +57,4 @@ public class CustomJSLibImportableServiceCEImpl implements ImportableServiceCE customJSLibList) { - boolean isXmlParserLibFound = false; - for (CustomJSLib customJSLib : customJSLibList) { - if (!customJSLib.getUidString().equals(ApplicationConstants.XML_PARSER_LIBRARY_UID)) { - continue; - } - - isXmlParserLibFound = true; - break; - } - - if (!isXmlParserLibFound) { - CustomJSLib xmlParserJsLib = ApplicationConstants.getDefaultParserCustomJsLibCompatibilityDTO(); - customJSLibList.add(xmlParserJsLib); - } - } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaMigration.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaMigration.java index d2b53919c8..2a0a5b6df4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaMigration.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaMigration.java @@ -66,6 +66,9 @@ public class JsonSchemaMigration { case 5: MigrationHelperMethods.migrateGoogleSheetsActionsToUqi(applicationJson); applicationJson.setServerSchemaVersion(6); + case 6: + MigrationHelperMethods.ensureXmlParserPresenceInCustomJsLibList(applicationJson); + applicationJson.setServerSchemaVersion(7); default: // Unable to detect the serverSchema } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaVersions.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaVersions.java index 7ce889a98f..2b81d9a020 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaVersions.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/JsonSchemaVersions.java @@ -12,6 +12,6 @@ import lombok.Getter; */ @Getter public class JsonSchemaVersions { - public static final Integer serverVersion = 6; + public static final Integer serverVersion = 7; public static final Integer clientVersion = 1; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/MigrationHelperMethods.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/MigrationHelperMethods.java index a361eb3965..b7448f8491 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/MigrationHelperMethods.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/MigrationHelperMethods.java @@ -3,9 +3,11 @@ package com.appsmith.server.migrations; import com.appsmith.external.models.ActionDTO; import com.appsmith.external.models.BaseDomain; import com.appsmith.external.models.InvisibleActionFields; +import com.appsmith.server.constants.ApplicationConstants; import com.appsmith.server.constants.FieldName; import com.appsmith.server.constants.ResourceModes; import com.appsmith.server.domains.ApplicationPage; +import com.appsmith.server.domains.CustomJSLib; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.QUser; @@ -256,4 +258,37 @@ public class MigrationHelperMethods { where(FieldName.DELETED_AT).exists(false), where(FieldName.DELETED_AT).is(null))); } + + /** + * This method takes customJSLibList from application JSON, checks if an entry for XML parser exists, + * otherwise adds the entry. + * This has been done to add the xmlParser entry in imported application as appsmith is stopping native support + * for xml parser. + * Read More: https://github.com/appsmithorg/appsmith/pull/28012 + * + * @param applicationJson + */ + public static void ensureXmlParserPresenceInCustomJsLibList(ApplicationJson applicationJson) { + + if (CollectionUtils.isNullOrEmpty(applicationJson.getCustomJSLibList())) { + applicationJson.setCustomJSLibList(new ArrayList<>()); + } + + List customJSLibList = applicationJson.getCustomJSLibList(); + boolean isXmlParserLibFound = false; + + for (CustomJSLib customJSLib : customJSLibList) { + if (!customJSLib.getUidString().equals(ApplicationConstants.XML_PARSER_LIBRARY_UID)) { + continue; + } + + isXmlParserLibFound = true; + break; + } + + if (!isXmlParserLibFound) { + CustomJSLib xmlParserJsLib = ApplicationConstants.getDefaultParserCustomJsLibCompatibilityDTO(); + customJSLibList.add(xmlParserJsLib); + } + } } From b6fde7c6ed15351399937dc5db7f562fa91b566b Mon Sep 17 00:00:00 2001 From: Nidhi Date: Thu, 26 Oct 2023 14:37:43 +0530 Subject: [PATCH 38/48] chore: Fixes imported action refs getting reset to null in layouts (#28394) --- ...tionCollectionImportableServiceCEImpl.java | 1 + .../NewActionImportableServiceCEImpl.java | 1 + .../NewPageImportableServiceCEImpl.java | 117 +++++++++--------- 3 files changed, 58 insertions(+), 61 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/imports/ActionCollectionImportableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/imports/ActionCollectionImportableServiceCEImpl.java index 28d7a16ac6..4991788632 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/imports/ActionCollectionImportableServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/imports/ActionCollectionImportableServiceCEImpl.java @@ -74,6 +74,7 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic if (importingMetaDTO.getAppendToApp()) { importedActionCollectionMono = importedActionCollectionMono.map(importedActionCollectionList1 -> { List importedNewPages = mappedImportableResourcesDTO.getPageNameMap().values().stream() + .distinct() .toList(); Map newToOldNameMap = mappedImportableResourcesDTO.getNewPageNameToOldPageNameMap(); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/imports/NewActionImportableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/imports/NewActionImportableServiceCEImpl.java index c5598d40a6..0fcce1f9fe 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/imports/NewActionImportableServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/imports/NewActionImportableServiceCEImpl.java @@ -70,6 +70,7 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE { List importedNewPages = mappedImportableResourcesDTO.getPageNameMap().values().stream() + .distinct() .toList(); Map newToOldNameMap = mappedImportableResourcesDTO.getNewPageNameToOldPageNameMap(); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/imports/NewPageImportableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/imports/NewPageImportableServiceCEImpl.java index eecca835f6..03537ddc91 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/imports/NewPageImportableServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/imports/NewPageImportableServiceCEImpl.java @@ -117,8 +117,9 @@ public class NewPageImportableServiceCEImpl implements ImportableServiceCE newPages = - mappedImportableResourcesDTO.getPageNameMap().values().stream().toList(); + List newPages = mappedImportableResourcesDTO.getPageNameMap().values().stream() + .distinct() + .toList(); return Flux.fromIterable(newPages) .flatMap(newPage -> { if (newPage.getDefaultResources() != null) { @@ -564,65 +565,57 @@ public class NewPageImportableServiceCEImpl implements ImportableServiceCE> unpublishedActionIdToCollectionIdsMap, Map> publishedActionIdToCollectionIdsMap) { - return Mono.just(newPage) - .flatMap(page -> { - return newActionService - .findAllById(getLayoutOnLoadActionsForPage( - page, - actionIdMap, - unpublishedActionIdToCollectionIdsMap, - publishedActionIdToCollectionIdsMap)) - .map(newAction -> { - final String defaultActionId = - newAction.getDefaultResources().getActionId(); - if (page.getUnpublishedPage().getLayouts() != null) { - final String defaultCollectionId = newAction - .getUnpublishedAction() - .getDefaultResources() - .getCollectionId(); - page.getUnpublishedPage().getLayouts().forEach(layout -> { - if (layout.getLayoutOnLoadActions() != null) { - layout.getLayoutOnLoadActions() - .forEach(onLoadAction -> onLoadAction.stream() - .filter(actionDTO -> StringUtils.equals( - actionDTO.getId(), newAction.getId())) - .forEach(actionDTO -> { - actionDTO.setDefaultActionId(defaultActionId); - actionDTO.setDefaultCollectionId(defaultCollectionId); - })); - } - }); - } + Set layoutOnLoadActionsForPage = getLayoutOnLoadActionsForPage( + newPage, actionIdMap, unpublishedActionIdToCollectionIdsMap, publishedActionIdToCollectionIdsMap); - if (page.getPublishedPage() != null - && page.getPublishedPage().getLayouts() != null) { - page.getPublishedPage().getLayouts().forEach(layout -> { - if (layout.getLayoutOnLoadActions() != null) { - layout.getLayoutOnLoadActions() - .forEach(onLoadAction -> onLoadAction.stream() - .filter(actionDTO -> StringUtils.equals( - actionDTO.getId(), newAction.getId())) - .forEach(actionDTO -> { - actionDTO.setDefaultActionId(defaultActionId); - if (newAction.getPublishedAction() != null - && newAction - .getPublishedAction() - .getDefaultResources() - != null) { - actionDTO.setDefaultCollectionId(newAction - .getPublishedAction() - .getDefaultResources() - .getCollectionId()); - } - })); - } - }); - } - return newAction; - }) - .collectList() - .thenReturn(page); + return newActionService + .findAllById(layoutOnLoadActionsForPage) + .map(newAction -> { + final String defaultActionId = + newAction.getDefaultResources().getActionId(); + if (newPage.getUnpublishedPage().getLayouts() != null) { + final String defaultCollectionId = newAction + .getUnpublishedAction() + .getDefaultResources() + .getCollectionId(); + newPage.getUnpublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(onLoadAction -> onLoadAction.stream() + .filter(actionDTO -> StringUtils.equals(actionDTO.getId(), newAction.getId())) + .forEach(actionDTO -> { + actionDTO.setDefaultActionId(defaultActionId); + actionDTO.setDefaultCollectionId(defaultCollectionId); + })); + } + }); + } + + if (newPage.getPublishedPage() != null + && newPage.getPublishedPage().getLayouts() != null) { + newPage.getPublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(onLoadAction -> onLoadAction.stream() + .filter(actionDTO -> StringUtils.equals(actionDTO.getId(), newAction.getId())) + .forEach(actionDTO -> { + actionDTO.setDefaultActionId(defaultActionId); + if (newAction.getPublishedAction() != null + && newAction + .getPublishedAction() + .getDefaultResources() + != null) { + actionDTO.setDefaultCollectionId(newAction + .getPublishedAction() + .getDefaultResources() + .getCollectionId()); + } + })); + } + }); + } + return newAction; }) + .collectList() + .thenReturn(newPage) .onErrorResume(error -> { log.error("Error while updating action collection id in page layout", error); return Mono.error(error); @@ -641,7 +634,8 @@ public class NewPageImportableServiceCEImpl implements ImportableServiceCE onLoadAction.forEach(actionDTO -> { - actionDTO.setId(actionIdMap.get(actionDTO.getId())); + String oldActionDTOId = actionDTO.getId(); + actionDTO.setId(actionIdMap.get(oldActionDTOId)); if (!CollectionUtils.sizeIsEmpty(unpublishedActionIdToCollectionIdsMap) && !CollectionUtils.isEmpty( unpublishedActionIdToCollectionIdsMap.get(actionDTO.getId()))) { @@ -661,7 +655,8 @@ public class NewPageImportableServiceCEImpl implements ImportableServiceCE onLoadAction.forEach(actionDTO -> { - actionDTO.setId(actionIdMap.get(actionDTO.getId())); + String oldActionDTOId = actionDTO.getId(); + actionDTO.setId(actionIdMap.get(oldActionDTOId)); if (!CollectionUtils.sizeIsEmpty(publishedActionIdToCollectionIdsMap) && !CollectionUtils.isEmpty( publishedActionIdToCollectionIdsMap.get(actionDTO.getId()))) { From e99d854618b7ccd81cd50ff0a61ea73d21097ab2 Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 26 Oct 2023 15:03:57 +0600 Subject: [PATCH 39/48] fix: Analytics events not fired during export application (#28372) ## Description #### PR fixes following issue(s) Fixes #28369 --- .../ExportApplicationServiceCEImpl.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/ExportApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/ExportApplicationServiceCEImpl.java index faf47a905d..2fe5fe6515 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/ExportApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/ExportApplicationServiceCEImpl.java @@ -174,7 +174,7 @@ public class ExportApplicationServiceCEImpl implements ExportApplicationServiceC }); }) .then(sessionUserService.getCurrentUser()) - .map(user -> { + .flatMap(user -> { stopwatch.stopTimer(); final Map data = Map.of( FieldName.APPLICATION_ID, @@ -189,12 +189,11 @@ public class ExportApplicationServiceCEImpl implements ExportApplicationServiceC stopwatch.getFlow(), "executionTime", stopwatch.getExecutionTime()); - analyticsService.sendEvent( - AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), user.getUsername(), data); - return applicationJson; + return analyticsService + .sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), user.getUsername(), data) + .thenReturn(applicationJson); }) - .then(applicationMono) - .map(application -> sendImportExportApplicationAnalyticsEvent(application, AnalyticsEvents.EXPORT)) + .flatMap(unused -> sendImportExportApplicationAnalyticsEvent(applicationId, AnalyticsEvents.EXPORT)) .thenReturn(applicationJson); } @@ -226,23 +225,24 @@ public class ExportApplicationServiceCEImpl implements ExportApplicationServiceC /** * To send analytics event for import and export of application * - * @param application Application object imported or exported + * @param applicationId String application id * @param event AnalyticsEvents event * @return The application which is imported or exported */ - private Mono sendImportExportApplicationAnalyticsEvent( - Application application, AnalyticsEvents event) { - return workspaceService.getById(application.getWorkspaceId()).flatMap(workspace -> { - final Map eventData = Map.of( - FieldName.APPLICATION, application, - FieldName.WORKSPACE, workspace); + private Mono sendImportExportApplicationAnalyticsEvent(String applicationId, AnalyticsEvents event) { + return applicationService.findById(applicationId).flatMap(application -> workspaceService + .getById(application.getWorkspaceId()) + .flatMap(workspace -> { + final Map eventData = Map.of( + FieldName.APPLICATION, application, + FieldName.WORKSPACE, workspace); - final Map data = Map.of( - FieldName.APPLICATION_ID, application.getId(), - FieldName.WORKSPACE_ID, workspace.getId(), - FieldName.EVENT_DATA, eventData); + final Map data = Map.of( + FieldName.APPLICATION_ID, application.getId(), + FieldName.WORKSPACE_ID, workspace.getId(), + FieldName.EVENT_DATA, eventData); - return analyticsService.sendObjectEvent(event, application, data); - }); + return analyticsService.sendObjectEvent(event, application, data); + })); } } From bcd7d471c68274bf08db2a89929dff59ae5af662 Mon Sep 17 00:00:00 2001 From: Anagh Hegde Date: Mon, 30 Oct 2023 21:08:01 +0530 Subject: [PATCH 40/48] fix: replace default object mapper due to the serilisation of Json views (#28484) --- .../services/ce/ApplicationTemplateServiceCEImpl.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java index ee8ac1b7bb..107a4c79ae 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCEImpl.java @@ -21,6 +21,7 @@ import com.appsmith.server.solutions.ReleaseNotesService; import com.appsmith.util.WebClientUtils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -343,8 +344,13 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ String authHeader = "Authorization"; String payload; try { - ObjectWriter ow = objectMapper.writer().withDefaultPrettyPrinter(); - payload = ow.writeValueAsString(communityTemplate); + // Please don't use the default ObjectMapper. + // The default mapper is registered with views.public.class and removes few attributes due to this + // The templates flow has different requirement hence not using the same + ObjectMapper ow = new ObjectMapper(); + ow.registerModule(new JavaTimeModule()); + ObjectWriter writer = ow.writer().withDefaultPrettyPrinter(); + payload = writer.writeValueAsString(communityTemplate); } catch (Exception e) { return Mono.error(e); } From dfcfc49672a62efd4eb408b4a20ca3755647ede6 Mon Sep 17 00:00:00 2001 From: Rudraprasad Das Date: Tue, 31 Oct 2023 15:28:26 +0530 Subject: [PATCH 41/48] fix: adding missing `fetch` api with `status` call (#28470) ## Description Does `fetch/remote` first and then `/status` on canvas load #### PR fixes following issue(s) Fixes #28462 #### Media #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../ClientSide/BugTests/GitBugs_Spec.ts | 61 +--------- .../Git/GitSync/GitStatusLite_spec.ts | 105 ++++++++++++++++++ .../src/entities/Engine/AppEditorEngine.ts | 17 ++- .../src/pages/Editor/gitSync/Tabs/Deploy.tsx | 2 +- .../src/pages/Editor/gitSync/Tabs/Merge.tsx | 12 +- app/client/src/sagas/GitSyncSagas.ts | 15 ++- 6 files changed, 148 insertions(+), 64 deletions(-) create mode 100644 app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts index 009b5e09c1..6234eb0e30 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts @@ -1,4 +1,3 @@ -import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags"; import * as _ from "../../../../support/Objects/ObjectsCore"; let repoName: any; @@ -144,60 +143,8 @@ describe("Git Bugs", function () { _.gitSync.SwitchGitBranch("origin/test-24486", false, true); }); - it("7. Bug 26038 - 1 : Simultaneous git status and remote compare api calls", function () { - featureFlagIntercept({ - release_git_status_lite_enabled: true, - }); - - cy.wait(1000); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/status/app/**", - query: { compareRemote: "false" }, - }).as("gitStatusApi"); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/fetch/remote/app/**", - }).as("gitRemoteStatusApi"); - - _.agHelper.GetNClick(_.locators._publishButton); - - cy.wait("@gitStatusApi").then((res1) => { - expect(res1.response).to.have.property("statusCode", 200); - cy.wait("@gitRemoteStatusApi").then((res2) => { - expect(res2.response).to.have.property("statusCode", 200); - - _.agHelper.GetNClick(_.locators._dialogCloseButton); - }); - }); - }); - - it("8. Bug 26038 - 2 : Simultaneous git status and remote compare api calls", function () { - featureFlagIntercept({ - release_git_status_lite_enabled: false, - }); - - _.gitSync.SwitchGitBranch("master"); - - cy.wait(1000); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/status/app/**", - query: { compareRemote: "true" }, - }).as("gitStatusApi"); - - _.agHelper.GetNClick(_.locators._publishButton); - - cy.wait("@gitStatusApi").then((res1) => { - expect(res1.response).to.have.property("statusCode", 200); - _.agHelper.GetNClick(_.locators._dialogCloseButton); - }); - }); - - it("9. Bug 24920: Not able to discard app settings changes for the first time in git connected app ", function () { + it("7. Bug 24920: Not able to discard app settings changes for the first time in git connected app ", function () { + _.gitSync.SwitchGitBranch("master", false, true); // add navigation settings changes _.agHelper.GetNClick(_.appSettings.locators._appSettings); _.agHelper.GetNClick(_.appSettings.locators._navigationSettingsTab); @@ -210,7 +157,7 @@ describe("Git Bugs", function () { _.gitSync.VerifyChangeLog(false); }); - it("10. Bug 23858 : Branch list in git sync modal is not scrollable", function () { + it("8. Bug 23858 : Branch list in git sync modal is not scrollable", function () { // create git branches _.gitSync.CreateGitBranch(tempBranch1, true); _.gitSync.CreateGitBranch(tempBranch2, true); @@ -229,7 +176,7 @@ describe("Git Bugs", function () { _.gitSync.CloseGitSyncModal(); }); - it("10. Bug 24206 : Open repository button is not functional in git sync modal", function () { + it("9. Bug 24206 : Open repository button is not functional in git sync modal", function () { _.gitSync.SwitchGitBranch("master"); _.appSettings.OpenPaneAndChangeTheme("Moon"); _.gitSync.CommitAndPush(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts new file mode 100644 index 0000000000..d2ed9ce35c --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts @@ -0,0 +1,105 @@ +import { featureFlagIntercept } from "../../../../../support/Objects/FeatureFlags"; +import * as _ from "../../../../../support/Objects/ObjectsCore"; + +let wsName: string; +let appName: string; +let repoName: any; + +describe("Git Connect V2", function () { + before(() => { + _.agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + wsName = "GitStatusLite" + uid; + appName = "GitStatusLite" + uid; + _.homePage.CreateNewWorkspace(wsName, true); + _.homePage.CreateAppInWorkspace(wsName, appName); + _.gitSync.CreateNConnectToGit(); + cy.get("@gitRepoName").then((repName) => { + repoName = repName; + }); + }); + }); + + it("Issue 26038 - 1 : Simultaneous git status and remote compare api calls on commit modal", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: true, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/fetch/remote/app/**", + }).as("gitRemoteStatusApi"); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "false" }, + }).as("gitStatusApi"); + + _.agHelper.GetNClick(_.locators._publishButton); + + cy.wait("@gitRemoteStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + cy.wait("@gitStatusApi").then((res2) => { + expect(res2.response).to.have.property("statusCode", 200); + + _.agHelper.GetNClick(_.locators._dialogCloseButton); + }); + }); + }); + + it("Issue 26038 - 2 : Simultaneous git status and remote compare api calls on commit modal", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: false, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "true" }, + }).as("gitStatusApi"); + + _.agHelper.GetNClick(_.locators._publishButton); + + cy.wait("@gitStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + _.agHelper.GetNClick(_.locators._dialogCloseButton); + }); + }); + + it("Issue 28462 : Simultaneous git status and remote compare api calls on canvas load", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: true, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/fetch/remote/app/**", + }).as("gitRemoteStatusApi"); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "false" }, + }).as("gitStatusApi"); + + cy.reload(); + + cy.wait("@gitRemoteStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + cy.wait("@gitStatusApi").then((res2) => { + expect(res2.response).to.have.property("statusCode", 200); + }); + }); + }); + + after(() => { + _.gitSync.DeleteTestGithubRepo(repoName); + }); +}); diff --git a/app/client/src/entities/Engine/AppEditorEngine.ts b/app/client/src/entities/Engine/AppEditorEngine.ts index 7b518ec235..c314639146 100644 --- a/app/client/src/entities/Engine/AppEditorEngine.ts +++ b/app/client/src/entities/Engine/AppEditorEngine.ts @@ -7,6 +7,7 @@ import { fetchMockDatasources, } from "actions/datasourceActions"; import { + fetchGitRemoteStatusInit, fetchGitStatusInit, remoteUrlInputValue, resetPullMergeStatus, @@ -42,7 +43,10 @@ import { waitForWidgetConfigBuild, } from "sagas/InitSagas"; import { getCurrentApplication } from "selectors/editorSelectors"; -import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; +import { + getCurrentGitBranch, + getIsGitStatusLiteEnabled, +} from "selectors/gitSyncSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import history from "utils/history"; import PerformanceTracker, { @@ -265,6 +269,9 @@ export default class AppEditorEngine extends AppEngine { public *loadGit(applicationId: string) { const branchInStore: string = yield select(getCurrentGitBranch); + const isGitStatusLiteEnabled: boolean = yield select( + getIsGitStatusLiteEnabled, + ); yield put( restoreRecentEntitiesRequest({ applicationId, @@ -276,7 +283,13 @@ export default class AppEditorEngine extends AppEngine { // add branch query to path and fetch status if (branchInStore) { history.replace(addBranchParam(branchInStore)); - yield put(fetchGitStatusInit({ compareRemote: false })); + + if (isGitStatusLiteEnabled) { + yield put(fetchGitRemoteStatusInit()); + yield put(fetchGitStatusInit({ compareRemote: false })); + } else { + yield put(fetchGitStatusInit({ compareRemote: true })); + } } yield put(resetPullMergeStatus()); } diff --git a/app/client/src/pages/Editor/gitSync/Tabs/Deploy.tsx b/app/client/src/pages/Editor/gitSync/Tabs/Deploy.tsx index 2b5685f17f..4585c28ec5 100644 --- a/app/client/src/pages/Editor/gitSync/Tabs/Deploy.tsx +++ b/app/client/src/pages/Editor/gitSync/Tabs/Deploy.tsx @@ -161,8 +161,8 @@ function Deploy() { useEffect(() => { if (isGitStatusLiteEnabled) { - dispatch(fetchGitStatusInit({ compareRemote: false })); dispatch(fetchGitRemoteStatusInit()); + dispatch(fetchGitStatusInit({ compareRemote: false })); } else { dispatch(fetchGitStatusInit({ compareRemote: true })); } diff --git a/app/client/src/pages/Editor/gitSync/Tabs/Merge.tsx b/app/client/src/pages/Editor/gitSync/Tabs/Merge.tsx index 0bca25a0a6..ceac5e1b15 100644 --- a/app/client/src/pages/Editor/gitSync/Tabs/Merge.tsx +++ b/app/client/src/pages/Editor/gitSync/Tabs/Merge.tsx @@ -22,6 +22,7 @@ import { getGitStatus, getIsFetchingGitStatus, getIsFetchingMergeStatus, + getIsGitStatusLiteEnabled, getIsMergeInProgress, getMergeError, getMergeStatus, @@ -29,6 +30,7 @@ import { import type { DropdownOptions } from "../../GeneratePage/components/constants"; import { fetchBranchesInit, + fetchGitRemoteStatusInit, fetchGitStatusInit, fetchMergeStatusInit, mergeBranchInit, @@ -79,6 +81,7 @@ function MergeSuccessIndicator() { export default function Merge() { const dispatch = useDispatch(); const gitMetaData = useSelector(getCurrentAppGitMetaData); + const isGitStatusLiteEnabled = useSelector(getIsGitStatusLiteEnabled); const gitBranches = useSelector(getGitBranches); const isFetchingBranches = useSelector(getFetchingBranches); const isFetchingMergeStatus = useSelector(getIsFetchingMergeStatus); @@ -172,12 +175,17 @@ export default function Merge() { }, [currentBranch, selectedBranchOption?.value, dispatch]); useEffect(() => { - dispatch(fetchGitStatusInit({ compareRemote: false })); + if (isGitStatusLiteEnabled) { + dispatch(fetchGitRemoteStatusInit()); + dispatch(fetchGitStatusInit({ compareRemote: false })); + } else { + dispatch(fetchGitStatusInit({ compareRemote: true })); + } dispatch(fetchBranchesInit()); return () => { dispatch(resetMergeStatus()); }; - }, []); + }, [isGitStatusLiteEnabled]); useEffect(() => { // when user selects a branch to merge diff --git a/app/client/src/sagas/GitSyncSagas.ts b/app/client/src/sagas/GitSyncSagas.ts index 7f84bdda42..9cad5448bd 100644 --- a/app/client/src/sagas/GitSyncSagas.ts +++ b/app/client/src/sagas/GitSyncSagas.ts @@ -31,7 +31,10 @@ import type { GetSSHKeyResponseData, GitStatusParams, } from "actions/gitSyncActions"; -import { fetchGitRemoteStatusSuccess } from "actions/gitSyncActions"; +import { + fetchGitRemoteStatusInit, + fetchGitRemoteStatusSuccess, +} from "actions/gitSyncActions"; import { commitToRepoSuccess, connectToGitSuccess, @@ -132,6 +135,9 @@ function* commitToGitRepoSaga( let response: ApiResponse | undefined; try { const applicationId: string = yield select(getCurrentApplicationId); + const isGitStatusLiteEnabled: boolean = yield select( + getIsGitStatusLiteEnabled, + ); const gitMetaData: GitApplicationMetadata = yield select( getCurrentAppGitMetaData, ); @@ -159,7 +165,12 @@ function* commitToGitRepoSaga( payload: curApplication, }); } - yield put(fetchGitStatusInit()); + if (isGitStatusLiteEnabled) { + yield put(fetchGitRemoteStatusInit()); + yield put(fetchGitStatusInit({ compareRemote: false })); + } else { + yield put(fetchGitStatusInit({ compareRemote: true })); + } } else { yield put({ type: ReduxActionErrorTypes.COMMIT_TO_GIT_REPO_ERROR, From 37ef5b0ddd8486b2716a416363de74bd08e7dc64 Mon Sep 17 00:00:00 2001 From: Goutham Pratapa Date: Tue, 31 Oct 2023 15:13:34 +0530 Subject: [PATCH 42/48] fix: failing github-release pipeline (#28515) Add Build arguements to github-release build and push pipeline --- .github/workflows/github-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index c4a7caa967..63aba9ca46 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -285,5 +285,6 @@ jobs: platforms: linux/arm64,linux/amd64 build-args: | APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} + BASE=${{ vars.DOCKER_HUB_ORGANIZATION }}/base-${{ vars.EDITION }}:nightly tags: | ${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-${{ vars.EDITION }}:latest From 769f8634b58f96631c774ca8f5301e6f5a8adb59 Mon Sep 17 00:00:00 2001 From: Goutham Pratapa Date: Tue, 31 Oct 2023 16:13:55 +0530 Subject: [PATCH 43/48] fix: update github-release.yml (#28521) --- .github/workflows/github-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 63aba9ca46..da1ada2977 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -272,6 +272,7 @@ jobs: platforms: linux/arm64,linux/amd64 build-args: | APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} + BASE=${{ vars.DOCKER_HUB_ORGANIZATION }}/base-${{ vars.EDITION }}:nightly tags: | ${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-${{ vars.EDITION }}:${{needs.prelude.outputs.tag}} From 9438ae144aae4f98354d47fcc41b2b12d467189b Mon Sep 17 00:00:00 2001 From: Anagh Hegde Date: Fri, 3 Nov 2023 16:27:17 +0530 Subject: [PATCH 44/48] fix: Call out message in git sync modal for JS library is incorrect (#28567) ## Description #### PR fixes following issue(s) Fixes https://github.com/appsmithorg/appsmith/issues/28563 #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [ ] Manual - [ ] JUnit #### Test Plan #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: manish kumar --- .../v1.9.24/DSCrudAndBindings_Spec.ts | 15 ++++++++++----- .../com/appsmith/git/service/GitExecutorImpl.java | 2 -- .../internal/ImportApplicationServiceCEImpl.java | 2 ++ .../solutions/ImportApplicationServiceTests.java | 2 ++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts index 96588134f4..c27fe53dd7 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts @@ -80,11 +80,16 @@ describe("Import and validate older app (app created in older versions of Appsmi // Commenting it as part of #28012 - to be added back later // agHelper.AssertContains(/[0-9] librar(y|ies) modified/, "not.exist"); - agHelper.GetNAssertElementText( - gitSync._gitStatusChanges, - "Some of the changes above are due to an improved file structure designed to reduce merge conflicts. You can safely commit them to your repository.", - "contain.text", - ); + // This assertions is commented out due to issue #https://github.com/appsmithorg/appsmith/issues/28563 + // Since we don't want this specific message appearing when we are just migrating the metadata, + // this assertion is not required. + // Slack conversation: https://theappsmith.slack.com/archives/C04HERDNZPA/p1698851532418569 + + // agHelper.GetNAssertElementText( + // gitSync._gitStatusChanges, + // "Some of the changes above are due to an improved file structure designed to reduce merge conflicts. You can safely commit them to your repository.", + // "contain.text", + // ); agHelper.GetNClick(gitSync._commitButton); assertHelper.AssertNetworkStatus("@commit", 201); gitSync.CloseGitSyncModal(); diff --git a/app/server/appsmith-git/src/main/java/com/appsmith/git/service/GitExecutorImpl.java b/app/server/appsmith-git/src/main/java/com/appsmith/git/service/GitExecutorImpl.java index 2227ce787e..2c00f0a1e3 100644 --- a/app/server/appsmith-git/src/main/java/com/appsmith/git/service/GitExecutorImpl.java +++ b/app/server/appsmith-git/src/main/java/com/appsmith/git/service/GitExecutorImpl.java @@ -552,8 +552,6 @@ public class GitExecutorImpl implements GitExecutor { modifiedDatasources++; } else if (x.contains(GitDirectories.JS_LIB_DIRECTORY + CommonConstants.DELIMITER_PATH)) { modifiedJSLibs++; - } else if (x.equals(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION)) { - response.setMigrationMessage(CommonConstants.FILE_MIGRATION_MESSAGE); } } response.setModified(modifiedAssets); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/ImportApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/ImportApplicationServiceCEImpl.java index ca73be9f74..ed4b5a5e32 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/ImportApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/ImportApplicationServiceCEImpl.java @@ -503,6 +503,8 @@ public class ImportApplicationServiceCEImpl implements ImportApplicationServiceC MappedImportableResourcesDTO mappedImportableResourcesDTO = new MappedImportableResourcesDTO(); Application importedApplication = importedDoc.getExportedApplication(); + importedApplication.setServerSchemaVersion(importedDoc.getServerSchemaVersion()); + importedApplication.setClientSchemaVersion(importedDoc.getClientSchemaVersion()); Mono workspaceMono = workspaceService .findById(workspaceId, permissionProvider.getRequiredPermissionOnTargetWorkspace()) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportApplicationServiceTests.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportApplicationServiceTests.java index 49164f48a6..a4e14f57f8 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportApplicationServiceTests.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportApplicationServiceTests.java @@ -1968,6 +1968,8 @@ public class ImportApplicationServiceTests { final List actionList = tuple.getT2(); assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getServerSchemaVersion()).isNotNull(); + assertThat(application.getClientSchemaVersion()).isNotNull(); List actionNames = new ArrayList<>(); actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); From 13e63d2c6e29e0e1a39374a744af1b553fb8d111 Mon Sep 17 00:00:00 2001 From: Rahul Barwal Date: Thu, 2 Nov 2023 16:25:33 +0530 Subject: [PATCH 45/48] chore: Fixes failing spec, Uses a different template (#28573) ## Description Meeting scheduler is causing problems repeatedly, using another template(Vehicale maintenence app) - as this spec should be template agnostic. #### PR fixes following issue(s) Fixes # (issue number) #### Type of change - Bug fix (non-breaking change which fixes an issue) - Chore (housekeeping or task changes that don't impact user perception) ## Testing #### How Has This Been Tested? - [x] Cypress #### Test Plan #### Issues raised during DP testing ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../Templates/Fork_Template_Existing_app_spec.js | 7 ++----- app/client/cypress/locators/TemplatesLocators.json | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js index 30ce81f8c5..fe1fa175fa 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js @@ -27,10 +27,7 @@ describe( agHelper.AssertElementExist(template.templateDialogBox); agHelper.AssertElementVisibility(templates.locators._templateCard); agHelper.Sleep(4000); - cy.xpath("//h1[text()='Meeting Scheduler']/parent::div") - .scrollIntoView() - .wait(500) - .click(); + agHelper.GetNClick(template.vehicleMaintenenceApp); agHelper.WaitUntilEleDisappear("//*[text()='Loading template details']"); agHelper.Sleep(); agHelper.CheckForErrorToast( @@ -58,7 +55,7 @@ describe( 0, 30000, ); - agHelper.GetNClick(template.meetingSchedulerDashboard); + agHelper.GetNClick(template.vehicleMaintenenceApp); //agHelper.WaitUntilEleDisappear("//*[text()='Loading template details']"); cy.wait("@getTemplatePages").should( "have.nested.property", diff --git a/app/client/cypress/locators/TemplatesLocators.json b/app/client/cypress/locators/TemplatesLocators.json index a35b0ce37d..4ff4ded720 100644 --- a/app/client/cypress/locators/TemplatesLocators.json +++ b/app/client/cypress/locators/TemplatesLocators.json @@ -9,5 +9,5 @@ "selectCheckbox": ".ads-v2-checkbox", "closeButton": "//button[@aria-label='Close']//span", "marketingDashboard": "//h1[text()='Marketing Dashboard']", - "meetingSchedulerDashboard": "//h1[text()='Meeting Scheduler']" + "vehicleMaintenenceApp": "//h1[text()='Vehicle Maintenance App']" } From 3023fcf9550ee49247508fba0f0966db12098db3 Mon Sep 17 00:00:00 2001 From: Somangshu Goswami Date: Mon, 6 Nov 2023 20:12:29 +0530 Subject: [PATCH 46/48] chore: Release v1.9.44 (#28477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a promotion branch for the release v1.9.44 --------- Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Anagh Hegde Co-authored-by: Nilesh Sarupriya Co-authored-by: Nilesh Sarupriya <20905988+nsarupr@users.noreply.github.com> Co-authored-by: Abhinav Jha Co-authored-by: Dhruvik Neharia Co-authored-by: Pawan Kumar Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Nilansh Bansal Co-authored-by: arunvjn <32433245+arunvjn@users.noreply.github.com> Co-authored-by: Aishwarya UR Co-authored-by: Shrikant Sharat Kandula Co-authored-by: Hetu Nandu Co-authored-by: Preet Sidhu Co-authored-by: Nidhi Co-authored-by: Nayan Co-authored-by: Aman Agarwal Co-authored-by: Guilherme Ventura <1488378+danguilherme@users.noreply.github.com> Co-authored-by: Aishwarya-U-R <91450662+Aishwarya-U-R@users.noreply.github.com> Co-authored-by: Ayush Pahwa Co-authored-by: Favour Ohanekwu Co-authored-by: Manish Kumar <107841575+sondermanish@users.noreply.github.com> Co-authored-by: Nikhil Nandagopal Co-authored-by: Saroj <43822041+sarojsarab@users.noreply.github.com> Co-authored-by: sneha122 Co-authored-by: “sneha122” <“sneha@appsmith.com”> Co-authored-by: Ankita Kinger Co-authored-by: danceAndJive <99446612+danceAndJive@users.noreply.github.com> Co-authored-by: Kyle Zhang Co-authored-by: Rishabh Rathod Co-authored-by: sharanya-appsmith <135708039+sharanya-appsmith@users.noreply.github.com> Co-authored-by: Diljit Co-authored-by: Vemparala Surya Vamsi <121419957+vsvamsi1@users.noreply.github.com> Co-authored-by: Rahul Barwal Co-authored-by: Jacques Ikot Co-authored-by: Satish Gandham Co-authored-by: Satish Gandham Co-authored-by: Victor Kostyuk Co-authored-by: Rudraprasad Das Co-authored-by: manish kumar --- .github/config.json | 2 +- README.md | 13 +- .../Regression/Apps/CommunityIssues_Spec.ts | 4 +- .../e2e/Regression/Apps/EchoApiCMS_spec.js | 37 +-- .../ActionSelector_JsToNonJSMode_2_spec.ts | 7 +- .../AdminSettings/Admin_settings_spec.js | 2 +- .../AppNavigation/AppNavigation_spec.ts | 4 +- .../ClientSide/AppNavigation/Sidebar_spec.ts | 4 +- .../AppNavigation/TopInline_spec.ts | 6 +- .../AppNavigation/TopStacked_spec.ts | 4 +- .../Autocomplete/Autocomplete_setters_spec.ts | 24 +- .../ClientSide/Autocomplete/Bugs_AC_Spec.ts | 2 +- .../ClientSide/Autocomplete/JS_AC1_spec.ts | 10 +- .../ClientSide/Autocomplete/JS_AC2_spec.ts | 4 +- .../PropertyPaneSuggestion_spec.ts | 2 +- .../ClientSide/BugTests/Bug24486_Spec.ts | 4 +- .../ClientSide/BugTests/Bug28287_Spec.ts | 46 +++ .../ClientSide/BugTests/GitBugs_Spec.ts | 62 +--- .../ClientSide/BugTests/Moment_Spec.ts | 3 +- .../DynamicHeight/List_TextWidget_Spec.ts | 62 ++-- .../ExplorerTests/Admin_settings_2_spec.js | 4 +- ...y_Paste_Delete_Undo_Keyboard_Event_spec.js | 1 + .../FormLogin/EnableFormLogin_spec.js | 2 +- .../v1.9.24/DSCrudAndBindings_Spec.ts | 17 +- .../Git/GitImport/GitImport_spec.js | 10 +- .../Git/GitSync/GitStatusLite_spec.ts | 105 ++++++ .../Git/GitSync/GitSyncedApps_spec.js | 8 + .../Git/GitSync/MergeViaRemote_spec.ts | 74 ++--- .../GitwithCustomJSLibrary_spec.js | 61 ++-- .../ClientSide/Google/EnableGoogle_spec.js | 4 +- .../IDE/MaintainContext&Focus_spec.js | 2 +- .../ClientSide/JSLibrary/Library_spec.ts | 17 +- .../ClientSide/Linting/BasicLint_spec.ts | 4 +- ...ithm_FixedLayout_Mobile_Validation_Spec.js | 1 + .../OtherUIFeatures/ApplicationURL_spec.js | 30 +- .../ClientSide/OtherUIFeatures/Logs1_spec.js | 7 +- .../OtherUIFeatures/PageOnLoad_spec.ts | 28 +- .../OtherUIFeatures/Replay_Editor_spec.js | 3 +- .../Fork_Template_Existing_app_spec.js | 7 +- .../Templates/Fork_Template_spec.js | 6 +- .../Widgets/Chart/Custom3DChartSpec.ts | 2 +- .../Widgets/Iframe/IframeTest_spec.ts | 17 +- .../ClientSide/Widgets/List/List4_2_spec.js | 15 +- .../ClientSide/Widgets/Modal/Modal_spec.ts | 47 ++- .../Widgets/Multiselect/MultiSelect5_spec.ts | 70 ++++ .../Widgets/Others/Autocomplete_spec.js | 126 ++----- .../Widgets/Others/IconButton_2_spec.ts | 19 +- .../ClientSide/Widgets/Radio/Radio2_spec.ts | 1 + .../Widgets/Select/Select_Validation_spec.js | 69 ++++ .../ClientSide/Widgets/Tab/Tabs_2_spec.ts | 40 +++ .../TableV2/TableV2_DisplayText_spec.ts | 2 +- .../CreateSameAppInDiffWorkspace_spec.js | 1 - .../Workspace/DeleteMultipleApplications.ts | 4 +- .../ClientSide/Workspace/MemberRoles_Spec.ts | 1 - .../WorkspaceImportApplication_spec.js | 1 - .../ServerSide/OnLoadTests/JSOnLoad1_Spec.ts | 4 +- .../ServerSide/QueryPane/Mongo_Spec.js | 2 +- .../ServerSide/QueryPane/Postgres_Spec.js | 2 +- .../ServerSide/QueryPane/S3_1_spec.js | 2 +- .../ServerSide/QueryPane/S3_2_spec.ts | 4 +- .../Sanity/Datasources/Arango_Basic_Spec.ts | 3 +- .../Smoke/Apps/ReconnectDatasource_spec.js | 1 - .../cypress/locators/TemplatesLocators.json | 2 +- app/client/cypress/scripts/cypress-split.ts | 3 +- .../cypress/support/Pages/AggregateHelper.ts | 101 ++++-- app/client/cypress/support/Pages/ApiPage.ts | 4 +- .../cypress/support/Pages/AssertHelper.ts | 94 ++++-- .../cypress/support/Pages/DataSources.ts | 41 +-- .../cypress/support/Pages/DeployModeHelper.ts | 23 +- .../cypress/support/Pages/EntityExplorer.ts | 18 +- app/client/cypress/support/Pages/GitSync.ts | 26 +- app/client/cypress/support/Pages/HomePage.ts | 50 ++- app/client/cypress/support/Pages/JSEditor.ts | 2 +- .../cypress/support/Pages/LibraryInstaller.ts | 6 +- .../cypress/support/Pages/Onboarding.ts | 5 +- .../cypress/support/Pages/PropertyPane.ts | 2 + .../cypress/support/WorkspaceCommands.js | 31 +- app/client/cypress/support/commands.js | 42 ++- app/client/cypress/support/e2e.js | 54 ++- app/client/cypress/support/gitSync.js | 19 +- .../docker/templates/nginx-app.conf.template | 5 + app/client/jest.config.js | 1 + app/client/package.json | 5 +- app/client/packages/ast/src/index.ts | 6 +- app/client/packages/ast/src/utils.ts | 11 + app/client/public/index.html | 69 ++-- app/client/src/WidgetProvider/constants.ts | 4 +- .../src/WidgetProvider/factory/index.tsx | 17 + app/client/src/actions/datasourceActions.ts | 2 +- app/client/src/actions/templateActions.ts | 24 ++ .../templates/starter-template-dashboard.svg | 35 ++ .../starter-template-datasource-prompt.svg | 85 +++++ .../icons/templates/starter-template-form.svg | 19 ++ .../starter-template-record-details.svg | 22 ++ .../starter-template-record-edit.svg | 20 ++ .../assets/images/data-main-blank-state.svg | 83 +++++ app/client/src/ce/api/ApplicationApi.tsx | 5 + .../components/EnvDeployInfoModal/index.tsx | 3 - .../src/ce/constants/ReduxActionConstants.tsx | 10 + app/client/src/ce/constants/messages.ts | 43 ++- app/client/src/ce/entities/DataTree/types.ts | 1 + app/client/src/ce/entities/FeatureFlag.ts | 3 + app/client/src/ce/pages/Editor/routes.tsx | 10 +- app/client/src/ce/pages/workspace/Members.tsx | 1 - .../lib/entity/entityConstructorMap.ts | 74 +++++ .../Linting/lib/entity/isDynamicEntity.ts | 18 + .../ce/plugins/Linting/lib/entity/types.ts | 18 + app/client/src/ce/sagas/index.tsx | 2 + app/client/src/ce/utils/analyticsUtilTypes.ts | 3 +- .../utils/autocomplete/EntityDefinitions.ts | 26 +- app/client/src/ce/utils/preloadHelpers.ts | 3 + app/client/src/ce/utils/signupHelpers.ts | 5 + .../utils/getEntityDependenciesByType.ts | 12 + .../viewComponents/TextView/index.tsx | 1 + .../ActionRightPane/index.tsx | 6 +- .../CodeEditor/EditorConfig.ts | 1 + .../editorComponents/CodeEditor/index.tsx | 50 ++- .../editorComponents/EntityBottomTabs.tsx | 3 - .../GlobalSearch/GlobalSearchHooks.tsx | 8 +- .../editorComponents/JSResponseView.tsx | 1 + .../components/editorComponents/Sidebar.tsx | 5 +- .../propertyControls/InputTextControl.tsx | 4 +- app/client/src/components/wds/constants.ts | 1 + .../constants/PropertyControlConstants.tsx | 1 - .../src/constants/TemplatesConstants.ts | 3 - .../src/constants/TemplatesConstants.tsx | 76 +++++ app/client/src/constants/routes/appRoutes.ts | 1 + .../components/EnvDeployInfoModal/index.tsx | 1 - .../src/ee/components/EnvInfoHeader/index.tsx | 3 - .../lib/entity/entityConstructorMap.ts | 1 + .../Linting/lib/entity/isDynamicEntity.ts | 1 + .../ee/plugins/Linting/lib/entity/types.ts | 1 + app/client/src/ee/utils/preloadHelpers.ts | 1 + app/client/src/entities/Action/index.ts | 13 + .../entities/DataTree/dataTreeWidget.test.ts | 1 + .../src/entities/DataTree/dataTreeWidget.ts | 3 + app/client/src/entities/Datasource/index.ts | 8 + .../src/entities/Engine/AppEditorEngine.ts | 17 +- app/client/src/entities/IDE/utils.ts | 29 +- .../anvil/canvas/AnvilCanvas.tsx | 5 +- .../src/layoutSystems/anvil/canvas/types.ts | 3 +- .../canvasArenas/hooks/useAnvilDnDStates.ts | 9 +- .../canvasArenas/hooks/useAnvilWidgetDrop.ts | 5 +- .../canvasArenas/hooks/useCanvasActivation.ts | 53 ++- .../anvil/canvasArenas/hooks/utils.ts | 75 ++++- .../anvil/common/AnvilFlexComponent.tsx | 15 +- .../anvil/editor/AnvilEditorWidgetOnion.tsx | 9 +- .../anvil/integrations/actions/actionTypes.ts | 1 + .../anvil/integrations/actions/index.ts | 7 + .../reducers/layoutElementPositionsReducer.ts | 23 +- .../anvil/integrations/sagas/draggingSagas.ts | 11 + .../anvil/integrations/utils.test.ts | 151 --------- .../layoutSystems/anvil/integrations/utils.ts | 90 ----- .../components/AlignedLayoutColumn.tsx | 11 +- .../components/AlignedWidgetColumn.tsx | 11 +- .../components/AlignedWidgetRow.tsx | 11 +- .../components/FlexLayout.tsx | 13 +- .../components/LayoutColumn.tsx | 11 +- .../layoutComponents/components/LayoutRow.tsx | 11 +- .../components/WidgetColumn.tsx | 11 +- .../layoutComponents/components/WidgetRow.tsx | 11 +- .../presets/DefaultLayoutPreset.tsx | 1 + .../anvil/utils/AnvilDSLTransformer.ts | 2 +- .../layoutSystems/anvil/utils/anvilTypes.ts | 19 +- .../layoutSystems/anvil/utils/constants.ts | 2 +- .../alignedColumnHighlights.test.ts | 36 +- .../highlights/alignedColumnHighlights.ts | 73 +--- .../highlights/alignedRowHighlights.test.ts | 10 +- .../highlights/alignedRowHighlights.ts | 36 +- .../highlights/columnHighlights.test.ts | 45 ++- .../layouts/highlights/columnHighlights.ts | 50 +-- .../utils/layouts/highlights/dropZoneUtils.ts | 139 -------- .../layouts/highlights/highlightUtils.ts | 25 +- .../highlights/horizontalHighlights.ts | 308 ++++++++++++----- .../layouts/highlights/rowHighlights.test.ts | 23 +- .../utils/layouts/highlights/rowHighlights.ts | 313 ++++++++++++------ .../layouts/highlights/verticalHighlights.ts | 47 --- .../anvil/utils/layouts/renderUtils.tsx | 109 ++++-- .../src/layoutSystems/anvil/utils/types.ts | 2 + .../layoutSystems/anvil/utils/widgetUtils.ts | 4 +- .../anvil/viewer/AnvilViewerWidgetOnion.tsx | 2 + .../WidgetNamesCanvas/WidgetNameTypes.ts | 19 +- .../common/WidgetNamesCanvas/eventHandlers.ts | 23 +- .../common/WidgetNamesCanvas/index.tsx | 47 +-- .../widgetNameRenderUtils.ts | 27 +- .../common/dropTarget/DropTargetComponent.tsx | 69 +++- .../StyledComponents.tsx | 141 ++++++++ .../starterBuildingBlocks/index.tsx | 166 ++++++++++ .../MainContainerResizer.tsx | 13 +- .../src/layoutSystems/common/selectors.ts | 13 +- .../common/useLayoutSystemFeatures.ts | 4 + .../LayoutElementPositionsObserver/index.ts | 88 +++-- .../layoutSystems/common/utils/canvasUtils.ts | 2 +- .../layoutComponents/layoutComponentMock.ts | 2 + app/client/src/navigation/FocusElements.ts | 6 +- app/client/src/navigation/FocusSelectors.ts | 4 + app/client/src/pages/AppViewer/utils.ts | 8 +- .../pages/Editor/APIEditor/ApiRightPane.tsx | 3 +- .../Editor/AppSettingsPane/PaneHeader.tsx | 29 +- .../pages/Editor/CanvasPropertyPane/index.tsx | 52 +-- .../pages/Editor/DataSourceEditor/DBForm.tsx | 130 +------- .../Editor/DataSourceEditor/DSFormHeader.tsx | 14 +- .../DataSourceEditor/DatasourceBlankState.tsx | 66 ++++ .../DataSourceEditor/DatasourceSection.tsx | 12 + .../Editor/DataSourceEditor/Debugger.tsx | 10 + .../Editor/DataSourceEditor/JSONtoForm.tsx | 5 +- .../pages/Editor/DataSourceEditor/index.tsx | 162 ++++++--- .../Editor/DatasourceInfo/DatasorceTabs.tsx | 104 ++++++ .../DatasourceEntity.tsx | 8 +- .../DatasourceField.tsx | 2 +- .../DatasourceStructure.tsx | 15 +- .../DatasourceStructureContainer.tsx | 40 +-- .../DatasourceStructureHeader.tsx | 25 +- .../DatasourceStructureLoadingContainer.tsx | 0 .../DatasourceStructureNotFound.tsx | 2 +- .../DatasourceViewModeSchema.tsx | 215 ++++++------ .../GoogleSheetSchema.tsx | 37 ++- .../QueryTemplates.tsx | 3 +- app/client/src/pages/Editor/EditorHeader.tsx | 14 +- .../Editor/EditorName/NavigationMenuData.ts | 7 +- .../Editor/Explorer/Common/components.tsx | 3 +- .../src/pages/Editor/Explorer/Datasources.tsx | 2 +- .../Datasources/DataSourceContextMenu.tsx | 2 +- .../DatasourceStarterLayoutPrompt.tsx | 138 ++++++++ .../pages/Editor/Explorer/EntityExplorer.tsx | 38 ++- .../FirstTimeUserOnboarding/Overlay.tsx | 10 +- .../pages/Editor/IDESidePane/AppSettings.tsx | 35 ++ .../IDESidePane/CreateDatasourcePopover.tsx | 28 ++ .../pages/Editor/IDESidePane/DataSidePane.tsx | 131 ++++++++ .../src/pages/Editor/IDESidePane/index.tsx | 66 ++-- .../CreateNewDatasourceTab.tsx | 297 +++++++++++++++++ .../IntegrationsHomeScreen.tsx | 224 +------------ .../IntegrationEditor/MockDataSources.tsx | 2 +- .../Editor/QueryEditor/EditorJSONtoForm.tsx | 3 +- .../src/pages/Editor/QueryEditor/Table.tsx | 9 +- .../Editor/SaaSEditor/DatasourceForm.tsx | 35 +- app/client/src/pages/Editor/WidgetCard.tsx | 3 +- .../WidgetsEditor/MainContainerWrapper.tsx | 21 +- .../src/pages/Editor/WidgetsEditor/index.tsx | 20 +- .../gitSync/GitSyncModal/GitSyncModalV1.tsx | 2 - .../gitSync/GitSyncModal/GitSyncModalV2.tsx | 2 - .../src/pages/Editor/gitSync/Tabs/Deploy.tsx | 2 +- .../src/pages/Editor/gitSync/Tabs/Merge.tsx | 12 +- app/client/src/pages/Editor/index.tsx | 2 - app/client/src/pages/routes.tsx | 10 +- app/client/src/pages/setup/SignupSuccess.tsx | 6 + .../Linting/lib/entity/ActionEntity.ts | 50 +++ .../Linting/lib/entity/AppsmithEntity.ts | 44 +++ .../plugins/Linting/lib/entity/EntityTree.ts | 2 +- .../Linting/lib/entity/JSActionEntity.ts | 77 +++++ .../Linting/lib/entity/PagelistEntity.ts | 33 ++ .../Linting/lib/entity/WidgetEntity.ts | 50 +++ .../src/plugins/Linting/lib/entity/index.ts | 296 +---------------- .../plugins/Linting/utils/diffGenerator.ts | 3 +- .../Linting/utils/getEntityDependencies.ts | 11 +- .../src/plugins/Linting/utils/pathUtils.ts | 5 +- app/client/src/preload-route-chunks.ts | 5 +- .../reducers/uiReducers/templateReducer.ts | 47 +++ .../sagas/ActionExecution/PluginActionSaga.ts | 7 - app/client/src/sagas/ActionSagas.ts | 63 ++-- app/client/src/sagas/ApiPaneSagas.ts | 68 ++-- app/client/src/sagas/ContextSwitchingSaga.ts | 9 +- app/client/src/sagas/DatasourcesSagas.ts | 2 +- app/client/src/sagas/EvaluationsSaga.ts | 9 +- app/client/src/sagas/GitSyncSagas.ts | 15 +- app/client/src/sagas/OneClickBindingSaga.ts | 48 ++- app/client/src/sagas/PluginSagas.ts | 16 - app/client/src/sagas/PostEvaluationSagas.ts | 9 +- app/client/src/sagas/QueryPaneSagas.ts | 32 +- app/client/src/sagas/TemplatesSagas.ts | 258 +++++++++++---- app/client/src/sagas/TernSaga.ts | 76 +++++ app/client/src/sagas/WidgetAdditionSagas.ts | 1 + app/client/src/sagas/WidgetDeletionSagas.ts | 6 +- .../src/sagas/WidgetEnhancementHelpers.ts | 6 +- app/client/src/selectors/dataTreeSelectors.ts | 23 +- .../src/selectors/datasourceSelectors.ts | 7 + app/client/src/selectors/editorSelectors.tsx | 5 + .../src/selectors/layoutSystemSelectors.ts | 4 - .../src/selectors/templatesSelectors.tsx | 6 + .../autocomplete/AutocompleteSortRules.ts | 127 +++++-- .../autocomplete/CodemirrorTernService.ts | 103 ++++-- .../__tests__/AutocompleteSortRules.test.ts | 190 +++++++++++ .../{ => __tests__}/TernServer.test.ts | 70 ++-- .../dataTreeTypeDefCreator.test.ts | 2 +- .../utils/autocomplete/dataTypeSortRules.ts | 2 + .../src/utils/autocomplete/defCreatorUtils.ts | 9 +- .../utils/autocomplete/keywordCompletion.ts | 4 +- .../utils/hooks/useAllowEditorDragToSelect.ts | 8 +- .../utils/hooks/useClickToSelectWidget.tsx | 4 +- .../src/utils/hooks/useDynamicAppLayout.tsx | 6 +- .../utils/hooks/useWidgetFocus/handleTab.ts | 11 +- app/client/src/utils/storage.ts | 31 +- app/client/src/widgets/BaseWidget.tsx | 4 + .../widgets/ContainerWidget/widget/index.tsx | 50 ++- .../src/widgets/IframeWidget/widget/index.tsx | 14 + .../JSONFormWidget/widget/propertyConfig.ts | 2 +- .../ListWidgetV2/widget/defaultProps.ts | 2 +- .../MultiSelectWidgetV2/widget/index.tsx | 9 +- .../src/widgets/SelectWidget/widget/index.tsx | 11 +- .../src/widgets/TabsWidget/widget/index.tsx | 7 +- app/client/src/widgets/WidgetUtils.ts | 8 +- app/client/src/widgets/index.ts | 2 + .../wds/WDSBaseInputWidget/widget/index.tsx | 16 +- .../WDSCheckboxWidget/config/anvilConfig.ts | 10 + .../wds/WDSCheckboxWidget/config/index.ts | 1 + .../wds/WDSCheckboxWidget/widget/index.tsx | 5 + .../WDSIconButtonWidget/component/index.tsx | 19 ++ .../WDSIconButtonWidget/component/types.ts | 16 + .../config/autocompleteConfig.ts | 8 + .../config/defaultsConfig.ts | 19 ++ .../wds/WDSIconButtonWidget/config/index.ts | 5 + .../WDSIconButtonWidget/config/metaConfig.ts | 9 + .../propertyPaneConfig/contentConfig.ts | 87 +++++ .../config/propertyPaneConfig/index.ts | 2 + .../config/propertyPaneConfig/styleConfig.ts | 53 +++ .../config/settersConfig.ts | 12 + .../widgets/wds/WDSIconButtonWidget/icon.svg | 3 + .../widgets/wds/WDSIconButtonWidget/index.tsx | 3 + .../wds/WDSIconButtonWidget/widget/index.tsx | 125 +++++++ .../wds/WDSIconButtonWidget/widget/types.ts | 16 + .../component/TableStyledWrappers.tsx | 4 + .../wds/WDSTableWidget/widget/index.tsx | 20 +- .../wds/WDSTextWidget/config/anvilConfig.ts | 10 + .../widgets/wds/WDSTextWidget/config/index.ts | 1 + .../wds/WDSTextWidget/widget/index.tsx | 5 + .../Evaluation/__tests__/evaluation.test.ts | 82 +++-- .../workers/Evaluation/evalTreeWithChanges.ts | 8 - .../workers/Evaluation/handlers/evalTree.ts | 9 - .../Evaluation/handlers/evalTrigger.ts | 10 +- app/client/src/workers/Evaluation/types.ts | 1 - .../workers/common/DataTreeEvaluator/index.ts | 233 ++++++------- .../mockData/mockConfigTree.ts | 1 - .../workers/common/DataTreeEvaluator/test.ts | 150 +++------ .../DataTreeEvaluator/validationUtils.ts | 97 +----- .../src/workers/common/DependencyMap/index.ts | 78 +---- .../src/workers/common/DependencyMap/test.ts | 53 --- .../utils/getValidationDependencies.ts | 53 --- .../JSLibrary/ternDefinitionGenerator.ts | 2 +- app/client/yarn.lock | 182 +++------- .../appsmith/git/service/GitExecutorImpl.java | 2 - .../external/constants/AnalyticsEvents.java | 4 +- .../external/plugins/ArangoDBPluginTest.java | 67 ++++ .../plugins/ElasticSearchPluginTest.java | 76 +++++ .../com/external/plugins/MssqlPluginTest.java | 66 ++++ .../com/external/plugins/MySqlPluginTest.java | 174 ++++++++++ .../OracleConnectionRateLimitTest.java | 90 +++++ .../external/plugins/PostgresPluginTest.java | 66 ++++ .../com/external/plugins/RedisPluginTest.java | 66 ++++ .../external/plugins/RedshiftPluginTest.java | 66 ++++ .../com/external/plugins/SmtpPluginTest.java | 67 ++++ ...tionCollectionExportableServiceCEImpl.java | 99 +++--- ...tionCollectionImportableServiceCEImpl.java | 7 +- .../server/constants/ce/FieldNameCE.java | 1 + .../controllers/ApplicationController.java | 10 +- .../ce/ApplicationControllerCE.java | 43 ++- .../DatasourceExportableServiceCEImpl.java | 73 ++-- .../DatasourceImportableServiceCEImpl.java | 12 +- .../appsmith/server/domains/CustomJSLib.java | 7 + .../appsmith/server/dtos/ApplicationJson.java | 3 + .../server/dtos/PartialExportFileDTO.java | 20 ++ .../exportable/ExportableServiceCE.java | 9 +- .../ExportApplicationServiceCEImpl.java | 117 +++++-- .../internal/PartialExportService.java | 3 + .../internal/PartialExportServiceCE.java | 10 + .../internal/PartialExportServiceCEImpl.java | 259 +++++++++++++++ .../internal/PartialExportServiceImpl.java | 46 +++ .../server/helpers/ImportExportUtils.java | 35 +- .../importable/ImportableServiceCE.java | 4 +- .../ImportApplicationServiceCEImpl.java | 180 +++++++--- .../internal/PartialImportService.java | 3 + .../internal/PartialImportServiceCE.java | 11 + .../internal/PartialImportServiceCEImpl.java | 280 ++++++++++++++++ .../internal/PartialImportServiceImpl.java | 72 ++++ .../jslibs/base/CustomJSLibServiceCEImpl.java | 7 +- .../CustomJSLibExportableServiceCEImpl.java | 14 +- .../CustomJSLibImportableServiceCEImpl.java | 14 +- .../NewActionExportableServiceCEImpl.java | 145 ++++---- .../NewActionImportableServiceCEImpl.java | 8 +- .../NewPageExportableServiceCEImpl.java | 15 +- .../NewPageImportableServiceCEImpl.java | 6 +- .../PageLoadExecutablesUtilCEImpl.java | 5 +- .../PluginExportableServiceCEImpl.java | 6 +- .../PluginImportableServiceCEImpl.java | 9 +- .../ce/CustomApplicationRepositoryCE.java | 2 +- .../ce/CustomApplicationRepositoryCEImpl.java | 6 +- .../services/ApplicationServiceImpl.java | 6 +- .../ce/ApplicationPageServiceCEImpl.java | 28 +- .../services/ce/ApplicationServiceCE.java | 2 +- .../services/ce/ApplicationServiceCEImpl.java | 170 +++++----- .../ce/ApplicationTemplateServiceCEImpl.java | 12 +- .../ApplicationServiceCECompatibleImpl.java | 7 +- .../exports/ThemeExportableServiceCEImpl.java | 41 +-- .../imports/ThemeImportableServiceCEImpl.java | 27 +- .../ApplicationControllerTest.java | 8 + .../services/ApplicationPageServiceTest.java | 39 +++ .../services/ce/ApplicationServiceCETest.java | 106 ++++++ .../ImportApplicationServiceTests.java | 115 +++++++ ...alid-application-with-app-positioning.json | 148 +++++++++ deploy/helm/Chart.yaml | 2 +- deploy/helm/templates/statefulset.yaml | 3 +- 400 files changed, 9147 insertions(+), 4246 deletions(-) create mode 100644 app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28287_Spec.ts create mode 100644 app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts create mode 100644 app/client/src/assets/icons/templates/starter-template-dashboard.svg create mode 100644 app/client/src/assets/icons/templates/starter-template-datasource-prompt.svg create mode 100644 app/client/src/assets/icons/templates/starter-template-form.svg create mode 100644 app/client/src/assets/icons/templates/starter-template-record-details.svg create mode 100644 app/client/src/assets/icons/templates/starter-template-record-edit.svg create mode 100644 app/client/src/assets/images/data-main-blank-state.svg delete mode 100644 app/client/src/ce/components/EnvDeployInfoModal/index.tsx create mode 100644 app/client/src/ce/plugins/Linting/lib/entity/entityConstructorMap.ts create mode 100644 app/client/src/ce/plugins/Linting/lib/entity/isDynamicEntity.ts create mode 100644 app/client/src/ce/plugins/Linting/lib/entity/types.ts create mode 100644 app/client/src/ce/utils/preloadHelpers.ts delete mode 100644 app/client/src/constants/TemplatesConstants.ts create mode 100644 app/client/src/constants/TemplatesConstants.tsx delete mode 100644 app/client/src/ee/components/EnvDeployInfoModal/index.tsx delete mode 100644 app/client/src/ee/components/EnvInfoHeader/index.tsx create mode 100644 app/client/src/ee/plugins/Linting/lib/entity/entityConstructorMap.ts create mode 100644 app/client/src/ee/plugins/Linting/lib/entity/isDynamicEntity.ts create mode 100644 app/client/src/ee/plugins/Linting/lib/entity/types.ts create mode 100644 app/client/src/ee/utils/preloadHelpers.ts delete mode 100644 app/client/src/layoutSystems/anvil/integrations/utils.test.ts delete mode 100644 app/client/src/layoutSystems/anvil/integrations/utils.ts delete mode 100644 app/client/src/layoutSystems/anvil/utils/layouts/highlights/dropZoneUtils.ts delete mode 100644 app/client/src/layoutSystems/anvil/utils/layouts/highlights/verticalHighlights.ts create mode 100644 app/client/src/layoutSystems/common/dropTarget/starterBuildingBlocks/StyledComponents.tsx create mode 100644 app/client/src/layoutSystems/common/dropTarget/starterBuildingBlocks/index.tsx create mode 100644 app/client/src/pages/Editor/DataSourceEditor/DatasourceBlankState.tsx create mode 100644 app/client/src/pages/Editor/DatasourceInfo/DatasorceTabs.tsx rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceEntity.tsx (95%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceField.tsx (98%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceStructure.tsx (94%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceStructureContainer.tsx (86%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceStructureHeader.tsx (66%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceStructureLoadingContainer.tsx (100%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/DatasourceStructureNotFound.tsx (97%) rename app/client/src/pages/Editor/{DataSourceEditor => DatasourceInfo}/DatasourceViewModeSchema.tsx (65%) rename app/client/src/pages/Editor/{SaaSEditor => DatasourceInfo}/GoogleSheetSchema.tsx (94%) rename app/client/src/pages/Editor/{Explorer/Datasources => DatasourceInfo}/QueryTemplates.tsx (99%) create mode 100644 app/client/src/pages/Editor/Explorer/Datasources/DatasourceStarterLayoutPrompt.tsx create mode 100644 app/client/src/pages/Editor/IDESidePane/AppSettings.tsx create mode 100644 app/client/src/pages/Editor/IDESidePane/CreateDatasourcePopover.tsx create mode 100644 app/client/src/pages/Editor/IDESidePane/DataSidePane.tsx create mode 100644 app/client/src/pages/Editor/IntegrationEditor/CreateNewDatasourceTab.tsx create mode 100644 app/client/src/plugins/Linting/lib/entity/ActionEntity.ts create mode 100644 app/client/src/plugins/Linting/lib/entity/AppsmithEntity.ts create mode 100644 app/client/src/plugins/Linting/lib/entity/JSActionEntity.ts create mode 100644 app/client/src/plugins/Linting/lib/entity/PagelistEntity.ts create mode 100644 app/client/src/plugins/Linting/lib/entity/WidgetEntity.ts create mode 100644 app/client/src/sagas/TernSaga.ts create mode 100644 app/client/src/utils/autocomplete/__tests__/AutocompleteSortRules.test.ts rename app/client/src/utils/autocomplete/{ => __tests__}/TernServer.test.ts (89%) rename app/client/src/utils/autocomplete/{ => __tests__}/dataTreeTypeDefCreator.test.ts (99%) create mode 100644 app/client/src/widgets/wds/WDSCheckboxWidget/config/anvilConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/component/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/component/types.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/autocompleteConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/defaultsConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/index.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/metaConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/contentConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/index.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/styleConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/settersConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/icon.svg create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/widget/types.ts create mode 100644 app/client/src/widgets/wds/WDSTextWidget/config/anvilConfig.ts delete mode 100644 app/client/src/workers/common/DependencyMap/utils/getValidationDependencies.ts create mode 100644 app/server/appsmith-plugins/oraclePlugin/src/test/java/com/external/plugins/OracleConnectionRateLimitTest.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/PartialExportFileDTO.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportService.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportService.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceImpl.java create mode 100644 app/server/appsmith-server/src/test/resources/test_assets/ImportExportServiceTest/valid-application-with-app-positioning.json diff --git a/.github/config.json b/.github/config.json index e1a3b313de..d8594a5c1c 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1 +1 @@ -{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Widget design system":{"conditions":[{"label":"App Theming","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Checkbox Component","type":"hasLabel","value":true},{"label":"WDS team","type":"hasLabel","value":true}],"requires":1},"Performance Pod":{"conditions":[{"label":"Performance","type":"hasLabel","value":true},{"label":"Performance infra","type":"hasLabel","value":true}],"requires":1},"Billing & Usage Pod":{"conditions":[{"label":"CE Instance","type":"hasLabel","value":true},{"label":"Customer Portal","type":"hasLabel","value":true},{"label":"Cloud Services","type":"hasLabel","value":true},{"label":"Billing Integrations","type":"hasLabel","value":true},{"label":"Billing","type":"hasLabel","value":true},{"label":"Self Serve","type":"hasLabel","value":true},{"label":"Enterprise Billing","type":"hasLabel","value":true},{"label":"In-app ramps","type":"hasLabel","value":true},{"label":"Analytics Improvements","type":"hasLabel","value":true},{"label":"Self Serve 1.0","type":"hasLabel","value":true},{"label":"License","type":"hasLabel","value":true},{"label":"1-click upgrade","type":"hasLabel","value":true},{"label":"Appsmith Business Cloud","type":"hasLabel","value":true},{"label":"BE instance","type":"hasLabel","value":true}],"requires":1},"Mobile Pod":{"conditions":[],"requires":1},"Git Pod":{"conditions":[{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true}],"requires":1},"Integrations Pod":{"conditions":[{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"Postgres","type":"hasLabel","value":true},{"label":"GraphQL Plugin","type":"hasLabel","value":true},{"label":"ArangoDB","type":"hasLabel","value":true},{"label":"MsSQL","type":"hasLabel","value":true},{"label":"REST API plugin","type":"hasLabel","value":true},{"label":"Elastic Search","type":"hasLabel","value":true},{"label":"OAuth","type":"hasLabel","value":true},{"label":"Airtable","type":"hasLabel","value":true},{"label":"CURL","type":"hasLabel","value":true},{"label":"DynamoDB","type":"hasLabel","value":true},{"label":"Zendesk","type":"hasLabel","value":true},{"label":"Hubspot","type":"hasLabel","value":true},{"label":"Query Forms","type":"hasLabel","value":true},{"label":"Twilio","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Connection pool","type":"hasLabel","value":true},{"label":"MariaDB","type":"hasLabel","value":true},{"label":"Integrations Pod General","type":"hasLabel","value":true},{"label":"SMTP plugin","type":"hasLabel","value":true},{"label":"Oracle SQL DB","type":"hasLabel","value":true},{"label":"Query filter","type":"hasLabel","value":true},{"label":"Activation - datasources","type":"hasLabel","value":true}],"requires":1},"Data Platform Pod":{"conditions":[{"label":"Datasource Environments","type":"hasLabel","value":true},{"label":"Datatype issue","type":"hasLabel","value":true},{"label":"Entity Refactor","type":"hasLabel","value":true},{"label":"Core Query Execution","type":"hasLabel","value":true},{"label":"Query Management","type":"hasLabel","value":true},{"label":"Query Settings","type":"hasLabel","value":true},{"label":"SmartSubstitution","type":"hasLabel","value":true},{"label":"Query Generation","type":"hasLabel","value":true},{"label":"Query performance","type":"hasLabel","value":true},{"label":"Suggested Widgets","type":"hasLabel","value":true},{"label":"Page load executions","type":"hasLabel","value":true},{"label":"DSL Update","type":"hasLabel","value":true},{"label":"AST-backend","type":"hasLabel","value":true},{"label":"Deploy App","type":"hasLabel","value":true},{"label":"File upload issues","type":"hasLabel","value":true},{"label":"Datasources","type":"hasLabel","value":true},{"label":"DocumentDB","type":"hasLabel","value":true},{"label":"Multiple Environments","type":"hasLabel","value":true},{"label":"Platformization","type":"hasLabel","value":true},{"label":"Custom environments","type":"hasLabel","value":true},{"label":"Schema","type":"hasLabel","value":true}],"requires":1},"Design System Pod":{"conditions":[{"label":"Design System Pod","type":"hasLabel","value":true},{"label":"ADS Component Issue","type":"hasLabel","value":true},{"label":"Keyboard accessibility ","type":"hasLabel","value":true},{"label":"Toggle button","type":"hasLabel","value":true},{"label":"ADS Category Token","type":"hasLabel","value":true},{"label":"ADS Component Documentation","type":"hasLabel","value":true},{"label":"ADS Migration","type":"hasLabel","value":true},{"label":"ADS Deduplication ","type":"hasLabel","value":true},{"label":"ADS Revamp","type":"hasLabel","value":true},{"label":"ADS Deduplication","type":"hasLabel","value":true},{"label":"ADS Unit Test","type":"hasLabel","value":true},{"label":"ADS Components","type":"hasLabel","value":true},{"label":"ADS Grayscale","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"ADS Typography","type":"hasLabel","value":true},{"label":"ADS Visual Styles","type":"hasLabel","value":true},{"label":"ADS Component Design","type":"hasLabel","value":true},{"label":"Modal Component","type":"hasLabel","value":true}],"requires":1},"DevOps Pod":{"conditions":[{"label":"Docker","type":"hasLabel","value":true},{"label":"Super Admin","type":"hasLabel","value":true},{"label":"Deployment","type":"hasLabel","value":true},{"label":"K8s","type":"hasLabel","value":true},{"label":"Email Config","type":"hasLabel","value":true},{"label":"Backup & Restore","type":"hasLabel","value":true},{"label":"AWS AMI","type":"hasLabel","value":true},{"label":"Observability","type":"hasLabel","value":true},{"label":"Heroku","type":"hasLabel","value":true}],"requires":1},"Team Managers Pod":{"conditions":[{"label":"Settings","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true},{"label":"Realtime Commenting","type":"hasLabel","value":true},{"label":"SSO","type":"hasLabel","value":true},{"label":"Multi User Realtime","type":"hasLabel","value":true},{"label":"RBAC","type":"hasLabel","value":true},{"label":"ABAC","type":"hasLabel","value":true},{"label":"Audit Logs","type":"hasLabel","value":true},{"label":"Multitenancy","type":"hasLabel","value":true},{"label":"Airgap","type":"hasLabel","value":true},{"label":"Enterprise Edition","type":"hasLabel","value":true},{"label":"SCIM","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true},{"label":"Welcome Screen","type":"hasLabel","value":true},{"label":"IDE Navigation","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Clean URLs","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true},{"label":"Feature Flagging","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"App setting","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"SAAS Plugins","type":"hasLabel","value":true},{"label":"SAAS Manager App","type":"hasLabel","value":true},{"label":"Data Platform Pod","type":"hasLabel","value":true},{"label":"Integrations Pod","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true},{"label":"New JS Function","type":"hasLabel","value":true},{"label":"JS Promises","type":"hasLabel","value":true},{"label":"JS Usability","type":"hasLabel","value":true},{"label":"Code Refactoring","type":"hasLabel","value":true},{"label":"storeValue","type":"hasLabel","value":true},{"label":"OnPageLoad","type":"hasLabel","value":true},{"label":"Framework Functions","type":"hasLabel","value":true},{"label":"Code Editor","type":"hasLabel","value":true},{"label":"JS Objects","type":"hasLabel","value":true},{"label":"JS Evaluation","type":"hasLabel","value":true},{"label":"AST-frontend","type":"hasLabel","value":true},{"label":"Custom JS Libraries","type":"hasLabel","value":true},{"label":"Action Selector","type":"hasLabel","value":true},{"label":"JS Function execution","type":"hasLabel","value":true},{"label":"Widget setter method","type":"hasLabel","value":true},{"label":"Error Handling","type":"hasLabel","value":true},{"label":"AI","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true},{"label":"Document Viewer Widget","type":"hasLabel","value":true},{"label":"Radio Group Widget","type":"hasLabel","value":true},{"label":"Currency Input Widget","type":"hasLabel","value":true},{"label":"TreeSelect","type":"hasLabel","value":true},{"label":"MultiTree Select Widget","type":"hasLabel","value":true},{"label":"Phone Input Widget","type":"hasLabel","value":true},{"label":"JSON Form","type":"hasLabel","value":true},{"label":"All Widgets","type":"hasLabel","value":true},{"label":"Button Group widget","type":"hasLabel","value":true},{"label":"Progress bar widget","type":"hasLabel","value":true},{"label":"Audio Recorder Widget","type":"hasLabel","value":true},{"label":"Camera Widget","type":"hasLabel","value":true},{"label":"Table Widget V2","type":"hasLabel","value":true},{"label":"Branding","type":"hasLabel","value":true},{"label":"Map Chart Widget","type":"hasLabel","value":true},{"label":"Code Scanner Widget","type":"hasLabel","value":true},{"label":"Widget keyboard accessibility","type":"hasLabel","value":true},{"label":"List Widget V2","type":"hasLabel","value":true},{"label":"Slider Widget","type":"hasLabel","value":true},{"label":"Widget design system","type":"hasLabel","value":true},{"label":"One-click Binding","type":"hasLabel","value":true},{"label":"Old widget version","type":"hasLabel","value":true},{"label":"Widget Discoverability","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true},{"label":"Widget Grouping","type":"hasLabel","value":true},{"label":"Reflow & Resize","type":"hasLabel","value":true},{"label":"Canvas / Grid","type":"hasLabel","value":true},{"label":"Canvas Zooms","type":"hasLabel","value":true},{"label":"Frontend Libraries Upgrade","type":"hasLabel","value":true},{"label":"Auto Height","type":"hasLabel","value":true},{"label":"Responsive Canvas","type":"hasLabel","value":true},{"label":"Responsive Widget","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Conversion Algorithm","type":"hasLabel","value":true},{"label":"Spacing","type":"hasLabel","value":true},{"label":"Browser specific","type":"hasLabel","value":true},{"label":"widget vertical alignment","type":"hasLabel","value":true},{"label":"Auto Layout","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"Templates pod":{"conditions":[{"label":"Templates","type":"hasLabel","value":true},{"label":"Community template","type":"hasLabel","value":true},{"label":"Partial-import-export","type":"hasLabel","value":true}],"requires":1},"Error Handling":{"conditions":[],"requires":1},"Workflows":{"conditions":[],"requires":1},"AI Pod":{"conditions":[],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"Checkbox Group widget":{"color":"88054d","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"c9ddc6","name":"MySQL","description":"Issues related to MySQL plugin"},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"8078b0","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"8078b0","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"Widget Styling":{"color":"905420","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"88054d","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"8078b0","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"Import-Export-App":{"color":"15076d","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"2958a4","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"771e69","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"93491f","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Google Sheets":{"color":"8078b0","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"8078b0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"858172","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"8078b0","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"5052f6","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"name":"In App Comms","description":"Issues around communication with appsmith instances","color":"463cca"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8078b0"},"New Datasource":{"color":"60b14c","name":"New Datasource","description":"Requests for new datasources"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"d12d2e","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"26ef4f"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"},"Old App Issues":{"name":"Old App Issues","description":"Issues related to apps old apps a few weeks old and app issues in stale browser session","color":"87ab18"},"Document Viewer Widget":{"name":"Document Viewer Widget","description":"Issues related to Document Viewer Widget","color":"899d4b"},"Radio Group Widget":{"name":"Radio Group Widget","description":"Issues related to radio group widget","color":"b68495"},"Super Admin":{"name":"Super Admin","description":"Issues related to the super admin page","color":"aa95cf"},"Postgres":{"name":"Postgres","description":"Postgres related issues","color":"8078b0"},"REST API plugin":{"name":"REST API plugin","description":"REST API plugin related issues","color":"8078b0"},"New JS Function":{"name":"New JS Function","description":"Issues related to adding a JS Function","color":"8e8aa4"},"Cannot Reproduce Issue":{"color":"93c9cc","name":"Cannot Reproduce Issue","description":"Issues that cannot be reproduced"},"Widget Grouping":{"name":"Widget Grouping","description":"Issues related to Widget Grouping","color":"a49951"},"K8s":{"name":"K8s","description":"Kubernetes related issues","color":"5f318a"},"Docker":{"name":"Docker","description":"Issues related to docker","color":"89b808"},"Camera Widget":{"name":"Camera Widget","description":"Issues and enhancements related to camera widget","color":"e6038e"},"SAAS Plugins":{"name":"SAAS Plugins","description":"Issues related to SAAS Plugins","color":"ef9c9d"},"JS Promises":{"name":"JS Promises","description":"Issues related to promises","color":"d7771f"},"OnPageLoad":{"name":"OnPageLoad","description":"OnPageLoad issues on functions and queries","color":"50559d"},"JS Usability":{"name":"JS Usability","description":"usability issues with JS editor and JS elsewhere","color":"a302b0"},"Currency Input Widget":{"name":"Currency Input Widget","description":"Issues related to currency input widget","color":"b2164f"},"TreeSelect":{"name":"TreeSelect","description":"Issues related to TreeSelect Widget","color":"a1633e"},"MultiTree Select Widget":{"name":"MultiTree Select Widget","description":"Issues related to MultiTree Select Widget","color":"a1633e"},"Welcome Screen":{"name":"Welcome Screen","description":"Issues related to the welcome screen","color":"3897be"},"Realtime Commenting":{"color":"a70b86","name":"Realtime Commenting","description":"In-app communication between teams"},"Phone Input Widget":{"name":"Phone Input Widget","description":"Issues related to the Phone Input widget","color":"a70b86"},"JSON Form":{"name":"JSON Form","description":"Issue / features related to the JSON form wiget","color":"46b209"},"All Widgets":{"name":"All Widgets","description":"Issues related to all widgets","color":"972b36"},"V1":{"name":"V1","description":"V1","color":"67ab2e"},"Reflow & Resize":{"name":"Reflow & Resize","description":"All issues related to reflow and resize experience","color":"748a13"},"App Theming":{"name":"App Theming","description":"Items that are related to the App level theming controls epic","color":"905420"},"SSO":{"name":"SSO","description":"Issues, requests and enhancements around Single sign-on.","color":"bf019b"},"Multi User Realtime":{"name":"Multi User Realtime","description":"Issues related to multiple users using or editing an application","color":"e7b6ce"},"Templates":{"name":"Templates","description":"Issues related to templates","color":"b7e568"},"Ready for design":{"name":"Ready for design","description":"this issue is ready for design: it contains clear problem statements and other required information","color":"ebf442"},"Support":{"name":"Support","description":"Issues created by the A-force team to address user queries","color":"1740f3"},"Button Group widget":{"name":"Button Group widget","description":"Issue and enhancements related to the button group widget","color":"f17025"},"GraphQL Plugin":{"name":"GraphQL Plugin","description":"Issues related to GraphQL plugin","color":"8078b0"},"DevOps Pod":{"name":"DevOps Pod","description":"Issues related to devops","color":"d956c7"},"medium":{"name":"medium","description":"Issues that frustrate users due to poor UX","color":"23dfd9"},"ArangoDB":{"name":"ArangoDB","description":"Issues related to arangoDB","color":"8078b0"},"Code Refactoring":{"name":"Code Refactoring","description":"Issues related to code refactoring","color":"76310e"},"Progress bar widget":{"name":"Progress bar widget","description":"To track issues related to progress bar","color":"2d7abf"},"Audio Recorder Widget":{"name":"Audio Recorder Widget","description":"Issues related to Audio Recorder Widget","color":"9accef"},"Airtable":{"name":"Airtable","description":"Issues for Airtable","color":"60885f"},"RBAC":{"name":"RBAC","description":"Issues, requests and enhancements around RBAC.","color":"9211c3"},"Canvas / Grid":{"name":"Canvas / Grid","description":"Issues related to the canvas","color":"16b092"},"Email Config":{"name":"Email Config","description":"Issues related to configuring the email service","color":"2a21d1"},"CURL":{"name":"CURL","description":"Issues related to CURL impor","color":"60885f"},"Canvas Zooms":{"name":"Canvas Zooms","description":"Issues related to zooming the canvas","color":"e6038e"},"business":{"name":"business","description":"Features that will be a part of our business edition","color":"cd59eb"},"Action Pod":{"name":"Action Pod","description":"","color":"ee2e36"},"AutomationGap1":{"color":"a5e07c","name":"AutomationGap1","description":"Issues that needs automated tests"},"A-Force11":{"name":"A-Force11","description":"Issues raised by A-Force team","color":"d667b6"},"Business Edition":{"name":"Business Edition","description":"Features that will be a part of our business edition","color":"89bb6c"},"storeValue":{"name":"storeValue","description":"Issues related to the store value function","color":"5d3e66"},"Tests":{"name":"Tests","description":"test item","color":"1c6990"},"DynamoDB":{"name":"DynamoDB","description":"Issues that are related to DynamoDB should have this label","color":"60885f"},"Design System Pod":{"name":"Design System Pod","description":"Appsmith design system related issues","color":"706f03"},"ABAC":{"color":"e009a5","name":"ABAC","description":"User permissions and access controls"},"Backup & Restore":{"name":"Backup & Restore","description":"Issues related to backup and restore","color":"86874d"},"Billing":{"name":"Billing","description":"Billing infrastructure and flows for Business Edition and Trial users","color":"d2bc40"},"Datatype issue":{"name":"Datatype issue","description":"Issues that have risen because data types weren't handled","color":"60885f"},"OAuth":{"name":"OAuth","description":"OAuth related bugs or features","color":"60885f"},"Table Widget V2":{"name":"Table Widget V2","description":"Issues related to Table Widget V2","color":"3a7192"},"IDE Navigation":{"name":"IDE Navigation","description":"Issues/feature requests related to IDE navigation, and context switching","color":"bc0cba"},"Query performance":{"name":"Query performance","description":"Issues that have to do with lack in performance of query execution","color":"e4d966"},"SAAS Manager App":{"name":"SAAS Manager App","description":"Issues with the SAAS manager app","color":"d427db"},"Twilio":{"name":"Twilio","description":"Issues related to Twilio integration","color":"23ba8d"},"Hubspot":{"name":"Hubspot","description":"Issues related to Hubspot integration","color":"60885f"},"Zendesk":{"name":"Zendesk","description":"Issues related to Zendesk integration","color":"60885f"},"Entity Refactor":{"name":"Entity Refactor","description":"Issues related to refactor logic","color":"418fa4"},"Branding":{"name":"Branding","description":"All issues under branding and whitelabelling appsmith ecosystem","color":"7aaaf1"},"Map Chart Widget":{"name":"Map Chart Widget","description":"Issues related to Map Chart Widgets","color":"c8397f"},"Product Catchup":{"name":"Product Catchup","description":"Issues created in the product catchup","color":"29cd2c"},"Framework Functions":{"name":"Framework Functions","description":"Issues related to internal functions like showAlert(), navigateTo() etc...","color":"c25a09"},"Frontend Libraries Upgrade":{"name":"Frontend Libraries Upgrade","description":"Issues related to frontend libraries upgrade","color":"ede1fc"},"Audit Logs":{"name":"Audit Logs","description":"Audit trails to ensure data security","color":"f3fd62"},"MsSQL":{"name":"MsSQL","description":"Issues related to MsSQL plugin","color":"8078b0"},"Data Platform Pod":{"name":"Data Platform Pod","description":"Issues related to the underlying data platform","color":"3f8c3a"},"Integrations Pod":{"name":"Integrations Pod","description":"Issues related to a specific integration","color":"5dbbb1"},"Datasource Environments":{"name":"Datasource Environments","description":"Issues related to datasource environments","color":"bb7a14"},"Elastic Search":{"name":"Elastic Search","description":"Issues related to the elastic search datasource","color":"8078b0"},"Core Query Execution":{"color":"418fa4","name":"Core Query Execution","description":"Issues related to the execution of all queries"},"Query Management":{"name":"Query Management","description":"Issues related to the CRUD of actions or queries","color":"6a5b42"},"Query Settings":{"name":"Query Settings","description":"Issues related to the settings of all queries","color":"c7da7a"},"Code Editor":{"name":"Code Editor","description":"Issues related to the code editor","color":"4ca16e"},"Query Forms":{"color":"12b253","name":"Query Forms","description":"Isuses related to the query forms"},"JS Objects":{"color":"22962c","name":"JS Objects","description":"Issues related to JS Objects"},"JS Evaluation":{"color":"22962c","name":"JS Evaluation","description":"Issues related to JS evaluation on the platform"},"SmartSubstitution":{"name":"SmartSubstitution","description":"Issues related to Smart substitution of mustache bindings in queries","color":"e4d966"},"Query Generation":{"name":"Query Generation","description":"Issues related to query generation","color":"e4d966"},"Suggested Widgets":{"name":"Suggested Widgets","description":"Issues related to suggesting widgets based on query response","color":"e4d966"},"Page load executions":{"name":"Page load executions","description":"Issues related to page load execution","color":"5696b2"},"Code Scanner Widget":{"name":"Code Scanner Widget","description":"Issues related to code scanner widget","color":"9bc1a0"},"Clean URLs":{"name":"Clean URLs","description":"Issues related to clean URLs epic","color":"112623"},"Widget keyboard accessibility":{"name":"Widget keyboard accessibility","description":"All issues related to keyboard accessibility in widgets","color":"b626fd"},"Connection pool":{"name":"Connection pool","description":"issues to do with connection pooling of various plugins","color":"94fe36"},"List Widget V2":{"name":"List Widget V2","description":"Issues related to the list widget v2","color":"adaaf7"},"Auto Height":{"name":"Auto Height","description":"Issues related to dynamic height of widgets","color":"5149cf"},"cypress_failed_test":{"name":"cypress_failed_test","description":"Cypress failed tests","color":"4745d5"},"Needs validation":{"name":"Needs validation","description":"Needs problem validation before being picked up","color":"66673d"},"Slider Widget":{"name":"Slider Widget","description":"Issues raised for slider widgets.","color":"2eef5f"},"Multitenancy":{"name":"Multitenancy","description":"Support multitenancy within single appsmith instance","color":"8c49a9"},"Git Pod":{"name":"Git Pod","description":"Anything related to git sync","color":"2e5ba4"},"Mobile Pod":{"name":"Mobile Pod","description":"All issues related to mobile responsiveness","color":"6c97fd"},"Responsive Widget":{"name":"Responsive Widget","description":"All issues related to widget responsiveness","color":"d12d2e"},"Responsive Canvas":{"name":"Responsive Canvas","description":"All issues related to canvas responsiveness","color":"45a0a8"},"Conversion Algorithm":{"name":"Conversion Algorithm","description":"All issue related to converting app from fixed to flex mode & vice versa","color":"d12d2e"},"Spacing":{"name":"Spacing","description":"All issue related to spacing between widgets in auto layout","color":"d12d2e"},"Browser specific":{"name":"Browser specific","description":"All issue related to browser","color":"d12d2e"},"Error Handling":{"name":"Error Handling","description":"Issues related to error handling","color":"4e1872"},"Performance infra":{"name":"Performance infra","description":"all issue related to the performance infra","color":"8a60f6"},"DSL Update":{"name":"DSL Update","description":"Issues related to storing and updating the DSL","color":"e16cf3"},"AST-frontend":{"name":"AST-frontend","description":"Issues related to maintaining AST logic","color":"434a3a"},"AST-backend":{"name":"AST-backend","description":"Backend issues related to AST parsing","color":"c476eb"},"MariaDB":{"name":"MariaDB","description":"MariaDB datasource","color":"8428c3"},"Billing & Usage Pod":{"name":"Billing & Usage Pod","description":"Issues pertaining to licensing, billing, usage across self serve and enterprise customers","color":"256808"},"ADS Component Issue":{"name":"ADS Component Issue","description":"Issues which are caused due to ADS components","color":"d89119"},"Regressed":{"color":"723fd0","name":"Regressed","description":"Scenarios that were working before but have now regressed"},"Needs RCA":{"name":"Needs RCA","description":"a critical or high priority issue that needs an RCA","color":"2cc68f"},"Custom JS Libraries":{"name":"Custom JS Libraries","description":"Issues related to adding custom JS library","color":"bacb6d"},"Integrations Pod General":{"name":"Integrations Pod General","description":"Issues related to the Integrations Pod that don't fit into other tags.","color":"287823"},"Performance Pod":{"name":"Performance Pod","description":"All things related to Appsmith performance","color":"b5a25d"},"Performance":{"name":"Performance","description":"Issues related to performance","color":"9a18d7"},"File upload issues":{"name":"File upload issues","description":"Issues related to uploading any type of files from within Appsmith","color":"8154df"},"Action Selector":{"name":"Action Selector","description":"Issues related to action selector on the property pane","color":"2f9e20"},"Widget design system":{"name":"Widget design system","description":"","color":"11cc90"},"Deploy App":{"name":"Deploy App","description":"Issues related to app deployment","color":"6f6152"},"Community Reported":{"name":"Community Reported","description":"issues reported by community members","color":"1402e5"},"JS Function execution":{"name":"JS Function execution","description":"JS function execution","color":"7c2de1"},"Self Serve":{"name":"Self Serve","description":"For all issues related to self-serve flow for business edition","color":"4dacfc"},"Self Serve 1.0":{"name":"Self Serve 1.0","description":"For all issues related to v1 of the self serve project","color":"ae839e"},"CE Instance":{"name":"CE Instance","description":"For all issues relating to usage, licensing or billing on the CE instance","color":"d2bc40"},"Customer Portal":{"name":"Customer Portal","description":"For all tasks/issues pertaining to customer.appsmith.com","color":"d2bc40"},"Cloud Services":{"name":"Cloud Services","description":"For all tasks/issues on Appsmith cloud-services relating to licensing, usage and billing","color":"d2bc40"},"Billing Integrations":{"name":"Billing Integrations","description":"For all issues relating to 3P integrations Appsmith is using for billing & usage","color":"d2bc40"},"One-click Binding":{"name":"One-click Binding","description":"Issues related to the One click binding epic","color":"f1661c"},"Airgap":{"name":"Airgap","description":"Tickets related to supporting air-gapped Appsmith instances","color":"1cb294"},"SMTP plugin":{"name":"SMTP plugin","description":"Issues related to SMTP plugin","color":"541457"},"AWS AMI":{"name":"AWS AMI","description":"Issues Related to AWS AMI","color":"b44680"},"Old widget version":{"name":"Old widget version","description":"Use this label to raise issue specific only to an older version of a widget","color":"ff3814"},"Enterprise Billing":{"name":"Enterprise Billing","description":"To track all tasks/issues related to licensing & billing for enterprise customers","color":"14c156"},"Appsmith Business Cloud":{"name":"Appsmith Business Cloud","description":"Issues related to our business cloud offering","color":"89bb6c"},"Oracle SQL DB":{"name":"Oracle SQL DB","description":"Issues related to the Oracle plugin","color":"cbabcb"},"Community Contributor":{"name":"Community Contributor","description":"Meant to track issues that are assigned to external contributors","color":"149ab6"},"widget vertical alignment":{"name":"widget vertical alignment","description":"All issue related widget vertical alignment on the auto layout canvas","color":"d12d2e"},"Observability":{"name":"Observability","description":"Issues related to observability on the Appsmith instance","color":"dff913"},"Checkbox Component":{"name":"Checkbox Component","description":"This labels deals with checkbox component in wds package","color":"75a401"},"In-app ramps":{"name":"In-app ramps","description":"For all tasks/issues relating to adding in-app ramps in the community edition of the product","color":"8abae0"},"Analytics Improvements":{"name":"Analytics Improvements","description":"For all tasks focused on improving our overall analytics and fixing any issues ","color":"29b8ed"},"WDS team":{"name":"WDS team","description":"","color":"8d675a"},"Enterprise Edition":{"name":"Enterprise Edition","description":"Features that will be supported in Enterprise Edition only","color":"984f5e"},"Query filter":{"name":"Query filter","description":"Issues related to query filtering, e.g., WHERE clause","color":"a15134"},"Keyboard accessibility ":{"name":"Keyboard accessibility ","description":"All issue related to ADS component keyboard accessibility","color":"2ba696"},"Toggle button":{"name":"Toggle button","description":"All issue related to ADS toggle button","color":"edc47f"},"1-click upgrade":{"name":"1-click upgrade","description":"For all issues/tasks related to 1-click upgrade & downgrade project","color":"129082"},"Feature Flagging":{"name":"Feature Flagging","description":"Anything related feature flagging flagsmith","color":"728126"},"SCIM":{"name":"SCIM","description":"Label to collate our SCIM issues","color":"61a852"},"ADS Category Token":{"name":"ADS Category Token","description":"All issues related appsmith design system category tokens","color":"920961"},"ADS Component Documentation":{"name":"ADS Component Documentation","description":"All issues Appsmith design system component documentation","color":"64c46a"},"ADS Migration":{"name":"ADS Migration","description":"All issues related to Appsmith design system migration","color":"b082d6"},"ADS Deduplication ":{"name":"ADS Deduplication ","description":"Replacing component with ADS components","color":"b082d6"},"ADS Revamp":{"name":"ADS Revamp","description":"All issues related to ads revamp. ","color":"b082d6"},"ADS Deduplication":{"name":"ADS Deduplication","description":"Replacing component with ADS components","color":"b082d6"},"ADS Grayscale":{"name":"ADS Grayscale","description":"Support grayscale color changes","color":"b03577"},"ADS Unit Test":{"name":"ADS Unit Test","description":"All issue related ads unit cases ","color":"b082d6"},"ADS Components":{"name":"ADS Components","description":"All issues related ADS components","color":"b082d6"},"Widget Discoverability":{"name":"Widget Discoverability","description":"Issues related to Widget Discoverability","color":"7b55ce"},"Widget setter method":{"name":"Widget setter method","description":"Issues with widget property setters","color":"8dce87"},"License":{"name":"License","description":"For all issues/tasks related to licensing of appsmith-ee edition","color":"90ee98"},"Templates pod":{"name":"Templates pod","description":"Issues related to Templates","color":"b7e568"},"Community template":{"name":"Community template","description":"Label for development of community templates and its integration to platform","color":"8a0510"},"DocumentDB":{"name":"DocumentDB","description":"Issues related to support DocumentDB in Appsmith Data layer","color":"2c8b56"},"Multiple Environments":{"name":"Multiple Environments","description":"Issues or tasks related to multiple environments","color":"4e972b"},"Platformization":{"name":"Platformization","description":"Issues or tasks related to platformization of Appsmith codebase","color":"4e972b"},"Activation - datasources":{"name":"Activation - datasources","description":"issues related to activation projects","color":"7c7ace"},"Partial-import-export":{"name":"Partial-import-export","description":"Label for granular reusability.","color":"1e439c"},"AI":{"name":"AI","description":"All tasks related to AI","color":"69c7ca"},"Custom environments":{"name":"Custom environments","description":"Issues with creating or working with custom environments","color":"2137d6"},"ADS Typography":{"name":"ADS Typography","description":"All issue related typographical changes","color":"2dbe8d"},"Auto Layout":{"name":"Auto Layout","description":"Issues relates to auto layout","color":"92cf8c"},"Heroku":{"name":"Heroku","description":"Issues related to Heroku","color":"a81b69"},"ADS Visual Styles":{"name":"ADS Visual Styles","description":"All issues related to ADS visual styles","color":"d3da89"},"ADS Component Design":{"name":"ADS Component Design","description":"All issue related to component design","color":"5cc91e"},"Modal Component":{"name":"Modal Component","description":"All issue related to ads modal component","color":"ee63f3"},"App setting":{"name":"App setting","description":"Related to app settings panel within the app","color":"144206"},"BE instance":{"name":"BE instance","description":"For all issues related to license, billing on BE instance","color":"ae8f98"},"Workflows":{"name":"Workflows","description":"For all issues related to the Workflows feature","color":"ae2aa6"},"Schema":{"name":"Schema","description":"Issues related to database schema","color":"c470c2"},"AI Pod":{"name":"AI Pod","description":"Pod for all AI related tasks","color":"d18528"}},"success":true} \ No newline at end of file +{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Widget design system":{"conditions":[{"label":"App Theming","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Checkbox Component","type":"hasLabel","value":true},{"label":"WDS team","type":"hasLabel","value":true}],"requires":1},"Performance Pod":{"conditions":[{"label":"Performance","type":"hasLabel","value":true},{"label":"Performance infra","type":"hasLabel","value":true}],"requires":1},"Billing & Usage Pod":{"conditions":[{"label":"CE Instance","type":"hasLabel","value":true},{"label":"Customer Portal","type":"hasLabel","value":true},{"label":"Cloud Services","type":"hasLabel","value":true},{"label":"Billing Integrations","type":"hasLabel","value":true},{"label":"Billing","type":"hasLabel","value":true},{"label":"Self Serve","type":"hasLabel","value":true},{"label":"Enterprise Billing","type":"hasLabel","value":true},{"label":"In-app ramps","type":"hasLabel","value":true},{"label":"Analytics Improvements","type":"hasLabel","value":true},{"label":"Self Serve 1.0","type":"hasLabel","value":true},{"label":"License","type":"hasLabel","value":true},{"label":"1-click upgrade","type":"hasLabel","value":true},{"label":"Appsmith Business Cloud","type":"hasLabel","value":true},{"label":"BE instance","type":"hasLabel","value":true},{"label":"Feature Flagging","type":"hasLabel","value":true}],"requires":1},"Mobile Pod":{"conditions":[],"requires":1},"Git Pod":{"conditions":[{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true}],"requires":1},"Integrations Pod":{"conditions":[{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"Postgres","type":"hasLabel","value":true},{"label":"GraphQL Plugin","type":"hasLabel","value":true},{"label":"ArangoDB","type":"hasLabel","value":true},{"label":"MsSQL","type":"hasLabel","value":true},{"label":"REST API plugin","type":"hasLabel","value":true},{"label":"Elastic Search","type":"hasLabel","value":true},{"label":"OAuth","type":"hasLabel","value":true},{"label":"Airtable","type":"hasLabel","value":true},{"label":"CURL","type":"hasLabel","value":true},{"label":"DynamoDB","type":"hasLabel","value":true},{"label":"Zendesk","type":"hasLabel","value":true},{"label":"Hubspot","type":"hasLabel","value":true},{"label":"Query Forms","type":"hasLabel","value":true},{"label":"Twilio","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Connection pool","type":"hasLabel","value":true},{"label":"MariaDB","type":"hasLabel","value":true},{"label":"Integrations Pod General","type":"hasLabel","value":true},{"label":"SMTP plugin","type":"hasLabel","value":true},{"label":"Oracle SQL DB","type":"hasLabel","value":true},{"label":"Query filter","type":"hasLabel","value":true},{"label":"Activation - datasources","type":"hasLabel","value":true}],"requires":1},"Data Platform Pod":{"conditions":[{"label":"Datasource Environments","type":"hasLabel","value":true},{"label":"Datatype issue","type":"hasLabel","value":true},{"label":"Entity Refactor","type":"hasLabel","value":true},{"label":"Core Query Execution","type":"hasLabel","value":true},{"label":"Query Management","type":"hasLabel","value":true},{"label":"Query Settings","type":"hasLabel","value":true},{"label":"SmartSubstitution","type":"hasLabel","value":true},{"label":"Query Generation","type":"hasLabel","value":true},{"label":"Query performance","type":"hasLabel","value":true},{"label":"Suggested Widgets","type":"hasLabel","value":true},{"label":"Page load executions","type":"hasLabel","value":true},{"label":"DSL Update","type":"hasLabel","value":true},{"label":"AST-backend","type":"hasLabel","value":true},{"label":"Deploy App","type":"hasLabel","value":true},{"label":"File upload issues","type":"hasLabel","value":true},{"label":"Datasources","type":"hasLabel","value":true},{"label":"DocumentDB","type":"hasLabel","value":true},{"label":"Multiple Environments","type":"hasLabel","value":true},{"label":"Platformization","type":"hasLabel","value":true},{"label":"Custom environments","type":"hasLabel","value":true},{"label":"Schema","type":"hasLabel","value":true}],"requires":1},"Design System Pod":{"conditions":[{"label":"Design System Pod","type":"hasLabel","value":true},{"label":"ADS Component Issue","type":"hasLabel","value":true},{"label":"Keyboard accessibility ","type":"hasLabel","value":true},{"label":"Toggle button","type":"hasLabel","value":true},{"label":"ADS Category Token","type":"hasLabel","value":true},{"label":"ADS Component Documentation","type":"hasLabel","value":true},{"label":"ADS Migration","type":"hasLabel","value":true},{"label":"ADS Deduplication ","type":"hasLabel","value":true},{"label":"ADS Revamp","type":"hasLabel","value":true},{"label":"ADS Deduplication","type":"hasLabel","value":true},{"label":"ADS Unit Test","type":"hasLabel","value":true},{"label":"ADS Components","type":"hasLabel","value":true},{"label":"ADS Grayscale","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"ADS Typography","type":"hasLabel","value":true},{"label":"ADS Visual Styles","type":"hasLabel","value":true},{"label":"ADS Component Design","type":"hasLabel","value":true},{"label":"Modal Component","type":"hasLabel","value":true}],"requires":1},"DevOps Pod":{"conditions":[{"label":"Docker","type":"hasLabel","value":true},{"label":"Super Admin","type":"hasLabel","value":true},{"label":"Deployment","type":"hasLabel","value":true},{"label":"K8s","type":"hasLabel","value":true},{"label":"Email Config","type":"hasLabel","value":true},{"label":"Backup & Restore","type":"hasLabel","value":true},{"label":"AWS AMI","type":"hasLabel","value":true},{"label":"Observability","type":"hasLabel","value":true},{"label":"Heroku","type":"hasLabel","value":true}],"requires":1},"Team Managers Pod":{"conditions":[{"label":"Settings","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true},{"label":"Realtime Commenting","type":"hasLabel","value":true},{"label":"SSO","type":"hasLabel","value":true},{"label":"Multi User Realtime","type":"hasLabel","value":true},{"label":"RBAC","type":"hasLabel","value":true},{"label":"ABAC","type":"hasLabel","value":true},{"label":"Audit Logs","type":"hasLabel","value":true},{"label":"Multitenancy","type":"hasLabel","value":true},{"label":"Airgap","type":"hasLabel","value":true},{"label":"Enterprise Edition","type":"hasLabel","value":true},{"label":"SCIM","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true},{"label":"Welcome Screen","type":"hasLabel","value":true},{"label":"IDE Navigation","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Clean URLs","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"App setting","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"SAAS Plugins","type":"hasLabel","value":true},{"label":"SAAS Manager App","type":"hasLabel","value":true},{"label":"Data Platform Pod","type":"hasLabel","value":true},{"label":"Integrations Pod","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true},{"label":"New JS Function","type":"hasLabel","value":true},{"label":"JS Promises","type":"hasLabel","value":true},{"label":"JS Usability","type":"hasLabel","value":true},{"label":"Code Refactoring","type":"hasLabel","value":true},{"label":"storeValue","type":"hasLabel","value":true},{"label":"OnPageLoad","type":"hasLabel","value":true},{"label":"Framework Functions","type":"hasLabel","value":true},{"label":"Code Editor","type":"hasLabel","value":true},{"label":"JS Objects","type":"hasLabel","value":true},{"label":"JS Evaluation","type":"hasLabel","value":true},{"label":"AST-frontend","type":"hasLabel","value":true},{"label":"Custom JS Libraries","type":"hasLabel","value":true},{"label":"Action Selector","type":"hasLabel","value":true},{"label":"JS Function execution","type":"hasLabel","value":true},{"label":"Widget setter method","type":"hasLabel","value":true},{"label":"Error Handling","type":"hasLabel","value":true},{"label":"AI","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true},{"label":"Document Viewer Widget","type":"hasLabel","value":true},{"label":"Radio Group Widget","type":"hasLabel","value":true},{"label":"Currency Input Widget","type":"hasLabel","value":true},{"label":"TreeSelect","type":"hasLabel","value":true},{"label":"MultiTree Select Widget","type":"hasLabel","value":true},{"label":"Phone Input Widget","type":"hasLabel","value":true},{"label":"JSON Form","type":"hasLabel","value":true},{"label":"All Widgets","type":"hasLabel","value":true},{"label":"Button Group widget","type":"hasLabel","value":true},{"label":"Progress bar widget","type":"hasLabel","value":true},{"label":"Audio Recorder Widget","type":"hasLabel","value":true},{"label":"Camera Widget","type":"hasLabel","value":true},{"label":"Table Widget V2","type":"hasLabel","value":true},{"label":"Branding","type":"hasLabel","value":true},{"label":"Map Chart Widget","type":"hasLabel","value":true},{"label":"Code Scanner Widget","type":"hasLabel","value":true},{"label":"Widget keyboard accessibility","type":"hasLabel","value":true},{"label":"List Widget V2","type":"hasLabel","value":true},{"label":"Slider Widget","type":"hasLabel","value":true},{"label":"Widget design system","type":"hasLabel","value":true},{"label":"One-click Binding","type":"hasLabel","value":true},{"label":"Old widget version","type":"hasLabel","value":true},{"label":"Widget Discoverability","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true},{"label":"Widget Grouping","type":"hasLabel","value":true},{"label":"Reflow & Resize","type":"hasLabel","value":true},{"label":"Canvas / Grid","type":"hasLabel","value":true},{"label":"Canvas Zooms","type":"hasLabel","value":true},{"label":"Frontend Libraries Upgrade","type":"hasLabel","value":true},{"label":"Auto Height","type":"hasLabel","value":true},{"label":"Responsive Canvas","type":"hasLabel","value":true},{"label":"Responsive Widget","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Conversion Algorithm","type":"hasLabel","value":true},{"label":"Spacing","type":"hasLabel","value":true},{"label":"Browser specific","type":"hasLabel","value":true},{"label":"widget vertical alignment","type":"hasLabel","value":true},{"label":"Auto Layout","type":"hasLabel","value":true},{"label":"Fixed layout","type":"hasLabel","value":true},{"label":"Anvil layout","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"Templates pod":{"conditions":[{"label":"Templates","type":"hasLabel","value":true},{"label":"Community template","type":"hasLabel","value":true},{"label":"Partial-import-export","type":"hasLabel","value":true}],"requires":1},"Error Handling":{"conditions":[],"requires":1},"Workflows":{"conditions":[],"requires":1},"AI Pod":{"conditions":[],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"Checkbox Group widget":{"color":"88054d","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"c9ddc6","name":"MySQL","description":"Issues related to MySQL plugin"},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"8078b0","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"8078b0","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"Widget Styling":{"color":"905420","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"88054d","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"8078b0","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"Import-Export-App":{"color":"15076d","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"2958a4","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"771e69","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"93491f","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Google Sheets":{"color":"8078b0","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"8078b0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"858172","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"8078b0","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"5052f6","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"name":"In App Comms","description":"Issues around communication with appsmith instances","color":"463cca"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8078b0"},"New Datasource":{"color":"60b14c","name":"New Datasource","description":"Requests for new datasources"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"d12d2e","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"26ef4f"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"},"Old App Issues":{"name":"Old App Issues","description":"Issues related to apps old apps a few weeks old and app issues in stale browser session","color":"87ab18"},"Document Viewer Widget":{"name":"Document Viewer Widget","description":"Issues related to Document Viewer Widget","color":"899d4b"},"Radio Group Widget":{"name":"Radio Group Widget","description":"Issues related to radio group widget","color":"b68495"},"Super Admin":{"name":"Super Admin","description":"Issues related to the super admin page","color":"aa95cf"},"Postgres":{"name":"Postgres","description":"Postgres related issues","color":"8078b0"},"REST API plugin":{"name":"REST API plugin","description":"REST API plugin related issues","color":"8078b0"},"New JS Function":{"name":"New JS Function","description":"Issues related to adding a JS Function","color":"8e8aa4"},"Cannot Reproduce Issue":{"color":"93c9cc","name":"Cannot Reproduce Issue","description":"Issues that cannot be reproduced"},"Widget Grouping":{"name":"Widget Grouping","description":"Issues related to Widget Grouping","color":"a49951"},"K8s":{"name":"K8s","description":"Kubernetes related issues","color":"5f318a"},"Docker":{"name":"Docker","description":"Issues related to docker","color":"89b808"},"Camera Widget":{"name":"Camera Widget","description":"Issues and enhancements related to camera widget","color":"e6038e"},"SAAS Plugins":{"name":"SAAS Plugins","description":"Issues related to SAAS Plugins","color":"ef9c9d"},"JS Promises":{"name":"JS Promises","description":"Issues related to promises","color":"d7771f"},"OnPageLoad":{"name":"OnPageLoad","description":"OnPageLoad issues on functions and queries","color":"50559d"},"JS Usability":{"name":"JS Usability","description":"usability issues with JS editor and JS elsewhere","color":"a302b0"},"Currency Input Widget":{"name":"Currency Input Widget","description":"Issues related to currency input widget","color":"b2164f"},"TreeSelect":{"name":"TreeSelect","description":"Issues related to TreeSelect Widget","color":"a1633e"},"MultiTree Select Widget":{"name":"MultiTree Select Widget","description":"Issues related to MultiTree Select Widget","color":"a1633e"},"Welcome Screen":{"name":"Welcome Screen","description":"Issues related to the welcome screen","color":"3897be"},"Realtime Commenting":{"color":"a70b86","name":"Realtime Commenting","description":"In-app communication between teams"},"Phone Input Widget":{"name":"Phone Input Widget","description":"Issues related to the Phone Input widget","color":"a70b86"},"JSON Form":{"name":"JSON Form","description":"Issue / features related to the JSON form wiget","color":"46b209"},"All Widgets":{"name":"All Widgets","description":"Issues related to all widgets","color":"972b36"},"V1":{"name":"V1","description":"V1","color":"67ab2e"},"Reflow & Resize":{"name":"Reflow & Resize","description":"All issues related to reflow and resize experience","color":"748a13"},"App Theming":{"name":"App Theming","description":"Items that are related to the App level theming controls epic","color":"905420"},"SSO":{"name":"SSO","description":"Issues, requests and enhancements around Single sign-on.","color":"bf019b"},"Multi User Realtime":{"name":"Multi User Realtime","description":"Issues related to multiple users using or editing an application","color":"e7b6ce"},"Templates":{"name":"Templates","description":"Issues related to templates","color":"b7e568"},"Ready for design":{"name":"Ready for design","description":"this issue is ready for design: it contains clear problem statements and other required information","color":"ebf442"},"Support":{"name":"Support","description":"Issues created by the A-force team to address user queries","color":"1740f3"},"Button Group widget":{"name":"Button Group widget","description":"Issue and enhancements related to the button group widget","color":"f17025"},"GraphQL Plugin":{"name":"GraphQL Plugin","description":"Issues related to GraphQL plugin","color":"8078b0"},"DevOps Pod":{"name":"DevOps Pod","description":"Issues related to devops","color":"d956c7"},"medium":{"name":"medium","description":"Issues that frustrate users due to poor UX","color":"23dfd9"},"ArangoDB":{"name":"ArangoDB","description":"Issues related to arangoDB","color":"8078b0"},"Code Refactoring":{"name":"Code Refactoring","description":"Issues related to code refactoring","color":"76310e"},"Progress bar widget":{"name":"Progress bar widget","description":"To track issues related to progress bar","color":"2d7abf"},"Audio Recorder Widget":{"name":"Audio Recorder Widget","description":"Issues related to Audio Recorder Widget","color":"9accef"},"Airtable":{"name":"Airtable","description":"Issues for Airtable","color":"60885f"},"RBAC":{"name":"RBAC","description":"Issues, requests and enhancements around RBAC.","color":"9211c3"},"Canvas / Grid":{"name":"Canvas / Grid","description":"Issues related to the canvas","color":"16b092"},"Email Config":{"name":"Email Config","description":"Issues related to configuring the email service","color":"2a21d1"},"CURL":{"name":"CURL","description":"Issues related to CURL impor","color":"60885f"},"Canvas Zooms":{"name":"Canvas Zooms","description":"Issues related to zooming the canvas","color":"e6038e"},"business":{"name":"business","description":"Features that will be a part of our business edition","color":"cd59eb"},"Action Pod":{"name":"Action Pod","description":"","color":"ee2e36"},"AutomationGap1":{"color":"a5e07c","name":"AutomationGap1","description":"Issues that needs automated tests"},"A-Force11":{"name":"A-Force11","description":"Issues raised by A-Force team","color":"d667b6"},"Business Edition":{"name":"Business Edition","description":"Features that will be a part of our business edition","color":"89bb6c"},"storeValue":{"name":"storeValue","description":"Issues related to the store value function","color":"5d3e66"},"Tests":{"name":"Tests","description":"test item","color":"1c6990"},"DynamoDB":{"name":"DynamoDB","description":"Issues that are related to DynamoDB should have this label","color":"60885f"},"Design System Pod":{"name":"Design System Pod","description":"Appsmith design system related issues","color":"706f03"},"ABAC":{"color":"e009a5","name":"ABAC","description":"User permissions and access controls"},"Backup & Restore":{"name":"Backup & Restore","description":"Issues related to backup and restore","color":"86874d"},"Billing":{"name":"Billing","description":"Billing infrastructure and flows for Business Edition and Trial users","color":"d2bc40"},"Datatype issue":{"name":"Datatype issue","description":"Issues that have risen because data types weren't handled","color":"60885f"},"OAuth":{"name":"OAuth","description":"OAuth related bugs or features","color":"60885f"},"Table Widget V2":{"name":"Table Widget V2","description":"Issues related to Table Widget V2","color":"3a7192"},"IDE Navigation":{"name":"IDE Navigation","description":"Issues/feature requests related to IDE navigation, and context switching","color":"bc0cba"},"Query performance":{"name":"Query performance","description":"Issues that have to do with lack in performance of query execution","color":"e4d966"},"SAAS Manager App":{"name":"SAAS Manager App","description":"Issues with the SAAS manager app","color":"d427db"},"Twilio":{"name":"Twilio","description":"Issues related to Twilio integration","color":"23ba8d"},"Hubspot":{"name":"Hubspot","description":"Issues related to Hubspot integration","color":"60885f"},"Zendesk":{"name":"Zendesk","description":"Issues related to Zendesk integration","color":"60885f"},"Entity Refactor":{"name":"Entity Refactor","description":"Issues related to refactor logic","color":"418fa4"},"Branding":{"name":"Branding","description":"All issues under branding and whitelabelling appsmith ecosystem","color":"7aaaf1"},"Map Chart Widget":{"name":"Map Chart Widget","description":"Issues related to Map Chart Widgets","color":"c8397f"},"Product Catchup":{"name":"Product Catchup","description":"Issues created in the product catchup","color":"29cd2c"},"Framework Functions":{"name":"Framework Functions","description":"Issues related to internal functions like showAlert(), navigateTo() etc...","color":"c25a09"},"Frontend Libraries Upgrade":{"name":"Frontend Libraries Upgrade","description":"Issues related to frontend libraries upgrade","color":"ede1fc"},"Audit Logs":{"name":"Audit Logs","description":"Audit trails to ensure data security","color":"f3fd62"},"MsSQL":{"name":"MsSQL","description":"Issues related to MsSQL plugin","color":"8078b0"},"Data Platform Pod":{"name":"Data Platform Pod","description":"Issues related to the underlying data platform","color":"3f8c3a"},"Integrations Pod":{"name":"Integrations Pod","description":"Issues related to a specific integration","color":"5dbbb1"},"Datasource Environments":{"name":"Datasource Environments","description":"Issues related to datasource environments","color":"bb7a14"},"Elastic Search":{"name":"Elastic Search","description":"Issues related to the elastic search datasource","color":"8078b0"},"Core Query Execution":{"color":"418fa4","name":"Core Query Execution","description":"Issues related to the execution of all queries"},"Query Management":{"name":"Query Management","description":"Issues related to the CRUD of actions or queries","color":"6a5b42"},"Query Settings":{"name":"Query Settings","description":"Issues related to the settings of all queries","color":"c7da7a"},"Code Editor":{"name":"Code Editor","description":"Issues related to the code editor","color":"4ca16e"},"Query Forms":{"color":"12b253","name":"Query Forms","description":"Isuses related to the query forms"},"JS Objects":{"color":"22962c","name":"JS Objects","description":"Issues related to JS Objects"},"JS Evaluation":{"color":"22962c","name":"JS Evaluation","description":"Issues related to JS evaluation on the platform"},"SmartSubstitution":{"name":"SmartSubstitution","description":"Issues related to Smart substitution of mustache bindings in queries","color":"e4d966"},"Query Generation":{"name":"Query Generation","description":"Issues related to query generation","color":"e4d966"},"Suggested Widgets":{"name":"Suggested Widgets","description":"Issues related to suggesting widgets based on query response","color":"e4d966"},"Page load executions":{"name":"Page load executions","description":"Issues related to page load execution","color":"5696b2"},"Code Scanner Widget":{"name":"Code Scanner Widget","description":"Issues related to code scanner widget","color":"9bc1a0"},"Clean URLs":{"name":"Clean URLs","description":"Issues related to clean URLs epic","color":"112623"},"Widget keyboard accessibility":{"name":"Widget keyboard accessibility","description":"All issues related to keyboard accessibility in widgets","color":"b626fd"},"Connection pool":{"name":"Connection pool","description":"issues to do with connection pooling of various plugins","color":"94fe36"},"List Widget V2":{"name":"List Widget V2","description":"Issues related to the list widget v2","color":"adaaf7"},"Auto Height":{"name":"Auto Height","description":"Issues related to dynamic height of widgets","color":"5149cf"},"cypress_failed_test":{"name":"cypress_failed_test","description":"Cypress failed tests","color":"4745d5"},"Needs validation":{"name":"Needs validation","description":"Needs problem validation before being picked up","color":"66673d"},"Slider Widget":{"name":"Slider Widget","description":"Issues raised for slider widgets.","color":"2eef5f"},"Multitenancy":{"name":"Multitenancy","description":"Support multitenancy within single appsmith instance","color":"8c49a9"},"Git Pod":{"name":"Git Pod","description":"Anything related to git sync","color":"2e5ba4"},"Mobile Pod":{"name":"Mobile Pod","description":"All issues related to mobile responsiveness","color":"6c97fd"},"Responsive Widget":{"name":"Responsive Widget","description":"All issues related to widget responsiveness","color":"d12d2e"},"Responsive Canvas":{"name":"Responsive Canvas","description":"All issues related to canvas responsiveness","color":"45a0a8"},"Conversion Algorithm":{"name":"Conversion Algorithm","description":"All issue related to converting app from fixed to flex mode & vice versa","color":"d12d2e"},"Spacing":{"name":"Spacing","description":"All issue related to spacing between widgets in auto layout","color":"d12d2e"},"Browser specific":{"name":"Browser specific","description":"All issue related to browser","color":"d12d2e"},"Error Handling":{"name":"Error Handling","description":"Issues related to error handling","color":"4e1872"},"Performance infra":{"name":"Performance infra","description":"all issue related to the performance infra","color":"8a60f6"},"DSL Update":{"name":"DSL Update","description":"Issues related to storing and updating the DSL","color":"e16cf3"},"AST-frontend":{"name":"AST-frontend","description":"Issues related to maintaining AST logic","color":"434a3a"},"AST-backend":{"name":"AST-backend","description":"Backend issues related to AST parsing","color":"c476eb"},"MariaDB":{"name":"MariaDB","description":"MariaDB datasource","color":"8428c3"},"Billing & Usage Pod":{"name":"Billing & Usage Pod","description":"Issues pertaining to licensing, billing, usage across self serve and enterprise customers","color":"256808"},"ADS Component Issue":{"name":"ADS Component Issue","description":"Issues which are caused due to ADS components","color":"d89119"},"Regressed":{"color":"723fd0","name":"Regressed","description":"Scenarios that were working before but have now regressed"},"Needs RCA":{"name":"Needs RCA","description":"a critical or high priority issue that needs an RCA","color":"2cc68f"},"Custom JS Libraries":{"name":"Custom JS Libraries","description":"Issues related to adding custom JS library","color":"bacb6d"},"Integrations Pod General":{"name":"Integrations Pod General","description":"Issues related to the Integrations Pod that don't fit into other tags.","color":"287823"},"Performance Pod":{"name":"Performance Pod","description":"All things related to Appsmith performance","color":"b5a25d"},"Performance":{"name":"Performance","description":"Issues related to performance","color":"9a18d7"},"File upload issues":{"name":"File upload issues","description":"Issues related to uploading any type of files from within Appsmith","color":"8154df"},"Action Selector":{"name":"Action Selector","description":"Issues related to action selector on the property pane","color":"2f9e20"},"Widget design system":{"name":"Widget design system","description":"","color":"11cc90"},"Deploy App":{"name":"Deploy App","description":"Issues related to app deployment","color":"6f6152"},"Community Reported":{"name":"Community Reported","description":"issues reported by community members","color":"1402e5"},"JS Function execution":{"name":"JS Function execution","description":"JS function execution","color":"7c2de1"},"Self Serve":{"name":"Self Serve","description":"For all issues related to self-serve flow for business edition","color":"4dacfc"},"Self Serve 1.0":{"name":"Self Serve 1.0","description":"For all issues related to v1 of the self serve project","color":"ae839e"},"CE Instance":{"name":"CE Instance","description":"For all issues relating to usage, licensing or billing on the CE instance","color":"d2bc40"},"Customer Portal":{"name":"Customer Portal","description":"For all tasks/issues pertaining to customer.appsmith.com","color":"d2bc40"},"Cloud Services":{"name":"Cloud Services","description":"For all tasks/issues on Appsmith cloud-services relating to licensing, usage and billing","color":"d2bc40"},"Billing Integrations":{"name":"Billing Integrations","description":"For all issues relating to 3P integrations Appsmith is using for billing & usage","color":"d2bc40"},"One-click Binding":{"name":"One-click Binding","description":"Issues related to the One click binding epic","color":"f1661c"},"Airgap":{"name":"Airgap","description":"Tickets related to supporting air-gapped Appsmith instances","color":"1cb294"},"SMTP plugin":{"name":"SMTP plugin","description":"Issues related to SMTP plugin","color":"541457"},"AWS AMI":{"name":"AWS AMI","description":"Issues Related to AWS AMI","color":"b44680"},"Old widget version":{"name":"Old widget version","description":"Use this label to raise issue specific only to an older version of a widget","color":"ff3814"},"Enterprise Billing":{"name":"Enterprise Billing","description":"To track all tasks/issues related to licensing & billing for enterprise customers","color":"14c156"},"Appsmith Business Cloud":{"name":"Appsmith Business Cloud","description":"Issues related to our business cloud offering","color":"89bb6c"},"Oracle SQL DB":{"name":"Oracle SQL DB","description":"Issues related to the Oracle plugin","color":"cbabcb"},"Community Contributor":{"name":"Community Contributor","description":"Meant to track issues that are assigned to external contributors","color":"149ab6"},"widget vertical alignment":{"name":"widget vertical alignment","description":"All issue related widget vertical alignment on the auto layout canvas","color":"d12d2e"},"Observability":{"name":"Observability","description":"Issues related to observability on the Appsmith instance","color":"dff913"},"Checkbox Component":{"name":"Checkbox Component","description":"This labels deals with checkbox component in wds package","color":"75a401"},"In-app ramps":{"name":"In-app ramps","description":"For all tasks/issues relating to adding in-app ramps in the community edition of the product","color":"8abae0"},"Analytics Improvements":{"name":"Analytics Improvements","description":"For all tasks focused on improving our overall analytics and fixing any issues ","color":"29b8ed"},"WDS team":{"name":"WDS team","description":"","color":"8d675a"},"Enterprise Edition":{"name":"Enterprise Edition","description":"Features that will be supported in Enterprise Edition only","color":"984f5e"},"Query filter":{"name":"Query filter","description":"Issues related to query filtering, e.g., WHERE clause","color":"a15134"},"Keyboard accessibility ":{"name":"Keyboard accessibility ","description":"All issue related to ADS component keyboard accessibility","color":"2ba696"},"Toggle button":{"name":"Toggle button","description":"All issue related to ADS toggle button","color":"edc47f"},"1-click upgrade":{"name":"1-click upgrade","description":"For all issues/tasks related to 1-click upgrade & downgrade project","color":"129082"},"Feature Flagging":{"name":"Feature Flagging","description":"Anything related feature flagging","color":"77443f"},"SCIM":{"name":"SCIM","description":"Label to collate our SCIM issues","color":"61a852"},"ADS Category Token":{"name":"ADS Category Token","description":"All issues related appsmith design system category tokens","color":"920961"},"ADS Component Documentation":{"name":"ADS Component Documentation","description":"All issues Appsmith design system component documentation","color":"64c46a"},"ADS Migration":{"name":"ADS Migration","description":"All issues related to Appsmith design system migration","color":"b082d6"},"ADS Deduplication ":{"name":"ADS Deduplication ","description":"Replacing component with ADS components","color":"b082d6"},"ADS Revamp":{"name":"ADS Revamp","description":"All issues related to ads revamp. ","color":"b082d6"},"ADS Deduplication":{"name":"ADS Deduplication","description":"Replacing component with ADS components","color":"b082d6"},"ADS Grayscale":{"name":"ADS Grayscale","description":"Support grayscale color changes","color":"b03577"},"ADS Unit Test":{"name":"ADS Unit Test","description":"All issue related ads unit cases ","color":"b082d6"},"ADS Components":{"name":"ADS Components","description":"All issues related ADS components","color":"b082d6"},"Widget Discoverability":{"name":"Widget Discoverability","description":"Issues related to Widget Discoverability","color":"7b55ce"},"Widget setter method":{"name":"Widget setter method","description":"Issues with widget property setters","color":"8dce87"},"License":{"name":"License","description":"For all issues/tasks related to licensing of appsmith-ee edition","color":"90ee98"},"Templates pod":{"name":"Templates pod","description":"Issues related to Templates","color":"b7e568"},"Community template":{"name":"Community template","description":"Label for development of community templates and its integration to platform","color":"8a0510"},"DocumentDB":{"name":"DocumentDB","description":"Issues related to support DocumentDB in Appsmith Data layer","color":"2c8b56"},"Multiple Environments":{"name":"Multiple Environments","description":"Issues or tasks related to multiple environments","color":"4e972b"},"Platformization":{"name":"Platformization","description":"Issues or tasks related to platformization of Appsmith codebase","color":"4e972b"},"Activation - datasources":{"name":"Activation - datasources","description":"issues related to activation projects","color":"7c7ace"},"Partial-import-export":{"name":"Partial-import-export","description":"Label for granular reusability.","color":"1e439c"},"AI":{"name":"AI","description":"All tasks related to AI","color":"69c7ca"},"Custom environments":{"name":"Custom environments","description":"Issues with creating or working with custom environments","color":"2137d6"},"ADS Typography":{"name":"ADS Typography","description":"All issue related typographical changes","color":"2dbe8d"},"Auto Layout":{"name":"Auto Layout","description":"Issues relates to auto layout","color":"92cf8c"},"Heroku":{"name":"Heroku","description":"Issues related to Heroku","color":"a81b69"},"ADS Visual Styles":{"name":"ADS Visual Styles","description":"All issues related to ADS visual styles","color":"d3da89"},"ADS Component Design":{"name":"ADS Component Design","description":"All issue related to component design","color":"5cc91e"},"Modal Component":{"name":"Modal Component","description":"All issue related to ads modal component","color":"ee63f3"},"App setting":{"name":"App setting","description":"Related to app settings panel within the app","color":"144206"},"BE instance":{"name":"BE instance","description":"For all issues related to license, billing on BE instance","color":"ae8f98"},"Workflows":{"name":"Workflows","description":"For all issues related to the Workflows feature","color":"ae2aa6"},"Schema":{"name":"Schema","description":"Issues related to database schema","color":"c470c2"},"AI Pod":{"name":"AI Pod","description":"Pod for all AI related tasks","color":"d18528"},"Fixed layout":{"name":"Fixed layout","description":"issues related to fixed layout","color":"b66681"},"Anvil layout":{"name":"Anvil layout","description":"issues related to the new layout system anvil","color":"722bf0"}},"success":true} \ No newline at end of file diff --git a/README.md b/README.md index 6972103b3f..5db2e83c35 100644 --- a/README.md +++ b/README.md @@ -94,20 +94,20 @@ Let's build great software together. [![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa) [![ApekshaBhosale](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7846888?v=4&w=50&h=50&mask=circle)](https://github.com/ApekshaBhosale) [![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith) +[![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122) [![Parthvi12](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80334441?v=4&w=50&h=50&mask=circle)](https://github.com/Parthvi12) [![somangshu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11089579?v=4&w=50&h=50&mask=circle)](https://github.com/somangshu) -[![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122) [![pratapaprasanna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15846947?v=4&w=50&h=50&mask=circle)](https://github.com/pratapaprasanna) [![pranavkanade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13262095?v=4&w=50&h=50&mask=circle)](https://github.com/pranavkanade) -[![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith) [![marks0351](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35134347?v=4&w=50&h=50&mask=circle)](https://github.com/marks0351) +[![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith) [![nsarupr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20905988?v=4&w=50&h=50&mask=circle)](https://github.com/nsarupr) [![ashit-rath](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/88306433?v=4&w=50&h=50&mask=circle)](https://github.com/ashit-rath) [![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish) [![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal) [![rajatagrawal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1189106?v=4&w=50&h=50&mask=circle)](https://github.com/rajatagrawal) -[![KelvinOm](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11555074?v=4&w=50&h=50&mask=circle)](https://github.com/KelvinOm) [![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn) +[![KelvinOm](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11555074?v=4&w=50&h=50&mask=circle)](https://github.com/KelvinOm) [![areyabhishek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30255708?v=4&w=50&h=50&mask=circle)](https://github.com/areyabhishek) [![Druthi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20187542?v=4&w=50&h=50&mask=circle)](https://github.com/Druthi) [![subrata71](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3524599?v=4&w=50&h=50&mask=circle)](https://github.com/subrata71) @@ -124,7 +124,6 @@ Let's build great software together. [![dvj1988](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/18716465?v=4&w=50&h=50&mask=circle)](https://github.com/dvj1988) [![ramsaptami](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79509062?v=4&w=50&h=50&mask=circle)](https://github.com/ramsaptami) [![rohan-arthur](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/94514895?v=4&w=50&h=50&mask=circle)](https://github.com/rohan-arthur) -[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith) [![danciaclara](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/32227135?v=4&w=50&h=50&mask=circle)](https://github.com/danciaclara) [![kocharrahul7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20532920?v=4&w=50&h=50&mask=circle)](https://github.com/kocharrahul7) [![riteshkew](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20280935?v=4&w=50&h=50&mask=circle)](https://github.com/riteshkew) @@ -148,7 +147,6 @@ Let's build great software together. [![RoopKrrish9696](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16741628?v=4&w=50&h=50&mask=circle)](https://github.com/RoopKrrish9696) [![bharath31](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17562726?v=4&w=50&h=50&mask=circle)](https://github.com/bharath31) [![GreenFlux](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/24459976?v=4&w=50&h=50&mask=circle)](https://github.com/GreenFlux) -[![z-l-t](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/32358743?v=4&w=50&h=50&mask=circle)](https://github.com/z-l-t) [![shadabbuchh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/39921438?v=4&w=50&h=50&mask=circle)](https://github.com/shadabbuchh) [![harshilp24](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/43263619?v=4&w=50&h=50&mask=circle)](https://github.com/harshilp24) [![appsmith-bot](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74705725?v=4&w=50&h=50&mask=circle)](https://github.com/appsmith-bot) @@ -238,6 +236,7 @@ Let's build great software together. [![AR10X](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/59757602?v=4&w=50&h=50&mask=circle)](https://github.com/AR10X) [![shreemaan-abhishek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61597896?v=4&w=50&h=50&mask=circle)](https://github.com/shreemaan-abhishek) [![A-Scratchy](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25309929?v=4&w=50&h=50&mask=circle)](https://github.com/A-Scratchy) +[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith) [![manish535](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14247890?v=4&w=50&h=50&mask=circle)](https://github.com/manish535) [![shinnlok](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/860730?v=4&w=50&h=50&mask=circle)](https://github.com/shinnlok) [![suhasranganath](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7945917?v=4&w=50&h=50&mask=circle)](https://github.com/suhasranganath) @@ -300,6 +299,7 @@ Let's build great software together. [![felixsuarez0727](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25110207?v=4&w=50&h=50&mask=circle)](https://github.com/felixsuarez0727) [![gitstart](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1501599?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart) [![Gretel8](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/73624682?v=4&w=50&h=50&mask=circle)](https://github.com/Gretel8) +[![danguilherme](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1488378?v=4&w=50&h=50&mask=circle)](https://github.com/danguilherme) [![harshmange44](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/47944044?v=4&w=50&h=50&mask=circle)](https://github.com/harshmange44) [![indrajitbnikam](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/24988127?v=4&w=50&h=50&mask=circle)](https://github.com/indrajitbnikam) [![ishaanmehta4](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61118240?v=4&w=50&h=50&mask=circle)](https://github.com/ishaanmehta4) @@ -317,7 +317,7 @@ Let's build great software together. [![micarner](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20423592?v=4&w=50&h=50&mask=circle)](https://github.com/micarner) [![me-heer](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/50193156?v=4&w=50&h=50&mask=circle)](https://github.com/me-heer) [![MuhammadAakash](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/64223049?v=4&w=50&h=50&mask=circle)](https://github.com/MuhammadAakash) -[![Hammad9219](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/66510387?v=4&w=50&h=50&mask=circle)](https://github.com/Hammad9219) +[![dammahhammad](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/66510387?v=4&w=50&h=50&mask=circle)](https://github.com/dammahhammad) [![moulik-deepsource](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/68378241?v=4&w=50&h=50&mask=circle)](https://github.com/moulik-deepsource) [![Nandanha](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5731505?v=4&w=50&h=50&mask=circle)](https://github.com/Nandanha) [![wasabigeek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4256705?v=4&w=50&h=50&mask=circle)](https://github.com/wasabigeek) @@ -354,6 +354,7 @@ Let's build great software together. [![Yash-Bhange](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/60029585?v=4&w=50&h=50&mask=circle)](https://github.com/Yash-Bhange) [![YogeshJayaseelan](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35128442?v=4&w=50&h=50&mask=circle)](https://github.com/YogeshJayaseelan) [![Ian-Yy](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/23068019?v=4&w=50&h=50&mask=circle)](https://github.com/Ian-Yy) +[![danceAndJive](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/99446612?v=4&w=50&h=50&mask=circle)](https://github.com/danceAndJive) [![devnamrits](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/54183698?v=4&w=50&h=50&mask=circle)](https://github.com/devnamrits) [![deepakchethan](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20895544?v=4&w=50&h=50&mask=circle)](https://github.com/deepakchethan) [![IAmAnubhavSaini](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1573771?v=4&w=50&h=50&mask=circle)](https://github.com/IAmAnubhavSaini) diff --git a/app/client/cypress/e2e/Regression/Apps/CommunityIssues_Spec.ts b/app/client/cypress/e2e/Regression/Apps/CommunityIssues_Spec.ts index 09bf4042e9..2fa6410cbf 100644 --- a/app/client/cypress/e2e/Regression/Apps/CommunityIssues_Spec.ts +++ b/app/client/cypress/e2e/Regression/Apps/CommunityIssues_Spec.ts @@ -31,9 +31,9 @@ describe("AForce - Community Issues page validations", function () { homePage.ImportApp("CommunityIssuesExport.json"); assertHelper .WaitForNetworkCall("importNewApplication") - .then((interception: any) => { + .then((response: any) => { agHelper.Sleep(); - const { isPartialImport } = interception.response.body.data; + const { isPartialImport } = response.body.data; if (isPartialImport) { // should reconnect modal dataSources.ReconnectSingleDSNAssert( diff --git a/app/client/cypress/e2e/Regression/Apps/EchoApiCMS_spec.js b/app/client/cypress/e2e/Regression/Apps/EchoApiCMS_spec.js index 0c2eafa100..24a371ad5c 100644 --- a/app/client/cypress/e2e/Regression/Apps/EchoApiCMS_spec.js +++ b/app/client/cypress/e2e/Regression/Apps/EchoApiCMS_spec.js @@ -117,26 +117,23 @@ describe("Content Management System App", function () { gitSync.CreateNConnectToGit(repoName); cy.get("@gitRepoName").then((repName) => { repoName = repName; - }); - cy.latestDeployPreview(); - cy.wait(2000); - cy.xpath("//span[text()='Curt50@gmail.com']") - .should("be.visible") - .click({ force: true }); - cy.get(appPage.mailButton).closest("div").click(); - cy.xpath(appPage.sendMailText).should("be.visible"); - cy.xpath(appPage.subjectField).type("Test"); - cy.get(appPage.contentField) - .last() - .find("textarea") - .type("Task completed", { force: true }); - cy.get(appPage.confirmButton).closest("div").click({ force: true }); - cy.get(appPage.closeButton).closest("div").click({ force: true }); - deployMode.NavigateBacktoEditor(); - }); - after(() => { - //clean up - gitSync.DeleteTestGithubRepo(repoName); + cy.latestDeployPreview(); + cy.wait(2000); + cy.xpath("//span[text()='Curt50@gmail.com']") + .should("be.visible") + .click({ force: true }); + cy.get(appPage.mailButton).closest("div").click(); + cy.xpath(appPage.sendMailText).should("be.visible"); + cy.xpath(appPage.subjectField).type("Test"); + cy.get(appPage.contentField) + .last() + .find("textarea") + .type("Task completed", { force: true }); + cy.get(appPage.confirmButton).closest("div").click({ force: true }); + cy.get(appPage.closeButton).closest("div").click({ force: true }); + deployMode.NavigateBacktoEditor(); + gitSync.DeleteTestGithubRepo(repoName); + }); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/ActionSelector_JsToNonJSMode_2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/ActionSelector_JsToNonJSMode_2_spec.ts index b129d73e37..9668a80e37 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/ActionSelector_JsToNonJSMode_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/ActionSelector_JsToNonJSMode_2_spec.ts @@ -1,19 +1,16 @@ import { agHelper, + draggableWidgets, entityExplorer, - locators, propPane, } from "../../../../support/Objects/ObjectsCore"; describe("JS to non-JS mode in Action Selector", () => { before(() => { - agHelper.AddDsl("promisesBtnDsl", locators._buttonByText("Submit")); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON); }); it("1. shows fields for navigate to from js to non-js mode", () => { - entityExplorer.SelectEntityByName("Page1", "Pages"); - entityExplorer.SelectEntityByName("Button1", "Widgets"); - propPane.EnterJSContext("onClick", "{{navigateTo()}}", true, false); propPane.ToggleJSMode("onClick", false); diff --git a/app/client/cypress/e2e/Regression/ClientSide/AdminSettings/Admin_settings_spec.js b/app/client/cypress/e2e/Regression/ClientSide/AdminSettings/Admin_settings_spec.js index 3f790297ea..7201e85f28 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AdminSettings/Admin_settings_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/AdminSettings/Admin_settings_spec.js @@ -36,7 +36,7 @@ describe("Admin settings page", function () { cy.visit("/settings/general", { timeout: 60000 }); // non super users are redirected to home page cy.url().should("contain", "/applications"); - cy.LogOut(); + cy.LogOut(false); }); it("3. Should test that settings page is redirected to default tab", () => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigation_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigation_spec.ts index 393c80467a..b8016972a9 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigation_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigation_spec.ts @@ -48,9 +48,9 @@ describe("General checks for app navigation", function () { homePage.ImportApp("appNavigationTestingApp.json"); assertHelper .WaitForNetworkCall("@importNewApplication") - .then((interception) => { + .then((response) => { agHelper.Sleep(); - const { isPartialImport } = interception.response.body.data; + const { isPartialImport } = response.body.data; if (isPartialImport) { homePage.AssertNCloseImport(); } else { diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts index abc60b9bf5..e6e0b7cb4c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts @@ -14,9 +14,9 @@ describe("Test Sidebar navigation style", function () { homePage.ImportApp("appNavigationTestingAppWithLongPageNamesAndTitle.json"); assertHelper .WaitForNetworkCall("@importNewApplication") - .then((interception) => { + .then((response) => { agHelper.Sleep(); - const { isPartialImport } = interception.response.body.data; + const { isPartialImport } = response.body.data; if (isPartialImport) { homePage.AssertNCloseImport(); } else { diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts index 0eb5410943..9ff0e9db70 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts @@ -15,11 +15,9 @@ describe("Test Top + Inline navigation style", function () { assertHelper .WaitForNetworkCall("@importNewApplication") - .then((interception) => { + .then((response) => { agHelper.Sleep(); - - const { isPartialImport } = interception.response.body.data; - + const { isPartialImport } = response.body.data; if (isPartialImport) { homePage.AssertNCloseImport(); } else { diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts index 49d5e47dc5..1cf689aeaa 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts @@ -14,9 +14,9 @@ describe("Test Top + Stacked navigation style", function () { homePage.ImportApp("appNavigationTestingAppWithLongPageNamesAndTitle.json"); assertHelper .WaitForNetworkCall("importNewApplication") - .then((interception: any) => { + .then((response: any) => { agHelper.Sleep(); - const { isPartialImport } = interception.response.body.data; + const { isPartialImport } = response.body.data; if (isPartialImport) { homePage.AssertNCloseImport(); } else { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts index a3826f622b..2f23daf1c4 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts @@ -40,22 +40,22 @@ describe("Autocomplete tests for setters", () => { agHelper.GetElementsNAssertTextPresence( locators._hints, - "Button1.setColor()", + "Button1.setColor", ); agHelper.GetElementsNAssertTextPresence( locators._hints, - "Button1.setDisabled()", + "Button1.setDisabled", ); agHelper.GetElementsNAssertTextPresence( locators._hints, - "Button1.setVisibility()", + "Button1.setVisibility", ); agHelper.RemoveCharsNType(locators._codeMirrorTextArea, 7, "Input1.set"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setValue()"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled()"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setVisibility()"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setValue"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setVisibility"); agHelper.RemoveCharsNType( locators._codeMirrorTextArea, @@ -63,14 +63,14 @@ describe("Autocomplete tests for setters", () => { "Checkbox1.set", ); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setValue()"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled()"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setVisibility()"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setValue"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setVisibility"); agHelper.RemoveCharsNType(locators._codeMirrorTextArea, 13, "Switch1.set"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled()"); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setRequired()"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setRequired"); agHelper.Sleep(); //a bit for time for CI }); @@ -78,6 +78,6 @@ describe("Autocomplete tests for setters", () => { entityExplorer.DragDropWidgetNVerify(draggableWidgets.INPUT_V2, 500, 500); entityExplorer.SelectEntityByName("Button1"); propPane.EnterJSContext("onClick", "{{Input1.set", true, false); - agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled()"); + agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled"); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Bugs_AC_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Bugs_AC_Spec.ts index caee6aa86d..2dbf74a72e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Bugs_AC_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Bugs_AC_Spec.ts @@ -92,7 +92,7 @@ describe("Autocomplete bug fixes", function () { function () { entityExplorer.ExpandCollapseEntity("Libraries"); installer.OpenInstaller(); - installer.installLibrary("uuidjs", "UUID"); + installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); entityExplorer.SelectEntityByName("Text1"); propPane.TypeTextIntoField("Text", "{{UUI"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC1_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC1_spec.ts index 0a46d4af68..73a39e4106 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC1_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC1_spec.ts @@ -130,22 +130,22 @@ describe("Autocomplete tests", () => { // eval function verification { type: "eval", - expected: "eval()", + expected: "eval", haveOrNotHave: false, }, { type: "Blob", - expected: "Blob()", + expected: "Blob", haveOrNotHave: true, }, { type: "FormData", - expected: "FormData()", + expected: "FormData", haveOrNotHave: true, }, { type: "FileReader", - expected: "FileReader()", + expected: "FileReader", haveOrNotHave: true, }, ]; @@ -167,7 +167,7 @@ describe("Autocomplete tests", () => { agHelper.GetNClick(jsEditor._lineinJsEditor(5)); agHelper.TypeText(locators._codeMirrorTextArea, "this."); - ["myFun2()", "myVar1", "myVar2"].forEach((element, index) => { + ["myFun2", "myVar1", "myVar2"].forEach((element, index) => { agHelper.AssertContains(element); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC2_spec.ts index 23a1c1b117..2b9839205c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/JS_AC2_spec.ts @@ -49,7 +49,7 @@ describe("Autocomplete tests", () => { 0, ); - agHelper.GetNAssertElementText(locators._hints, "myFun1()", "have.text", 4); + agHelper.GetNAssertElementText(locators._hints, "myFun1", "have.text", 4); // Same check in JSObject1 entityExplorer.SelectEntityByName("JSObject1", "Queries/JS"); @@ -65,7 +65,7 @@ describe("Autocomplete tests", () => { 0, ); - agHelper.GetNAssertElementText(locators._hints, "myFun1()", "have.text", 4); + agHelper.GetNAssertElementText(locators._hints, "myFun1", "have.text", 4); entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "JSObject1", action: "Delete", diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/PropertyPaneSuggestion_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/PropertyPaneSuggestion_spec.ts index 0664635888..ff037f1084 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/PropertyPaneSuggestion_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/PropertyPaneSuggestion_spec.ts @@ -46,6 +46,6 @@ describe("Property Pane Suggestions", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore cy.get("body").tab(); - propPane.ValidatePropertyFieldValue("Label", "{{appsmith}}"); + propPane.ValidatePropertyFieldValue("Label", "{{JSObject1}}"); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug24486_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug24486_Spec.ts index e9addda8c9..e82e0417f2 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug24486_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug24486_Spec.ts @@ -19,7 +19,7 @@ describe("Issue 24486 - Issue with Export Application", () => { }); }); - it("Should export the current branch", () => { + it("1. Should export the current branch", () => { _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.BUTTON, 200, 200); _.gitSync.CreateNConnectToGit(); cy.get("@gitRepoName").then((repName) => { @@ -60,7 +60,7 @@ describe("Issue 24486 - Issue with Export Application", () => { }); }); - it("Should import the exported branch", () => { + it("2. Should import the exported branch", () => { ws2Name = "Bug24486-w2" + guid; _.homePage.CreateNewWorkspace(ws2Name, true); _.homePage.ImportApp("app-b24486.json", ws2Name); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28287_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28287_Spec.ts new file mode 100644 index 0000000000..8d443d7879 --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28287_Spec.ts @@ -0,0 +1,46 @@ +import { + dataSources, + agHelper, + entityExplorer, + propPane, + debuggerHelper, + draggableWidgets, +} from "../../../../support/Objects/ObjectsCore"; + +let dsName: any; +let queryName: string; + +describe("Bug 28287: Binding query to widget, check query response in query editor on page load", function () { + before("Drag drop a text widget", () => { + entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT); + }); + + it("1. Check query response in query editor on page load", () => { + agHelper.GenerateUUID(); + cy.get("@guid").then((uuid) => { + dataSources.CreateDataSource("Postgres"); + cy.get("@dsName").then(($dsName) => { + dsName = $dsName; + }); + queryName = `Query_${uuid}`; + dataSources.CreateQueryAfterDSSaved( + "SELECT * FROM users LIMIT 10", + queryName, + ); + dataSources.ToggleUsePreparedStatement(false); + + entityExplorer.SelectEntityByName("Text1"); + propPane.TypeTextIntoField("Text", `{{${queryName}.data}}`); + + agHelper.RefreshPage(); + + agHelper.Sleep(1000); + + entityExplorer.SelectEntityByName(queryName, "Queries/JS"); + debuggerHelper.ClickDebuggerIcon(); + debuggerHelper.ClickResponseTab(); + + agHelper.AssertElementVisibility(dataSources._queryResponse("TABLE")); + }); + }); +}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts index fbea935bdb..6234eb0e30 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/GitBugs_Spec.ts @@ -1,4 +1,3 @@ -import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags"; import * as _ from "../../../../support/Objects/ObjectsCore"; let repoName: any; @@ -47,6 +46,7 @@ describe("Git Bugs", function () { }); }); }); + it("3. Bug 18376: navigateTo fails to set queryParams if the app is connected to Git", () => { _.entityExplorer.AddNewPage(); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT); @@ -143,60 +143,8 @@ describe("Git Bugs", function () { _.gitSync.SwitchGitBranch("origin/test-24486", false, true); }); - it("7. Bug 26038 - 1 : Simultaneous git status and remote compare api calls", function () { - featureFlagIntercept({ - release_git_status_lite_enabled: true, - }); - - cy.wait(1000); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/status/app/**", - query: { compareRemote: "false" }, - }).as("gitStatusApi"); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/fetch/remote/app/**", - }).as("gitRemoteStatusApi"); - - _.agHelper.GetNClick(_.locators._publishButton); - - cy.wait("@gitStatusApi").then((res1) => { - expect(res1.response).to.have.property("statusCode", 200); - cy.wait("@gitRemoteStatusApi").then((res2) => { - expect(res2.response).to.have.property("statusCode", 200); - - _.agHelper.GetNClick(_.locators._dialogCloseButton); - }); - }); - }); - - it("8. Bug 26038 - 2 : Simultaneous git status and remote compare api calls", function () { - featureFlagIntercept({ - release_git_status_lite_enabled: false, - }); - - _.gitSync.SwitchGitBranch("master"); - - cy.wait(1000); - - cy.intercept({ - method: "GET", - url: "/api/v1/git/status/app/**", - query: { compareRemote: "true" }, - }).as("gitStatusApi"); - - _.agHelper.GetNClick(_.locators._publishButton); - - cy.wait("@gitStatusApi").then((res1) => { - expect(res1.response).to.have.property("statusCode", 200); - _.agHelper.GetNClick(_.locators._dialogCloseButton); - }); - }); - - it("9. Bug 24920: Not able to discard app settings changes for the first time in git connected app ", function () { + it("7. Bug 24920: Not able to discard app settings changes for the first time in git connected app ", function () { + _.gitSync.SwitchGitBranch("master", false, true); // add navigation settings changes _.agHelper.GetNClick(_.appSettings.locators._appSettings); _.agHelper.GetNClick(_.appSettings.locators._navigationSettingsTab); @@ -209,7 +157,7 @@ describe("Git Bugs", function () { _.gitSync.VerifyChangeLog(false); }); - it("10. Bug 23858 : Branch list in git sync modal is not scrollable", function () { + it("8. Bug 23858 : Branch list in git sync modal is not scrollable", function () { // create git branches _.gitSync.CreateGitBranch(tempBranch1, true); _.gitSync.CreateGitBranch(tempBranch2, true); @@ -228,7 +176,7 @@ describe("Git Bugs", function () { _.gitSync.CloseGitSyncModal(); }); - it("10. Bug 24206 : Open repository button is not functional in git sync modal", function () { + it("9. Bug 24206 : Open repository button is not functional in git sync modal", function () { _.gitSync.SwitchGitBranch("master"); _.appSettings.OpenPaneAndChangeTheme("Moon"); _.gitSync.CommitAndPush(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts index 9a20297bcf..57c3edc845 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts @@ -110,8 +110,7 @@ describe("Bug #14299 - The data from the query does not show up on the widget", after( "Verify Deletion of the datasource after all created queries are deleted", () => { - deployMode.NavigateBacktoEditor(); - agHelper.AssertContains("ran successfully"); //runAstros triggered on PageLaoad of Edit page! + deployMode.NavigateBacktoEditor("ran successfully"); //runAstros triggered on PageLaoad of Edit page! entityExplorer.ExpandCollapseEntity("Queries/JS"); entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "JSObject1", diff --git a/app/client/cypress/e2e/Regression/ClientSide/DynamicHeight/List_TextWidget_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/DynamicHeight/List_TextWidget_Spec.ts index 70644210ab..047380b19c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/DynamicHeight/List_TextWidget_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/DynamicHeight/List_TextWidget_Spec.ts @@ -9,7 +9,6 @@ import { describe("Dynamic Height Width validation list widget", function () { it("1. Validate change with auto height width for list widgets", function () { - const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; const textMsg = "Dynamic panel validation for text widget wrt height"; agHelper.AddDsl("DynamicHeightListTextDsl"); @@ -17,16 +16,12 @@ describe("Dynamic Height Width validation list widget", function () { entityExplorer.SelectEntityByName("List1", "Widgets"); //Widgets which were not possible to be added to list widget cannot be pasted/moved into the list widget with multitreeselect entityExplorer.SelectEntityByName("MultiTreeSelect1", "List1"); - agHelper.TypeText(locators._body, `{${modifierKey}}c`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("copy"); agHelper.WaitUntilAllToastsDisappear(); agHelper.Sleep(2000); entityExplorer.SelectEntityByName("List1", "Widgets"); propPane.MoveToTab("Style"); - agHelper.TypeText(locators._body, `{${modifierKey}}v`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("paste"); agHelper.ValidateToastMessage( "This widget cannot be used inside the list widget.", 0, @@ -34,7 +29,7 @@ describe("Dynamic Height Width validation list widget", function () { ); agHelper .GetWidgetCSSHeight(locators._widgetInDeployed(draggableWidgets.LIST)) - .then((currentListHeight: number) => { + .then((currentListHeight: any) => { //Widgets within list widget have no dynamic height agHelper.AssertElementAbsence(propPane._propertyPaneHeightLabel); //Widgets within list widget in existing applications have no dynamic height @@ -49,7 +44,7 @@ describe("Dynamic Height Width validation list widget", function () { propPane.UpdatePropertyFieldValue("Text", textMsg, true); agHelper .GetWidgetCSSHeight(locators._widgetInDeployed(draggableWidgets.LIST)) - .then((updatedListHeight: number) => { + .then((updatedListHeight: any) => { expect(currentListHeight).to.equal(updatedListHeight); }); entityExplorer.SelectEntityByName("Container1", "List1"); @@ -58,34 +53,29 @@ describe("Dynamic Height Width validation list widget", function () { //Widgets when moved into the list widget have no dynamic height entityExplorer.SelectEntityByName("Text3", "Widgets"); propPane.MoveToTab("Style"); - agHelper.TypeText(locators._body, `{${modifierKey}}c`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("copy"); + entityExplorer.SelectEntityByName("List1", "Widgets"); propPane.MoveToTab("Style"); - agHelper.TypeText(locators._body, `{${modifierKey}}v`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("paste"); assertHelper.AssertNetworkStatus("@updateLayout", 200); - entityExplorer.NavigateToSwitcher("Explorer"); + entityExplorer.SelectEntityByName("Text3Copy"); agHelper.AssertElementAbsence(propPane._propertyPaneHeightLabel); - agHelper.TypeText(locators._body, `{${modifierKey}}c`, { - parseSpecialCharSeq: true, - }); - //agHelper.GetElement(locators._body).click({ force: true }); - agHelper.GetElement(locators._canvasBody).click({ force: true }); - agHelper.TypeText(locators._body, `{${modifierKey}}v`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("copy"); + agHelper.WaitUntilAllToastsDisappear(); + agHelper.Sleep(2000); + agHelper.GetNClick(locators._canvasBody); + agHelper.SimulateCopyPaste("paste"); assertHelper.AssertNetworkStatus("@updateLayout"); //Widgets when moved out of the list widget have dynamic height in property pane entityExplorer.SelectEntityByName("Text3CopyCopy", "Widgets"); agHelper.AssertElementVisibility(propPane._propertyPaneHeightLabel); + agHelper.GetNClick(locators._widgetInDeployed(draggableWidgets.TEXT)); agHelper .GetWidgetCSSHeight(locators._widgetInDeployed(draggableWidgets.TEXT)) - .then((height: number) => { + .then((height: any) => { propPane.SelectPropertiesDropDown("height", "Auto Height"); assertHelper.AssertNetworkStatus("@updateLayout", 200); agHelper.GetNClick( @@ -96,33 +86,29 @@ describe("Dynamic Height Width validation list widget", function () { locators._widgetInDeployed(draggableWidgets.TEXT), ) .wait(1000) - .then((updatedListHeight: number) => { + .then((updatedListHeight: any) => { expect(height).to.not.equal(updatedListHeight); }); }); entityExplorer.SelectEntityByName("Text3CopyCopy", "Widgets"); - agHelper.TypeText(locators._body, `{${modifierKey}}c`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("copy"); + entityExplorer.SelectEntityByName("List1", "Widgets"); propPane.MoveToTab("Style"); - agHelper.TypeText(locators._body, `{${modifierKey}}v`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("paste"); assertHelper.AssertNetworkStatus("@updateLayout", 200); //Widgets when copied and pasted into the list widget no longer have dynamic height entityExplorer.SelectEntityByName("Text3CopyCopyCopy", "Container1"); agHelper.AssertElementAbsence(propPane._propertyPaneHeightLabel); + agHelper.Sleep(2000); //wait a bit to ensure that the 'Text3CopyCopy' is selected for cut + entityExplorer.SelectEntityByName("Text3CopyCopy"); - agHelper.TypeText(locators._body, `{${modifierKey}}x`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("cut"); entityExplorer.SelectEntityByName("List1"); propPane.MoveToTab("Style"); agHelper.Sleep(500); - agHelper.TypeText(locators._body, `{${modifierKey}}v`, { - parseSpecialCharSeq: true, - }); + agHelper.SimulateCopyPaste("paste"); + assertHelper.AssertNetworkStatus("@updateLayout", 200); entityExplorer.SelectEntityByName("Text3CopyCopy", "Widgets"); agHelper.AssertElementAbsence(propPane._propertyPaneHeightLabel); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_2_spec.js index 116f302685..86695fa129 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_2_spec.js @@ -144,13 +144,13 @@ describe("Admin settings page", function () { ); it("6. should test that settings page is not accessible to normal users", () => { - cy.LogOut(); + cy.LogOut(false); cy.wait(2000); cy.LoginFromAPI(Cypress.env("TESTUSERNAME3"), Cypress.env("TESTPASSWORD3")); cy.get(".admin-settings-menu-option").should("not.exist"); cy.visit(routes.GENERAL, { timeout: 60000 }); // non super users are redirected to home page cy.url().should("contain", routes.APPLICATIONS); - cy.LogOut(); + cy.LogOut(false); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index c1ff5ece8c..6a81933cd4 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -58,5 +58,6 @@ describe("Test Suite to validate copy/delete/undo functionalites", function () { cy.get(".bp3-input").first().click({ force: true }); cy.get(".bp3-input").first().type(`{${modifierKey}}v`, { force: true }); }); + agHelper.RemoveUIElement("Toast", "7 widgets are added back."); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/FormLogin/EnableFormLogin_spec.js b/app/client/cypress/e2e/Regression/ClientSide/FormLogin/EnableFormLogin_spec.js index 0667a2a8a7..68abe6cf2c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/FormLogin/EnableFormLogin_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/FormLogin/EnableFormLogin_spec.js @@ -65,7 +65,7 @@ describe("Form Login test functionality", function () { "excludeForAirgap", "2. Go to admin settings and disable Form Login", function () { - cy.LogOut(); + cy.LogOut(false); cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); cy.openAuthentication(); cy.get(adminSettings.formloginButton) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts index a24ac844b6..c27fe53dd7 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/ExistingApps/v1.9.24/DSCrudAndBindings_Spec.ts @@ -34,6 +34,7 @@ describe("Import and validate older app (app created in older versions of Appsmi dataSources.ReconnectDSbyType("MongoDB"); dataSources.ReconnectDSbyType("MySQL"); dataSources.ReconnectDSbyType("PostgreSQL"); + agHelper.Sleep(3000); //for CI to reconnect successfully homePage.AssertNCloseImport(); }); @@ -79,11 +80,16 @@ describe("Import and validate older app (app created in older versions of Appsmi // Commenting it as part of #28012 - to be added back later // agHelper.AssertContains(/[0-9] librar(y|ies) modified/, "not.exist"); - agHelper.GetNAssertElementText( - gitSync._gitStatusChanges, - "Some of the changes above are due to an improved file structure designed to reduce merge conflicts. You can safely commit them to your repository.", - "contain.text", - ); + // This assertions is commented out due to issue #https://github.com/appsmithorg/appsmith/issues/28563 + // Since we don't want this specific message appearing when we are just migrating the metadata, + // this assertion is not required. + // Slack conversation: https://theappsmith.slack.com/archives/C04HERDNZPA/p1698851532418569 + + // agHelper.GetNAssertElementText( + // gitSync._gitStatusChanges, + // "Some of the changes above are due to an improved file structure designed to reduce merge conflicts. You can safely commit them to your repository.", + // "contain.text", + // ); agHelper.GetNClick(gitSync._commitButton); assertHelper.AssertNetworkStatus("@commit", 201); gitSync.CloseGitSyncModal(); @@ -154,6 +160,7 @@ describe("Import and validate older app (app created in older versions of Appsmi agHelper.Sleep(500); agHelper.ClickButton("Update"); agHelper.Sleep(2000); //for CI update to be successful + table.WaitUntilTableLoad(0, 0, "v1"); //Validate updated values in table table.ReadTableRowColumnData(0, 3).then(($cellData) => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitImport/GitImport_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Git/GitImport/GitImport_spec.js index 26fc13a5c4..1773164809 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitImport/GitImport_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitImport/GitImport_spec.js @@ -19,12 +19,13 @@ import { describe("Git import flow ", function () { before(() => { homePage.NavigateToHome(); - cy.createWorkspace(); - cy.wait("@createWorkspace").then((interception) => { - newWorkspaceName = interception.response.body.data.name; - cy.CreateAppForWorkspace(newWorkspaceName, newWorkspaceName); + homePage.CreateNewWorkspace(); + cy.get("@workspaceName").then((workspaceName) => { + newWorkspaceName = workspaceName; + homePage.CreateAppInWorkspace(workspaceName); }); }); + it("1. Import an app from JSON with Postgres, MySQL, Mongo db & then connect it to Git", () => { homePage.NavigateToHome(); cy.get(homePageLocators.optionsIcon).first().click(); @@ -147,6 +148,7 @@ describe("Git import flow ", function () { // verify js object binded to input widget cy.xpath("//input[@value='Success']").should("be.visible"); }); + it("4. Create a new branch, clone page and validate data on that branch in view and edit mode", () => { //cy.createGitBranch(newBranch); gitSync.CreateGitBranch(newBranch, true); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts new file mode 100644 index 0000000000..d2ed9ce35c --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitStatusLite_spec.ts @@ -0,0 +1,105 @@ +import { featureFlagIntercept } from "../../../../../support/Objects/FeatureFlags"; +import * as _ from "../../../../../support/Objects/ObjectsCore"; + +let wsName: string; +let appName: string; +let repoName: any; + +describe("Git Connect V2", function () { + before(() => { + _.agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + wsName = "GitStatusLite" + uid; + appName = "GitStatusLite" + uid; + _.homePage.CreateNewWorkspace(wsName, true); + _.homePage.CreateAppInWorkspace(wsName, appName); + _.gitSync.CreateNConnectToGit(); + cy.get("@gitRepoName").then((repName) => { + repoName = repName; + }); + }); + }); + + it("Issue 26038 - 1 : Simultaneous git status and remote compare api calls on commit modal", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: true, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/fetch/remote/app/**", + }).as("gitRemoteStatusApi"); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "false" }, + }).as("gitStatusApi"); + + _.agHelper.GetNClick(_.locators._publishButton); + + cy.wait("@gitRemoteStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + cy.wait("@gitStatusApi").then((res2) => { + expect(res2.response).to.have.property("statusCode", 200); + + _.agHelper.GetNClick(_.locators._dialogCloseButton); + }); + }); + }); + + it("Issue 26038 - 2 : Simultaneous git status and remote compare api calls on commit modal", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: false, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "true" }, + }).as("gitStatusApi"); + + _.agHelper.GetNClick(_.locators._publishButton); + + cy.wait("@gitStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + _.agHelper.GetNClick(_.locators._dialogCloseButton); + }); + }); + + it("Issue 28462 : Simultaneous git status and remote compare api calls on canvas load", function () { + featureFlagIntercept({ + release_git_status_lite_enabled: true, + }); + + cy.wait(1000); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/fetch/remote/app/**", + }).as("gitRemoteStatusApi"); + + cy.intercept({ + method: "GET", + url: "/api/v1/git/status/app/**", + query: { compareRemote: "false" }, + }).as("gitStatusApi"); + + cy.reload(); + + cy.wait("@gitRemoteStatusApi").then((res1) => { + expect(res1.response).to.have.property("statusCode", 200); + cy.wait("@gitStatusApi").then((res2) => { + expect(res2.response).to.have.property("statusCode", 200); + }); + }); + }); + + after(() => { + _.gitSync.DeleteTestGithubRepo(repoName); + }); +}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitSyncedApps_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitSyncedApps_spec.js index a55f6cab6e..dd6490b6f0 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitSyncedApps_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitSyncedApps_spec.js @@ -114,6 +114,7 @@ describe("Git sync apps", function () { 201, ); }); + it("2. Create api queries from api pane and cURL import , bind it to widget and clone page from page settings", () => { cy.fixture("datasources").then((datasourceFormData) => { cy.Createpage(newPage); @@ -277,6 +278,7 @@ describe("Git sync apps", function () { .should("be.oneOf", ["morpheus", "This is a test"]); deployMode.NavigateBacktoEditor(); }); + it("4. Create a new branch tempBranch, add jsObject and datasource query, move them to new page i.e. Child_Page and bind to widgets", () => { //cy.createGitBranch(tempBranch); gitSync.CreateGitBranch(tempBranch, true); @@ -342,6 +344,7 @@ describe("Git sync apps", function () { }); cy.wait(2000); }); + it("5. Commit and push changes, validate data binding on all pages in edit and deploy mode on tempBranch", () => { // commit and push changes cy.get(homePageLocators.publishButton).click(); @@ -439,6 +442,7 @@ describe("Git sync apps", function () { expect(cellData).to.be.equal("New Config"); }); */ }); + it("6. Switch to master and verify no uncommitted changes should be shown on master", () => { cy.switchGitBranch("master"); cy.wait(2000); @@ -449,6 +453,7 @@ describe("Git sync apps", function () { .and("have.text", "No changes to commit"); cy.get(gitSyncLocators.closeGitSyncModal).click(); }); + it("7. Switch to tempBranch , Clone the Child_Page, change it's visiblity to hidden and deploy, merge to master", () => { cy.switchGitBranch(tempBranch); cy.wait(2000); @@ -479,6 +484,7 @@ describe("Git sync apps", function () { agHelper.AssertContains("Child_Page Copy", "not.exist"); deployMode.NavigateBacktoEditor(); }); + it("8. Verify Page visiblity on master in edit and deploy mode", () => { cy.switchGitBranch(mainBranch); cy.wait(2000); @@ -486,6 +492,7 @@ describe("Git sync apps", function () { agHelper.AssertContains("Child_Page Copy", "not.exist"); deployMode.NavigateBacktoEditor(); }); + it("9. Create new branch, delete a page and merge back to master, verify page is deleted on master", () => { //cy.createGitBranch(tempBranch1); gitSync.CreateGitBranch(tempBranch1, true); @@ -508,6 +515,7 @@ describe("Git sync apps", function () { cy.CheckAndUnfoldEntityItem("Pages"); cy.get(`.t--entity-name:contains("Child_Page Copy")`).should("not.exist"); }); + it("10. Import app from git and verify page order should not change", () => { cy.get(homePageLocators.homeIcon).click(); cy.get(homePageLocators.optionsIcon).first().click(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/MergeViaRemote_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/MergeViaRemote_spec.ts index 734890ec79..f424d86f6e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/MergeViaRemote_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/MergeViaRemote_spec.ts @@ -1,6 +1,5 @@ -const widgetsPage = require("../../../../../locators/Widgets.json"); -const commonlocators = require("../../../../../locators/commonlocators.json"); -const explorerLocators = require("../../../../../locators/explorerlocators.json"); +import widgetsPage from "../../../../../locators/Widgets.json"; +import commonlocators from "../../../../../locators/commonlocators.json"; import gitSyncLocators from "../../../../../locators/gitSyncLocators"; import homePage from "../../../../../locators/HomePage"; import * as _ from "../../../../../support/Objects/ObjectsCore"; @@ -22,20 +21,21 @@ const inputNameTempBranch31 = "inputNameTempBranch31"; const cleanUrlBranch = "feat/clean_url"; -let applicationId = null; -let applicationName = null; +let applicationId: any; +let applicationName: any; +let repoName: any; -let repoName; describe("Git sync: Merge changes via remote", function () { before(() => { _.homePage.NavigateToHome(); - cy.createWorkspace(); - cy.wait("@createWorkspace").then((interception) => { - const newWorkspaceName = interception.response.body.data.name; - cy.generateUUID().then((uid) => { - cy.CreateAppForWorkspace(newWorkspaceName, uid); + _.homePage.CreateNewWorkspace(); + + _.agHelper.GenerateUUID(); + cy.get("@guid").then((uid: any) => { + cy.get("@workspaceName").then((workspaceName: any) => { + _.homePage.CreateAppInWorkspace(workspaceName, uid); applicationName = uid; - cy.get("@currentApplicationId").then( + cy.get("@applicationId").then( (currentAppId) => (applicationId = currentAppId), ); }); @@ -139,7 +139,7 @@ describe("Git sync: Merge changes via remote", function () { cy.commitAndPush(); cy.merge(mainBranch); cy.get(gitSyncLocators.closeGitSyncModal).click(); - cy.wait(8000); + cy.wait(4000); cy.switchGitBranch(mainBranch); cy.wait(4000); // wait for switch branch cy.contains("NewPage"); @@ -259,33 +259,33 @@ describe("Git sync: Merge changes via remote", function () { expect(location.pathname).includes(newPathname); }); - _.gitSync.CreateGitBranch(cleanUrlBranch, false); + _.gitSync.CreateGitBranch(cleanUrlBranch, false, false); //false is sent for assertCreateBranch since here it only goes to the branch already created cy.location().should((location) => { expect(location.pathname).includes(legacyPathname); }); }); - after(() => { - // _.gitSync.DeleteTestGithubRepo(repoName); - // //cy.deleteTestGithubRepo(repoName); - // // TODO remove when app deletion with conflicts is fixed - // cy.get(homePage.homeIcon).click({ force: true }); - // cy.get(homePage.createNew) - // .first() - // .click({ force: true }); - // cy.wait("@createNewApplication").should( - // "have.nested.property", - // "response.body.responseMeta.status", - // 201, - // ); - // cy.get("#loading").should("not.exist"); - // cy.wait(2000); - // cy.AppSetupForRename(); - // cy.get(homePage.applicationName).type(repoName + "{enter}"); - // cy.wait("@updateApplication").should( - // "have.nested.property", - // "response.body.responseMeta.status", - // 200, - // ); - }); + // after(() => { + // // _.gitSync.DeleteTestGithubRepo(repoName); + // // //cy.deleteTestGithubRepo(repoName); + // // // TODO remove when app deletion with conflicts is fixed + // // cy.get(homePage.homeIcon).click({ force: true }); + // // cy.get(homePage.createNew) + // // .first() + // // .click({ force: true }); + // // cy.wait("@createNewApplication").should( + // // "have.nested.property", + // // "response.body.responseMeta.status", + // // 201, + // // ); + // // cy.get("#loading").should("not.exist"); + // // cy.wait(2000); + // // cy.AppSetupForRename(); + // // cy.get(homePage.applicationName).type(repoName + "{enter}"); + // // cy.wait("@updateApplication").should( + // // "have.nested.property", + // // "response.body.responseMeta.status", + // // 200, + // // ); + // }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitWithJSLibrary/GitwithCustomJSLibrary_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Git/GitWithJSLibrary/GitwithCustomJSLibrary_spec.js index 64adbb88c3..4a82a357c0 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitWithJSLibrary/GitwithCustomJSLibrary_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitWithJSLibrary/GitwithCustomJSLibrary_spec.js @@ -1,6 +1,12 @@ import HomePage from "../../../../../locators/HomePage"; import gitSyncLocators from "../../../../../locators/gitSyncLocators"; -import * as _ from "../../../../../support/Objects/ObjectsCore"; +import { + agHelper, + entityExplorer, + homePage, + gitSync, + installer, +} from "../../../../../support/Objects/ObjectsCore"; const mainBranch = "master"; const tempBranch = "feat/tempBranch"; @@ -8,48 +14,49 @@ let repoName; describe("excludeForAirgap", "Tests JS Library with Git", () => { before(() => { - _.homePage.NavigateToHome(); + homePage.NavigateToHome(); cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { const newWorkspaceName = interception.response.body.data.name; cy.CreateAppForWorkspace(newWorkspaceName, newWorkspaceName); }); // connect app to git - _.gitSync.CreateNConnectToGit(repoName); + gitSync.CreateNConnectToGit(repoName); cy.get("@gitRepoName").then((repName) => { repoName = repName; }); }); it("1. Install JS Library and commit changes, create branch and verify JS library changes are present on new branch ", () => { - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.OpenInstaller(); - _.installer.installLibrary("uuidjs", "UUID"); - cy.commitAndPush(); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.OpenInstaller(); + installer.InstallLibrary("uuidjs", "UUID"); + gitSync.CommitAndPush(); // create new branch - _.gitSync.CreateGitBranch(tempBranch, true); + gitSync.CreateGitBranch(tempBranch, true); // verify js library changes are present - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.AssertLibraryinExplorer("uuidjs"); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.AssertLibraryinExplorer("uuidjs"); }); + it("2. Discard custom js library changes, verify changes are discarded also verify it deosnt show uncommitted changes", () => { - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.uninstallLibrary("uuidjs"); - _.installer.assertUnInstall("uuidjs"); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.uninstallLibrary("uuidjs"); + installer.assertUnInstall("uuidjs"); // discard js library uninstallation cy.gitDiscardChanges(); // verify js library is present - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.AssertLibraryinExplorer("uuidjs"); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.AssertLibraryinExplorer("uuidjs"); // verify no uncommitted changes are there - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); + agHelper.AssertElementExist(gitSync._bottomBarPull); cy.get(gitSyncLocators.bottomBarCommitButton).click(); cy.get(gitSyncLocators.commitCommentInput).should("be.disabled"); cy.get(gitSyncLocators.commitButton).should("be.disabled"); cy.get(gitSyncLocators.closeGitSyncModal).click(); // swtich to master, verify no uncommitted changes cy.switchGitBranch("master"); - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); + agHelper.AssertElementExist(gitSync._bottomBarPull); cy.get(gitSyncLocators.bottomBarCommitButton).click(); cy.get(gitSyncLocators.commitCommentInput).should("be.disabled"); cy.get(gitSyncLocators.commitButton).should("be.disabled"); @@ -58,17 +65,17 @@ describe("excludeForAirgap", "Tests JS Library with Git", () => { it("3. Merge custom js lib changes from child branch to master, verify changes are merged", () => { cy.switchGitBranch(tempBranch); - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.OpenInstaller(); - _.installer.installLibrary("jspdf", "jspdf"); + agHelper.AssertElementExist(gitSync._bottomBarPull); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.OpenInstaller(); + installer.InstallLibrary("jspdf", "jspdf"); //cy.commitAndPush(); cy.get(HomePage.publishButton).click(); - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); + agHelper.AssertElementExist(gitSync._bottomBarPull); cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); cy.get(gitSyncLocators.commitButton).click(); - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); + agHelper.AssertElementExist(gitSync._bottomBarPull); cy.get(gitSyncLocators.closeGitSyncModal).click(); cy.wait(2000); cy.merge(mainBranch); @@ -76,12 +83,12 @@ describe("excludeForAirgap", "Tests JS Library with Git", () => { cy.wait(2000); // verify custom js library is present in master branch cy.switchGitBranch(mainBranch); - _.agHelper.AssertElementExist(_.gitSync._bottomBarPull); - _.entityExplorer.ExpandCollapseEntity("Libraries"); - _.installer.AssertLibraryinExplorer("jspdf"); + agHelper.AssertElementExist(gitSync._bottomBarPull); + entityExplorer.ExpandCollapseEntity("Libraries"); + installer.AssertLibraryinExplorer("jspdf"); }); after(() => { //clean up - _.gitSync.DeleteTestGithubRepo(repoName); + gitSync.DeleteTestGithubRepo(repoName); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Google/EnableGoogle_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Google/EnableGoogle_spec.js index 699c516e26..9f9947103a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Google/EnableGoogle_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Google/EnableGoogle_spec.js @@ -27,7 +27,7 @@ describe("excludeForAirgap", "SSO with Google test functionality", function () { }); it("2. Go to admin settings and enable Google", function () { - cy.LogOut(); + cy.LogOut(false); cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); cy.get(".admin-settings-menu-option").should("be.visible"); cy.get(".admin-settings-menu-option").click(); @@ -64,7 +64,7 @@ describe("excludeForAirgap", "SSO with Google test functionality", function () { }); it("3. Go to admin settings and disable Google", function () { - cy.LogOut(); + cy.LogOut(false); cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); cy.get(".admin-settings-menu-option").should("be.visible"); cy.get(".admin-settings-menu-option").click(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/IDE/MaintainContext&Focus_spec.js b/app/client/cypress/e2e/Regression/ClientSide/IDE/MaintainContext&Focus_spec.js index 2dafa2f92f..ce72d9ea7e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/IDE/MaintainContext&Focus_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/IDE/MaintainContext&Focus_spec.js @@ -214,7 +214,7 @@ describe("MaintainContext&Focus", function () { cy.assertCursorOnCodeInput(".js-editor", { ch: 2, line: 4 }); cy.get(locators._codeMirrorTextArea).type("showA"); - agHelper.GetNAssertElementText(locators._hints, "showAlert()"); + agHelper.GetNAssertElementText(locators._hints, "showAlert"); agHelper.PressEscape(); cy.assertCursorOnCodeInput(".js-editor", { ch: 7, line: 4 }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts index e7282223c1..b86b9bf19e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts @@ -5,7 +5,7 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { it("1. Validates Library install/uninstall", () => { _.entityExplorer.ExpandCollapseEntity("Libraries"); _.installer.OpenInstaller(); - _.installer.installLibrary("uuidjs", "UUID"); + _.installer.InstallLibrary("uuidjs", "UUID"); _.installer.uninstallLibrary("uuidjs"); _.installer.assertUnInstall("uuidjs"); }); @@ -16,13 +16,13 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { _.entityExplorer.RenameEntityFromExplorer("Table1", "jsonwebtoken"); _.entityExplorer.ExpandCollapseEntity("Libraries"); _.installer.OpenInstaller(); - _.installer.installLibrary("jsonwebtoken", "jsonwebtoken_1", true); + _.installer.InstallLibrary("jsonwebtoken", "jsonwebtoken_1", true); }); it("3. Checks jspdf library", () => { _.entityExplorer.ExpandCollapseEntity("Libraries"); _.installer.OpenInstaller(); - _.installer.installLibrary("jspdf", "jspdf"); + _.installer.InstallLibrary("jspdf", "jspdf"); _.jsEditor.CreateJSObject( `export default { myFun1: () => { @@ -48,7 +48,7 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { it("4. ESM build should pass installation, uninstallation and reinstallation", () => { _.entityExplorer.ExpandCollapseEntity("Libraries"); _.installer.OpenInstaller(); - _.installer.installLibraryViaURL( + _.installer.InstallLibraryViaURL( "https://cdn.jsdelivr.net/npm/fast-xml-parser@4.2.7/+esm", "fast_xml_parser", ); @@ -59,7 +59,7 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { // Reinstallation should succeed with the same accessor _.installer.OpenInstaller(); - _.installer.installLibraryViaURL( + _.installer.InstallLibraryViaURL( "https://cdn.jsdelivr.net/npm/fast-xml-parser@4.2.7/+esm", "fast_xml_parser", ); @@ -77,20 +77,21 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { it("6. Checks installation in exported/forked app", () => { _.homePage.NavigateToHome(); _.homePage.ImportApp("library_export.json"); - _.agHelper.AssertContains("true"); + _.homePage.AssertImportToast(); + _.agHelper.ValidateToastMessage("true"); _.agHelper.WaitUntilAllToastsDisappear(); //Checks installation in forked app _.homePage.NavigateToHome(); _.homePage.ForkApplication("Library_export"); - _.agHelper.AssertContains("true"); + _.agHelper.ValidateToastMessage("true"); _.agHelper.WaitUntilAllToastsDisappear(); //Deploy app and check installation _.deployMode.DeployApp(); _.agHelper.WaitUntilToastDisappear("true"); _.deployMode.NavigateBacktoEditor(); - _.agHelper.AssertContains("true"); + _.agHelper.ValidateToastMessage("true"); }); it("7. Tests library access and installation in public apps", () => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts index ba7f85703b..ed608f9636 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts @@ -345,7 +345,7 @@ describe("Linting", () => { entityExplorer.ExpandCollapseEntity("Libraries"); // install the library installer.OpenInstaller(); - installer.installLibrary("uuidjs", "UUID"); + installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); agHelper.AssertElementAbsence(locators._lintErrorElement); @@ -355,7 +355,7 @@ describe("Linting", () => { agHelper.AssertElementExist(locators._lintErrorElement); agHelper.Sleep(2000); installer.OpenInstaller(); - installer.installLibrary("uuidjs", "UUID"); + installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); homePage.NavigateToHome(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/ConversionAlgorithm_FixedLayout_Mobile_Validation_Spec.js b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/ConversionAlgorithm_FixedLayout_Mobile_Validation_Spec.js index a0ebb981c1..e1fc44e483 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/ConversionAlgorithm_FixedLayout_Mobile_Validation_Spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/ConversionAlgorithm_FixedLayout_Mobile_Validation_Spec.js @@ -65,5 +65,6 @@ describe("Auto conversion algorithm usecases for fixed Layout", function () { }); }); }); + agHelper.Sleep(); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApplicationURL_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApplicationURL_spec.js index dfcbbbb30e..d3e1b982e3 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApplicationURL_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApplicationURL_spec.js @@ -32,7 +32,7 @@ describe("Slug URLs", () => { }); }); - it("2. Checks if application slug updates on the URL when application name changes", () => { + it("2. Checks if application slug updates & page slug updates on the URL when application name/page name changes", () => { cy.generateUUID().then((appName) => { applicationName = appName; homePage.RenameApplication(applicationName); @@ -42,29 +42,27 @@ describe("Slug URLs", () => { expect(pathname).to.be.equal(`/app/${appName}/page1-${pageId}/edit`); }); }); - }); - - it("3. Checks if page slug updates on the URL when page name changes", () => { entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "Page1", action: "Edit name", }); - cy.get(explorer.editEntity).last().type("Page renamed", { force: true }); - cy.get("body").click(0, 0, { force: true }); - cy.wait("@updatePage").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); - cy.location("pathname").then((pathname) => { + cy.get(explorer.editEntity) + .last() + .type("Renamed" + "{enter}", { force: true }); + agHelper.Sleep(2000); //for new name to settle & url to update + assertHelper.AssertNetworkStatus("updatePage"); + // cy.location("pathname").then((pathname) => { + cy.url().then((url) => { + const urlObject = new URL(url); + const pathname = urlObject.pathname; const pageId = pathname.split("/")[3]?.split("-").pop(); expect(pathname).to.be.equal( - `/app/${applicationName}/page-renamed-${pageId}/edit`, + `/app/${applicationName}/renamed-${pageId}/edit`, ); }); }); - it("4. Check the url of old applications, upgrades version and compares appsmith.URL values", () => { + it("3. Check the url of old applications, upgrades version and compares appsmith.URL values", () => { cy.request("PUT", `/api/v1/applications/${applicationId}`, { applicationVersion: 1, }).then((response) => { @@ -141,9 +139,9 @@ describe("Slug URLs", () => { }); }); - it("5. Checks redirect url", () => { + it("4. Checks redirect url", () => { cy.url().then((url) => { - cy.LogOut(); + cy.LogOut(false); agHelper.VisitNAssert(url + "?embed=true&a=b", "signUpLogin"); agHelper.Sleep(2000); // cy.location().should((loc) => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs1_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs1_spec.js index aa5108bb33..f45f3d2708 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs1_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs1_spec.js @@ -1,4 +1,5 @@ import commonlocators from "../../../../locators/commonlocators.json"; +import { homePage } from "../../../../support/Objects/ObjectsCore"; import { ObjectsRegistry } from "../../../../support/Objects/Registry"; const { @@ -34,10 +35,8 @@ describe("Debugger logs", function () { cy.get(".t--property-control-visible").find(".t--js-toggle").click(); cy.testJsontext("visible", "Test"); cy.get(commonlocators.homeIcon).click({ force: true }); - cy.generateUUID().then((id) => { - cy.CreateAppInFirstListedWorkspace(id); - debuggerHelper.AssertErrorCount(0); - }); + homePage.CreateNewApplication(); + debuggerHelper.AssertErrorCount(0); }); it("3. Console log on button click with normal moustache binding", function () { diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/PageOnLoad_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/PageOnLoad_spec.ts index f7fd6faffa..d5033b1f39 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/PageOnLoad_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/PageOnLoad_spec.ts @@ -1,22 +1,30 @@ const explorer = require("../../../../locators/explorerlocators.json"); const testdata = require("../../../../fixtures/testdata.json"); -import * as _ from "../../../../support/Objects/ObjectsCore"; + +import { + agHelper, + entityExplorer, + propPane, + apiPage, + debuggerHelper, +} from "../../../../support/Objects/ObjectsCore"; describe("Check debugger logs state when there are onPageLoad actions", function () { before(() => { - _.agHelper.AddDsl("debuggerTableDsl"); + agHelper.AddDsl("debuggerTableDsl"); }); + it("1. Check debugger logs state when there are onPageLoad actions", function () { - _.entityExplorer.SelectEntityByName("Table1", "Widgets"); - _.propPane.UpdatePropertyFieldValue("Table data", "{{TestApi.data.users}}"); - _.apiPage.CreateAndFillApi(testdata.baseUrl + testdata.methods, "TestApi"); - _.apiPage.RunAPI(); - _.agHelper.GetNClick(explorer.addWidget); - _.agHelper.RefreshPage(); + entityExplorer.SelectEntityByName("Table1", "Widgets"); + propPane.UpdatePropertyFieldValue("Table data", "{{TestApi.data.users}}"); + apiPage.CreateAndFillApi(testdata.baseUrl + testdata.methods, "TestApi"); + apiPage.RunAPI(); + agHelper.GetNClick(explorer.addWidget); + agHelper.RefreshPage(); // Wait for the debugger icon to be visible - _.agHelper.AssertElementVisibility(".t--debugger-count"); + agHelper.AssertElementVisibility(".t--debugger-count"); // debuggerHelper.isErrorCount(0); cy.wait("@postExecute"); - _.debuggerHelper.AssertErrorCount(1); + debuggerHelper.AssertErrorCount(1); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Replay_Editor_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Replay_Editor_spec.js index c566047417..33f286819d 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Replay_Editor_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Replay_Editor_spec.js @@ -57,8 +57,7 @@ describe("Undo/Redo functionality", function () { "https://mock-api.appsmith.com/users", //testing placeholder! ); cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods); - agHelper.RemoveTooltip("Add a new query/JS Object"); - + agHelper.RemoveUIElement("Tooltip", "Add a new query/JS Object"); cy.get(`${apiwidget.headerKey}`).type("Authorization"); cy.get("body").click(0, 0); cy.get(apiwidget.settings).click({ force: true }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js index 30ce81f8c5..fe1fa175fa 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_Existing_app_spec.js @@ -27,10 +27,7 @@ describe( agHelper.AssertElementExist(template.templateDialogBox); agHelper.AssertElementVisibility(templates.locators._templateCard); agHelper.Sleep(4000); - cy.xpath("//h1[text()='Meeting Scheduler']/parent::div") - .scrollIntoView() - .wait(500) - .click(); + agHelper.GetNClick(template.vehicleMaintenenceApp); agHelper.WaitUntilEleDisappear("//*[text()='Loading template details']"); agHelper.Sleep(); agHelper.CheckForErrorToast( @@ -58,7 +55,7 @@ describe( 0, 30000, ); - agHelper.GetNClick(template.meetingSchedulerDashboard); + agHelper.GetNClick(template.vehicleMaintenenceApp); //agHelper.WaitUntilEleDisappear("//*[text()='Loading template details']"); cy.wait("@getTemplatePages").should( "have.nested.property", diff --git a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js index 118a436570..8c1391574b 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js @@ -17,8 +17,10 @@ describe("excludeForAirgap", "Fork a template to an workspace", () => { cy.get(templateLocators.templateViewForkButton).click(); } }); - cy.get(templateLocators.dialogForkButton).click(); - + _.agHelper.WaitUntilEleAppear( + `div[role="dialog"]:has(` + templateLocators.dialogForkButton + `)`, + ); + cy.get(templateLocators.dialogForkButton).click({ force: true }); cy.get(commonlocators.canvas, { timeout: 30000 }).should("be.visible"); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts index a366d91c05..ab44a2c03b 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts @@ -3,7 +3,7 @@ import * as _ from "../../../../../support/Objects/ObjectsCore"; const publicWidgetsPage = require("../../../../../locators/publishWidgetspage.json"); describe("3D Custom EChart feature", function () { - it("5. 3D EChart Custom Chart Widget Functionality", function () { + it("1. 3D EChart Custom Chart Widget Functionality", function () { featureFlagIntercept({ release_custom_echarts_enabled: true, }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Iframe/IframeTest_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Iframe/IframeTest_spec.ts index fac8bae5f9..de45a3f2c6 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Iframe/IframeTest_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Iframe/IframeTest_spec.ts @@ -3,6 +3,7 @@ import { locators, entityExplorer, propPane, + deployMode, } from "../../../../../support/Objects/ObjectsCore"; import testdata from "../../../../../fixtures/testdata.json"; @@ -113,20 +114,22 @@ describe("Iframe widget Tests", function () { agHelper.ValidateToastMessage("URL Changed"); }); - // Border Color is failing Skipping to unblock - Fixing it in a separate PR by 20/10/2023 - it.skip("3. Verify colors, borders and shadows", () => { + it("3. Verify colors, borders and shadows", () => { propPane.MoveToTab("Style"); - // Border Color + // Change Border Color propPane.SelectColorFromColorPicker("bordercolor", 10); - agHelper.AssertCSS("iframe", "border-color", "rgb(185, 28, 28)"); - // Verify border + // Change border agHelper.GetNClick(propPane._segmentedControl("0px")); - agHelper.AssertCSS("iframe", "border-radius", "0px"); - // Verify Box Shadow + // Change Box Shadow agHelper.GetNClick(`${propPane._segmentedControl("0")}:contains('Large')`); + + //Verify details in Deploy mode + deployMode.DeployApp(); + //agHelper.AssertCSS("iframe", "border-color", "rgb(185, 28, 28)"); + agHelper.AssertCSS("iframe", "border-radius", "0px"); agHelper.AssertCSS( "iframe", "box-shadow", diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/List/List4_2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/List/List4_2_spec.js index 74b79e33b8..f84361e977 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/List/List4_2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/List/List4_2_spec.js @@ -103,17 +103,11 @@ describe("Container Widget Functionality", function () { }); it("6. Renaming the widget from Property pane and Entity explorer ", function () { - // Open Property pane - _.entityExplorer.SelectEntityByName("List1", "Widgets"); - - // Change the list widget name from property pane and Verify it - cy.widgetText( - "List2", - widgetsPage.listWidgetName, - widgetsPage.widgetNameSpan, - ); + // Open Property pane & rename the list widget + _.propPane.RenameWidget("List1", "List2"); + _.agHelper.Sleep(); //for renaming the widget // Change the list widget name from Entity Explorer - cy.renameEntity("List2", "List1"); + _.entityExplorer.RenameEntityFromExplorer("List2", "List1", false); // Mouse over to list name _.entityExplorer.SelectEntityByName("List1"); @@ -126,6 +120,5 @@ describe("Container Widget Functionality", function () { "List1", ); _.deployMode.DeployApp(); - _.deployMode.NavigateBacktoEditor(); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Modal/Modal_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Modal/Modal_spec.ts index f0030bb8eb..c665fa739d 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Modal/Modal_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Modal/Modal_spec.ts @@ -255,34 +255,31 @@ describe("Modal Widget test cases", function () { locators._widgetInDeployed(draggableWidgets.ICONBUTTON), 1, ); - assertHelper - .WaitForNetworkCall("@postExecute") - .then((interception: any) => { - agHelper.Sleep(); + assertHelper.WaitForNetworkCall("@postExecute").then((response: any) => { + agHelper.Sleep(); + const name = response.body.data.body[0].name; + agHelper.ValidateToastMessage("Executed api!"); - const name = interception.response.body.data.body[0].name; - agHelper.ValidateToastMessage("Executed api!"); + //Tab + agHelper.GetNClick(propPane._tabId1); - //Tab - agHelper.GetNClick(propPane._tabId1); - - //Table - table.SearchTable(name); - table.ReadTableRowColumnData(0, 5, "v2").then(($cellData) => { - expect($cellData).to.contain(name); - }); - table.SelectTableRow(0, 0, true, "v2"); - - //Text - agHelper.ScrollTo(locators._modal, "top"); - agHelper.AssertContains( - name, - "be.visible", - locators._modal + - " " + - locators._widgetInDeployed(draggableWidgets.TEXT), - ); + //Table + table.SearchTable(name); + table.ReadTableRowColumnData(0, 5, "v2").then(($cellData) => { + expect($cellData).to.contain(name); }); + table.SelectTableRow(0, 0, true, "v2"); + + //Text + agHelper.ScrollTo(locators._modal, "top"); + agHelper.AssertContains( + name, + "be.visible", + locators._modal + + " " + + locators._widgetInDeployed(draggableWidgets.TEXT), + ); + }); }); it("5. Verify that the Modal widget opens correctly when configured on various events for different widget types.", () => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect5_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect5_spec.ts index 5bb17df6ac..4bb61a8052 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect5_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect5_spec.ts @@ -296,4 +296,74 @@ describe("Multi Select widget Tests", function () { "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", ); }); + + it("8. Verify validation error in default selected values", () => { + entityExplorer.SelectEntityByName("NewMultiSelect", "Widgets"); + + propPane.MoveToTab("Content"); + + propPane.UpdatePropertyFieldValue( + "Default selected values", + '["GREEN1", "RED1"]', + true, + ); + + agHelper.VerifyEvaluatedErrorMessage( + "Some or all default values are missing from options. Please update the values.", + ); + + propPane.ToggleJSMode("Source Data", true); + + // Updates the options and asserts that the validation error is fixed + propPane.UpdatePropertyFieldValue( + "Source Data", + '[{"name": "Green", "code":"GREEN1"}, { "name": "Red","code": "RED1" }]', + true, + ); + + agHelper.FocusElement( + locators._propertyInputField("Default selected values"), + ); + + agHelper.Sleep(1000); + agHelper.AssertElementAbsence(locators._evaluatedErrorMessage); + + // Changes options to bring back validation error + propPane.UpdatePropertyFieldValue( + "Source Data", + '[{"name": "Green", "code":"GREEN1"}, { "name": "Red","code": "RED" }]', + true, + ); + + agHelper.FocusElement( + locators._propertyInputField("Default selected values"), + ); + + agHelper.VerifyEvaluatedErrorMessage( + "Some or all default values are missing from options. Please update the values.", + ); + + // Reload to check if the error persists + agHelper.RefreshPage(); + + entityExplorer.SelectEntityByName("NewMultiSelect", "Widgets"); + + agHelper.FocusElement( + locators._propertyInputField("Default selected values"), + ); + + agHelper.VerifyEvaluatedErrorMessage( + "Some or all default values are missing from options. Please update the values.", + ); + + // Fixes the validation error + propPane.UpdatePropertyFieldValue( + "Default selected values", + '{{["GREEN1"]}}', + true, + ); + + agHelper.Sleep(1000); + agHelper.AssertElementAbsence(locators._evaluatedErrorMessage); + }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/Autocomplete_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/Autocomplete_spec.js index f3783c3532..9cca4d369a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/Autocomplete_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/Autocomplete_spec.js @@ -62,16 +62,8 @@ describe("Autocomplete using slash command and mustache tests", function () { .type("{shift}{{}{shift}{{}") .then(() => { cy.get(dynamicInputLocators.hints).should("exist"); - // validates all autocomplete functions on entering {{}} in onClick field - cy.get(`${dynamicInputLocators.hints} li`) - .eq(7) - .should("have.text", "storeValue()"); - cy.get(`${dynamicInputLocators.hints} li`) - .eq(8) - .should("have.text", "showAlert()"); - cy.get(`${dynamicInputLocators.hints} li`) - .eq(9) - .should("have.text", "navigateTo()"); + _.agHelper.AssertContains("storeValue"); + _.agHelper.AssertContains("showAlert"); }); }); @@ -101,13 +93,8 @@ describe("Autocomplete using slash command and mustache tests", function () { cy.get(dynamicInputLocators.input) .first() .type("{shift}{{}{shift}{{}"); - // validates autocomplete binding on entering {{}} in text field - cy.get(`${dynamicInputLocators.hints} li`) - .eq(1) - .should("have.text", "Button1.text"); - cy.get(`${dynamicInputLocators.hints} li`) - .eq(2) - .should("have.text", "Button1.recaptchaToken"); + _.agHelper.AssertContains("Button1.text"); + _.agHelper.AssertContains("Button1.recaptchaToken"); }); }, ); @@ -147,83 +134,32 @@ describe("Autocomplete using slash command and mustache tests", function () { ); it("Bug 9003: Autocomplete not working for Appsmith specific JS APIs", function () { - cy.openPropertyPane("buttonwidget"); - cy.get(".t--property-control-onclick") - .find(".t--js-toggle") - .click({ force: true }); - cy.EnableAllCodeEditors(); - cy.get(".CodeMirror textarea") - .last() - .focus() - .clear() - .type("{{re") - .then(() => { - cy.get(dynamicInputLocators.hints).should("exist"); - // validates autocomplete suggestion for resetWidget() in onClick field - cy.get(`${dynamicInputLocators.hints} li`) - .eq(0) - .should("have.text", "removeValue()"); - cy.get(`${dynamicInputLocators.hints} li`) - .eq(1) - .should("have.text", "resetWidget()"); - }); - cy.EnableAllCodeEditors(); - cy.get(".CodeMirror textarea") - .last() - .focus() - // clearing the onClick field - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{rightarrow}{rightarrow}", { parseSpecialCharSequences: true }) - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{{s") - .then(() => { - cy.get(dynamicInputLocators.hints).should("exist"); - // validates autocomplete function suggestions on entering '{{s' in onClick field - cy.get(`${dynamicInputLocators.hints} li`) - .should("contain.text", "storeValue()") - .and("contain.text", "showModal()") - .and("contain.text", "setInterval()") - .and("contain.text", "showAlert()"); - }); - cy.EnableAllCodeEditors(); - cy.get(".CodeMirror textarea") - .last() - .focus() - // clearing the onClick field - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{rightarrow}{rightarrow}", { parseSpecialCharSequences: true }) - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{{c") - .then(() => { - cy.get(dynamicInputLocators.hints).should("exist"); - // validates autocomplete function suggestions on entering '{{c' in onClick field - cy.get(`${dynamicInputLocators.hints} li`) - .should("contain.text", "closeModal()") - .and("contain.text", "copyToClipboard()") - .and("contain.text", "clearInterval()") - .and("contain.text", "clearStore()"); - }); - cy.EnableAllCodeEditors(); - cy.get(".CodeMirror textarea") - .last() - .focus() - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{rightarrow}{rightarrow}", { parseSpecialCharSequences: true }) - .type("{ctrl}{shift}{uparrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - .type("{{n") - .then(() => { - cy.get(dynamicInputLocators.hints).should("exist"); - // validates autocomplete suggestions on entering '{{n' in onClick field - cy.get(`${dynamicInputLocators.hints} li`).should( - "contain.text", - "navigateTo()", - ); - }); + _.entityExplorer.SelectEntityByName("Button1", "Widgets"); + _.propPane.ToggleJSMode("onClick", true); + _.propPane.TypeTextIntoField("onClick", "{{storeValue", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "storeValue"); + _.propPane.TypeTextIntoField("onClick", "{{removeValue", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "removeValue"); + _.propPane.TypeTextIntoField("onClick", "{{showAlert", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "showAlert"); + _.propPane.TypeTextIntoField("onClick", "{{setInterval", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "setInterval"); + _.propPane.TypeTextIntoField("onClick", "{{setTimeout", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "setTimeout"); + _.propPane.TypeTextIntoField("onClick", "{{resetWidget", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "resetWidget"); + _.propPane.TypeTextIntoField("onClick", "{{showModal", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "showModal"); + _.propPane.TypeTextIntoField("onClick", "{{copyToClipboard", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "copyToClipboard"); + _.propPane.TypeTextIntoField("onClick", "{{closeModal", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "closeModal"); + _.propPane.TypeTextIntoField("onClick", "{{Text1.setDisabled", true); + _.agHelper.GetNAssertElementText(_.locators._hints, "setDisabled"); + + _.propPane.TypeTextIntoField("Label", "{{storeValue", true); + _.agHelper.AssertElementAbsence(_.locators._hints); + _.propPane.TypeTextIntoField("Label", "{{Text1.setDisabled", true); + _.agHelper.AssertElementAbsence(_.locators._hints); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/IconButton_2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/IconButton_2_spec.ts index 5bceb35158..e5c1c5c1f9 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/IconButton_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/IconButton_2_spec.ts @@ -5,6 +5,7 @@ import { entityExplorer, propPane, draggableWidgets, + assertHelper, } from "../../../../../support/Objects/ObjectsCore"; describe("Icon Button widget Tests", function () { @@ -67,10 +68,12 @@ describe("Icon Button widget Tests", function () { it("3. Validate icon can be selected from dropdown and icon can be searched by typing", () => { // Select from dropdown agHelper.GetNClick(`${locators._propertyControl}icon`); - agHelper.GetNClick(propPane._dataIcon("power")); + agHelper.GetElement(propPane._iconDropdown).scrollTo("top"); + agHelper.Sleep(); + agHelper.GetNClick(propPane._dataIcon("airplane")); agHelper.AssertElementVisibility( `${locators._widgetInDeployed("iconbuttonwidget")} ${propPane._dataIcon( - "power", + "airplane", )}`, ); @@ -101,10 +104,18 @@ describe("Icon Button widget Tests", function () { "onClick", "{{navigateTo('www.yahoo.com', {}, 'SAME_WINDOW');}}", ); - deployMode.DeployApp(); + deployMode.DeployApp( + locators._widgetInDeployed(draggableWidgets.ICONBUTTON), + ); agHelper.GetNClick(`${locators._widgetInDeployed("iconbuttonwidget")}`); agHelper.AssertURL("yahoo.com"); - agHelper.BrowserNavigation(-1); + // agHelper.BrowserNavigation(-1); + cy.window({ timeout: 60000 }).then((win) => { + win.history.back(); + }); + assertHelper.AssertNetworkResponseData("@viewPage"); + assertHelper.AssertDocumentReady(); + agHelper.Sleep(3000); //for view page to complete loading & then navigate back deployMode.NavigateBacktoEditor(); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Radio/Radio2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Radio/Radio2_spec.ts index 4ee3313ed6..2daf363873 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Radio/Radio2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Radio/Radio2_spec.ts @@ -347,6 +347,7 @@ describe("Radio Widget test cases", function () { entityExplorer.SelectEntityByName("Text1", "Widgets"); propPane.UpdatePropertyFieldValue("Text", "false"); deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.TEXT)); + agHelper.Sleep(2000); //for Radio Group to load fully, for CI flakyness agHelper.WaitUntilEleAppear( locators._widgetInDeployed(draggableWidgets.RADIO_GROUP), ); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select_Validation_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select_Validation_spec.js index a4a1a7e706..0aa0664347 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select_Validation_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select_Validation_spec.js @@ -29,5 +29,74 @@ describe("Select Widget Functionality", function () { "contain.text", "Green", ); + _.deployMode.NavigateBacktoEditor(); + }); + + it("2. Shows validation error for invalid defaultSelectedValue", () => { + const { agHelper, entityExplorer, locators, propPane } = _; + + entityExplorer.SelectEntityByName("SelectRenamed", "Widgets"); + + propPane.UpdatePropertyFieldValue("Default selected value", "GREEN1", true); + + agHelper.VerifyEvaluatedErrorMessage( + "Default value is missing in options. Please update the value.", + ); + + propPane.ToggleJSMode("Source Data", true); + + // Updates the options and asserts that the validation error is fixed + propPane.UpdatePropertyFieldValue( + "Source Data", + '[{"name": "Green", "code":"GREEN1"}]', + true, + ); + + agHelper.FocusElement( + locators._propertyInputField("Default selected value"), + ); + + agHelper.AssertElementAbsence(locators._evaluatedErrorMessage); + + // Changes options to bring back validation error + propPane.UpdatePropertyFieldValue( + "Source Data", + '[{"name": "Green", "code":"GREEN"}]', + true, + ); + + agHelper.FocusElement( + locators._propertyInputField("Default selected value"), + ); + + agHelper.VerifyEvaluatedErrorMessage( + "Default value is missing in options. Please update the value.", + ); + + // Reload to check if the error persists + agHelper.RefreshPage(); + + entityExplorer.SelectEntityByName("SelectRenamed", "Widgets"); + + agHelper.FocusElement( + locators._propertyInputField("Default selected value"), + ); + + agHelper.VerifyEvaluatedErrorMessage( + "Default value is missing in options. Please update the value.", + ); + + // Fixes the validation error + propPane.UpdatePropertyFieldValue( + "Source Data", + '[{"name": "Green", "code": {{"GREEN1"}}}]', + true, + ); + + agHelper.FocusElement( + locators._propertyInputField("Default selected value"), + ); + + agHelper.AssertElementAbsence(locators._evaluatedErrorMessage); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts index 57240928a3..b6cc1f61b4 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Tab/Tabs_2_spec.ts @@ -231,4 +231,44 @@ describe("Tabs widget Tests", function () { "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", ); }); + + it("8. Checks validation error in default selected tab", () => { + entityExplorer.SelectEntityByName("NewTabs", "Widgets"); + + propPane.MoveToTab("Content"); + + propPane.TypeTextIntoField("Default tab", "Tab 11", true); + + agHelper.VerifyEvaluatedErrorMessage("Tab name Tab 11 does not exist"); + + agHelper.ClearNType( + locators._draggableFieldConfig("tab1") + " input", + "Tab 11", + 0, + ); + + agHelper.AssertAutoSave(); + + agHelper.Sleep(1000); + + agHelper.FocusElement(locators._propertyInputField("Default tab")); + + agHelper.AssertElementAbsence(locators._evaluatedErrorMessage); + + propPane.TypeTextIntoField("Default tab", "Tab 13", true); + + agHelper.VerifyEvaluatedErrorMessage("Tab name Tab 13 does not exist"); + + agHelper.AssertAutoSave(); + + agHelper.Sleep(1000); + + cy.reload(); + + entityExplorer.SelectEntityByName("NewTabs", "Widgets"); + + agHelper.FocusElement(locators._propertyInputField("Default tab")); + + agHelper.VerifyEvaluatedErrorMessage("Tab name Tab 13 does not exist"); + }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_DisplayText_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_DisplayText_spec.ts index 59195265ce..eb1ec4f80e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_DisplayText_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_DisplayText_spec.ts @@ -65,7 +65,7 @@ describe("Table V2 sort & filter using display text functionality", () => { true, false, ); - agHelper.RemoveEvaluatedPopUp(); + agHelper.RemoveUIElement("EvaluatedPopUp"); table.OpenNFilterTable("name", "contains", "Y"); table.ReadTableRowColumnData(0, 0, "v2").then(($cellData) => { expect($cellData).to.eq("Y"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Workspace/CreateSameAppInDiffWorkspace_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Workspace/CreateSameAppInDiffWorkspace_spec.js index 5c169d1925..d03ea962d5 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Workspace/CreateSameAppInDiffWorkspace_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Workspace/CreateSameAppInDiffWorkspace_spec.js @@ -15,7 +15,6 @@ describe("Create app same name in different workspace", function () { cy.generateUUID().then((uid) => { workspaceId = uid; appid = uid; - localStorage.setItem("WorkspaceName", workspaceId); cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { newWorkspaceName = interception.response.body.data.name; diff --git a/app/client/cypress/e2e/Regression/ClientSide/Workspace/DeleteMultipleApplications.ts b/app/client/cypress/e2e/Regression/ClientSide/Workspace/DeleteMultipleApplications.ts index 07e311dacc..b2a4ad97dc 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Workspace/DeleteMultipleApplications.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Workspace/DeleteMultipleApplications.ts @@ -85,8 +85,8 @@ describe("Delete multiple application", function () { ); agHelper.ClickButton("Delete"); agHelper.ClickButton("Yes"); - assertHelper.WaitForNetworkCall("@deleteMultipleApp").then((intercept) => { - expect(intercept.response?.body?.data?.length).to.be.equal(2); + assertHelper.WaitForNetworkCall("@deleteMultipleApp").then((response) => { + expect(response?.body?.data?.length).to.be.equal(2); agHelper.AssertElementAbsence( homePage._appCard(MultipleDeleteFirstWorkspace.applicationName), ); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Workspace/MemberRoles_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Workspace/MemberRoles_Spec.ts index df4dc208d0..490d5cdd15 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Workspace/MemberRoles_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Workspace/MemberRoles_Spec.ts @@ -13,7 +13,6 @@ describe("Create new workspace and invite user & validate all roles", () => { appid = uid; featureFlagIntercept({ license_gac_enabled: true }); _.agHelper.Sleep(2000); - //localStorage.setItem("WorkspaceName", workspaceId); _.homePage.CreateNewWorkspace(workspaceId); _.homePage.CheckWorkspaceShareUsersCount(workspaceId, 1); _.homePage.InviteUserToWorkspaceErrorMessage(workspaceId, "abcdef"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Workspace/WorkspaceImportApplication_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Workspace/WorkspaceImportApplication_spec.js index 46bfbb73b6..3426ef0649 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Workspace/WorkspaceImportApplication_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Workspace/WorkspaceImportApplication_spec.js @@ -37,7 +37,6 @@ describe("Workspace Import Application", function () { cy.generateUUID().then((uid) => { workspaceId = uid; - localStorage.setItem("WorkspaceName", workspaceId); cy.createWorkspace(); cy.wait("@createWorkspace").then((createWorkspaceInterception) => { newWorkspaceName = diff --git a/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/JSOnLoad1_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/JSOnLoad1_Spec.ts index 3678175de5..6616af78e8 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/JSOnLoad1_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/JSOnLoad1_Spec.ts @@ -136,8 +136,8 @@ describe("JSObjects OnLoad Actions tests", function () { jsEditor.EnableDisableAsyncFuncSettings("getEmployee", false, true); deployMode.DeployApp(locators._widgetInDeployed("tablewidget"), false); agHelper.WaitUntilToastDisappear('The action "GetEmployee" has failed'); - deployMode.NavigateBacktoEditor(); - agHelper.WaitUntilToastDisappear('The action "GetEmployee" has failed'); + deployMode.NavigateBacktoEditor('The action "GetEmployee" has failed'); + //agHelper.WaitUntilToastDisappear('The action "GetEmployee" has failed'); // ee.ExpandCollapseEntity("Queries/JS"); // ee.SelectEntityByName(jsName as string); // jsEditor.EnableDisableAsyncFuncSettings("getEmployee", true, true); diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo_Spec.js b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo_Spec.js index 6ee10793b3..db662156bb 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo_Spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo_Spec.js @@ -365,7 +365,7 @@ describe("Validate Mongo query commands", function () { it("9. Delete the datasource after NewPage deletion is success", () => { cy.NavigateToQueryEditor(); - dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200 | 409]); + dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200, 409]); }); it("10. Bug 6375: Cyclic Dependency error occurs and the app crashes when the user generate table and chart from mongo query", function () { diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Postgres_Spec.js b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Postgres_Spec.js index 549eb775de..fcfb673a40 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Postgres_Spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Postgres_Spec.js @@ -321,6 +321,6 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", it("13. Deletes the datasource", () => { cy.NavigateToQueryEditor(); - dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200 | 409]); + dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200, 409]); }); }); diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js index 3142fc5dbe..fa4001575d 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js @@ -522,6 +522,6 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications", }); after("Deletes the datasource", () => { - dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200 | 409]); + dataSources.DeleteDatasouceFromActiveTab(datasourceName, [200, 409]); }); }); diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_2_spec.ts b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_2_spec.ts index c35920b726..30ad1019c3 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_2_spec.ts @@ -433,7 +433,7 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications", }); }); - it("6. Verify Adding Suggested widget with specific name functionality - S3 ", () => { + it("6. Verify Adding Suggested widget with already present widget - S3 ", () => { entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE); agHelper.Sleep(2500); //allowing sometime for widget to settle down dataSources.NavigateFromActiveDS(datasourceName, true); @@ -442,11 +442,11 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications", dataSources.ValidateNSelectDropdown("Commands", "List files in bucket"); agHelper.UpdateCodeInput(formControls.s3BucketName, bucketName); dataSources.RunQuery(); + agHelper.ScrollIntoView("." + dataSources._addSuggestedExisting); dataSources.AddSuggestedWidget( Widgets.Table, dataSources._addSuggestedExisting, ); - propPane.DeleteWidgetDirectlyFromPropertyPane(); entityExplorer.SelectEntityByName($queryName, "Queries/JS"); agHelper.ActionContextMenuWithInPane({ diff --git a/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts b/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts index 2c1f07b6d2..66eb812064 100644 --- a/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts +++ b/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts @@ -293,7 +293,8 @@ describe("Validate Arango & CURL Import Datasources", () => { propPane.AssertPropertiesDropDownCurrentValue("Table data", "Connect data"); entityExplorer.NavigateToSwitcher("Explorer"); entityExplorer.SelectEntityByName("Query6"); - dataSources.FilterAndVerifyDatasourceSchemaBySearch(collectionName); + //dataSources.FilterAndVerifyDatasourceSchemaBySearch("countries"); + dataSources.VerifyTableSchemaOnQueryEditor(collectionName); dataSources.RunQuery(); dataSources.AddSuggestedWidget(Widgets.Table); //Binding to new table from schema explorer propPane.AssertPropertiesDropDownCurrentValue("Table data", "Query6"); diff --git a/app/client/cypress/e2e/Smoke/Apps/ReconnectDatasource_spec.js b/app/client/cypress/e2e/Smoke/Apps/ReconnectDatasource_spec.js index 1de44d21d1..0ee6113677 100644 --- a/app/client/cypress/e2e/Smoke/Apps/ReconnectDatasource_spec.js +++ b/app/client/cypress/e2e/Smoke/Apps/ReconnectDatasource_spec.js @@ -12,7 +12,6 @@ describe("Reconnect Datasource Modal validation while importing application", fu // import application cy.generateUUID().then((uid) => { workspaceId = uid; - localStorage.setItem("WorkspaceName", workspaceId); cy.createWorkspace(); cy.wait("@createWorkspace").then((createWorkspaceInterception) => { newWorkspaceName = createWorkspaceInterception.response.body.data.name; diff --git a/app/client/cypress/locators/TemplatesLocators.json b/app/client/cypress/locators/TemplatesLocators.json index a35b0ce37d..4ff4ded720 100644 --- a/app/client/cypress/locators/TemplatesLocators.json +++ b/app/client/cypress/locators/TemplatesLocators.json @@ -9,5 +9,5 @@ "selectCheckbox": ".ads-v2-checkbox", "closeButton": "//button[@aria-label='Close']//span", "marketingDashboard": "//h1[text()='Marketing Dashboard']", - "meetingSchedulerDashboard": "//h1[text()='Meeting Scheduler']" + "vehicleMaintenenceApp": "//h1[text()='Vehicle Maintenance App']" } diff --git a/app/client/cypress/scripts/cypress-split.ts b/app/client/cypress/scripts/cypress-split.ts index fc5025db85..3bfc47e606 100644 --- a/app/client/cypress/scripts/cypress-split.ts +++ b/app/client/cypress/scripts/cypress-split.ts @@ -74,7 +74,8 @@ export class cypressSplit { ); const specsToRun = await this.getSpecsWithTime(specFilePaths, attemptId); - return specsToRun === undefined + console.log("SPECS TO RUN ----------> :", specsToRun); + return specsToRun === undefined || specsToRun.length === 0 ? [] : specsToRun[0].map((spec) => spec.name); } catch (err) { diff --git a/app/client/cypress/support/Pages/AggregateHelper.ts b/app/client/cypress/support/Pages/AggregateHelper.ts index 4e51124f36..f13eccf4d6 100644 --- a/app/client/cypress/support/Pages/AggregateHelper.ts +++ b/app/client/cypress/support/Pages/AggregateHelper.ts @@ -37,7 +37,6 @@ const DEFAULT_ENTERVALUE_OPTIONS = { export class AggregateHelper extends ReusableHelper { private locator = ObjectsRegistry.CommonLocators; - public _modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; private assertHelper = ObjectsRegistry.AssertHelper; public get isMac() { @@ -49,6 +48,7 @@ export class AggregateHelper extends ReusableHelper { public get removeLine() { return "{backspace}"; } + public _modifierKey = `${this.isMac ? "meta" : "ctrl"}`; private selectAll = `${this.isMac ? "{cmd}{a}" : "{ctrl}{a}"}`; private lazyCodeEditorFallback = ".t--lazyCodeEditor-fallback"; private lazyCodeEditorRendered = ".t--lazyCodeEditor-editor"; @@ -100,6 +100,28 @@ export class AggregateHelper extends ReusableHelper { }); } + public SimulateCopyPaste(action: "copy" | "paste" | "cut") { + const actionToKey = { + copy: "c", + paste: "v", + cut: "x", + }; + const keyToSimulate = actionToKey[action]; + + // Simulate Ctrl keypress (Ctrl down) + this.GetElement(this.locator._body).type(`{${this._modifierKey}}`, { + release: false, + }); + + // Simulate 'C' keypress while Ctrl is held (Ctrl + C) + this.GetElement(this.locator._body).type(keyToSimulate, { release: false }); + + // Release the Ctrl key + this.GetElement(this.locator._body).type(`{${this._modifierKey}}`, { + release: true, + }); + } + public AddDsl( dslFile: string, elementToCheckPresenceaftDslLoad: string | "" = "", // reloadWithoutCache = true, @@ -301,30 +323,57 @@ export class AggregateHelper extends ReusableHelper { } else this.GetNAssertContains(this.locator._toastMsg, text); } - public RemoveTooltip(toolTip: string) { - cy.get("body").then(($body) => { - if ($body.find(this.locator._appLeveltooltip(toolTip)).length > 0) { - this.GetElement(this.locator._appLeveltooltip(toolTip)) - .parents("div.rc-tooltip") - .then(($tooltipElement) => { - $tooltipElement.remove(); - cy.log(toolTip + " tooltip removed"); - }); - } - }); - } - public AssertTooltip(toolTipText: string) { this.GetNAssertContains(this.toolTipSpan, toolTipText); } - public RemoveEvaluatedPopUp() { + public RemoveUIElement( + elementToRemove: "EvaluatedPopUp" | "Tooltip" | "Toast", + toolTipOrToasttext = "", + ) { cy.get("body").then(($body) => { - if ($body.find(this.locator._evalPopup).length > 0) { - this.GetElement(this.locator._evalPopup).then(($evalPopUp) => { - $evalPopUp.remove(); - cy.log("Eval pop up removed"); - }); + switch (elementToRemove) { + case "EvaluatedPopUp": + if ($body.find(this.locator._evalPopup).length > 0) { + this.GetElement(this.locator._evalPopup).then(($evalPopUp) => { + $evalPopUp.remove(); + cy.log("Eval pop up removed"); + }); + } + break; + case "Tooltip": + if ( + $body.find(this.locator._appLeveltooltip(toolTipOrToasttext)) + .length > 0 + ) { + this.GetElement(this.locator._appLeveltooltip(toolTipOrToasttext)) + .parents("div.rc-tooltip") + .then(($tooltipElement) => { + $tooltipElement.remove(); + cy.log(toolTipOrToasttext + " tooltip removed"); + }); + } + break; + case "Toast": + if ( + $body.find( + this.locator._toastContainer + + " span:contains(" + + toolTipOrToasttext + + ")", + ).length > 0 + ) { + this.GetElement( + this.locator._toastContainer + + ":has(:contains('" + + toolTipOrToasttext + + "'))", + ).then(($toastContainer) => { + $toastContainer.remove(); + cy.log(toolTipOrToasttext + " toast removed"); + }); + } + break; } }); } @@ -890,7 +939,7 @@ export class AggregateHelper extends ReusableHelper { ) { return cy .get(selector) - .contains(containsText) + .contains(containsText, { matchCase: false }) .eq(index) .click({ force: force }) .wait(waitTimeInterval); @@ -1041,7 +1090,7 @@ export class AggregateHelper extends ReusableHelper { }); }); this.assertHelper.AssertDocumentReady(); - this.Sleep(2000); + this.Sleep(4000); //for page to load for CI runs networkCallAlias && this.assertHelper.AssertNetworkStatus("@" + networkCallAlias); //getWorkspace for Edit page! } @@ -1195,9 +1244,9 @@ export class AggregateHelper extends ReusableHelper { setTimeout(() => { // Move cursor to the end of the line input.execCommand("goLineEnd"); - }, 300); - }, 300); - }, 300); + }, 500); + }, 500); + }, 500); } else { input.focus(); this.Sleep(200); @@ -1207,7 +1256,7 @@ export class AggregateHelper extends ReusableHelper { this.Sleep(200); } }); - this.Sleep(500); //for value set to settle + this.Sleep(); //for value set to settle } public UpdateFieldInput(selector: string, value: string) { diff --git a/app/client/cypress/support/Pages/ApiPage.ts b/app/client/cypress/support/Pages/ApiPage.ts index ecb92de629..7f6fa59f48 100644 --- a/app/client/cypress/support/Pages/ApiPage.ts +++ b/app/client/cypress/support/Pages/ApiPage.ts @@ -88,10 +88,10 @@ export class ApiPage { ) { if (aftDSSaved) this.agHelper.GetNClick(this._createQuery); else { - this.agHelper.RemoveEvaluatedPopUp(); + this.agHelper.RemoveUIElement("EvaluatedPopUp"); this.agHelper.GetHoverNClick(this.locator._createNew); this.agHelper.GetNClick(this._blankAPI, 0, true); - this.agHelper.RemoveTooltip("Add a new query/JS Object"); + this.agHelper.RemoveUIElement("Tooltip", "Add a new query/JS Object"); } this.assertHelper.AssertNetworkStatus("@createNewApi", 201); diff --git a/app/client/cypress/support/Pages/AssertHelper.ts b/app/client/cypress/support/Pages/AssertHelper.ts index 50c93a324d..b8aa9d4561 100644 --- a/app/client/cypress/support/Pages/AssertHelper.ts +++ b/app/client/cypress/support/Pages/AssertHelper.ts @@ -14,7 +14,6 @@ export const EntityItems = { export type EntityItemsType = (typeof EntityItems)[keyof typeof EntityItems]; export class AssertHelper extends ReusableHelper { - private locator = ObjectsRegistry.CommonLocators; public _modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; public isMac = Cypress.platform === "darwin"; @@ -64,41 +63,84 @@ export class AssertHelper extends ReusableHelper { return aliasName; } - public WaitForNetworkCall(aliasName: string, responseTimeout = 60000) { + public WaitForNetworkCall(aliasName: string, responseTimeout = 150000) { // cy.wait(aliasName).then(($apiCall: any) => { // expect($apiCall.response.body.responseMeta.status).to.eq(expectedStatus); // }); - // cy.wait(aliasName).should( - // "have.nested.property", - // "response.body.responseMeta.status", - // expectedStatus, - // ); - this.Sleep(); //Wait a bit for call to finish! - return cy.wait(this.GetAliasName(aliasName), { responseTimeout }); + this.Sleep(); //wait a bit to avoid flaky tests + return cy + .wait(this.GetAliasName(aliasName), { timeout: responseTimeout }) + .then((interceptions) => { + return cy + .get(this.GetAliasName(aliasName), { timeout: responseTimeout }) + .its("response"); + }); } - public AssertNetworkStatus(aliasName: string, expectedStatus = 200) { - this.WaitForNetworkCall(aliasName); - cy.get(this.GetAliasName(aliasName)) - .its("response.body.responseMeta.status") - .should("eq", expectedStatus); - - //To improve below: - // cy.wait(aliasName, { timeout: timeout }).should((response: any) => { - // expect(response.status).to.be.oneOf([expectedStatus]); - // }); + public AssertNetworkStatus( + aliasName: string, + expectedStatus: number | number[] = 200, + waitForNetworkCall = true, + ) { + if (waitForNetworkCall) { + // If waitForNetworkCall is true, then use the response from WaitForNetworkCall call + return this.WaitForNetworkCall(aliasName).then((response: any) => + this.processNetworkStatus(response, expectedStatus), + ); + } else { + // If interception is not available, directly get the alias & use it + return cy + .get(this.GetAliasName(aliasName)) + .its("response") + .then((interception: any) => + this.processNetworkStatus(interception, expectedStatus), + ); + } } - public AssertNetworkResponseData(aliasName: string) { - this.WaitForNetworkCall(aliasName, 100000); - cy.get(this.GetAliasName(aliasName)) - .its("response.body.data") - .should("not.be.empty"); + private processNetworkStatus( + response: any, + expectedStatus: number | number[], + ) { + const responseStatus = Number(response.body.responseMeta.status); + const expectedStatusArray = Array.isArray(expectedStatus) + ? expectedStatus + : [expectedStatus]; + + expect(expectedStatusArray).to.include(responseStatus); + return responseStatus; } - public AssertNetworkExecutionSuccess(aliasName: string, expectedRes = true) { - this.WaitForNetworkCall(aliasName); + public AssertNetworkResponseData( + aliasName: string, + waitForNetworkCall = true, + ) { + if (waitForNetworkCall) { + // If waitForNetworkCall is true, then use the interception from received call + this.WaitForNetworkCall(aliasName).then((interception: any) => { + this.processNetworkResponseData(interception); + }); + } else { + // If interception is not available, directly get the alias & use it + cy.get(this.GetAliasName(aliasName)) + .its("response") + .then((interception: any) => { + this.processNetworkResponseData(interception); + }); + } + } + + private processNetworkResponseData(response: any) { + expect(response.body.data).to.not.be.empty; + } + + public AssertNetworkExecutionSuccess( + aliasName: string, + expectedRes = true, + waitForNetworkCall = true, + ) { + waitForNetworkCall && this.WaitForNetworkCall(aliasName); cy.get(aliasName) .its("response.body.data.isExecutionSuccess") .should("eq", expectedRes); diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 6b812f0c87..8d8d90aeee 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -332,7 +332,7 @@ export class DataSources { this.assertHelper.AssertNetworkStatus("@getDatasourceStructure"); //Making sure table dropdown is populated this.agHelper.GetNClick(this._selectTableDropdown, 0, true); this.agHelper.GetNClickByContains(this._dropdownOption, "public.users"); - this.agHelper.GetNClick(this._generatePageBtn); + this.agHelper.GetNClick(this._generatePageBtn, 0, true); this.assertHelper.AssertNetworkStatus("@replaceLayoutWithCRUDPage", 201); this.agHelper.ClickButton("Got it"); } @@ -449,7 +449,7 @@ export class DataSources { this.agHelper.GetNClick(this._addNewDataSource, 0, true); this.agHelper.Sleep(); }); - this.agHelper.RemoveTooltip("Add a new datasource"); + this.agHelper.RemoveUIElement("Tooltip", "Add a new datasource"); cy.get(this._newDatasourceContainer).scrollTo("bottom", { ensureScrollable: false, }); @@ -875,7 +875,7 @@ export class DataSources { public DeleteDatasouceFromActiveTab( datasourceName: string, - expectedRes = 200 || 409 || [200 | 409], + expectedRes = 200 || 409 || [200, 409], ) { this.ClickActiveTabDSContextMenu(datasourceName); this.agHelper.GetNClick(this._dsOptionMenuItem("Delete"), 0, false, 200); @@ -885,7 +885,7 @@ export class DataSources { public DeleteDatasourceFromWithinDS( datasourceName: string, - expectedRes: number | number[] = 200 || 409 || [200 | 409], + expectedRes: number | number[] = 200 || 409 || [200, 409], ) { this.NavigateToActiveTab(); cy.get(this._datasourceCard) @@ -913,7 +913,7 @@ export class DataSources { } public DeleteDSDirectly( - expectedRes: number | number[] = 200 || 409 || [200 | 409], + expectedRes: number | number[] = 200 || 409 || [200, 409], toNavigateToDSInfoPage = true, ) { toNavigateToDSInfoPage && @@ -938,20 +938,15 @@ export class DataSources { } public ValidateDSDeletion(expectedRes: number | number[] = 200) { - let toValidateRes = expectedRes == 200 || expectedRes == 409 ? true : false; - if (toValidateRes) { - if (expectedRes == 200) - this.agHelper.AssertContains("datasource deleted successfully"); - else this.agHelper.AssertContains("action(s) using it."); - this.assertHelper.AssertNetworkStatus( - "@deleteDatasource", - expectedRes as number, - ); - } else { - cy.wait("@deleteDatasource") - .its("response.body.responseMeta.status") - .should("be.oneOf", [200, 409]); - } + this.assertHelper + .AssertNetworkStatus("@deleteDatasource", expectedRes) + .then((responseStatus) => { + this.agHelper.AssertContains( + responseStatus === 200 + ? "datasource deleted successfully" + : "action(s) using it", + ); + }); } public NavigateToActiveTab() { @@ -1358,7 +1353,7 @@ export class DataSources { queryName = "", sleep = 500, ) { - this.agHelper.RemoveEvaluatedPopUp(); //to close the evaluated pop-up + this.agHelper.RemoveUIElement("EvaluatedPopUp"); //to close the evaluated pop-up this.entityExplorer.CreateNewDsQuery(dsName); if (query) { this.EnterQuery(query, sleep); @@ -1946,9 +1941,8 @@ export class DataSources { .WaitForNetworkCall( `@getDatasourceStructureUpdated_${ds_entity_name}`, ) - .then(async (interception) => { - const tables: any[] = - interception?.response?.body.data?.tables || []; + .then(async (response) => { + const tables: any[] = response?.body.data?.tables || []; const indexOfTable = tables.findIndex( (table) => table.name === targetTableName, ); @@ -1991,7 +1985,6 @@ export class DataSources { ); } } - this.agHelper.AssertElementVisibility( this._dsVirtuosoElementTable(targetTableName), ); diff --git a/app/client/cypress/support/Pages/DeployModeHelper.ts b/app/client/cypress/support/Pages/DeployModeHelper.ts index ebd740047b..53c3e5f837 100644 --- a/app/client/cypress/support/Pages/DeployModeHelper.ts +++ b/app/client/cypress/support/Pages/DeployModeHelper.ts @@ -31,9 +31,6 @@ export class DeployMode { private _backtoHome = ".t--app-viewer-navigation-header .t--app-viewer-back-to-apps-button"; private _homeAppsmithImage = "a.t--appsmith-logo"; - public envInfoModal = `[data-testid="t--env-info-modal"]`; - public envInfoModalDismissCheckbox = `[data-testid="t--env-info-dismiss-checkbox"]`; - public envInfoModalDeployButton = `[data-testid="t--env-info-modal-deploy-button"]`; //refering PublishtheApp from command.js public DeployApp( @@ -41,8 +38,6 @@ export class DeployMode { toCheckFailureToast = true, toValidateSavedState = true, addDebugFlag = true, - assertEnvInfoModal?: "present" | "absent", - dismissModal = false, ) { //cy.intercept("POST", "/api/v1/applications/publish/*").as("publishAppli"); @@ -53,16 +48,6 @@ export class DeployMode { this.assertHelper.AssertDocumentReady(); this.StubbingDeployPage(addDebugFlag); this.agHelper.ClickButton("Deploy"); - if (!!assertEnvInfoModal && assertEnvInfoModal === "present") { - this.agHelper.WaitUntilEleAppear(this.envInfoModal); - this.agHelper.AssertElementExist(this.envInfoModal); - if (dismissModal) { - this.agHelper.CheckUncheck(this.envInfoModalDismissCheckbox); - } - } else { - this.agHelper.AssertElementAbsence(this.envInfoModal); - } - this.agHelper.GetNClickIfPresent(this.envInfoModalDeployButton); this.agHelper.AssertElementAbsence(this.locator._btnSpinner, 10000); //to make sure we have started navigation from Edit page //cy.get("@windowDeployStub").should("be.calledOnce"); this.assertHelper.AssertDocumentReady(); @@ -160,11 +145,14 @@ export class DeployMode { this.assertHelper.AssertDocumentReady(); } - public NavigateBacktoEditor() { + public NavigateBacktoEditor(toastToCheck = "") { this.assertHelper.AssertDocumentReady(); this.agHelper.GetNClick(this.locator._backToEditor, 0, true); this.agHelper.Sleep(); localStorage.setItem("inDeployedMode", "false"); + if (toastToCheck) { + this.agHelper.ValidateToastMessage(toastToCheck); + } //Assert no error toast in Edit mode when navigating back from Deploy mode this.agHelper.AssertElementAbsence( this.locator._specificToast("There was an unexpected error"), @@ -174,6 +162,9 @@ export class DeployMode { "Internal server error while processing request", ), ); + this.agHelper.AssertElementAbsence( + this.locator._specificToast("Cannot read properties of undefined"), + ); this.assertHelper.AssertNetworkResponseData("@getPluginForm"); //for auth rest api this.assertHelper.AssertNetworkResponseData("@getPluginForm"); //for graphql this.assertHelper.AssertNetworkStatus("@getWorkspace"); diff --git a/app/client/cypress/support/Pages/EntityExplorer.ts b/app/client/cypress/support/Pages/EntityExplorer.ts index 48a94a178a..0bca90e2b7 100644 --- a/app/client/cypress/support/Pages/EntityExplorer.ts +++ b/app/client/cypress/support/Pages/EntityExplorer.ts @@ -148,11 +148,17 @@ export class EntityExplorer { index = 0, force = false, ) { - this.agHelper.GetNClick( - this._openNavigationTab(navigationTab), - index, - force, - ); + this.agHelper + .GetAttribute(this._openNavigationTab(navigationTab), "data-selected") + .then(($value) => { + if ($value === "true") return; + else + this.agHelper.GetNClick( + this._openNavigationTab(navigationTab), + index, + force, + ); + }); } public AssertEntityPresenceInExplorer(entityNameinLeftSidebar: string) { @@ -307,7 +313,7 @@ export class EntityExplorer { } public SearchWidgetPane(widgetType: string) { - this.NavigateToSwitcher("Widgets"); + this.NavigateToSwitcher("Widgets", 0, true); this.agHelper.Sleep(); this.agHelper.ClearTextField(this.locator._entityExplorersearch); this.agHelper.TypeText( diff --git a/app/client/cypress/support/Pages/GitSync.ts b/app/client/cypress/support/Pages/GitSync.ts index 99f44545f3..93333061de 100644 --- a/app/client/cypress/support/Pages/GitSync.ts +++ b/app/client/cypress/support/Pages/GitSync.ts @@ -120,7 +120,8 @@ export class GitSync { this.agHelper.ClickButton("Generate key"); this.agHelper.GenerateUUID(); cy.get("@guid").then((uid) => { - cy.wait(`@generateKey-${repo}`).then((result: any) => { + this.assertHelper.AssertNetworkStatus("@generateKey-" + repo, [200, 201]); + cy.get(`@generateKey-${repo}`).then((result: any) => { generatedKey = result.response.body.data.publicKey; generatedKey = generatedKey.slice(0, generatedKey.length - 1); // fetch the generated key and post to the github repo @@ -156,7 +157,13 @@ export class GitSync { this.assertHelper.AssertNetworkStatus("@connectGitLocalRepo"); this.agHelper.AssertElementExist(this._bottomBarCommit, 0, 30000); this.CloseGitSyncModal(); + this.agHelper.Sleep(2000); //for generatedKey to be available in CI runs + this.assertHelper.AssertNetworkStatus("@generatedKey", 201); } else { + this.assertHelper.AssertContains( + "Error while accessing the file system", + "not.exist", + ); this.assertHelper.AssertNetworkStatus("@importFromGit", 201); } } @@ -336,7 +343,11 @@ export class GitSync { }); } - CreateGitBranch(branch = "br", toUseNewGuid = false) { + CreateGitBranch( + branch = "br", + toUseNewGuid = false, + assertCreateBranch = true, + ) { if (toUseNewGuid) this.agHelper.GenerateUUID(); this.agHelper.AssertElementExist(this._bottomBarCommit); this.agHelper.GetNClick(this._branchButton); @@ -348,6 +359,13 @@ export class GitSync { `{selectall}` + `${branch + uid}` + `{enter}`, { parseSpecialCharSeq: true }, ); + assertCreateBranch && + this.assertHelper.AssertNetworkStatus("createBranch", 201); + this.agHelper.AssertElementAbsence( + this.locator._specificToast( + "Unable to import application in workspace", + ), + ); this.agHelper.AssertElementExist(this.locator._btnSpinner); this.agHelper.AssertElementAbsence(this.locator._btnSpinner, 70000); //Since page taking more time to laod in some cases this.agHelper.AssertElementVisibility(this._branchName(branch + uid)); @@ -416,13 +434,13 @@ export class GitSync { this.agHelper.GetNClick(this._openRepoButton); } - CommitAndPush(assertFailure?: true) { + CommitAndPush(assertSuccess = true) { this.agHelper.GetNClick(this.locator._publishButton); this.agHelper.AssertElementExist(this._bottomBarPull); //cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); this.agHelper.TypeText(this._commitCommentInput, "Initial commit"); this.agHelper.GetNClick(this._commitButton); - if (!assertFailure) { + if (assertSuccess) { // check for commit success //adding timeout since commit is taking longer sometimes this.assertHelper.AssertNetworkStatus("@commit", 201); diff --git a/app/client/cypress/support/Pages/HomePage.ts b/app/client/cypress/support/Pages/HomePage.ts index 1fb9d5b23c..5a216d42ac 100644 --- a/app/client/cypress/support/Pages/HomePage.ts +++ b/app/client/cypress/support/Pages/HomePage.ts @@ -124,6 +124,18 @@ export class HomePage { this.agHelper.GetNClick(this._homeTab); } + //trying to wrap multiple values, not used + public WrapAndAliasMultipleValues( + values: Record, + ): Record { + const aliases: Record = {}; + Object.keys(values).forEach((key) => { + aliases[key] = values[key]; + cy.wrap(values[key]).as(key); + }); + return aliases; + } + public CreateNewWorkspace(workspaceNewName = "", toNavigateToHome = false) { if (toNavigateToHome) this.NavigateToHome(); let oldName = ""; @@ -136,6 +148,7 @@ export class HomePage { "workspaceName", interception.response.body.data.name, ); + cy.wrap(interception.response.body.data.name).as("workspaceName"); }); workspaceNewName && @@ -172,6 +185,7 @@ export class HomePage { this.agHelper.Sleep(2000); this.assertHelper.AssertNetworkStatus("@updateWorkspace"); this.agHelper.AssertContains(newWorkspaceName); + this.agHelper.Sleep(2000); //for new workspace to settle for CI } //Maps to CheckShareIcon in command.js @@ -240,8 +254,8 @@ export class HomePage { } public NavigateToHome() { - cy.get(this._homeIcon).click({ force: true }); - this.agHelper.Sleep(2000); + this.agHelper.Sleep(2000); //to avoid CI flakyness + this.agHelper.GetNClick(this._homeIcon, 0, true, 2500); if (!Cypress.env("AIRGAPPED")) { this.assertHelper.AssertNetworkStatus("@getReleaseItems"); } else { @@ -251,32 +265,40 @@ export class HomePage { this.agHelper.AssertElementVisibility(this._homeAppsmithImage); } + public AssertApplicationCreated() { + this.assertHelper.AssertNetworkStatus("@createNewApplication", 201); + cy.get("@createNewApplication").then((interception: any) => { + localStorage.setItem("applicationId", interception.response.body.data.id); + localStorage.setItem("appName", interception.response.body.data.name); + cy.wrap(interception.response.body.data.name).as("appName"); + cy.wrap(interception.response.body.data.id).as("applicationId"); + }); + this.agHelper.AssertElementAbsence(this.locator._loading); + } + public CreateNewApplication(skipSignposting = true) { cy.get(this._homePageAppCreateBtn).first().click({ force: true }); - this.assertHelper.AssertNetworkStatus("@createNewApplication", 201); - cy.get(this.locator._loading).should("not.exist"); - + this.AssertApplicationCreated(); if (skipSignposting) { this.agHelper.AssertElementVisibility( this.entityExplorer._entityExplorer, ); this.onboarding.skipSignposting(); } + this.agHelper.Sleep(2000); //for getWorkspace to go thru! this.assertHelper.AssertNetworkStatus("getWorkspace"); - cy.get("@createNewApplication").then((interception: any) => { - localStorage.setItem("appName", interception.response.body.data.name); - }); } //Maps to CreateAppForWorkspace in command.js public CreateAppInWorkspace(workspaceName: string, appname = "") { - cy.xpath(this._existingWorkspaceCreateNewApp(workspaceName)) + this.agHelper + .GetElement(this._existingWorkspaceCreateNewApp(workspaceName)) .last() .scrollIntoView() .should("be.visible") .click({ force: true }); - this.assertHelper.AssertNetworkStatus("@createNewApplication", 201); - cy.get(this.locator._loading).should("not.exist"); + this.AssertApplicationCreated(); + this.agHelper.AssertElementAbsence(this.locator._loading); this.agHelper.Sleep(2000); if (appname) this.RenameApplication(appname); //this.assertHelper.AssertNetworkStatus("@updateApplication", 200); @@ -295,7 +317,7 @@ export class HomePage { }); cy.get(this._applicationName).type(appName); this.agHelper.PressEnter(); - this.agHelper.RemoveTooltip("Rename application"); + this.agHelper.RemoveUIElement("Tooltip", "Rename application"); } public GetAppName() { @@ -538,6 +560,7 @@ export class HomePage { intoWorkspaceName = "", onlyImport = false, ) { + this.agHelper.Sleep(3000); //for new workspace to settle for CI if (onlyImport === false) { cy.get(this._homeIcon).click({ force: true }); if (intoWorkspaceName) @@ -552,6 +575,9 @@ export class HomePage { force: true, }); this.agHelper.Sleep(3500); + this.agHelper.AssertElementAbsence( + this.locator._specificToast("Unable to import application in workspace"), + ); } public ImportGitApp(intoWorkspaceName = "") { diff --git a/app/client/cypress/support/Pages/JSEditor.ts b/app/client/cypress/support/Pages/JSEditor.ts index 767a71f425..bc63e1983a 100644 --- a/app/client/cypress/support/Pages/JSEditor.ts +++ b/app/client/cypress/support/Pages/JSEditor.ts @@ -132,7 +132,7 @@ export class JSEditor { cy.get(this.locator._createNew).last().click({ force: true }); cy.get(this._newJSobj).eq(0).click({ force: true }); - this.agHelper.RemoveTooltip("Add a new query/JS Object"); + this.agHelper.RemoveUIElement("Tooltip", "Add a new query/JS Object"); //Checking JS object was created successfully this.assertHelper.AssertNetworkStatus("@jsCollections", 200); // Assert that the name of the JS Object is focused when newly created diff --git a/app/client/cypress/support/Pages/LibraryInstaller.ts b/app/client/cypress/support/Pages/LibraryInstaller.ts index e6e50de158..809e213d87 100644 --- a/app/client/cypress/support/Pages/LibraryInstaller.ts +++ b/app/client/cypress/support/Pages/LibraryInstaller.ts @@ -26,7 +26,7 @@ export class LibraryInstaller { this._aggregateHelper.GetNClick(this._installer_close_locator); } - public installLibrary( + public InstallLibrary( libraryName: string, accessor: string, checkIfSuccessful = true, @@ -37,7 +37,7 @@ export class LibraryInstaller { if (checkIfSuccessful) this.assertInstall(libraryName, accessor); } - public installLibraryViaURL( + public InstallLibraryViaURL( url: string, accessor: string, checkIfSuccessful = true, @@ -71,7 +71,7 @@ export class LibraryInstaller { } public assertUnInstall(libraryName: string) { - this._aggregateHelper.AssertContains( + this._aggregateHelper.WaitUntilToastDisappear( `${libraryName} is uninstalled successfully.`, ); this._aggregateHelper.AssertElementAbsence( diff --git a/app/client/cypress/support/Pages/Onboarding.ts b/app/client/cypress/support/Pages/Onboarding.ts index 35cc8c948f..d0c96a4e54 100644 --- a/app/client/cypress/support/Pages/Onboarding.ts +++ b/app/client/cypress/support/Pages/Onboarding.ts @@ -131,7 +131,10 @@ export class Onboarding { // is awaited until it resolves return localForage.setItem("ENABLE_START_SIGNPOSTING", false); }); - this._aggregateHelper.RefreshPage(); + //this._aggregateHelper.RefreshPage();//this is causing CI flakiness, so using below + cy.window().then((win) => { + win.location.reload(); + }); } }); } diff --git a/app/client/cypress/support/Pages/PropertyPane.ts b/app/client/cypress/support/Pages/PropertyPane.ts index ed91976e76..3f04b4b01d 100644 --- a/app/client/cypress/support/Pages/PropertyPane.ts +++ b/app/client/cypress/support/Pages/PropertyPane.ts @@ -169,6 +169,8 @@ export class PropertyPane { control; _dataIcon = (icon: string) => `[data-icon="${icon}"]`; + _iconDropdown = "[data-test-id='virtuoso-scroller']"; + public OpenJsonFormFieldSettings(fieldName: string) { this.agHelper.GetNClick(this._jsonFieldEdit(fieldName)); } diff --git a/app/client/cypress/support/WorkspaceCommands.js b/app/client/cypress/support/WorkspaceCommands.js index d0fc4d68ad..1c138a9789 100644 --- a/app/client/cypress/support/WorkspaceCommands.js +++ b/app/client/cypress/support/WorkspaceCommands.js @@ -254,14 +254,26 @@ Cypress.Commands.add("CreateAppForWorkspace", (workspaceName, appname) => { "response.body.responseMeta.status", 200, ); - agHelper.RemoveTooltip("Rename application"); + agHelper.RemoveUIElement("Tooltip", "Rename application"); }); -Cypress.Commands.add("CreateAppInFirstListedWorkspace", () => { +Cypress.Commands.add("CreateNewAppInNewWorkspace", () => { let applicationId, appName; - homePageTS.CreateNewWorkspace(); //Creating a new workspace for every test, since we are deleting the workspace in the end of the test - cy.get(homePage.createNew).last().click({ force: true }); - cy.wait("@createNewApplication").then((xhr) => { + let toNavigateToHome = false; + cy.get("body").then(($ele) => { + if ($ele.find(".t--appsmith-logo").length < 0) { + toNavigateToHome = false; + } else { + toNavigateToHome = true; + } + }); + homePageTS.CreateNewWorkspace("", toNavigateToHome); //Creating a new workspace for every test, since we are deleting the workspace in the end of the test + //agHelper.Sleep(2000); //for workspace to open + cy.get("@workspaceName").then((workspaceName) => { + localStorage.setItem("workspaceName", workspaceName); + homePageTS.CreateAppInWorkspace(localStorage.getItem("workspaceName")); + }); + cy.get("@createNewApplication").then((xhr) => { const response = xhr.response; expect(response.body.responseMeta.status).to.eq(201); applicationId = response.body.data.id; @@ -271,7 +283,7 @@ Cypress.Commands.add("CreateAppInFirstListedWorkspace", () => { localStorage.setItem("appName", appName); // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(2000); + cy.wait(4000); cy.get("#loading").should("not.exist"); cy.url().then((url) => { @@ -285,10 +297,7 @@ Cypress.Commands.add("CreateAppInFirstListedWorkspace", () => { assertHelper.AssertNetworkResponseData("@getPluginForm"); //for auth rest api assertHelper.AssertNetworkResponseData("@getPluginForm"); //for graphql - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(2000); - - // If the into modal is open close it + // If the intro modal is open, close it cy.skipSignposting(); //Removing renaming of app from all tests, since its also verified in other separate tests @@ -296,7 +305,7 @@ Cypress.Commands.add("CreateAppInFirstListedWorkspace", () => { // cy.get(homePage.applicationName).type(appname + "{enter}"); // assertHelper.AssertNetworkStatus("@updateApplication"); // // Remove tooltip on the Application Name - // agHelper.RemoveTooltip("Rename application"); + // agHelper.RemoveTooltip("Tooltip","Rename application"); /* The server created app always has an old dsl so the layout will migrate * To avoid race conditions between that update layout and this one diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 5bc1c4cc85..863d4f1eed 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -353,7 +353,7 @@ Cypress.Commands.add("LoginFromAPI", (uname, pword) => { // Check if cookie is present cy.getCookie("SESSION").then((cookie) => { expect(cookie).to.not.be.null; - cy.log(cookie.value); + cy.log("cookie.value is: " + cookie.value); cy.location().should((loc) => { expect(loc.href).to.eq(loc.origin + "/applications"); @@ -362,13 +362,9 @@ Cypress.Commands.add("LoginFromAPI", (uname, pword) => { if (CURRENT_REPO === REPO.EE) { cy.wait(2000); } else { - cy.wait("@getMe"); - cy.wait("@applications").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); - cy.wait("@getReleaseItems"); + assertHelper.AssertNetworkStatus("getMe"); + assertHelper.AssertNetworkStatus("applications"); + assertHelper.AssertNetworkStatus("getReleaseItems"); } }); }); @@ -392,7 +388,19 @@ Cypress.Commands.add("DeletepageFromSideBar", () => { cy.wait(2000); }); -Cypress.Commands.add("LogOut", () => { +Cypress.Commands.add("LogOut", (toCheckgetPluginForm = true) => { + agHelper.WaitUntilAllToastsDisappear(); + //Since these are coming in every self-hosted 1st time login, commenting for CI runs + // agHelper.AssertElementAbsence( + // locators._specificToast("There was an unexpected error"), + // ); + // agHelper.AssertElementAbsence( + // locators._specificToast("Internal server error while processing request"), + // ); + if (CURRENT_REPO === REPO.CE) + toCheckgetPluginForm && + assertHelper.AssertNetworkResponseData("@getPluginForm", false); + cy.request({ method: "POST", url: "/api/v1/logout", @@ -1148,6 +1156,7 @@ Cypress.Commands.add("startServerAndRoutes", () => { cy.intercept("GET", "/api/v1/admin/env").as("getEnvVariables"); cy.intercept("DELETE", "/api/v1/git/branch/app/*").as("deleteBranch"); cy.intercept("GET", "/api/v1/git/branch/app/*").as("getBranch"); + cy.intercept("POST", "/api/v1/git/create-branch/app/*").as("createBranch"); cy.intercept("GET", "/api/v1/git/status/app/*").as("gitStatus"); cy.intercept("PUT", "/api/v1/layouts/refactor").as("updateWidgetName"); cy.intercept("GET", "/api/v1/workspaces/*/members").as("getMembers"); @@ -1159,6 +1168,7 @@ Cypress.Commands.add("startServerAndRoutes", () => { ); cy.intercept("PUT", "/api/v1/datasources/*").as("updateDatasource"); cy.intercept("POST", "/api/v1/applications/ssh-keypair/*").as("generateKey"); + cy.intercept("GET", "/api/v1/applications/ssh-keypair/*").as("generatedKey"); cy.intercept("POST", "/api/v1/applications/snapshot/*").as("snapshotSuccess"); cy.intercept( { @@ -1171,9 +1181,9 @@ Cypress.Commands.add("startServerAndRoutes", () => { }, ).as("connectGitLocalRepo"); - cy.intercept({ - method: "PUT", - }).as("sucessSave"); + // cy.intercept({ + // method: "PUT", + // }).as("sucessSave"); cy.intercept("POST", "https://api.segment.io/v1/b", (req) => { req.reply({ @@ -1447,7 +1457,13 @@ Cypress.Commands.add("createSuperUser", () => { expect(interception.request.body).contains("signupForNewsletter=true"); }); } - if (CURRENT_REPO === REPO.CE) cy.wait("@getWorkspace"); + + cy.wait(2000); + + if (CURRENT_REPO === REPO.CE) { + cy.get("#loading").should("not.exist"); + cy.get("#sidebar").should("be.visible"); + } cy.LogOut(); cy.wait(2000); diff --git a/app/client/cypress/support/e2e.js b/app/client/cypress/support/e2e.js index 4af0d4c183..5631f3ac0c 100644 --- a/app/client/cypress/support/e2e.js +++ b/app/client/cypress/support/e2e.js @@ -29,6 +29,7 @@ import { initLocalstorageRegistry } from "./Objects/Registry"; import RapidMode from "./RapidMode.ts"; import "cypress-mochawesome-reporter/register"; import installLogsCollector from "cypress-terminal-report/src/installLogsCollector"; +import { CURRENT_REPO, REPO } from "../fixtures/REPO"; import "./WorkspaceCommands"; import "./queryCommands"; @@ -89,6 +90,8 @@ before(function () { cy.visit("/setup/welcome", { timeout: 60000 }); cy.wait("@getMe"); cy.wait(2000); + const username = Cypress.env("USERNAME"); + const password = Cypress.env("PASSWORD"); cy.url().then((url) => { if (url.indexOf("setup/welcome") > -1) { cy.createSuperUser(); @@ -112,9 +115,23 @@ before(function () { Cypress.env("TESTPASSWORD4"), ); cy.LogOut(); + cy.LoginFromAPI(username, password); + } else if (url.indexOf("user/login") > -1) { + //Cypress.Cookies.preserveOnce("SESSION", "remember_token"); + cy.LoginFromAPI(username, password); + cy.wait(3000); } }); + if (CURRENT_REPO === REPO.EE) { + cy.wait(2000); + cy.url().then((url) => { + if (url.indexOf("/license") > -1) { + cy.validateLicense(); + } + }); + } + if (!Cypress.currentTest.titlePath[0].includes(WALKTHROUGH_TEST_PAGE)) { // Adding key FEATURE_WALKTHROUGH (which is used to check if the walkthrough is already shown to the user or not) for non walkthrough cypress tests (to not show walkthrough) addIndexedDBKey(FEATURE_WALKTHROUGH_INDEX_KEY, { @@ -123,45 +140,14 @@ before(function () { binding_widget: true, }); } - //console.warn = () => {}; - //Cypress.Cookies.preserveOnce("SESSION", "remember_token"); - const username = Cypress.env("USERNAME"); - const password = Cypress.env("PASSWORD"); - cy.LoginFromAPI(username, password); - cy.wait(3000); - cy.get(".t--applications-container .createnew") - .should("be.visible") - .should("be.enabled"); - cy.CreateAppInFirstListedWorkspace(); //Creating new workspace and app + + cy.CreateNewAppInNewWorkspace(); //Creating new workspace and app cy.fixture("TestDataSet1").then(function (data) { this.dataSet = data; }); }); -// before(function () { -// if (RapidMode.config.enabled) { -// return; -// } -// // //console.warn = () => {}; -// // //Cypress.Cookies.preserveOnce("SESSION", "remember_token"); -// // const username = Cypress.env("USERNAME"); -// // const password = Cypress.env("PASSWORD"); -// // cy.LoginFromAPI(username, password); -// // cy.wait(3000); -// // cy.get(".t--applications-container .createnew") -// // .should("be.visible") -// // .should("be.enabled"); -// // cy.generateUUID().then((id) => { -// // cy.CreateAppInFirstListedWorkspace(id); -// // localStorage.setItem("AppName", id); -// // }); - -// // cy.fixture("TestDataSet1").then(function (data) { -// // this.dataSet = data; -// // }); -// }); - beforeEach(function () { //cy.window().then((win) => (win.onbeforeunload = undefined)); if (!navigator.userAgent.includes("Cypress")) { @@ -185,7 +171,7 @@ after(function () { cy.DeleteAppByApi(); cy.DeleteWorkspaceByApi(); //-- LogOut Application---// - cy.LogOut(); + //cy.LogOut(false); // Commenting until Upgrade Appsmith cases are fixed // const tedUrl = "http://localhost:5001/v1/parent/cmd"; // cy.log("Start the appsmith container"); diff --git a/app/client/cypress/support/gitSync.js b/app/client/cypress/support/gitSync.js index a543043350..0447218586 100644 --- a/app/client/cypress/support/gitSync.js +++ b/app/client/cypress/support/gitSync.js @@ -9,7 +9,8 @@ import { ObjectsRegistry } from "../support/Objects/Registry"; let gitSync = ObjectsRegistry.GitSync, agHelper = ObjectsRegistry.AggregateHelper, - dataManager = ObjectsRegistry.DataManager; + dataManager = ObjectsRegistry.DataManager, + assertHelper = ObjectsRegistry.AssertHelper; const commonLocators = require("../locators/commonlocators.json"); const GITHUB_API_BASE = "https://api.github.com"; @@ -303,7 +304,8 @@ Cypress.Commands.add("merge", (destinationBranch) => { cy.get(gitSyncLocators.mergeBranchDropdownDestination).click(); cy.get(commonLocators.dropdownmenu).contains(destinationBranch).click(); agHelper.AssertElementAbsence(gitSync._checkMergeability, 35000); - cy.wait("@mergeStatus", { timeout: 35000 }).should( + assertHelper.WaitForNetworkCall("mergeStatus"); + cy.get("@mergeStatus").should( "have.nested.property", "response.body.data.isMergeAble", true, @@ -311,11 +313,7 @@ Cypress.Commands.add("merge", (destinationBranch) => { cy.wait(2000); cy.contains(Cypress.env("MESSAGES").NO_MERGE_CONFLICT()); cy.get(gitSyncLocators.mergeCTA).click(); - cy.wait("@mergeBranch", { timeout: 35000 }).should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); //adding timeout since merge is taking longer sometimes + assertHelper.AssertNetworkStatus("mergeBranch", 200); cy.contains(Cypress.env("MESSAGES").MERGED_SUCCESSFULLY()); }); @@ -408,20 +406,21 @@ Cypress.Commands.add( Cypress.Commands.add("gitDiscardChanges", () => { cy.get(gitSyncLocators.bottomBarCommitButton).click(); cy.get(gitSyncLocators.discardChanges).should("be.visible"); - //cy.wait(6000); cy.get(gitSyncLocators.discardChanges) .children() .should("have.text", "Discard & pull"); - cy.get(gitSyncLocators.discardChanges).click(); cy.contains(Cypress.env("MESSAGES").DISCARD_CHANGES_WARNING()); - cy.get(gitSyncLocators.discardChanges) .children() .should("have.text", "Are you sure?"); cy.get(gitSyncLocators.discardChanges).click(); cy.contains(Cypress.env("MESSAGES").DISCARDING_AND_PULLING_CHANGES()); cy.wait(2000); + assertHelper.AssertContains( + "Unable to import application in workspace", + "not.exist", + ); cy.validateToastMessage("Discarded changes successfully."); }); diff --git a/app/client/docker/templates/nginx-app.conf.template b/app/client/docker/templates/nginx-app.conf.template index 45dd547ac1..cc11b83c09 100644 --- a/app/client/docker/templates/nginx-app.conf.template +++ b/app/client/docker/templates/nginx-app.conf.template @@ -48,6 +48,11 @@ server { sub_filter __APPSMITH_ZIPY_SDK_KEY__ '${APPSMITH_ZIPY_SDK_KEY}'; sub_filter __APPSMITH_HIDE_WATERMARK__ '${APPSMITH_HIDE_WATERMARK}'; sub_filter __APPSMITH_DISABLE_IFRAME_WIDGET_SANDBOX__ '${APPSMITH_DISABLE_IFRAME_WIDGET_SANDBOX}'; + sub_filter __APPSMITH_NEW_RELIC_ACCOUNT_ID__ '${APPSMITH_NEW_RELIC_ACCOUNT_ID}'; + sub_filter __APPSMITH_NEW_RELIC_APPLICATION_ID__ '${APPSMITH_NEW_RELIC_APPLICATION_ID}'; + sub_filter __APPSMITH_APP_NEW_RELIC_LICENSE_KEY__ '${APPSMITH_APP_NEW_RELIC_LICENSE_KEY}'; + sub_filter __APPSMITH_NEW_RELIC_ACCOUNT_ENABLE__ '${APPSMITH_NEW_RELIC_ACCOUNT_ENABLE}'; + } diff --git a/app/client/jest.config.js b/app/client/jest.config.js index 1b38ae9af7..678475e3b9 100644 --- a/app/client/jest.config.js +++ b/app/client/jest.config.js @@ -52,6 +52,7 @@ module.exports = { "^@blueprintjs/select$": "/node_modules/@blueprintjs/select/lib/esnext", "design-system": "/node_modules/design-system/build", + "^canvas$": "jest-canvas-mock", }, globals: { "ts-jest": { diff --git a/app/client/package.json b/app/client/package.json index 6ac102d603..081fee45e8 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -97,8 +97,8 @@ "cypress-log-to-output": "^1.1.2", "dayjs": "^1.10.6", "deep-diff": "^1.0.2", - "design-system": "npm:@appsmithorg/design-system@2.1.23", - "design-system-old": "npm:@appsmithorg/design-system-old@1.1.12", + "design-system": "npm:@appsmithorg/design-system@2.1.25", + "design-system-old": "npm:@appsmithorg/design-system-old@1.1.13", "downloadjs": "^1.4.7", "echarts": "^5.4.2", "echarts-gl": "^2.0.9", @@ -288,7 +288,6 @@ "@typescript-eslint/parser": "^6.7.4", "babel-plugin-lodash": "^3.3.4", "babel-plugin-module-resolver": "^4.1.0", - "canvas": "^2.11.2", "chalk": "^4.1.1", "compression-webpack-plugin": "^10.0.0", "cra-bundle-analyzer": "^0.1.0", diff --git a/app/client/packages/ast/src/index.ts b/app/client/packages/ast/src/index.ts index 39ceb5e437..9b88786a2e 100644 --- a/app/client/packages/ast/src/index.ts +++ b/app/client/packages/ast/src/index.ts @@ -2,8 +2,8 @@ import type { Node, SourceLocation, Options, Comment } from "acorn"; import { parse } from "acorn"; import { ancestor, simple } from "acorn-walk"; import { ECMA_VERSION, NodeTypes } from "./constants"; -import { has, isFinite, isString, toPath } from "lodash"; -import { isTrueObject, sanitizeScript } from "./utils"; +import { has, isFinite, isNil, isString, toPath } from "lodash"; +import { getStringValue, isTrueObject, sanitizeScript } from "./utils"; import { jsObjectDeclaration } from "./jsObject"; import { attachComments } from "astravel"; import { generate } from "astring"; @@ -907,7 +907,7 @@ export function getMemberExpressionObjectFromProperty( const propName = isLiteralNode(property) ? property.value : property.name; - if (propName && propName.toString() === propertyName) { + if (!isNil(propName) && getStringValue(propName) === propertyName) { const memberExpressionObjectString = generate(object); memberExpressionObjects.add(memberExpressionObjectString); } diff --git a/app/client/packages/ast/src/utils.ts b/app/client/packages/ast/src/utils.ts index b82005cb8b..6e595ebe8d 100644 --- a/app/client/packages/ast/src/utils.ts +++ b/app/client/packages/ast/src/utils.ts @@ -58,3 +58,14 @@ export const extractContentByPosition = ( } return returnedString; }; + +export const getStringValue = ( + inputValue: string | number | boolean | RegExp, +) => { + if (typeof inputValue === "object" || typeof inputValue === "boolean") { + inputValue = JSON.stringify(inputValue); + } else if (typeof inputValue === "number" || typeof inputValue === "string") { + inputValue += ""; + } + return inputValue; +}; diff --git a/app/client/public/index.html b/app/client/public/index.html index 9550e59fba..1bcb8a1026 100755 --- a/app/client/public/index.html +++ b/app/client/public/index.html @@ -17,30 +17,53 @@ transition: all ease-in 0.3s; } - + + + ;window.NREUM||(NREUM={});NREUM.init={distributed_tracing:{enabled:true},privacy:{cookies_enabled:true},ajax:{deny_list:["bam.nr-data.net"]}}; + + ;NREUM.loader_config={accountID:newRelicAccountId,trustKey:newRelicAccountId,agentID:newRelicApplicationId,licenseKey:newRelicLicenseKey,applicationID:newRelicApplicationId}; + ;NREUM.info={beacon:"bam.nr-data.net",errorBeacon:"bam.nr-data.net",licenseKey:newRelicLicenseKey,applicationID:newRelicApplicationId,sa:1}; + ;/*! For license information please see nr-loader-spa-1.246.0.min.js.LICENSE.txt */ + (()=>{"use strict";var e,t,r={234:(e,t,r)=>{r.d(t,{P_:()=>g,Mt:()=>v,C5:()=>s,DL:()=>A,OP:()=>D,lF:()=>N,Yu:()=>x,Dg:()=>m,CX:()=>c,GE:()=>w,sU:()=>j});var n=r(8632),i=r(9567);const o={beacon:n.ce.beacon,errorBeacon:n.ce.errorBeacon,licenseKey:void 0,applicationID:void 0,sa:void 0,queueTime:void 0,applicationTime:void 0,ttGuid:void 0,user:void 0,account:void 0,product:void 0,extra:void 0,jsAttributes:{},userAttributes:void 0,atts:void 0,transactionName:void 0,tNamePlain:void 0},a={};function s(e){if(!e)throw new Error("All info objects require an agent identifier!");if(!a[e])throw new Error("Info for ".concat(e," was never set"));return a[e]}function c(e,t){if(!e)throw new Error("All info objects require an agent identifier!");a[e]=(0,i.D)(t,o),(0,n.Qy)(e,a[e],"info")}const u=e=>{if(!e||"string"!=typeof e)return!1;try{document.createDocumentFragment().querySelector(e)}catch{return!1}return!0};var d=r(7056),l=r(50);const f=()=>{const e={mask_selector:"*",block_selector:"[data-nr-block]",mask_input_options:{color:!1,date:!1,"datetime-local":!1,email:!1,month:!1,number:!1,range:!1,search:!1,tel:!1,text:!1,time:!1,url:!1,week:!1,textarea:!1,select:!1,password:!0}};return{feature_flags:[],proxy:{assets:void 0,beacon:void 0},privacy:{cookies_enabled:!0},ajax:{deny_list:void 0,block_internal:!0,enabled:!0,harvestTimeSeconds:10,autoStart:!0},distributed_tracing:{enabled:void 0,exclude_newrelic_header:void 0,cors_use_newrelic_header:void 0,cors_use_tracecontext_headers:void 0,allowed_origins:void 0},session:{domain:void 0,expiresMs:d.oD,inactiveMs:d.Hb},ssl:void 0,obfuscate:void 0,jserrors:{enabled:!0,harvestTimeSeconds:10,autoStart:!0},metrics:{enabled:!0,autoStart:!0},page_action:{enabled:!0,harvestTimeSeconds:30,autoStart:!0},page_view_event:{enabled:!0,autoStart:!0},page_view_timing:{enabled:!0,harvestTimeSeconds:30,long_task:!1,autoStart:!0},session_trace:{enabled:!0,harvestTimeSeconds:10,autoStart:!0},harvest:{tooManyRequestsDelay:60},session_replay:{autoStart:!0,enabled:!1,harvestTimeSeconds:60,sampling_rate:50,error_sampling_rate:50,collect_fonts:!1,inline_images:!1,inline_stylesheet:!0,mask_all_inputs:!0,get mask_text_selector(){return e.mask_selector},set mask_text_selector(t){u(t)?e.mask_selector=t+",[data-nr-mask]":null===t?e.mask_selector=t:(0,l.Z)("An invalid session_replay.mask_selector was provided and will not be used",t)},get block_class(){return"nr-block"},get ignore_class(){return"nr-ignore"},get mask_text_class(){return"nr-mask"},get block_selector(){return e.block_selector},set block_selector(t){u(t)?e.block_selector+=",".concat(t):""!==t&&(0,l.Z)("An invalid session_replay.block_selector was provided and will not be used",t)},get mask_input_options(){return e.mask_input_options},set mask_input_options(t){t&&"object"==typeof t?e.mask_input_options={...t,password:!0}:(0,l.Z)("An invalid session_replay.mask_input_option was provided and will not be used",t)}},spa:{enabled:!0,harvestTimeSeconds:10,autoStart:!0}}},h={},p="All configuration objects require an agent identifier!";function g(e){if(!e)throw new Error(p);if(!h[e])throw new Error("Configuration for ".concat(e," was never set"));return h[e]}function m(e,t){if(!e)throw new Error(p);h[e]=(0,i.D)(t,f()),(0,n.Qy)(e,h[e],"config")}function v(e,t){if(!e)throw new Error(p);var r=g(e);if(r){for(var n=t.split("."),i=0;i{r.d(t,{D:()=>i});var n=r(50);function i(e,t){try{if(!e||"object"!=typeof e)return(0,n.Z)("Setting a Configurable requires an object as input");if(!t||"object"!=typeof t)return(0,n.Z)("Setting a Configurable requires a model to set its initial properties");const r=Object.create(Object.getPrototypeOf(t),Object.getOwnPropertyDescriptors(t)),o=0===Object.keys(r).length?e:r;for(let a in o)if(void 0!==e[a])try{Array.isArray(e[a])&&Array.isArray(t[a])?r[a]=Array.from(new Set([...e[a],...t[a]])):"object"==typeof e[a]&&"object"==typeof t[a]?r[a]=i(e[a],t[a]):r[a]=e[a]}catch(e){(0,n.Z)("An error occurred while setting a property of a Configurable",e)}return r}catch(e){(0,n.Z)("An error occured while setting a Configurable",e)}}},6818:(e,t,r)=>{r.d(t,{Re:()=>i,gF:()=>o,lF:()=>a,q4:()=>n});const n="1.246.0",i="PROD",o="CDN",a="2.0.0-alpha.11"},385:(e,t,r)=>{r.d(t,{FN:()=>s,IF:()=>d,Nk:()=>f,Tt:()=>c,_A:()=>o,cv:()=>h,iS:()=>a,il:()=>n,ux:()=>u,v6:()=>i,w1:()=>l});const n="undefined"!=typeof window&&!!window.document,i="undefined"!=typeof WorkerGlobalScope&&("undefined"!=typeof self&&self instanceof WorkerGlobalScope&&self.navigator instanceof WorkerNavigator||"undefined"!=typeof globalThis&&globalThis instanceof WorkerGlobalScope&&globalThis.navigator instanceof WorkerNavigator),o=n?window:"undefined"!=typeof WorkerGlobalScope&&("undefined"!=typeof self&&self instanceof WorkerGlobalScope&&self||"undefined"!=typeof globalThis&&globalThis instanceof WorkerGlobalScope&&globalThis),a=Boolean("hidden"===o?.document?.visibilityState),s=""+o?.location,c=/iPad|iPhone|iPod/.test(o.navigator?.userAgent),u=c&&"undefined"==typeof SharedWorker,d=(()=>{const e=o.navigator?.userAgent?.match(/Firefox[/\s](\d+\.\d+)/);return Array.isArray(e)&&e.length>=2?+e[1]:0})(),l=Boolean(n&&window.document.documentMode),f=!!o.navigator?.sendBeacon,h=Math.floor(o?.performance?.timeOrigin||o?.performance?.timing?.navigationStart||Date.now())},1117:(e,t,r)=>{r.d(t,{w:()=>o});var n=r(50);const i={agentIdentifier:"",ee:void 0};class o{constructor(e){try{if("object"!=typeof e)return(0,n.Z)("shared context requires an object as input");this.sharedContext={},Object.assign(this.sharedContext,i),Object.entries(e).forEach((e=>{let[t,r]=e;Object.keys(i).includes(t)&&(this.sharedContext[t]=r)}))}catch(e){(0,n.Z)("An error occured while setting SharedContext",e)}}}},8e3:(e,t,r)=>{r.d(t,{L:()=>d,R:()=>c});var n=r(8325),i=r(1284),o=r(4322),a=r(3325);const s={};function c(e,t){const r={staged:!1,priority:a.p[t]||0};u(e),s[e].get(t)||s[e].set(t,r)}function u(e){e&&(s[e]||(s[e]=new Map))}function d(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"feature";if(u(e),!e||!s[e].get(t))return a(t);s[e].get(t).staged=!0;const r=[...s[e]];function a(t){const r=e?n.ee.get(e):n.ee,a=o.X.handlers;if(r.backlog&&a){var s=r.backlog[t],c=a[t];if(c){for(var u=0;s&&u{let[t,r]=e;return r.staged}))&&(r.sort(((e,t)=>e[1].priority-t[1].priority)),r.forEach((t=>{let[r]=t;s[e].delete(r),a(r)})))}function l(e,t){var r=e[1];(0,i.D)(t[r],(function(t,r){var n=e[0];if(r[0]===n){var i=r[1],o=e[3],a=e[2];i.apply(o,a)}}))}},8325:(e,t,r)=>{r.d(t,{A:()=>c,ee:()=>u});var n=r(8632),i=r(2210),o=r(234);class a{constructor(e){this.contextId=e}}var s=r(3117);const c="nr@context:".concat(s.a),u=function e(t,r){var n={},s={},d={},f=!1;try{f=16===r.length&&(0,o.OP)(r).isolatedBacklog}catch(e){}var h={on:g,addEventListener:g,removeEventListener:function(e,t){var r=n[e];if(!r)return;for(var i=0;i{let[n,i]=e;s[i]=t,t in r||(r[t]=[])}))},abort:l,aborted:!1,isBuffering:function(e){return!!b()[s[e]]},debugId:r,backlog:f?{}:t&&"object"==typeof t.backlog?t.backlog:{}};return h;function p(e){return e&&e instanceof a?e:e?(0,i.X)(e,c,(()=>new a(c))):new a(c)}function g(e,t){n[e]=m(e).concat(t)}function m(e){return n[e]||[]}function v(t){return d[t]=d[t]||e(h,t)}function b(){return h.backlog}}(void 0,"globalEE"),d=(0,n.fP)();function l(){u.aborted=!0,u.backlog={}}d.ee||(d.ee=u)},5546:(e,t,r)=>{r.d(t,{E:()=>n,p:()=>i});var n=r(8325).ee.get("handle");function i(e,t,r,i,o){o?(o.buffer([e],i),o.emit(e,t,r)):(n.buffer([e],i),n.emit(e,t,r))}},4322:(e,t,r)=>{r.d(t,{X:()=>o});var n=r(5546);o.on=a;var i=o.handlers={};function o(e,t,r,o){a(o||n.E,i,e,t,r)}function a(e,t,r,i,o){o||(o="feature"),e||(e=n.E);var a=t[o]=t[o]||{};(a[r]=a[r]||[]).push([e,i])}},3239:(e,t,r)=>{r.d(t,{bP:()=>s,iz:()=>c,m$:()=>a});var n=r(385);let i=!1,o=!1;try{const e={get passive(){return i=!0,!1},get signal(){return o=!0,!1}};n._A.addEventListener("test",null,e),n._A.removeEventListener("test",null,e)}catch(e){}function a(e,t){return i||o?{capture:!!e,passive:i,signal:t}:!!e}function s(e,t){let r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3?arguments[3]:void 0;window.addEventListener(e,t,a(r,n))}function c(e,t){let r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3?arguments[3]:void 0;document.addEventListener(e,t,a(r,n))}},3117:(e,t,r)=>{r.d(t,{a:()=>n});const n=(0,r(4402).Rl)()},4402:(e,t,r)=>{r.d(t,{Ht:()=>u,M:()=>c,Rl:()=>a,ky:()=>s});var n=r(385);const i="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";function o(e,t){return e?15&e[t]:16*Math.random()|0}function a(){const e=n._A?.crypto||n._A?.msCrypto;let t,r=0;return e&&e.getRandomValues&&(t=e.getRandomValues(new Uint8Array(31))),i.split("").map((e=>"x"===e?o(t,++r).toString(16):"y"===e?(3&o()|8).toString(16):e)).join("")}function s(e){const t=n._A?.crypto||n._A?.msCrypto;let r,i=0;t&&t.getRandomValues&&(r=t.getRandomValues(new Uint8Array(31)));const a=[];for(var s=0;s{r.d(t,{Bq:()=>n,Hb:()=>o,oD:()=>i});const n="NRBA",i=144e5,o=18e5},7894:(e,t,r)=>{function n(){return Math.round(performance.now())}r.d(t,{z:()=>n})},7243:(e,t,r)=>{r.d(t,{e:()=>i});var n=r(385);function i(e){if(0===(e||"").indexOf("data:"))return{protocol:"data"};try{const t=new URL(e,location.href),r={port:t.port,hostname:t.hostname,pathname:t.pathname,search:t.search,protocol:t.protocol.slice(0,t.protocol.indexOf(":")),sameOrigin:t.protocol===n._A?.location?.protocol&&t.host===n._A?.location?.host};return r.port&&""!==r.port||("http:"===t.protocol&&(r.port="80"),"https:"===t.protocol&&(r.port="443")),r.pathname&&""!==r.pathname?r.pathname.startsWith("/")||(r.pathname="/".concat(r.pathname)):r.pathname="/",r}catch(e){return{}}}},50:(e,t,r)=>{function n(e,t){"function"==typeof console.warn&&(console.warn("New Relic: ".concat(e)),t&&console.warn(t))}r.d(t,{Z:()=>n})},2587:(e,t,r)=>{r.d(t,{N:()=>c,T:()=>u});var n=r(8325),i=r(5546),o=r(3325);const a={stn:[o.D.sessionTrace],err:[o.D.jserrors,o.D.metrics],ins:[o.D.pageAction],spa:[o.D.spa],sr:[o.D.sessionReplay,o.D.sessionTrace]},s=new Set;function c(e,t){const r=n.ee.get(t);e&&"object"==typeof e&&(s.has(t)||Object.entries(e).forEach((e=>{let[t,n]=e;a[t]?a[t].forEach((e=>{n?(0,i.p)("feat-"+t,[],void 0,e,r):(0,i.p)("block-"+t,[],void 0,e,r),(0,i.p)("rumresp-"+t,[Boolean(n)],void 0,e,r)})):n&&(0,i.p)("feat-"+t,[],void 0,void 0,r),u[t]=Boolean(n)})),Object.keys(a).forEach((e=>{void 0===u[e]&&(a[e]?.forEach((t=>(0,i.p)("rumresp-"+e,[!1],void 0,t,r))),u[e]=!1)})),s.add(t))}const u={}},2210:(e,t,r)=>{r.d(t,{X:()=>i});var n=Object.prototype.hasOwnProperty;function i(e,t,r){if(n.call(e,t))return e[t];var i=r();if(Object.defineProperty&&Object.keys)try{return Object.defineProperty(e,t,{value:i,writable:!0,enumerable:!1}),i}catch(e){}return e[t]=i,i}},1284:(e,t,r)=>{r.d(t,{D:()=>n});const n=(e,t)=>Object.entries(e||{}).map((e=>{let[r,n]=e;return t(r,n)}))},4351:(e,t,r)=>{r.d(t,{P:()=>o});var n=r(8325);const i=()=>{const e=new WeakSet;return(t,r)=>{if("object"==typeof r&&null!==r){if(e.has(r))return;e.add(r)}return r}};function o(e){try{return JSON.stringify(e,i())}catch(e){try{n.ee.emit("internal-error",[e])}catch(e){}}}},3960:(e,t,r)=>{r.d(t,{K:()=>a,b:()=>o});var n=r(3239);function i(){return"undefined"==typeof document||"complete"===document.readyState}function o(e,t){if(i())return e();(0,n.bP)("load",e,t)}function a(e){if(i())return e();(0,n.iz)("DOMContentLoaded",e)}},8632:(e,t,r)=>{r.d(t,{EZ:()=>u,Qy:()=>c,ce:()=>o,fP:()=>a,gG:()=>d,mF:()=>s});var n=r(7894),i=r(385);const o={beacon:"bam.nr-data.net",errorBeacon:"bam.nr-data.net"};function a(){return i._A.NREUM||(i._A.NREUM={}),void 0===i._A.newrelic&&(i._A.newrelic=i._A.NREUM),i._A.NREUM}function s(){let e=a();return e.o||(e.o={ST:i._A.setTimeout,SI:i._A.setImmediate,CT:i._A.clearTimeout,XHR:i._A.XMLHttpRequest,REQ:i._A.Request,EV:i._A.Event,PR:i._A.Promise,MO:i._A.MutationObserver,FETCH:i._A.fetch}),e}function c(e,t,r){let i=a();const o=i.initializedAgents||{},s=o[e]||{};return Object.keys(s).length||(s.initializedAt={ms:(0,n.z)(),date:new Date}),i.initializedAgents={...o,[e]:{...s,[r]:t}},i}function u(e,t){a()[e]=t}function d(){return function(){let e=a();const t=e.info||{};e.info={beacon:o.beacon,errorBeacon:o.errorBeacon,...t}}(),function(){let e=a();const t=e.init||{};e.init={...t}}(),s(),function(){let e=a();const t=e.loader_config||{};e.loader_config={...t}}(),a()}},7956:(e,t,r)=>{r.d(t,{N:()=>i});var n=r(3239);function i(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=arguments.length>2?arguments[2]:void 0,i=arguments.length>3?arguments[3]:void 0;(0,n.iz)("visibilitychange",(function(){if(t)return void("hidden"===document.visibilityState&&e());e(document.visibilityState)}),r,i)}},1214:(e,t,r)=>{r.d(t,{em:()=>b,u5:()=>D,QU:()=>C,_L:()=>I,Gm:()=>H,Lg:()=>L,BV:()=>G,Kf:()=>K});var n=r(8325),i=r(3117);const o="nr@original:".concat(i.a);var a=Object.prototype.hasOwnProperty,s=!1;function c(e,t){return e||(e=n.ee),r.inPlace=function(e,t,n,i,o){n||(n="");const a="-"===n.charAt(0);for(let s=0;s2?n-2:0),o=2;o{r(E[T],e,w),r(_[T],e,w)})),r(f._A,"fetch",A),t.on(A+"end",(function(e,r){var n=this;if(r){var i=r.headers.get("content-length");null!==i&&(n.rxSize=i),t.emit(A+"done",[null,r],n)}else t.emit(A+"done",[e],n)})),t}const j={},N=["pushState","replaceState"];function C(e){const t=function(e){return(e||n.ee).get("history")}(e);return!f.il||j[t.debugId]++||(j[t.debugId]=1,c(t).inPlace(window.history,N,"-")),t}var O=r(3239);const P={},R=["appendChild","insertBefore","replaceChild"];function I(e){const t=function(e){return(e||n.ee).get("jsonp")}(e);if(!f.il||P[t.debugId])return t;P[t.debugId]=!0;var r=c(t),i=/[?&](?:callback|cb)=([^&#]+)/,o=/(.*)\.([^.]+)/,a=/^(\w+)(\.|$)(.*)$/;function s(e,t){if(!e)return t;const r=e.match(a),n=r[1];return s(r[3],t[n])}return r.inPlace(Node.prototype,R,"dom-"),t.on("dom-start",(function(e){!function(e){if(!e||"string"!=typeof e.nodeName||"script"!==e.nodeName.toLowerCase())return;if("function"!=typeof e.addEventListener)return;var n=(a=e.src,c=a.match(i),c?c[1]:null);var a,c;if(!n)return;var u=function(e){var t=e.match(o);if(t&&t.length>=3)return{key:t[2],parent:s(t[1],window)};return{key:e,parent:window}}(n);if("function"!=typeof u.parent[u.key])return;var d={};function l(){t.emit("jsonp-end",[],d),e.removeEventListener("load",l,(0,O.m$)(!1)),e.removeEventListener("error",f,(0,O.m$)(!1))}function f(){t.emit("jsonp-error",[],d),t.emit("jsonp-end",[],d),e.removeEventListener("load",l,(0,O.m$)(!1)),e.removeEventListener("error",f,(0,O.m$)(!1))}r.inPlace(u.parent,[u.key],"cb-",d),e.addEventListener("load",l,(0,O.m$)(!1)),e.addEventListener("error",f,(0,O.m$)(!1)),t.emit("new-jsonp",[e.src],d)}(e[0])})),t}const k={};function H(e){const t=function(e){return(e||n.ee).get("mutation")}(e);if(!f.il||k[t.debugId])return t;k[t.debugId]=!0;var r=c(t),i=f._A.MutationObserver;return i&&(window.MutationObserver=function(e){return this instanceof i?new i(r(e,"fn-")):i.apply(this,arguments)},MutationObserver.prototype=i.prototype),t}const z={};function L(e){const t=function(e){return(e||n.ee).get("promise")}(e);if(z[t.debugId])return t;z[t.debugId]=!0;var r=t.context,i=c(t),a=f._A.Promise;return a&&function(){function e(r){var n=t.context(),o=i(r,"executor-",n,null,!1);const s=Reflect.construct(a,[o],e);return t.context(s).getCtx=function(){return n},s}f._A.Promise=e,Object.defineProperty(e,"name",{value:"Promise"}),e.toString=function(){return a.toString()},Object.setPrototypeOf(e,a),["all","race"].forEach((function(r){const n=a[r];e[r]=function(e){let i=!1;[...e||[]].forEach((e=>{this.resolve(e).then(a("all"===r),a(!1))}));const o=n.apply(this,arguments);return o;function a(e){return function(){t.emit("propagate",[null,!i],o,!1,!1),i=i||!e}}}})),["resolve","reject"].forEach((function(r){const n=a[r];e[r]=function(e){const r=n.apply(this,arguments);return e!==r&&t.emit("propagate",[e,!0],r,!1,!1),r}})),e.prototype=a.prototype;const n=a.prototype.then;a.prototype.then=function(){var e=this,o=r(e);o.promise=e;for(var a=arguments.length,s=new Array(a),c=0;c3&&!a.resolved&&(a.resolved=!0,r.emit("xhr-resolved",[],e)),i.inPlace(e,l,"fn-",w)}),(0,O.m$)(!1))}catch(e){(0,W.Z)("An error occurred while intercepting XHR",e);try{r.emit("internal-error",[e])}catch(e){}}var a;return t};function g(e,t){i.inPlace(t,["onreadystatechange"],"fn-",w)}if(function(e,t){for(var r in e)t[r]=e[r]}(o,p),p.prototype=o.prototype,i.inPlace(p.prototype,Q,"-xhr-",w),r.on("send-xhr-start",(function(e,t){g(e,t),function(e){h.push(e),a&&(m?m.then(A):u?u(A):(v=-v,y.data=v))}(t)})),r.on("open-xhr-start",g),a){var m=s&&s.resolve();if(!u&&!s){var v=1,y=document.createTextNode(v);new a(A).observe(y,{characterData:!0})}}else t.on("fn-end",(function(e){e[0]&&e[0].type===d||A()}));function A(){for(var e=0;e{r.d(t,{t:()=>n});const n=r(3325).D.ajax},6660:(e,t,r)=>{r.d(t,{t:()=>n});const n=r(3325).D.jserrors},3081:(e,t,r)=>{r.d(t,{gF:()=>o,mY:()=>i,t9:()=>n,vz:()=>s,xS:()=>a});const n=r(3325).D.metrics,i="sm",o="cm",a="storeSupportabilityMetrics",s="storeEventMetrics"},4649:(e,t,r)=>{r.d(t,{t:()=>n});const n=r(3325).D.pageAction},7633:(e,t,r)=>{r.d(t,{t:()=>n});const n=r(3325).D.pageViewEvent},9251:(e,t,r)=>{r.d(t,{t:()=>n});const n=r(3325).D.pageViewTiming},7144:(e,t,r)=>{r.d(t,{t:()=>n});const n=r(3325).D.sessionReplay},3614:(e,t,r)=>{r.d(t,{BST_RESOURCE:()=>i,END:()=>s,FEATURE_NAME:()=>n,FN_END:()=>u,FN_START:()=>c,PUSH_STATE:()=>d,RESOURCE:()=>o,START:()=>a});const n=r(3325).D.sessionTrace,i="bstResource",o="resource",a="-start",s="-end",c="fn"+a,u="fn"+s,d="pushState"},7836:(e,t,r)=>{r.d(t,{BODY:()=>x,CB_END:()=>E,CB_START:()=>u,END:()=>w,FEATURE_NAME:()=>i,FETCH:()=>T,FETCH_BODY:()=>v,FETCH_DONE:()=>m,FETCH_START:()=>g,FN_END:()=>c,FN_START:()=>s,INTERACTION:()=>f,INTERACTION_API:()=>d,INTERACTION_EVENTS:()=>o,JSONP_END:()=>b,JSONP_NODE:()=>p,JS_TIME:()=>_,MAX_TIMER_BUDGET:()=>a,REMAINING:()=>l,SPA_NODE:()=>h,START:()=>A,originalSetTimeout:()=>y});var n=r(234);const i=r(3325).D.spa,o=["click","submit","keypress","keydown","keyup","change"],a=999,s="fn-start",c="fn-end",u="cb-start",d="api-ixn-",l="remaining",f="interaction",h="spaNode",p="jsonpNode",g="fetch-start",m="fetch-done",v="fetch-body-",b="jsonp-end",y=n.Yu.ST,A="-start",w="-end",x="-body",E="cb"+w,_="jsTime",T="fetch"},5938:(e,t,r)=>{r.d(t,{W:()=>i});var n=r(8325);class i{constructor(e,t,r){this.agentIdentifier=e,this.aggregator=t,this.ee=n.ee.get(e),this.featureName=r,this.blocked=!1}}},7530:(e,t,r)=>{r.d(t,{j:()=>b});var n=r(3325),i=r(234),o=r(5546),a=r(8325),s=r(7894),c=r(8e3),u=r(3960),d=r(385),l=r(50),f=r(3081),h=r(8632);function p(){const e=(0,h.gG)();["setErrorHandler","finished","addToTrace","addRelease","addPageAction","setCurrentRouteName","setPageViewName","setCustomAttribute","interaction","noticeError","setUserId","setApplicationVersion","start"].forEach((t=>{e[t]=function(){for(var r=arguments.length,n=new Array(r),i=0;i1?r-1:0),i=1;i{e.exposed&&e.api[t]&&o.push(e.api[t](...n))})),o.length>1?o:o[0]}(t,...n)}}))}var g=r(2587);const m=e=>{const t=e.startsWith("http");e+="/",r.p=t?e:"https://"+e};let v=!1;function b(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},b=arguments.length>2?arguments[2]:void 0,y=arguments.length>3?arguments[3]:void 0,{init:A,info:w,loader_config:x,runtime:E={loaderType:b},exposed:_=!0}=t;const T=(0,h.gG)();w||(A=T.init,w=T.info,x=T.loader_config),(0,i.Dg)(e,A||{}),(0,i.GE)(e,x||{}),w.jsAttributes??={},d.v6&&(w.jsAttributes.isWorker=!0),(0,i.CX)(e,w);const S=(0,i.P_)(e),D=[w.beacon,w.errorBeacon];v||(v=!0,S.proxy.assets&&(m(S.proxy.assets),D.push(S.proxy.assets)),S.proxy.beacon&&D.push(S.proxy.beacon)),E.denyList=[...S.ajax.deny_list||[],...S.ajax.block_internal?D:[]],(0,i.sU)(e,E),p();const j=function(e,t){t||(0,c.R)(e,"api");const h={};var p=a.ee.get(e),g=p.get("tracer"),m="api-",v=m+"ixn-";function b(t,r,n,o){const a=(0,i.C5)(e);return null===r?delete a.jsAttributes[t]:(0,i.CX)(e,{...a,jsAttributes:{...a.jsAttributes,[t]:r}}),w(m,n,!0,o||null===r?"session":void 0)(t,r)}function y(){}["setErrorHandler","finished","addToTrace","addRelease"].forEach((e=>{h[e]=w(m,e,!0,"api")})),h.addPageAction=w(m,"addPageAction",!0,n.D.pageAction),h.setCurrentRouteName=w(m,"routeName",!0,n.D.spa),h.setPageViewName=function(t,r){if("string"==typeof t)return"/"!==t.charAt(0)&&(t="/"+t),(0,i.OP)(e).customTransaction=(r||"http://custom.transaction")+t,w(m,"setPageViewName",!0)()},h.setCustomAttribute=function(e,t){let r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if("string"==typeof e){if(["string","number","boolean"].includes(typeof t)||null===t)return b(e,t,"setCustomAttribute",r);(0,l.Z)("Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <".concat(typeof t,"> was provided."))}else(0,l.Z)("Failed to execute setCustomAttribute.\nName must be a string type, but a type of <".concat(typeof e,"> was provided."))},h.setUserId=function(e){if("string"==typeof e||null===e)return b("enduser.id",e,"setUserId",!0);(0,l.Z)("Failed to execute setUserId.\nNon-null value must be a string type, but a type of <".concat(typeof e,"> was provided."))},h.setApplicationVersion=function(e){if("string"==typeof e||null===e)return b("application.version",e,"setApplicationVersion",!1);(0,l.Z)("Failed to execute setApplicationVersion. Expected , but got <".concat(typeof e,">."))},h.start=e=>{try{const t=e?"defined":"undefined";(0,o.p)(f.xS,["API/start/".concat(t,"/called")],void 0,n.D.metrics,p);const r=Object.values(n.D);if(void 0===e)e=r;else{if((e=Array.isArray(e)&&e.length?e:[e]).some((e=>!r.includes(e))))return(0,l.Z)("Invalid feature name supplied. Acceptable feature names are: ".concat(r));e.includes(n.D.pageViewEvent)||e.push(n.D.pageViewEvent)}e.forEach((e=>{p.emit("".concat(e,"-opt-in"))}))}catch(e){(0,l.Z)("An unexpected issue occurred",e)}},h.interaction=function(){return(new y).get()};var A=y.prototype={createTracer:function(e,t){var r={},i=this,a="function"==typeof t;return(0,o.p)(v+"tracer",[(0,s.z)(),e,r],i,n.D.spa,p),function(){if(g.emit((a?"":"no-")+"fn-start",[(0,s.z)(),i,a],r),a)try{return t.apply(this,arguments)}catch(e){throw g.emit("fn-err",[arguments,this,e],r),e}finally{g.emit("fn-end",[(0,s.z)()],r)}}}};function w(e,t,r,i){return function(){return(0,o.p)(f.xS,["API/"+t+"/called"],void 0,n.D.metrics,p),i&&(0,o.p)(e+t,[(0,s.z)(),...arguments],r?null:this,i,p),r?void 0:this}}function x(){r.e(111).then(r.bind(r,7438)).then((t=>{let{setAPI:r}=t;r(e),(0,c.L)(e,"api")})).catch((()=>(0,l.Z)("Downloading runtime APIs failed...")))}return["actionText","setName","setAttribute","save","ignore","onEnd","getContext","end","get"].forEach((e=>{A[e]=w(v,e,void 0,n.D.spa)})),h.noticeError=function(e,t){"string"==typeof e&&(e=new Error(e)),(0,o.p)(f.xS,["API/noticeError/called"],void 0,n.D.metrics,p),(0,o.p)("err",[e,(0,s.z)(),!1,t],void 0,n.D.jserrors,p)},d.il?(0,u.b)((()=>x()),!0):x(),h}(e,y);return(0,h.Qy)(e,j,"api"),(0,h.Qy)(e,_,"exposed"),(0,h.EZ)("activatedFeatures",g.T),j}},3325:(e,t,r)=>{r.d(t,{D:()=>n,p:()=>i});const n={ajax:"ajax",jserrors:"jserrors",metrics:"metrics",pageAction:"page_action",pageViewEvent:"page_view_event",pageViewTiming:"page_view_timing",sessionReplay:"session_replay",sessionTrace:"session_trace",spa:"spa"},i={[n.pageViewEvent]:1,[n.pageViewTiming]:2,[n.metrics]:3,[n.jserrors]:4,[n.ajax]:5,[n.sessionTrace]:6,[n.pageAction]:7,[n.spa]:8,[n.sessionReplay]:9}}},n={};function i(e){var t=n[e];if(void 0!==t)return t.exports;var o=n[e]={exports:{}};return r[e](o,o.exports,i),o.exports}i.m=r,i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce(((t,r)=>(i.f[r](e,t),t)),[])),i.u=e=>({111:"nr-spa",164:"nr-spa-compressor",433:"nr-spa-recorder"}[e]+"-1.246.0.min.js"),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="NRBA-1.246.0.PROD:",i.l=(r,n,o,a)=>{if(e[r])e[r].push(n);else{var s,c;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d{s.onerror=s.onload=null,clearTimeout(h);var i=e[r];if(delete e[r],s.parentNode&&s.parentNode.removeChild(s),i&&i.forEach((e=>e(n))),t)return t(n)},h=setTimeout(f.bind(null,void 0,{type:"timeout",target:s}),12e4);s.onerror=f.bind(null,s.onerror),s.onload=f.bind(null,s.onload),c&&document.head.appendChild(s)}},i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.p="https://js-agent.newrelic.com/",(()=>{var e={801:0,92:0};i.f.j=(t,r)=>{var n=i.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var o=new Promise(((r,i)=>n=e[t]=[r,i]));r.push(n[2]=o);var a=i.p+i.u(t),s=new Error;i.l(a,(r=>{if(i.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var o=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;s.message="Loading chunk "+t+" failed.\n("+o+": "+a+")",s.name="ChunkLoadError",s.type=o,s.request=a,n[1](s)}}),"chunk-"+t,t)}};var t=(t,r)=>{var n,o,[a,s,c]=r,u=0;if(a.some((t=>0!==e[t]))){for(n in s)i.o(s,n)&&(i.m[n]=s[n]);if(c)c(i)}for(t&&t(r);u{var e=i(50);class t{addPageAction(t,r){(0,e.Z)("Call to agent api addPageAction failed. The page action feature is not currently initialized.")}setPageViewName(t,r){(0,e.Z)("Call to agent api setPageViewName failed. The page view feature is not currently initialized.")}setCustomAttribute(t,r,n){(0,e.Z)("Call to agent api setCustomAttribute failed. The js errors feature is not currently initialized.")}noticeError(t,r){(0,e.Z)("Call to agent api noticeError failed. The js errors feature is not currently initialized.")}setUserId(t){(0,e.Z)("Call to agent api setUserId failed. The js errors feature is not currently initialized.")}setApplicationVersion(t){(0,e.Z)("Call to agent api setApplicationVersion failed. The agent is not currently initialized.")}setErrorHandler(t){(0,e.Z)("Call to agent api setErrorHandler failed. The js errors feature is not currently initialized.")}finished(t){(0,e.Z)("Call to agent api finished failed. The page action feature is not currently initialized.")}addRelease(t,r){(0,e.Z)("Call to agent api addRelease failed. The js errors feature is not currently initialized.")}start(t){(0,e.Z)("Call to agent api addRelease failed. The agent is not currently initialized.")}}var r=i(3325),n=i(234);const o=Object.values(r.D);function a(e){const t={};return o.forEach((r=>{t[r]=function(e,t){return!1!==(0,n.Mt)(t,"".concat(e,".enabled"))}(r,e)})),t}var s=i(7530);var c=i(8e3),u=i(5938),d=i(3960),l=i(385);class f extends u.W{constructor(e,t,r){let i=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];super(e,t,r),this.auto=i,this.abortHandler=void 0,this.featAggregate=void 0,this.onAggregateImported=void 0,!1===(0,n.Mt)(this.agentIdentifier,"".concat(this.featureName,".autoStart"))&&(this.auto=!1),this.auto&&(0,c.R)(e,r)}importAggregator(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(this.featAggregate)return;if(!this.auto)return void this.ee.on("".concat(this.featureName,"-opt-in"),(()=>{(0,c.R)(this.agentIdentifier,this.featureName),this.auto=!0,this.importAggregator()}));const r=l.il&&!0===(0,n.Mt)(this.agentIdentifier,"privacy.cookies_enabled");let o;this.onAggregateImported=new Promise((e=>{o=e}));const a=async()=>{let n;try{if(r){const{setupAgentSession:e}=await i.e(111).then(i.bind(i,3228));n=e(this.agentIdentifier)}}catch(t){(0,e.Z)("A problem occurred when starting up session manager. This page will not start or extend any session.",t)}try{if(!this.shouldImportAgg(this.featureName,n))return(0,c.L)(this.agentIdentifier,this.featureName),void o(!1);const{lazyFeatureLoader:e}=await i.e(111).then(i.bind(i,8582)),{Aggregate:r}=await e(this.featureName,"aggregate");this.featAggregate=new r(this.agentIdentifier,this.aggregator,t),o(!0)}catch(t){(0,e.Z)("Downloading and initializing ".concat(this.featureName," failed..."),t),this.abortHandler?.(),(0,c.L)(this.agentIdentifier,this.featureName),o(!1)}};l.il?(0,d.b)((()=>a()),!0):a()}shouldImportAgg(e,t){return e!==r.D.sessionReplay||!!n.Yu.MO&&(!1!==(0,n.Mt)(this.agentIdentifier,"session_trace.enabled")&&(!!t?.isNew||!!t?.state.sessionReplayMode))}}var h=i(7633);class p extends f{static featureName=h.t;constructor(e,t){let r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];super(e,t,h.t,r),this.importAggregator()}}var g=i(1117),m=i(1284);class v extends g.w{constructor(e){super(e),this.aggregatedData={}}store(e,t,r,n,i){var o=this.getBucket(e,t,r,i);return o.metrics=function(e,t){t||(t={count:0});return t.count+=1,(0,m.D)(e,(function(e,r){t[e]=b(r,t[e])})),t}(n,o.metrics),o}merge(e,t,r,n,i){var o=this.getBucket(e,t,n,i);if(o.metrics){var a=o.metrics;a.count+=r.count,(0,m.D)(r,(function(e,t){if("count"!==e){var n=a[e],i=r[e];i&&!i.c?a[e]=b(i.t,n):a[e]=function(e,t){if(!t)return e;t.c||(t=y(t.t));return t.min=Math.min(e.min,t.min),t.max=Math.max(e.max,t.max),t.t+=e.t,t.sos+=e.sos,t.c+=e.c,t}(i,a[e])}}))}else o.metrics=r}storeMetric(e,t,r,n){var i=this.getBucket(e,t,r);return i.stats=b(n,i.stats),i}getBucket(e,t,r,n){this.aggregatedData[e]||(this.aggregatedData[e]={});var i=this.aggregatedData[e][t];return i||(i=this.aggregatedData[e][t]={params:r||{}},n&&(i.custom=n)),i}get(e,t){return t?this.aggregatedData[e]&&this.aggregatedData[e][t]:this.aggregatedData[e]}take(e){for(var t={},r="",n=!1,i=0;it.max&&(t.max=e),e2&&void 0!==arguments[2])||arguments[2];super(e,t,N.t,r),l.il&&((0,S.N)((()=>(0,T.p)("docHidden",[(0,j.z)()],void 0,N.t,this.ee)),!0),(0,D.bP)("pagehide",(()=>(0,T.p)("winPagehide",[(0,j.z)()],void 0,N.t,this.ee))),this.importAggregator())}}var O=i(3081);class P extends f{static featureName=O.t9;constructor(e,t){let r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];super(e,t,O.t9,r),this.importAggregator()}}var R=i(6660);class I{constructor(e,t,r,n){this.name="UncaughtError",this.message=e,this.sourceURL=t,this.line=r,this.column=n}}class k extends f{static featureName=R.t;#e=new Set;constructor(e,t){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];super(e,t,R.t,n);try{this.removeOnAbort=new AbortController}catch(e){}this.ee.on("fn-err",((e,t,n)=>{this.abortHandler&&!this.#e.has(n)&&(this.#e.add(n),(0,T.p)("err",[this.#t(n),(0,j.z)()],void 0,r.D.jserrors,this.ee))})),this.ee.on("internal-error",(e=>{this.abortHandler&&(0,T.p)("ierr",[this.#t(e),(0,j.z)(),!0],void 0,r.D.jserrors,this.ee)})),l._A.addEventListener("unhandledrejection",(e=>{this.abortHandler&&(0,T.p)("err",[this.#r(e),(0,j.z)(),!1,{unhandledPromiseRejection:1}],void 0,r.D.jserrors,this.ee)}),(0,D.m$)(!1,this.removeOnAbort?.signal)),l._A.addEventListener("error",(e=>{this.abortHandler&&(this.#e.has(e.error)?this.#e.delete(e.error):(0,T.p)("err",[this.#n(e),(0,j.z)()],void 0,r.D.jserrors,this.ee))}),(0,D.m$)(!1,this.removeOnAbort?.signal)),this.abortHandler=this.#i,this.importAggregator()}#i(){this.removeOnAbort?.abort(),this.#e.clear(),this.abortHandler=void 0}#t(e){return e instanceof Error?e:void 0!==e?.message?new I(e.message,e.filename||e.sourceURL,e.lineno||e.line,e.colno||e.col):new I("string"==typeof e?e:(0,_.P)(e))}#r(e){let t="Unhandled Promise Rejection: ";if(e?.reason instanceof Error)try{return e.reason.message=t+e.reason.message,e.reason}catch(t){return e.reason}if(void 0===e.reason)return new I(t);const r=this.#t(e.reason);return r.message=t+r.message,r}#n(e){return e.error instanceof Error?e.error:new I(e.message,e.filename,e.lineno,e.colno)}}var H=i(2210);let z=1;const L="nr@id";function M(e){const t=typeof e;return!e||"object"!==t&&"function"!==t?-1:e===l._A?0:(0,H.X)(e,L,(function(){return z++}))}function F(e){if("string"==typeof e&&e.length)return e.length;if("object"==typeof e){if("undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer&&e.byteLength)return e.byteLength;if("undefined"!=typeof Blob&&e instanceof Blob&&e.size)return e.size;if(!("undefined"!=typeof FormData&&e instanceof FormData))try{return(0,_.P)(e).length}catch(e){return}}}var B=i(1214),U=i(7243);class Z{constructor(e){this.agentIdentifier=e}generateTracePayload(e){if(!this.shouldGenerateTrace(e))return null;var t=(0,n.DL)(this.agentIdentifier);if(!t)return null;var r=(t.accountID||"").toString()||null,i=(t.agentID||"").toString()||null,o=(t.trustKey||"").toString()||null;if(!r||!i)return null;var a=(0,E.M)(),s=(0,E.Ht)(),c=Date.now(),u={spanId:a,traceId:s,timestamp:c};return(e.sameOrigin||this.isAllowedOrigin(e)&&this.useTraceContextHeadersForCors())&&(u.traceContextParentHeader=this.generateTraceContextParentHeader(a,s),u.traceContextStateHeader=this.generateTraceContextStateHeader(a,c,r,i,o)),(e.sameOrigin&&!this.excludeNewrelicHeader()||!e.sameOrigin&&this.isAllowedOrigin(e)&&this.useNewrelicHeaderForCors())&&(u.newrelicHeader=this.generateTraceHeader(a,s,c,r,i,o)),u}generateTraceContextParentHeader(e,t){return"00-"+t+"-"+e+"-01"}generateTraceContextStateHeader(e,t,r,n,i){return i+"@nr=0-1-"+r+"-"+n+"-"+e+"----"+t}generateTraceHeader(e,t,r,n,i,o){if(!("function"==typeof l._A?.btoa))return null;var a={v:[0,1],d:{ty:"Browser",ac:n,ap:i,id:e,tr:t,ti:r}};return o&&n!==o&&(a.d.tk=o),btoa((0,_.P)(a))}shouldGenerateTrace(e){return this.isDtEnabled()&&this.isAllowedOrigin(e)}isAllowedOrigin(e){var t=!1,r={};if((0,n.Mt)(this.agentIdentifier,"distributed_tracing")&&(r=(0,n.P_)(this.agentIdentifier).distributed_tracing),e.sameOrigin)t=!0;else if(r.allowed_origins instanceof Array)for(var i=0;i2&&void 0!==arguments[2])||arguments[2];if(super(e,t,V.t,i),(0,n.OP)(e).xhrWrappable){this.dt=new Z(e),this.handler=(e,t,r,n)=>(0,T.p)(e,t,r,n,this.ee);try{const e={xmlhttprequest:"xhr",fetch:"fetch",beacon:"beacon"};l._A?.performance?.getEntriesByType("resource").forEach((t=>{if(t.initiatorType in e&&0!==t.responseStatus){const n={status:t.responseStatus},i={rxSize:t.transferSize,duration:Math.floor(t.duration),cbTime:0};K(n,t.name),this.handler("xhr",[n,i,t.startTime,t.responseEnd,e[t.initiatorType]],void 0,r.D.ajax)}}))}catch(e){}(0,B.u5)(this.ee),(0,B.Kf)(this.ee),function(e,t,i,o){function a(e){var t=this;t.totalCbs=0,t.called=0,t.cbTime=0,t.end=x,t.ended=!1,t.xhrGuids={},t.lastSize=null,t.loadCaptureCalled=!1,t.params=this.params||{},t.metrics=this.metrics||{},e.addEventListener("load",(function(r){E(t,e)}),(0,D.m$)(!1)),l.IF||e.addEventListener("progress",(function(e){t.lastSize=e.loaded}),(0,D.m$)(!1))}function s(e){this.params={method:e[0]},K(this,e[1]),this.metrics={}}function c(t,r){var i=(0,n.DL)(e);i.xpid&&this.sameOrigin&&r.setRequestHeader("X-NewRelic-ID",i.xpid);var a=o.generateTracePayload(this.parsedOrigin);if(a){var s=!1;a.newrelicHeader&&(r.setRequestHeader("newrelic",a.newrelicHeader),s=!0),a.traceContextParentHeader&&(r.setRequestHeader("traceparent",a.traceContextParentHeader),a.traceContextStateHeader&&r.setRequestHeader("tracestate",a.traceContextStateHeader),s=!0),s&&(this.dt=a)}}function u(e,r){var n=this.metrics,i=e[0],o=this;if(n&&i){var a=F(i);a&&(n.txSize=a)}this.startTime=(0,j.z)(),this.body=i,this.listener=function(e){try{"abort"!==e.type||o.loadCaptureCalled||(o.params.aborted=!0),("load"!==e.type||o.called===o.totalCbs&&(o.onloadCalled||"function"!=typeof r.onload)&&"function"==typeof o.end)&&o.end(r)}catch(e){try{t.emit("internal-error",[e])}catch(e){}}};for(var s=0;s1?e[1]=i:e.push(i)}}function s(e,t){var r=!1;return t.newrelicHeader&&(e.set("newrelic",t.newrelicHeader),r=!0),t.traceContextParentHeader&&(e.set("traceparent",t.traceContextParentHeader),t.traceContextStateHeader&&e.set("tracestate",t.traceContextStateHeader),r=!0),r}}function A(e,t){this.params={},this.metrics={},this.startTime=(0,j.z)(),this.dt=t,e.length>=1&&(this.target=e[0]),e.length>=2&&(this.opts=e[1]);var r,n=this.opts||{},i=this.target;"string"==typeof i?r=i:"object"==typeof i&&i instanceof W?r=i.url:l._A?.URL&&"object"==typeof i&&i instanceof URL&&(r=i.href),K(this,r);var o=(""+(i&&i instanceof W&&i.method||n.method||"GET")).toUpperCase();this.params.method=o,this.body=n.body,this.txSize=F(n.body)||0}function w(e,t){var n;this.endTime=(0,j.z)(),this.params||(this.params={}),this.params.status=t?t.status:0,"string"==typeof this.rxSize&&this.rxSize.length>0&&(n=+this.rxSize);var o={txSize:this.txSize,rxSize:n,duration:(0,j.z)()-this.startTime};i("xhr",[this.params,o,this.startTime,this.endTime,"fetch"],this,r.D.ajax)}function x(e){var t=this.params,n=this.metrics;if(!this.ended){this.ended=!0;for(var o=0;o2&&void 0!==arguments[2])||arguments[2];super(e,t,se.t,r),this.importAggregator()}}var ue=i(7836);const{FEATURE_NAME:de,START:le,END:fe,BODY:he,CB_END:pe,JS_TIME:ge,FETCH:me,FN_START:ve,CB_START:be,FN_END:ye}=ue;var Ae=i(4649);class we extends f{static featureName=Ae.t;constructor(e,t){let r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];super(e,t,Ae.t,r),this.importAggregator()}}new class extends t{constructor(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(0,E.ky)(16);super(),l._A?(this.agentIdentifier=r,this.sharedAggregator=new v({agentIdentifier:this.agentIdentifier}),this.features={},this.desiredFeatures=new Set(t.features||[]),this.desiredFeatures.add(p),Object.assign(this,(0,s.j)(this.agentIdentifier,t,t.loaderType||"agent")),this.run()):(0,e.Z)("Failed to initial the agent. Could not determine the runtime environment.")}get config(){return{info:(0,n.C5)(this.agentIdentifier),init:(0,n.P_)(this.agentIdentifier),loader_config:(0,n.DL)(this.agentIdentifier),runtime:(0,n.OP)(this.agentIdentifier)}}run(){const t="features";try{const n=a(this.agentIdentifier),i=[...this.desiredFeatures];i.sort(((e,t)=>r.p[e.featureName]-r.p[t.featureName])),i.forEach((t=>{if(n[t.featureName]||t.featureName===r.D.pageViewEvent){const i=function(e){switch(e){case r.D.ajax:return[r.D.jserrors];case r.D.sessionTrace:return[r.D.ajax,r.D.pageViewEvent];case r.D.sessionReplay:return[r.D.sessionTrace];case r.D.pageViewTiming:return[r.D.pageViewEvent];default:return[]}}(t.featureName);i.every((e=>n[e]))||(0,e.Z)("".concat(t.featureName," is enabled but one or more dependent features has been disabled (").concat((0,_.P)(i),"). This may cause unintended consequences or missing data...")),this.features[t.featureName]=new t(this.agentIdentifier,this.sharedAggregator)}})),(0,x.Qy)(this.agentIdentifier,this.features,t)}catch(r){(0,e.Z)("Failed to initialize all enabled instrument classes (agent aborted) -",r);for(const e in this.features)this.features[e].abortHandler?.();const n=(0,x.fP)();return delete n.initializedAgents[this.agentIdentifier]?.api,delete n.initializedAgents[this.agentIdentifier]?.[t],delete this.sharedAggregator,n.ee?.abort(),delete n.ee?.get(this.agentIdentifier),!1}}addToTrace(t){(0,e.Z)("Call to agent api addToTrace failed. The session trace feature is not currently initialized.")}setCurrentRouteName(t){(0,e.Z)("Call to agent api setCurrentRouteName failed. The spa feature is not currently initialized.")}interaction(){(0,e.Z)("Call to agent api interaction failed. The spa feature is not currently initialized.")}}({features:[Q,p,C,class extends f{static featureName=ne;constructor(e,t){if(super(e,t,ne,!(arguments.length>2&&void 0!==arguments[2])||arguments[2]),!l.il)return;const n=this.ee;let i;(0,B.QU)(n),this.eventsEE=(0,B.em)(n),this.eventsEE.on(oe,(function(e,t){this.bstStart=(0,j.z)()})),this.eventsEE.on(ie,(function(e,t){(0,T.p)("bst",[e[0],t,this.bstStart,(0,j.z)()],void 0,r.D.sessionTrace,n)})),n.on(ae+te,(function(e){this.time=(0,j.z)(),this.startPath=location.pathname+location.hash})),n.on(ae+re,(function(e){(0,T.p)("bstHist",[location.pathname+location.hash,this.startPath,this.time],void 0,r.D.sessionTrace,n)}));try{i=new PerformanceObserver((e=>{const t=e.getEntries();(0,T.p)(J,[t],void 0,r.D.sessionTrace,n)})),i.observe({type:ee,buffered:!0})}catch(e){}this.importAggregator({resourceObserver:i})}},ce,P,we,k,class extends f{static featureName=de;constructor(e,t){if(super(e,t,de,!(arguments.length>2&&void 0!==arguments[2])||arguments[2]),!l.il)return;if(!(0,n.OP)(e).xhrWrappable)return;try{this.removeOnAbort=new AbortController}catch(e){}let r,i=0;const o=this.ee.get("tracer"),a=(0,B._L)(this.ee),s=(0,B.Lg)(this.ee),c=(0,B.BV)(this.ee),u=(0,B.Kf)(this.ee),d=this.ee.get("events"),f=(0,B.u5)(this.ee),h=(0,B.QU)(this.ee),p=(0,B.Gm)(this.ee);function g(e,t){h.emit("newURL",[""+window.location,t])}function m(){i++,r=window.location.hash,this[ve]=(0,j.z)()}function v(){i--,window.location.hash!==r&&g(0,!0);var e=(0,j.z)();this[ge]=~~this[ge]+e-this[ve],this[ye]=e}function b(e,t){e.on(t,(function(){this[t]=(0,j.z)()}))}this.ee.on(ve,m),s.on(be,m),a.on(be,m),this.ee.on(ye,v),s.on(pe,v),a.on(pe,v),this.ee.buffer([ve,ye,"xhr-resolved"],this.featureName),d.buffer([ve],this.featureName),c.buffer(["setTimeout"+fe,"clearTimeout"+le,ve],this.featureName),u.buffer([ve,"new-xhr","send-xhr"+le],this.featureName),f.buffer([me+le,me+"-done",me+he+le,me+he+fe],this.featureName),h.buffer(["newURL"],this.featureName),p.buffer([ve],this.featureName),s.buffer(["propagate",be,pe,"executor-err","resolve"+le],this.featureName),o.buffer([ve,"no-"+ve],this.featureName),a.buffer(["new-jsonp","cb-start","jsonp-error","jsonp-end"],this.featureName),b(f,me+le),b(f,me+"-done"),b(a,"new-jsonp"),b(a,"jsonp-end"),b(a,"cb-start"),h.on("pushState-end",g),h.on("replaceState-end",g),window.addEventListener("hashchange",g,(0,D.m$)(!0,this.removeOnAbort?.signal)),window.addEventListener("load",g,(0,D.m$)(!0,this.removeOnAbort?.signal)),window.addEventListener("popstate",(function(){g(0,i>1)}),(0,D.m$)(!0,this.removeOnAbort?.signal)),this.abortHandler=this.#i,this.importAggregator()}#i(){this.removeOnAbort?.abort(),this.abortHandler=void 0}}],loaderType:"spa"})})()})(); + } + const enableNewRelic = parseConfig("__APPSMITH_NEW_RELIC_ACCOUNT_ENABLE__"); + if(!!enableNewRelic){ + runNewRelicAnalytics(); + } + + + - - + const result = config.trim(); + if (result.toLowerCase() === "false" || result === "") { + return false; + } else if (result.toLowerCase() === "true") { + return true; + } + + return result; + }; + const CLOUD_HOSTING = parseConfig("__APPSMITH_CLOUD_HOSTING__"); + const ZIPY_KEY = parseConfig("__APPSMITH_ZIPY_SDK_KEY__"); + const AIRGAPPED = parseConfig("__APPSMITH_AIRGAP_ENABLED__"); + - -