diff --git a/.github/config.json b/.github/config.json index b3d24e5865..6b795c548b 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},{"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},{"label":"New Deployment Mode","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},{"label":"Custom widgets","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"},"New Deployment Mode":{"name":"New Deployment Mode","description":"Support a new mode of deployment","color":"108033"},"Custom widgets":{"name":"Custom widgets","description":"For all issues related to the custom widget project","color":"c9db9c"}},"success":true} \ No newline at end of file +{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"AI Pod":{"conditions":[{"label":"AI","type":"hasLabel","value":true}],"requires":1},"Workflows":{"conditions":[],"requires":1},"Error Handling":{"conditions":[],"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},"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":"Omnibar","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true},{"label":"IDE Navigation","type":"hasLabel","value":true},{"label":"Clean URLs","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}],"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":"One-click Binding","type":"hasLabel","value":true},{"label":"Old widget version","type":"hasLabel","value":true},{"label":"Widget Discoverability","type":"hasLabel","value":true},{"label":"Custom widgets","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},"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},{"label":"New Deployment Mode","type":"hasLabel","value":true},{"label":"Feature Flagging","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},"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},"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},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Welcome Screen","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true}],"requires":1},"Git Pod":{"conditions":[{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true},{"label":"Fork App","type":"hasLabel","value":true}],"requires":1},"Mobile Pod":{"conditions":[],"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":"Embedding Apps","type":"hasLabel","value":true},{"label":"TM_BU","type":"hasLabel","value":true},{"label":"Homepage Experience V2","type":"hasLabel","value":true}],"requires":1},"Performance Pod":{"conditions":[{"label":"Performance","type":"hasLabel","value":true},{"label":"Performance infra","type":"hasLabel","value":true}],"requires":1},"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},{"label":"Widget design system","type":"hasLabel","value":true}],"requires":1},"IDE 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":"30c76d","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":"30c76d","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":"30c76d","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":"30c76d","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":"30c76d"},"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":"30c76d"},"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":"30c76d"},"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":"cb6188"},"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":"30c76d"},"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":"361281"},"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"},"New Deployment Mode":{"name":"New Deployment Mode","description":"Support a new mode of deployment","color":"108033"},"Custom widgets":{"name":"Custom widgets","description":"For all issues related to the custom widget project","color":"c9db9c"},"IDE Pod":{"name":"IDE Pod","description":"https://app.zenhub.com/workspaces/new-developers-pod-60507ad1d4b98d00150a2858/board","color":"d3d248"},"TM_BU":{"name":"TM_BU","description":"The issues on Team Manager which needs to be taken up by Billing & Usage","color":"198cdf"},"Homepage Experience V2":{"name":"Homepage Experience V2","description":"Label for reporting new tasks and bug fixes related to revamped homepage experience","color":"c55d54"}},"success":true} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4af1d28854..55e3511a87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,10 @@ RUN cd ./utils && npm install --only=prod && npm install --only=prod -g . && cd && chmod +x *.sh templates/nginx-app.conf.sh /watchtower-hooks/*.sh \ # Disable setuid/setgid bits for the files inside container. && find / \( -path /proc -prune \) -o \( \( -perm -2000 -o -perm -4000 \) -print -exec chmod -s '{}' + \) || true \ - && node prepare-image.mjs + && node prepare-image.mjs \ + && mkdir -p /.mongodb/mongosh /appsmith-stacks \ + && chmod ugo+w /etc /appsmith-stacks \ + && chmod -R ugo+w /var/lib/nginx /var/log/nginx /var/run /usr/sbin/cron /.mongodb /etc/ssl /usr/local/share LABEL com.centurylinklabs.watchtower.lifecycle.pre-check=/watchtower-hooks/pre-check.sh LABEL com.centurylinklabs.watchtower.lifecycle.pre-update=/watchtower-hooks/pre-update.sh diff --git a/README.md b/README.md index 0dcbd00be6..9174c78f6d 100644 --- a/README.md +++ b/README.md @@ -93,40 +93,38 @@ 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) [![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122) [![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) [![Parthvi12](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80334441?v=4&w=50&h=50&mask=circle)](https://github.com/Parthvi12) +[![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith) [![somangshu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11089579?v=4&w=50&h=50&mask=circle)](https://github.com/somangshu) [![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) -[![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) +[![marks0351](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35134347?v=4&w=50&h=50&mask=circle)](https://github.com/marks0351) [![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) +[![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish) +[![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) [![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) [![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) [![ichik](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80973?v=4&w=50&h=50&mask=circle)](https://github.com/ichik) [![dipyamanbiswas07](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/26247571?v=4&w=50&h=50&mask=circle)](https://github.com/dipyamanbiswas07) [![rahulbarwal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/6761673?v=4&w=50&h=50&mask=circle)](https://github.com/rahulbarwal) [![ankitsrivas14](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67647761?v=4&w=50&h=50&mask=circle)](https://github.com/ankitsrivas14) [![vsvamsi1](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/121419957?v=4&w=50&h=50&mask=circle)](https://github.com/vsvamsi1) -[![sharanya-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/135708039?v=4&w=50&h=50&mask=circle)](https://github.com/sharanya-appsmith) [![brayn003](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8724051?v=4&w=50&h=50&mask=circle)](https://github.com/brayn003) -[![shubham7saxena7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/136057998?v=4&w=50&h=50&mask=circle)](https://github.com/shubham7saxena7) -[![dvj1988](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/18716465?v=4&w=50&h=50&mask=circle)](https://github.com/dvj1988) +[![sharanya-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/135708039?v=4&w=50&h=50&mask=circle)](https://github.com/sharanya-appsmith) [![vivonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25587962?v=4&w=50&h=50&mask=circle)](https://github.com/vivonk) +[![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) +[![jacquesikot](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40626453?v=4&w=50&h=50&mask=circle)](https://github.com/jacquesikot) [![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) [![srix](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5801636?v=4&w=50&h=50&mask=circle)](https://github.com/srix) -[![jacquesikot](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40626453?v=4&w=50&h=50&mask=circle)](https://github.com/jacquesikot) [![laveena-en](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/109572422?v=4&w=50&h=50&mask=circle)](https://github.com/laveena-en) [![RakshaKShetty](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/45958978?v=4&w=50&h=50&mask=circle)](https://github.com/RakshaKShetty) [![Rishabhkaul](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1650391?v=4&w=50&h=50&mask=circle)](https://github.com/Rishabhkaul) @@ -169,6 +167,7 @@ Let's build great software together. [![kavitasmoolya](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/134914630?v=4&w=50&h=50&mask=circle)](https://github.com/kavitasmoolya) [![NeelPattani1](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/138656126?v=4&w=50&h=50&mask=circle)](https://github.com/NeelPattani1) [![rashmi-sahoo-git](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/144310336?v=4&w=50&h=50&mask=circle)](https://github.com/rashmi-sahoo-git) +[![michael-peach-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/150825080?v=4&w=50&h=50&mask=circle)](https://github.com/michael-peach-appsmith) [![akash-codemonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67054171?v=4&w=50&h=50&mask=circle)](https://github.com/akash-codemonk) [![Tooluloope](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/31691737?v=4&w=50&h=50&mask=circle)](https://github.com/Tooluloope) [![rishabhsaxena](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1944800?v=4&w=50&h=50&mask=circle)](https://github.com/rishabhsaxena) @@ -194,6 +193,7 @@ Let's build great software together. [![tanvibhakta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13763558?v=4&w=50&h=50&mask=circle)](https://github.com/tanvibhakta) [![rashmigowda55](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/65769804?v=4&w=50&h=50&mask=circle)](https://github.com/rashmigowda55) [![ankurrsinghal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17961105?v=4&w=50&h=50&mask=circle)](https://github.com/ankurrsinghal) +[![Druthi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20187542?v=4&w=50&h=50&mask=circle)](https://github.com/Druthi) [![geekup-legodevops](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/72587752?v=4&w=50&h=50&mask=circle)](https://github.com/geekup-legodevops) [![megaconfidence](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/megaconfidence) [![vihar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16307796?v=4&w=50&h=50&mask=circle)](https://github.com/vihar) @@ -216,6 +216,7 @@ Let's build great software together. [![iamakulov](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/2953267?v=4&w=50&h=50&mask=circle)](https://github.com/iamakulov) [![iamrkcheers](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16760643?v=4&w=50&h=50&mask=circle)](https://github.com/iamrkcheers) [![sumanthyedoti](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30371888?v=4&w=50&h=50&mask=circle)](https://github.com/sumanthyedoti) +[![shubham7saxena7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/136057998?v=4&w=50&h=50&mask=circle)](https://github.com/shubham7saxena7) [![vaibh1297](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40293928?v=4&w=50&h=50&mask=circle)](https://github.com/vaibh1297) [![vnodecg](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4994565?v=4&w=50&h=50&mask=circle)](https://github.com/vnodecg) [![pc9795](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14848874?v=4&w=50&h=50&mask=circle)](https://github.com/pc9795) @@ -345,6 +346,7 @@ Let's build great software together. [![Sufiyan1997](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22118049?v=4&w=50&h=50&mask=circle)](https://github.com/Sufiyan1997) [![rayrny](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48341341?v=4&w=50&h=50&mask=circle)](https://github.com/rayrny) [![trishitapingolia](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35962310?v=4&w=50&h=50&mask=circle)](https://github.com/trishitapingolia) +[![trivikr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16024985?v=4&w=50&h=50&mask=circle)](https://github.com/trivikr) [![webdott](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/52088600?v=4&w=50&h=50&mask=circle)](https://github.com/webdott) [![vasanthkumar18](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/58137524?v=4&w=50&h=50&mask=circle)](https://github.com/vasanthkumar18) [![VanshajPoonia](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74147463?v=4&w=50&h=50&mask=circle)](https://github.com/VanshajPoonia) diff --git a/app/client/cypress/e2e/Regression/Apps/MongoDBShoppingCart_spec.ts b/app/client/cypress/e2e/Regression/Apps/MongoDBShoppingCart_spec.ts index cfc049ebdd..5092af0694 100644 --- a/app/client/cypress/e2e/Regression/Apps/MongoDBShoppingCart_spec.ts +++ b/app/client/cypress/e2e/Regression/Apps/MongoDBShoppingCart_spec.ts @@ -170,6 +170,7 @@ describe("Shopping cart App", function () { ); agHelper.GetNClick(appPage.addButton, 0, true); assertHelper.AssertNetworkStatus("@postExecute"); + agHelper.Sleep(3000); // Deleting the book from the cart agHelper.GetNClick(appPage.deleteButton, 1, false); assertHelper.AssertNetworkStatus("@postExecute"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/StoreValue_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/StoreValue_spec.ts index c299e2fb70..fe1af0cca7 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/StoreValue_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/StoreValue_spec.ts @@ -5,6 +5,8 @@ import { propPane, deployMode, debuggerHelper, + locators, + draggableWidgets, } from "../../../../support/Objects/ObjectsCore"; describe("storeValue Action test", () => { @@ -52,6 +54,9 @@ describe("storeValue Action test", () => { }); deployMode.DeployApp(); + agHelper.AssertElementVisibility( + locators._widgetInDeployed(draggableWidgets.BUTTON), + ); agHelper.ClickButton("StoreTest"); agHelper.AssertContains( JSON.stringify({ diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithAutoLayout_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithAutoLayout_spec.ts index c457265c23..3c5273962c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithAutoLayout_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithAutoLayout_spec.ts @@ -1,12 +1,15 @@ import { agHelper, - locators, - entityExplorer, - propPane, appSettings, autoLayout, draggableWidgets, + entityExplorer, + locators, + propPane, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Validating multiple widgets in auto layout mode with App navigation settings", function () { it("1. Drag and Drop multiple widgets in auto layout mode", function () { @@ -19,13 +22,13 @@ describe("Validating multiple widgets in auto layout mode with App navigation se it("2. Change App navigation settings and valdiate the layout settings", () => { entityExplorer.SelectEntityByName("Page1", "Pages"); - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._side, ); agHelper.AssertElementExist(appSettings.locators._sideNavbar); - agHelper.GetNClick(locators._canvas); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.AssertElementExist(locators._widgetInCanvas("inputwidgetv2")); agHelper.AssertElementExist(locators._widgetInCanvas("inputwidgetv2"), 1); agHelper.AssertElementExist(locators._fixedLayout); diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithMultiplePages_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithMultiplePages_spec.ts index 8b3a967e88..eca404bbac 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithMultiplePages_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithMultiplePages_spec.ts @@ -1,18 +1,22 @@ import { agHelper, - locators, - entityExplorer, - propPane, - deployMode, appSettings, autoLayout, + deployMode, draggableWidgets, + entityExplorer, + locators, + propPane, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; + let currentUrl: string; describe("Page orientation and navigation related usecases ", function () { it("1. Change 'Orientation' to 'Side', sidebar should appear", () => { - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._side, diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/NavigationSettings_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/NavigationSettings_spec.ts index fb75d971c7..0dd3e910a6 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/NavigationSettings_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/NavigationSettings_spec.ts @@ -5,10 +5,13 @@ import { deployMode, appSettings, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Test app's navigation settings", function () { it("1. Open app settings and navigation tab should be there and when the navigation tab is selected, navigation preview should be visible", () => { - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.AssertElementVisibility( appSettings.locators._navigationSettingsTab, ); @@ -34,7 +37,7 @@ describe("Test app's navigation settings", function () { //Browser back is used as the Navbar is off and there wont be option to come back to editor agHelper.BrowserNavigation(-1); // Wait for the app to load - agHelper.GetNClick(appSettings.locators._appSettings, 0, true); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); // Toggle show navbar back to on agHelper.GetNClick( @@ -60,7 +63,7 @@ describe("Test app's navigation settings", function () { it("4. Change 'Orientation' back to 'Top', and 'Nav style' to 'Inline', page navigation items should appear inline", () => { entityExplorer.AddNewPage(); - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._top, diff --git a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/SidebarCollapse_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/SidebarCollapse_spec.ts index 128d4223b1..02fb20f7c3 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/SidebarCollapse_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/SidebarCollapse_spec.ts @@ -4,11 +4,14 @@ import { appSettings, locators, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Test Sidebar Collapse", function () { it("1. Sidebar collapse button should be there", () => { // First make sure that nav orientation is set to side - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._side, 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 e6e0b7cb4c..cb27d48722 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/Sidebar_spec.ts @@ -6,6 +6,9 @@ import { assertHelper, locators, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Test Sidebar navigation style", function () { before(() => { @@ -26,7 +29,7 @@ describe("Test Sidebar navigation style", function () { }); it("1. Change 'Orientation' to 'Side', sidebar should appear", () => { - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._side, @@ -60,7 +63,7 @@ describe("Test Sidebar navigation style", function () { ); // Changing color style to theme should change navigation's background color deployMode.NavigateBacktoEditor(); - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick(appSettings.locators._colorStyleOptions._theme, 0, true); deployMode.DeployApp(); 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 9ff0e9db70..fd23928cc1 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopInline_spec.ts @@ -6,6 +6,9 @@ import { appSettings, locators, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Test Top + Inline navigation style", function () { before(() => { @@ -27,7 +30,7 @@ describe("Test Top + Inline navigation style", function () { }); it("1. Change 'Orientation' to 'Top', and 'Nav style' to 'Inline', page navigation items should appear inline", () => { - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick( appSettings.locators._navigationSettings._orientationOptions._top, @@ -101,7 +104,7 @@ describe("Test Top + Inline navigation style", function () { ); // Changing color style to theme should change navigation's background color deployMode.NavigateBacktoEditor(); - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick(appSettings.locators._colorStyleOptions._theme, 0, true); deployMode.DeployApp(); 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 1cf689aeaa..c388017a4b 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/AppNavigation/TopStacked_spec.ts @@ -6,6 +6,9 @@ import { appSettings, locators, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Test Top + Stacked navigation style", function () { before(() => { @@ -124,7 +127,7 @@ describe("Test Top + Stacked navigation style", function () { ); // Changing color style to theme should change navigation's background color deployMode.NavigateBacktoEditor(); - agHelper.GetNClick(appSettings.locators._appSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); agHelper.GetNClick(appSettings.locators._navigationSettingsTab); agHelper.GetNClick(appSettings.locators._colorStyleOptions._theme, 0, true); deployMode.DeployApp(); 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 2f23daf1c4..a9ab1ac4a7 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 @@ -80,4 +80,29 @@ describe("Autocomplete tests for setters", () => { propPane.EnterJSContext("onClick", "{{Input1.set", true, false); agHelper.GetElementsNAssertTextPresence(locators._hints, "setDisabled"); }); + + it("3. function arguments hint shows up", () => { + entityExplorer.SelectEntityByName("Button1"); + propPane.EnterJSContext("onClick", "{{", true, false); + agHelper.GetNClickByContains(locators._hints, "appsmith", 0, false); + agHelper.AssertElementVisibility(locators._evalValuePopover); + propPane.EnterJSContext("onClick", "{{showAlert", true, false); + agHelper.GetElementsNAssertTextPresence(locators._hints, "showAlert"); + agHelper.AssertElementAbsence(locators._evalValuePopover); + agHelper.GetNClickByContains(locators._hints, "showAlert", 0, false); + agHelper.GetNAssertElementText( + locators._argHintFnName, + "showAlert", + "contain.text", + ); + agHelper.AssertElementAbsence(locators._evalValuePopover); + }); + + it("4. function description tooltip shows up", () => { + entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 100, 100); + entityExplorer.SelectEntityByName("Button1"); + propPane.EnterJSContext("onClick", "{{showAlert", true, false); + agHelper.GetElementsNAssertTextPresence(locators._hints, "showAlert"); + agHelper.AssertElementExist(locators._tern_doc); + }); }); 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 2dbf74a72e..e0532155c0 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 @@ -8,6 +8,9 @@ import { installer, draggableWidgets, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Autocomplete bug fixes", function () { it("1. Bug #12790 Verifies if selectedRow is in best match", function () { @@ -90,10 +93,11 @@ describe("Autocomplete bug fixes", function () { "excludeForAirgap", "7. Installed library should show up in autocomplete", function () { - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.OpenInstaller(); installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); + EditorNavigation.ViaSidebar(SidebarButton.Pages); entityExplorer.SelectEntityByName("Text1"); propPane.TypeTextIntoField("Text", "{{UUI"); agHelper.GetNAssertElementText(locators._hints, "UUID"); @@ -105,7 +109,9 @@ describe("Autocomplete bug fixes", function () { "8. No autocomplete for Removed libraries", function () { entityExplorer.RenameEntityFromExplorer("Text1Copy", "UUIDTEXT"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.uninstallLibrary("uuidjs"); + EditorNavigation.ViaSidebar(SidebarButton.Pages); propPane.TypeTextIntoField("Text", "{{UUID."); agHelper.AssertElementAbsence(locators._hints); }, diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/autocomplete_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/autocomplete_spec.js index dd0150501e..d4d1e445f6 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/autocomplete_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/autocomplete_spec.js @@ -38,28 +38,7 @@ describe("Dynamic input autocomplete", () => { cy.testJsontext("label", "", { parseSpecialCharSequences: true, }); - // Tests if "No suggestions" message will pop if you type any garbage - cy.get(dynamicInputLocators.input) - .first() - .click({ force: true }) - .type("{uparrow}", { parseSpecialCharSequences: true }) - .type("{ctrl}{shift}{downarrow}", { parseSpecialCharSequences: true }) - .type("{backspace}", { parseSpecialCharSequences: true }) - - .then(() => { - cy.get(dynamicInputLocators.input) - .first() - .click({ force: true }) - .type("{{garbage", { - parseSpecialCharSequences: true, - }); - cy.get(".CodeMirror-Tern-tooltip").should( - "have.text", - "No suggestions", - ); - }); }); - cy.evaluateErrorMessage("garbage is not defined"); }); it("2. Test if action inside non event field throws error & open current value popup", () => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js index 8c65268747..bdfb7ee555 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js @@ -32,7 +32,5 @@ describe("Binding the button Widgets and validating NavigateTo Page functionalit locators._widgetInDeployed(draggableWidgets.BUTTON), ); cy.url().should("contain", testdata.externalPage); - - }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/ApiBugs_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/ApiBugs_Spec.ts index a320a1c4ad..974f21de8d 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/ApiBugs_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/ApiBugs_Spec.ts @@ -17,6 +17,9 @@ import { ERROR_ACTION_EXECUTE_FAIL, createMessage, } from "../../../../support/Objects/CommonErrorMessages"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("API Bugs", function () { before(() => { @@ -60,6 +63,7 @@ describe("API Bugs", function () { action: "Delete", entityType: entityItems.Api, }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("3. Bug 18876 Ensures application does not crash when saving datasource", () => { @@ -71,7 +75,7 @@ describe("API Bugs", function () { ); apiPage.SelectPaneTab("Authentication"); cy.get(apiPage._saveAsDS).last().click({ force: true }); - cy.get(".t--close-editor").click({ force: true }); + cy.go("back"); cy.get(dataSources._datasourceModalSave).click(); // ensures app does not crash and datasource is saved. cy.contains("Edit datasource to access authentication settings").should( diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug14987_spec.js b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug14987_spec.js index 81d712d999..5de1d4130c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug14987_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug14987_spec.js @@ -52,6 +52,6 @@ describe("Verify setting tab form controls not to have tooltip and tooltip (unde action: "Delete", entityType: entityItems.Query, }); - dataSources.DeleteDatasouceFromActiveTab(datasourceName, 200); + dataSources.DeleteDatasourceFromWithinDS(datasourceName, 200); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug18035_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug18035_Spec.ts index b2b461aafa..dce568310a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug18035_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug18035_Spec.ts @@ -1,4 +1,7 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const dataSources = ObjectsRegistry.DataSources, agHelper = ObjectsRegistry.AggregateHelper; @@ -10,7 +13,7 @@ describe( it("1. Create gsheet datasource, click on back button, discard popup should contain save and authorize", function () { dataSources.NavigateToDSCreateNew(); dataSources.CreatePlugIn("Google Sheets"); - agHelper.GoBack(); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.AssertContains( "Save & Authorize", "exist", @@ -22,7 +25,7 @@ describe( dataSources.CreatePlugIn("PostgreSQL"); // Need to add values since without that, going back won't show any popup dataSources.FillPostgresDSForm(); - agHelper.GoBack(); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.AssertContains( "Save", "exist", diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug21734_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug21734_Spec.ts index 1eab4b0c88..13ccd5a95a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug21734_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug21734_Spec.ts @@ -1,4 +1,7 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const dataSources = ObjectsRegistry.DataSources, agHelper = ObjectsRegistry.AggregateHelper, @@ -11,14 +14,16 @@ describe("Bug 21734: On exiting from the Datasources page without saving changes // Have to fill form since modal won't show for empty ds dataSources.FillMongoDSForm(); - ee.AddNewPage(); + agHelper.GetNClick(dataSources._addNewDataSource, 0, true); agHelper.AssertContains( "Don't save", "exist", dataSources._datasourceModalDoNotSave, ); - cy.get(dataSources._datasourceModalDoNotSave).click(); + cy.get(dataSources._datasourceModalDoNotSave).click({ force: true }); + + ee.AddNewPage(); ee.SelectEntityByName("Page1"); agHelper.AssertURL("page1"); @@ -32,13 +37,14 @@ describe("Bug 21734: On exiting from the Datasources page without saving changes // Have to fill form since modal won't show for empty ds dataSources.FillPostgresDSForm(); - ee.SelectEntityByName("Page1"); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.AssertContains( "Don't save", "exist", dataSources._datasourceModalDoNotSave, ); cy.get(dataSources._datasourceModalDoNotSave).click(); + ee.SelectEntityByName("Page1"); agHelper.AssertURL("page1"); ee.SelectEntityByName("Page2"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug26716_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug26716_Spec.ts index 88c20a7c80..8de0bed392 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug26716_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug26716_Spec.ts @@ -1,5 +1,4 @@ import { - agHelper, dataSources, entityExplorer, } from "../../../../support/Objects/ObjectsCore"; @@ -23,29 +22,13 @@ describe( cy.get("@dsName").then(($dsName) => { dsName = $dsName; // Select Users - entityExplorer.SelectEntityByName(userMock, "Datasources"); - agHelper.Sleep(200); - agHelper.AssertClassExists( - dataSources._entityExplorerID(userMock), - "active", - ); + dataSources.navigateToDatasource(userMock); // Switch to Movies - entityExplorer.SelectEntityByName(movieMock, "Datasources"); - agHelper.Sleep(200); - agHelper.AssertClassExists( - dataSources._entityExplorerID(movieMock), - "active", - ); + dataSources.navigateToDatasource(movieMock); // Switch to custom DS - entityExplorer.SelectEntityByName(dsName, "Datasources"); - entityExplorer.ExpandCollapseEntity(dsName, false); - agHelper.Sleep(200); - agHelper.AssertClassExists( - dataSources._entityExplorerID(dsName), - "active", - ); + dataSources.navigateToDatasource(dsName); // Delete all datasources entityExplorer.ActionContextMenuByEntityName({ @@ -61,9 +44,9 @@ describe( action: "Delete", }); - dataSources.DeleteDatasouceFromActiveTab(userMock); - dataSources.DeleteDatasouceFromActiveTab(movieMock); - dataSources.DeleteDatasouceFromActiveTab(dsName); + dataSources.DeleteDatasourceFromWithinDS(userMock); + dataSources.DeleteDatasourceFromWithinDS(movieMock); + dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28731_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28731_Spec.ts new file mode 100644 index 0000000000..c7fc42f397 --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28731_Spec.ts @@ -0,0 +1,41 @@ +import OneClickBindingLocator from "../../../../locators/OneClickBindingLocator"; +import { + agHelper, + entityExplorer, + apiPage, + dataManager, + draggableWidgets, + propPane, +} from "../../../../support/Objects/ObjectsCore"; + +describe("transformed one-click binding", function () { + before(() => { + entityExplorer.NavigateToSwitcher("Explorer"); + }); + + it("Transforms API data to match widget exppected type ", function () { + // Create anAPI that mreturns object response + apiPage.CreateAndFillApi( + dataManager.dsValues[dataManager.defaultEnviorment].mockApiObjectUrl, + ); + apiPage.RunAPI(); + + // Table + entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE, 300, 300); + + agHelper.GetNClick(OneClickBindingLocator.datasourceDropdownSelector); + agHelper.GetNClick(OneClickBindingLocator.datasourceQuerySelector("Api1")); + propPane.ToggleJSMode("Table Data", true); + agHelper.AssertContains("{{Api1.data.users}}"); + + // Select widget + entityExplorer.DragDropWidgetNVerify(draggableWidgets.SELECT, 100, 100); + + agHelper.GetNClick(OneClickBindingLocator.datasourceDropdownSelector); + agHelper.GetNClick(OneClickBindingLocator.datasourceQuerySelector("Api1")); + propPane.ToggleJSMode("Source Data", true); + agHelper.AssertContains( + "{{Api1.data.users.map( (obj) =>{ return {'label': obj.address, 'value': obj.avatar } })}}", + ); + }); +}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28750_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28750_Spec.ts new file mode 100644 index 0000000000..971849a204 --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28750_Spec.ts @@ -0,0 +1,24 @@ +import { agHelper, dataSources } from "../../../../support/Objects/ObjectsCore"; +import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags"; + +describe("Datasource structure schema preview data", () => { + before(() => { + featureFlagIntercept({ ab_gsheet_schema_enabled: true }); + dataSources.CreateMockDB("Users"); + }); + + it( + "excludeForAirgap", + "1. Verify if the schema table accordions is collapsed in case of search", + () => { + agHelper.TypeText( + dataSources._datasourceStructureSearchInput, + "public.us", + ); + agHelper.Sleep(1000); + agHelper.AssertElementAbsence( + `${dataSources._dsStructurePreviewMode} ${dataSources._datasourceSchemaColumn}`, + ); + }, + ); +}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28764_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28764_Spec.ts new file mode 100644 index 0000000000..d2759c4ae6 --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug28764_Spec.ts @@ -0,0 +1,74 @@ +import { + agHelper, + locators, + entityExplorer, + jsEditor, +} from "../../../../support/Objects/ObjectsCore"; + +describe("JS Function Execution", function () { + before(() => { + entityExplorer.NavigateToSwitcher("Explorer"); + }); + + it("Retains lint errors after navigation", function () { + // JS Object 1 + jsEditor.CreateJSObject( + `export default { + myVar1: [], + myVar2: {}, + myFun1 () { + // write code here + // this.myVar1 = [1,2,3] + }, + async myFun2 () { + // use async-await or promises + // Lint Error + fff + } + }`, + { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + prettify: false, + }, + ); + // JS Object 2 + jsEditor.CreateJSObject( + `export default { + myVar1: [], + myVar2: {}, + myFun1 () { + // write code here + // this.myVar1 = [1,2,3] + }, + async myFun2 () { + // use async-await or promises + } + }`, + { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + prettify: false, + }, + ); + + entityExplorer.SelectEntityByName("JSObject1", "Queries/JS"); + // Assert lint error + agHelper.AssertElementLength(locators._lintErrorElement, 1); + agHelper.HoverElement(locators._lintErrorElement); + agHelper.AssertContains(`'fff' is not defined`); + + entityExplorer.SelectEntityByName("JSObject2", "Queries/JS"); + agHelper.AssertElementAbsence(locators._lintErrorElement); + + entityExplorer.SelectEntityByName("JSObject1", "Queries/JS"); + // Assert lint error + agHelper.AssertElementLength(locators._lintErrorElement, 1); + agHelper.HoverElement(locators._lintErrorElement); + agHelper.AssertContains(`'fff' is not defined`); + }); +}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug9334_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug9334_Spec.ts index 540d3eb044..9061619eca 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug9334_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Bug9334_Spec.ts @@ -7,6 +7,9 @@ import { appSettings, entityExplorer, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; let dsName: any; @@ -17,6 +20,7 @@ describe("Bug 9334: The Select widget value is sent as null when user switches b cy.get("@dsName").then(($dsName) => { dsName = $dsName; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("1. Create dummy pages for navigating", () => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/DSDiscardBugs_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/DSDiscardBugs_spec.ts index c02e3abdd2..acaee673da 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/DSDiscardBugs_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/DSDiscardBugs_spec.ts @@ -1,4 +1,7 @@ import * as _ from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; let dsName: any; @@ -18,9 +21,9 @@ describe("datasource unsaved changes popup shows even without changes", function _.dataSources.SaveDatasource(); _.agHelper.Sleep(); _.dataSources.EditDatasource(); - _.agHelper.GoBack(); + _.dataSources.cancelDSEditAndAssertModalPopUp(false); _.agHelper.AssertElementVisibility(_.dataSources._activeDS); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); @@ -42,9 +45,9 @@ describe("datasource unsaved changes popup shows even without changes", function // Even if headers, and query parameters are being initialized, we shouldnt see the popup // as those are not initialized by user _.dataSources.EditDatasource(); - _.agHelper.GoBack(); + _.dataSources.cancelDSEditAndAssertModalPopUp(false); _.agHelper.AssertElementVisibility(_.dataSources._activeDS); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); @@ -68,7 +71,7 @@ describe("datasource unsaved changes popup shows even without changes", function // Assert that popup is visible _.dataSources.SaveDSFromDialog(false); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); @@ -92,7 +95,7 @@ describe("datasource unsaved changes popup shows even without changes", function // Assert that popup is visible _.dataSources.cancelDSEditAndAssertModalPopUp(true, false); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); @@ -115,7 +118,7 @@ describe("datasource unsaved changes popup shows even without changes", function // Assert that popup is visible _.dataSources.cancelDSEditAndAssertModalPopUp(false, false); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); @@ -148,8 +151,14 @@ describe("datasource unsaved changes popup shows even without changes", function _.dataSources._host(), _.dataManager.dsValues.Staging.mongo_host, ); + _.agHelper.GetNClick( + _.dataSources._cancelEditDatasourceButton, + 0, + true, + 200, + ); - _.dataSources.DeleteDatasouceFromActiveTab(dsName); + _.dataSources.DeleteDatasourceFromWithinDS(dsName); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/DatasourceSchema_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/DatasourceSchema_spec.ts index 7b8e8cf46d..2ca51d6979 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/DatasourceSchema_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/DatasourceSchema_spec.ts @@ -5,11 +5,19 @@ import { entityExplorer, homePage, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; +import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags"; let guid; let dataSourceName: string; describe("Datasource form related tests", function () { before(() => { + featureFlagIntercept({ + ab_gsheet_schema_enabled: true, + ab_mock_mongo_schema_enabled: true, + }); homePage.CreateNewWorkspace("FetchSchemaOnce", true); homePage.CreateAppInWorkspace("FetchSchemaOnce"); }); @@ -19,7 +27,6 @@ describe("Datasource form related tests", function () { cy.get("@guid").then((uid) => { guid = uid; dataSourceName = "Postgres " + guid; - entityExplorer.ExpandCollapseEntity("Datasources"); dataSources.NavigateToDSCreateNew(); dataSources.CreatePlugIn("PostgreSQL"); agHelper.RenameWithInPane(dataSourceName, false); @@ -42,12 +49,11 @@ describe("Datasource form related tests", function () { it("2. Verify if schema was fetched once #18448", () => { agHelper.RefreshPage(); - entityExplorer.ExpandCollapseEntity("Datasources"); - entityExplorer.ExpandCollapseEntity(dataSourceName, false); - entityExplorer.ExpandCollapseEntity("Datasources"); - entityExplorer.ExpandCollapseEntity(dataSourceName); + dataSources.navigateToDatasource(dataSourceName); agHelper.Sleep(1500); agHelper.VerifyCallCount(`@getDatasourceStructure`, 1); + EditorNavigation.ViaSidebar(SidebarButton.Pages); + entityExplorer.SelectEntityByName("Query1"); agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, 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 6234eb0e30..bfb96f3090 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,7 @@ import * as _ from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; let repoName: any; let tempBranch: any; @@ -81,12 +84,11 @@ describe("Git Bugs", function () { _.gitSync.CreateGitBranch(`st`, true); cy.get("@gitbranchName").then((branchName) => { statusBranch = branchName; - _.agHelper.GetNClick(_.locators._appEditMenuBtn); - // cy.wait(_.locators._appEditMenu); - _.agHelper.GetNClick(_.locators._appEditMenuSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); _.agHelper.GetNClick(_.locators._appThemeSettings); _.agHelper.GetNClick(_.locators._appChangeThemeBtn, 0, true); _.agHelper.GetNClick(_.locators._appThemeCard, 2); + EditorNavigation.ViaSidebar(SidebarButton.Pages); _.agHelper.GetNClick(_.locators._publishButton); _.agHelper.WaitUntilEleAppear(_.gitSync._gitStatusChanges); _.agHelper.AssertContains( @@ -95,11 +97,10 @@ describe("Git Bugs", function () { _.gitSync._gitStatusChanges, ); _.agHelper.GetNClick(_.locators._dialogCloseButton); - _.agHelper.GetNClick(_.locators._appEditMenuBtn); - // cy.wait(_.locators._appEditMenu); - _.agHelper.GetNClick(_.locators._appEditMenuSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); _.agHelper.GetNClick(_.locators._appNavigationSettings); _.agHelper.GetNClick(_.locators._appNavigationSettingsShowTitle); + EditorNavigation.ViaSidebar(SidebarButton.Pages); _.agHelper.GetNClick(_.locators._publishButton); _.agHelper.WaitUntilEleAppear(_.gitSync._gitStatusChanges); _.agHelper.AssertContains( @@ -116,10 +117,10 @@ describe("Git Bugs", function () { _.gitSync.CreateGitBranch(`b24946`, true); cy.get("@gitbranchName").then((branchName) => { statusBranch = branchName; - _.agHelper.GetNClick(_.locators._appEditMenuBtn); - _.agHelper.GetNClick(_.locators._appEditMenuSettings); + EditorNavigation.ViaSidebar(SidebarButton.Settings); _.agHelper.GetNClick(_.locators._appNavigationSettings); _.agHelper.GetNClick(_.locators._appNavigationSettingsShowTitle); + EditorNavigation.ViaSidebar(SidebarButton.Pages); _.agHelper.GetNClick(_.locators._publishButton); _.agHelper.WaitUntilEleAppear(_.gitSync._gitStatusChanges); _.agHelper.GetNClick(_.gitSync._discardChanges); @@ -146,7 +147,7 @@ describe("Git Bugs", 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); + EditorNavigation.ViaSidebar(SidebarButton.Settings); _.agHelper.GetNClick(_.appSettings.locators._navigationSettingsTab); _.agHelper.GetNClick( _.appSettings.locators._navigationSettings._orientationOptions._side, 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 57c3edc845..75eb985c64 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Moment_Spec.ts @@ -121,7 +121,6 @@ describe("Bug #14299 - The data from the query does not show up on the widget", agHelper.WaitUntilAllToastsDisappear(); deployMode.DeployApp(locators._widgetInDeployed("tablewidget"), false); deployMode.NavigateBacktoEditor(); - entityExplorer.ExpandCollapseEntity("Datasources"); dataSources.DeleteDatasourceFromWithinDS(dsName, 200); }, ); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Debugger/Query_pane_navigation.ts b/app/client/cypress/e2e/Regression/ClientSide/Debugger/Query_pane_navigation.ts index d2fddc1e9a..134cb5934a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Debugger/Query_pane_navigation.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Debugger/Query_pane_navigation.ts @@ -8,6 +8,9 @@ import { entityItems, debuggerHelper, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("excludeForAirgap", "Query pane navigation", () => { let ds1Name: string; @@ -27,6 +30,7 @@ describe("excludeForAirgap", "Query pane navigation", () => { cy.get("@dsName").then(($dsName) => { ds2Name = $dsName as unknown as string; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("1. Switching between S3 query and firestore query from the debugger", () => { @@ -46,6 +50,7 @@ describe("excludeForAirgap", "Query pane navigation", () => { cy.get("@dsName").then(($dsName) => { ds2Name = $dsName as unknown as string; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); entityExplorer.CreateNewDsQuery(ds2Name); agHelper.UpdateCodeInput( ".t--actionConfiguration\\.formData\\.limitDocuments\\.data", @@ -77,7 +82,7 @@ describe("excludeForAirgap", "Query pane navigation", () => { entityType: entityItems.Query, }); - dataSources.DeleteDSFromEntityExplorer(ds1Name); - dataSources.DeleteDSFromEntityExplorer(ds2Name); + dataSources.DeleteDatasourceFromWithinDS(ds1Name); + dataSources.DeleteDatasourceFromWithinDS(ds2Name); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/API_Pane_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/API_Pane_spec.js index 230c7d22dd..e2e1afb5f5 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/API_Pane_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/API_Pane_spec.js @@ -24,18 +24,9 @@ describe("Entity explorer API pane related testcases", function () { locator._visibleTextSpan("No query/JS to display"), ); agHelper.AssertElementVisibility(locator._visibleTextSpan("New query/JS")); - - ee.ExpandCollapseEntity("Datasources"); - agHelper.AssertElementVisibility( - locator._visibleTextSpan("No datasource to display"), - ); - agHelper.AssertElementVisibility( - locator._visibleTextSpan("New datasource"), - ); }); it("2. Move to page / edit API name /properties validation", function () { - cy.NavigateToAPI_Panel(); cy.CreateAPI("FirstAPI"); cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods); cy.SaveAndRunAPI(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/CopyQuery_RenameDatasource_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/CopyQuery_RenameDatasource_spec.js index 58b3dc1991..57adfde6c8 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/CopyQuery_RenameDatasource_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/CopyQuery_RenameDatasource_spec.js @@ -28,7 +28,6 @@ describe("Entity explorer tests related to copy query", function () { cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; - cy.CheckAndUnfoldEntityItem("Datasources"); cy.NavigateToActiveDSQueryPane(datasourceName); }); @@ -83,37 +82,4 @@ describe("Entity explorer tests related to copy query", function () { expect($lis.eq(4)).to.contain("{{Query1.clear()}}"); }); }); - - it("3. Rename datasource in explorer, Delete query and try to Delete datasource", function () { - entityExplorer.SelectEntityByName("Page1"); - cy.generateUUID().then((uid) => { - updatedName = uid; - cy.log("complete uid :" + updatedName); - updatedName = uid.replace(/-/g, "_").slice(1, 15); - cy.log("sliced id :" + updatedName); - entityExplorer.ExpandCollapseEntity("Queries/JS"); - entityExplorer.ExpandCollapseEntity("Datasources"); - entityExplorer.RenameEntityFromExplorer(datasourceName, updatedName); - //cy.EditEntityNameByDoubleClick(datasourceName, updatedName); - cy.wait(1000); - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: updatedName, - action: "Delete", - toAssertAction: false, - }); - cy.wait(1000); - //This is check to make sure if a datasource is active 409 - cy.wait("@deleteDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 409, - ); - }); - entityExplorer.SelectEntityByName("Query1", "Queries/JS"); - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "Query1", - action: "Delete", - entityType: entityItems.Query, - }); - }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Datasource_Structure_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Datasource_Structure_spec.js deleted file mode 100644 index 1b4607bf2a..0000000000 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Datasource_Structure_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -const explorer = require("../../../../locators/explorerlocators.json"); -const queryLocators = require("../../../../locators/QueryEditor.json"); -const apiwidget = require("../../../../locators/apiWidgetslocator.json"); -import { - entityExplorer, - dataSources, -} from "../../../../support/Objects/ObjectsCore"; - -describe("Entity explorer datasource structure", function () { - let datasourceName; - - beforeEach(() => { - //cy.ClearSearch(); - cy.startRoutesForDatasource(); - cy.createPostgresDatasource(); - cy.get("@saveDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); - }); - - it("1. Entity explorer datasource structure", function () { - cy.NavigateToActiveDSQueryPane(datasourceName); - cy.wait("@createNewApi").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); - - cy.get(apiwidget.apiTxt) - .clear() - .type("MyQuery", { force: true }) - .should("have.value", "MyQuery") - .blur(); - cy.WaitAutoSave(); - entityExplorer.ExpandCollapseEntity("Datasources"); - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: datasourceName, - action: "Refresh", - }); - cy.wait(2000); //for the tables to open - cy.wait("@getDatasourceStructure").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); - - // cy.get(explorer.datasourceStructure) - // .first() - // .find(explorer.collapse) - // .click(); - // cy.get(explorer.datasourceColumn) - // .first() - // .click(); - // cy.get(".bp3-popover-content").should("be.visible"); - - cy.get(explorer.templateMenuIcon).first().click({ force: true }); - // assert suggested tag is present - cy.get(".t--structure-template-menu-popover").last().contains("Suggested"); - cy.get(".t--structure-template-menu-popover") - .last() - .contains("Select") - .click({ force: true }); - cy.wait("@createNewApi").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); - - cy.deleteQueryUsingContext(); - entityExplorer.ExpandCollapseEntity("Queries/JS"); - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "MyQuery", - }); - cy.deleteDatasource(datasourceName); - }); - - it("2. Refresh datasource structure", function () { - cy.NavigateToActiveDSQueryPane(datasourceName); - - //cy.GlobalSearchEntity(datasourceName); - // cy.get(`.t--entity.datasource:contains(${datasourceName})`) - // .find(explorer.collapse) - // .as("datasourceEntityCollapse"); - // cy.wait("@getDatasourceStructure").should( - // "have.nested.property", - // "response.body.responseMeta.status", - // 200, - // ); - //cy.get(commonlocators.entityExplorersearch).clear({ force: true }); - - const tableName = Math.random() - .toString(36) - .replace(/[^a-z]+/g, ""); - dataSources.EnterQuery(`CREATE TABLE public.${tableName} ( ID int );`); - cy.onlyQueryRun(); - cy.wait("@postExecute").then(({ response }) => { - expect(response.body.data.request.requestParams.Query.value).to.contain( - tableName, - ); - }); - - //cy.wait(8000) - // cy.GlobalSearchEntity(datasourceName); - // cy.get("@datasourceEntityCollapse") - // .first() - // .click(); - // cy.xpath(explorer.datsourceEntityPopover) - // .last() - // .click({ force: true }); - dataSources.AssertTableInVirtuosoList( - datasourceName, - `public.${tableName}`, - ); - - cy.typeValueNValidate(`DROP TABLE public.${tableName}`); - cy.runQuery(); - dataSources.AssertTableInVirtuosoList( - datasourceName, - `public.${tableName}`, - false, - ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); - }); -}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Entity_Naming_conflict_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Entity_Naming_conflict_spec.js index fed3471386..8807fe4685 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Entity_Naming_conflict_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Entity_Naming_conflict_spec.js @@ -11,8 +11,6 @@ describe("Tab widget test", function () { it("1. Rename API with table widget name validation test", function () { cy.log("Login Successful"); - cy.NavigateToAPI_Panel(); - cy.log("Navigation to API Panel screen successful"); cy.CreateApiAndValidateUniqueEntityName(apiName); cy.get(apiwidget.apiTxt) .clear() diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Long_Name_Tooltip_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Long_Name_Tooltip_spec.js index 0d539be0b6..597c07311a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Long_Name_Tooltip_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Long_Name_Tooltip_spec.js @@ -9,7 +9,6 @@ const tooltTipQuery = `.rc-tooltip.ads-v2-tooltip:not(.rc-tooltip-hidden) > .rc- describe("Entity Explorer showing tooltips on long names", function () { it("1. Expect tooltip on long names only", function () { // create an API with a short name - cy.NavigateToAPI_Panel(); cy.CreateAPI(shortName); ee.ExpandCollapseEntity("Queries/JS", true); // assert that a tooltip does not show up during hover @@ -19,7 +18,6 @@ describe("Entity Explorer showing tooltips on long names", function () { cy.get("body").realHover({ position: "topLeft" }); // create another API with a long name - cy.NavigateToAPI_Panel(); cy.CreateAPI(longName); // assert that a tooltip does show up during hover diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pages_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pages_spec.js index 2b56f720cc..17f0972a58 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pages_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pages_spec.js @@ -8,7 +8,6 @@ describe("Pages", function () { let apiName = "someApi"; it("1. Clone page & check tooltip for long name", function () { - //cy.NavigateToAPI_Panel(); _.apiPage.CreateApi(apiName); _.entityExplorer.SelectEntityByName("Page1", "Pages"); _.entityExplorer.ClonePage("Page1"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pin_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pin_spec.js deleted file mode 100644 index 3269a17033..0000000000 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Pin_spec.js +++ /dev/null @@ -1,147 +0,0 @@ -import { - agHelper, - entityExplorer, - locators, - draggableWidgets, - installer, - homePage, -} from "../../../../support/Objects/ObjectsCore"; - -const ExplorerMenu = { - ADD_PAGE: "ADD_PAGE", - ENTITY: "ENTITY", - ADD_LIBRARY: "ADD_LIBRARY", - ADD_QUERY_JS: "ADD_QUERY_JS", -}; - -const OpenExplorerMenu = (menu) => { - switch (menu) { - case ExplorerMenu.ADD_PAGE: - agHelper.GetNClick(locators._newPage); - cy.get(locators._canvas).trigger("mousemove", 500, 400, { - force: true, - }); - break; - case ExplorerMenu.ENTITY: - cy.xpath(entityExplorer._contextMenu("Page1")) - .last() - .click({ force: true }); - cy.get(locators._canvas).trigger("mousemove", 500, 400, { - force: true, - }); - break; - case ExplorerMenu.ADD_QUERY_JS: - cy.get(locators._createNew).last().click({ force: true }); - cy.get(locators._canvas).trigger("mousemove", 500, 300, { - force: true, - }); - break; - case ExplorerMenu.ADD_LIBRARY: - installer.OpenInstaller(true); - cy.get(locators._canvas).trigger("mousemove", 500, 100, { - force: true, - }); - break; - default: - } -}; - -describe("Entity explorer tests related to pinning and unpinning", function () { - before(() => { - agHelper.AddDsl("displayWidgetDsl"); - }); - - it("1. checks entity explorer visibility on unpin", function () { - cy.wait(5000); - cy.get(".t--entity-explorer").should("be.visible"); - cy.get(".t--pin-entity-explorer").click(); - cy.wait(5000); - cy.get("[data-testid=widgets-editor]").click({ force: true }); - cy.wait(3000); - cy.get(".t--entity-explorer").should("not.be.visible"); - //checks entity explorer visibility on pin - cy.get(".t--pin-entity-explorer").click(); - cy.get(".t--entity-explorer").should("be.visible"); - }); - - it("2. Widgets visibility in widget pane", function () { - entityExplorer.NavigateToSwitcher("Widgets"); - agHelper.ScrollTo(locators._widgetPane, "bottom"); - agHelper.AssertElementVisibility( - locators._widgetPageIcon(draggableWidgets.VIDEO), - ); - entityExplorer.PinUnpinEntityExplorer(true); - agHelper.AssertElementVisibility( - locators._widgetPageIcon(draggableWidgets.VIDEO), - ); - entityExplorer.PinUnpinEntityExplorer(false); - entityExplorer.NavigateToSwitcher("Explorer"); - }); - - it( - "excludeForAirgap", - "3. Unpinned explorer is to be open when any context menu is open or when an entity name is being edited", - function () { - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - entityExplorer.PinUnpinEntityExplorer(true); - const menu = Object.keys(ExplorerMenu); - - Cypress._.times(menu.length, (index) => { - OpenExplorerMenu(menu[index]); - agHelper.Sleep(); - cy.get("[data-testid=sidebar-active]").should("exist"); - }); - - // when an entity is being edited - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "Page1", - action: "Edit name", - }); - cy.get(locators._canvas).trigger("mousemove", 500, 400); - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - entityExplorer.PinUnpinEntityExplorer(false); - }, - ); - - it( - "airgap", - "4. Unpinned explorer is to be open when any context menu is open or when an entity name is being edited", - function () { - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - entityExplorer.PinUnpinEntityExplorer(true); - // We cannot add libraries on airgap - const menu = Object.keys(ExplorerMenu).filter( - (menu) => menu !== ExplorerMenu.ADD_LIBRARY, - ); - - Cypress._.times(menu.length, (index) => { - OpenExplorerMenu(menu[index]); - agHelper.Sleep(); - cy.get("[data-testid=sidebar-active]").should("exist"); - }); - - // when an entity is being edited - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "Page1", - action: "Edit name", - }); - cy.get(locators._canvas).trigger("mousemove", 500, 400); - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - entityExplorer.PinUnpinEntityExplorer(false); - }, - ); - - it("5. Explorer should be visible by default on a new application", function () { - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - entityExplorer.PinUnpinEntityExplorer(true); - agHelper.GetElement(locators._canvas).trigger("mousemove", 500, 100, { - force: true, - }); - agHelper - .GetElement(entityExplorer._entityExplorer) - .should("not.be.visible"); - homePage.NavigateToHome(); - homePage.CreateNewApplication(); - agHelper.AssertElementVisibility(entityExplorer._entityExplorer); - }); -}); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Query_Datasource_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Query_Datasource_spec.js index 75eb1ea5e6..feae6abf2a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Query_Datasource_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Query_Datasource_spec.js @@ -1,6 +1,8 @@ /// -const datasource = require("../../../../locators/DatasourcesEditor.json"); +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const apiwidget = require("../../../../locators/apiWidgetslocator.json"); const commonlocators = require("../../../../locators/commonlocators.json"); @@ -52,26 +54,14 @@ describe("Entity explorer tests related to query and datasource", function () { cy.testSaveDatasource(); cy.NavigateToActiveDSQueryPane(datasourceName); - /* eslint-disable */ - cy.wait(2000); - cy.NavigateToQueryEditor(); - cy.CheckAndUnfoldEntityItem("Datasources"); - cy.contains(".t--entity-name", datasourceName).click(); - - cy.get(".t--edit-datasource-name").click(); - cy.get(".t--edit-datasource-name input") - .clear() - .type(`${datasourceName}new`, { force: true }) - .blur(); - - cy.contains(commonlocators.entityName, `${datasourceName}new`); + dataSources.navigateToDatasource(datasourceName); + agHelper.RenameWithInPane(`${datasourceName}new`, false); + cy.contains(dataSources._datasourceCard, `${datasourceName}new`); // reverting the name - cy.get(".t--edit-datasource-name").click(); - cy.get(".t--edit-datasource-name input") - .clear() - .type(`${datasourceName}`, { force: true }) - .blur(); + agHelper.RenameWithInPane(datasourceName, false); + + EditorNavigation.ViaSidebar(SidebarButton.Pages); // going to the query create page cy.CheckAndUnfoldEntityItem("Queries/JS"); @@ -122,6 +112,6 @@ describe("Entity explorer tests related to query and datasource", function () { action: "Delete", entityType: entityItems.Query, }); - dataSources.DeleteDatasouceFromActiveTab(datasourceName); + dataSources.DeleteDatasourceFromWithinDS(datasourceName); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Scrolling_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Scrolling_Spec.ts index f2daf9a419..e39b464d9e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Scrolling_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/ExplorerTests/Scrolling_Spec.ts @@ -3,7 +3,6 @@ import { dataSources, draggableWidgets, entityExplorer, - entityItems, locators, } from "../../../../support/Objects/ObjectsCore"; let mockDBNameUsers: string, mockDBNameMovies: string; @@ -13,6 +12,12 @@ describe("Entity explorer context menu should hide on scrolling", function () { "excludeForAirgap", "1. Bug #15474 - Entity explorer menu must close on scroll", function () { + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); agHelper.GetNClick(locators._closeModal, 0, true, 0); entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); @@ -22,27 +27,31 @@ describe("Entity explorer context menu should hide on scrolling", function () { entityExplorer.ExpandCollapseEntity("Modal1"); entityExplorer.ExpandCollapseEntity("Modal2"); entityExplorer.ExpandCollapseEntity("Modal3"); + entityExplorer.ExpandCollapseEntity("Modal4"); + entityExplorer.ExpandCollapseEntity("Modal5"); + entityExplorer.ExpandCollapseEntity("Modal6"); // Setup to make the explorer scrollable entityExplorer.ExpandCollapseEntity("Queries/JS"); - entityExplorer.ExpandCollapseEntity("Datasources"); - agHelper.ContainsNClick("Libraries"); dataSources.CreateMockDB("Users").then(($createdMockUsers) => { cy.log("Users DB created is " + $createdMockUsers); mockDBNameUsers = $createdMockUsers; dataSources.CreateQueryAfterDSSaved(); - dataSources.AssertTableInVirtuosoList(mockDBNameUsers, "public.users"); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); dataSources.CreateMockDB("Movies").then(($createdMockMovies) => { cy.log("Movies DB created is " + $createdMockMovies); mockDBNameMovies = $createdMockMovies; dataSources.CreateQueryAfterDSSaved(); - - dataSources.AssertTableInVirtuosoList(mockDBNameMovies, "movies"); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); agHelper.GetNClick(locators._createNew); agHelper.AssertElementVisibility(entityExplorer._adsPopup); - agHelper.ScrollTo(entityExplorer._entityExplorerWrapper, "bottom"); + agHelper.ScrollTo(entityExplorer._entityExplorerWrapper, "top"); agHelper.AssertElementAbsence(entityExplorer._adsPopup); }); }); @@ -53,6 +62,12 @@ describe("Entity explorer context menu should hide on scrolling", function () { "airgap", "1. Bug #15474 - Entity explorer menu must close on scroll - airgap", function () { + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); + agHelper.GetNClick(locators._closeModal, 0, true, 0); entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); agHelper.GetNClick(locators._closeModal, 0, true, 0); entityExplorer.DragDropWidgetNVerify(draggableWidgets.MODAL); @@ -62,51 +77,34 @@ describe("Entity explorer context menu should hide on scrolling", function () { entityExplorer.ExpandCollapseEntity("Modal1"); entityExplorer.ExpandCollapseEntity("Modal2"); entityExplorer.ExpandCollapseEntity("Modal3"); + entityExplorer.ExpandCollapseEntity("Modal4"); + entityExplorer.ExpandCollapseEntity("Modal5"); + entityExplorer.ExpandCollapseEntity("Modal6"); // Setup to make the explorer scrollable entityExplorer.ExpandCollapseEntity("Queries/JS"); - entityExplorer.ExpandCollapseEntity("Datasources"); - agHelper.ContainsNClick("Libraries"); dataSources.CreateDataSource("Postgres"); cy.get("@dsName").then(($createdMockUsers: any) => { mockDBNameUsers = $createdMockUsers; dataSources.CreateQueryAfterDSSaved(); - - dataSources.AssertTableInVirtuosoList(mockDBNameUsers, "public.users"); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); + entityExplorer.CreateNewDsQuery(mockDBNameUsers); dataSources.CreateDataSource("Mongo"); cy.get("@dsName").then(($createdMockMovies: any) => { mockDBNameMovies = $createdMockMovies; dataSources.CreateQueryAfterDSSaved(); - - dataSources.AssertTableInVirtuosoList( - mockDBNameMovies, - "listingAndReviews", - ); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); + entityExplorer.CreateNewDsQuery(mockDBNameMovies); agHelper.GetNClick(locators._createNew); agHelper.AssertElementVisibility(entityExplorer._adsPopup); - agHelper.ScrollTo(entityExplorer._entityExplorerWrapper, "bottom"); + agHelper.ScrollTo(entityExplorer._entityExplorerWrapper, "top"); agHelper.AssertElementAbsence(entityExplorer._adsPopup); }); }); }, ); - - after(() => { - //clean up - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "Query1", - action: "Delete", - entityType: entityItems.Query, - }); - - entityExplorer.ActionContextMenuByEntityName({ - entityNameinLeftSidebar: "Query2", - action: "Delete", - entityType: entityItems.Query, - }); - dataSources.DeleteDatasouceFromActiveTab(mockDBNameMovies); //Since sometimes after Queries are deleted, ds is no more visible in EE tree - dataSources.DeleteDatasouceFromActiveTab(mockDBNameUsers); - }); }); 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 dd6490b6f0..2451d218f0 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 @@ -121,8 +121,6 @@ describe("Git sync apps", function () { cy.get(`.t--entity-item:contains(${newPage})`).click(); cy.wait(1000); // create a get api call - cy.NavigateToAPI_Panel(); - cy.wait(2000); cy.CreateAPI("get_data"); // creating get request using echo cy.get(apiwidget.resourceUrl) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/SwitchBranches_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/SwitchBranches_spec.js index f254b4be72..c82e2f3a82 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/SwitchBranches_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/SwitchBranches_spec.js @@ -120,12 +120,9 @@ describe("Git sync:", function () { "ParentPageRenamed", true, ); - entityExplorer.SelectEntityByName("ParentApi1", "Queries/JS"); - entityExplorer.RenameEntityFromExplorer( - "ParentApi1", - "ParentApiRenamed", - true, - ); + agHelper.RemoveUIElement("Tooltip", "Add a new query/JS Object"); + entityExplorer.ExpandCollapseEntity("Queries/JS"); + entityExplorer.RenameEntityFromExplorer("ParentApi1", "ParentApiRenamed"); cy.switchGitBranch(parentBranchKey); 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 b78c0ec540..a5f2572f37 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 @@ -7,6 +7,9 @@ import { gitSync, installer, } from "../../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; const mainBranch = "master"; const tempBranch = "feat/tempBranch"; @@ -28,25 +31,24 @@ describe("excludeForAirgap", "Tests JS Library with Git", () => { }); it("1. Install JS Library and commit changes, create branch and verify JS library changes are present on new branch ", () => { - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.OpenInstaller(); installer.InstallLibrary("uuidjs", "UUID"); gitSync.CommitAndPush(); // create new branch gitSync.CreateGitBranch(tempBranch, true); // verify js library changes are present - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.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"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.uninstallLibrary("uuidjs"); - installer.assertUnInstall("uuidjs"); // discard js library uninstallation gitSync.DiscardChanges(); // verify js library is present - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.AssertLibraryinExplorer("uuidjs"); // verify no uncommitted changes are there agHelper.AssertElementExist(gitSync._bottomBarPull); @@ -66,7 +68,7 @@ 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"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.OpenInstaller(); installer.InstallLibrary("jspdf", "jspdf"); //cy.commitAndPush(); @@ -84,7 +86,7 @@ describe("excludeForAirgap", "Tests JS Library with Git", () => { // verify custom js library is present in master branch cy.switchGitBranch(mainBranch); agHelper.AssertElementExist(gitSync._bottomBarPull); - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.AssertLibraryinExplorer("jspdf"); }); after(() => { 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 ce72d9ea7e..0180575047 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 @@ -7,6 +7,9 @@ import { homePage, locators, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const apiwidget = require("../../../../locators/apiWidgetslocator.json"); const queryLocators = require("../../../../locators/QueryEditor.json"); @@ -183,16 +186,16 @@ describe("MaintainContext&Focus", function () { }); it("4. Datasource edit mode has to be maintained", () => { - entityExplorer.SelectEntityByName("Appsmith", "Datasources"); + dataSources.navigateToDatasource("Appsmith"); dataSources.EditDatasource(); - agHelper.GoBack(); - entityExplorer.SelectEntityByName("Github", "Datasources"); + dataSources.navigateToDatasource("Github"); dataSources.AssertDSEditViewMode("View"); - entityExplorer.SelectEntityByName("Appsmith", "Datasources"); + dataSources.navigateToDatasource("Appsmith"); dataSources.AssertDSEditViewMode("Edit"); }); it("5. Maintain focus of form control inputs", () => { + EditorNavigation.ViaSidebar(SidebarButton.Pages); entityExplorer.SelectEntityByName("SQL_Query"); dataSources.ToggleUsePreparedStatement(false); entityExplorer.SelectEntityByName("S3_Query"); 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 b86b9bf19e..01bad28547 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/JSLibrary/Library_spec.ts @@ -1,9 +1,12 @@ import HomePage from "../../../../locators/HomePage"; import * as _ from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("excludeForAirgap", "Tests JS Libraries", () => { it("1. Validates Library install/uninstall", () => { - _.entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); _.installer.OpenInstaller(); _.installer.InstallLibrary("uuidjs", "UUID"); _.installer.uninstallLibrary("uuidjs"); @@ -11,16 +14,17 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { }); it("2. Installs the library against a unique namespace when there is a collision with the existing entity", () => { + EditorNavigation.ViaSidebar(SidebarButton.Pages); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TABLE, 200, 200); _.entityExplorer.NavigateToSwitcher("Explorer"); _.entityExplorer.RenameEntityFromExplorer("Table1", "jsonwebtoken"); - _.entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); _.installer.OpenInstaller(); _.installer.InstallLibrary("jsonwebtoken", "jsonwebtoken_1", true); }); it("3. Checks jspdf library", () => { - _.entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); _.installer.OpenInstaller(); _.installer.InstallLibrary("jspdf", "jspdf"); _.jsEditor.CreateJSObject( @@ -46,7 +50,7 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { }); it("4. ESM build should pass installation, uninstallation and reinstallation", () => { - _.entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); _.installer.OpenInstaller(); _.installer.InstallLibraryViaURL( "https://cdn.jsdelivr.net/npm/fast-xml-parser@4.2.7/+esm", @@ -67,7 +71,7 @@ describe("excludeForAirgap", "Tests JS Libraries", () => { it("5. Shows list of recommended libraries", () => { const recommendedLibraryNames = ["jsonwebtoken", "jspdf", "bcryptjs"]; - _.entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); _.installer.OpenInstaller(); for (const recommendedLib of recommendedLibraryNames) { cy.contains(recommendedLib); 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 ed608f9636..556c4af893 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Linting/BasicLint_spec.ts @@ -11,6 +11,9 @@ import { entityItems, dataManager, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const successMessage = "Successful Trigger"; const errorMessage = "Unsuccessful Trigger"; @@ -60,6 +63,7 @@ describe("Linting", () => { cy.get("@dsName").then(($dsName) => { dsName = $dsName as unknown as string; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("1. TC 1927 - Shows correct lint error when Api is deleted or created", () => { @@ -342,22 +346,22 @@ describe("Linting", () => { }); agHelper.AssertElementExist(locators._lintErrorElement); - entityExplorer.ExpandCollapseEntity("Libraries"); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); // install the library installer.OpenInstaller(); installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); + entityExplorer.SelectEntityByName("JSObject3"); agHelper.AssertElementAbsence(locators._lintErrorElement); - + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.uninstallLibrary("uuidjs"); - + entityExplorer.SelectEntityByName("JSObject3"); agHelper.AssertElementExist(locators._lintErrorElement); - agHelper.Sleep(2000); + EditorNavigation.ViaSidebar(SidebarButton.Libraries); installer.OpenInstaller(); installer.InstallLibrary("uuidjs", "UUID"); installer.CloseInstaller(); - homePage.NavigateToHome(); homePage.CreateNewApplication(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_2_spec.js index c477e015f2..2248c0369e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_2_spec.js @@ -25,7 +25,7 @@ describe("Validating Mobile Views for Auto Fill Widgets", function () { cy.dragAndDropToCanvas("codescannerwidget", { x: 100, y: 200 }); cy.dragAndDropToCanvas("listwidgetv2", { x: 620, y: 820 }); cy.dragAndDropToCanvas("tablewidgetv2", { x: 620, y: 820 }); - cy.dragAndDropToCanvas("tabswidget", { x: 770, y: 770 }); + cy.dragAndDropToCanvas("tabswidget", { x: 670, y: 770 }); cy.wait(2000); _.deployMode.DeployApp(); cy.wait(2000); diff --git a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_spec.js b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_spec.js index 1c073c1045..fd6b44bbfd 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/AutoFillWidgets_Basic_spec.js @@ -42,9 +42,9 @@ describe("Validating Mobile Views for Auto Fill Widgets", function () { cy.dragAndDropToCanvas("currencyinputwidget", { x: 110, y: 210 }); cy.dragAndDropToCanvas("audiowidget", { x: 250, y: 300 }); cy.dragAndDropToCanvas("selectwidget", { x: 560, y: 560 }); - cy.dragAndDropToCanvas("checkboxwidget", { x: 770, y: 770 }); - cy.dragAndDropToCanvas("radiogroupwidget", { x: 770, y: 770 }); - cy.dragAndDropToCanvas("datepickerwidget2", { x: 770, y: 970 }); + cy.dragAndDropToCanvas("checkboxwidget", { x: 670, y: 770 }); + cy.dragAndDropToCanvas("radiogroupwidget", { x: 670, y: 770 }); + cy.dragAndDropToCanvas("datepickerwidget2", { x: 670, y: 970 }); cy.dragAndDropToCanvas("phoneinputwidget", { x: 660, y: 810 }); cy.dragAndDropToCanvas("categorysliderwidget", { x: 620, y: 810 }); cy.wait(5000); diff --git a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/Snipping_mode_Basic_test.js b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/Snipping_mode_Basic_test.js index 38f34eaced..ccaa3a517d 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/Snipping_mode_Basic_test.js +++ b/app/client/cypress/e2e/Regression/ClientSide/MobileResponsiveTests/Snipping_mode_Basic_test.js @@ -1,5 +1,8 @@ import * as _ from "../../../../support/Objects/ObjectsCore"; import { Widgets } from "../../../../support/Pages/DataSources"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; let datasourceName; @@ -9,12 +12,10 @@ describe("Add widget - Postgress DataSource", function () { cy.get("@dsName").then(($dsName) => { datasourceName = $dsName; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("1. Validate Snipping with query and table widget on canvas", () => { - cy.get(".t--close-editor span:contains('Back')").click({ force: true }); - cy.get(".t--back-button span:contains('Back')").click({ force: true }); - _.autoLayout.ConvertToAutoLayoutAndVerify(false); cy.NavigateToActiveDSQueryPane(datasourceName); _.dataSources.EnterQuery("select * from public.configs"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Onboarding/FirstTimeUserOnboarding_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Onboarding/FirstTimeUserOnboarding_spec.js index 8e290495ac..5ca32ff7d7 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Onboarding/FirstTimeUserOnboarding_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Onboarding/FirstTimeUserOnboarding_spec.js @@ -1,3 +1,7 @@ +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; + const OnboardingLocator = require("../../../../locators/FirstTimeUserOnboarding.json"); import { agHelper, @@ -23,6 +27,7 @@ describe("FirstTimeUserOnboarding", function () { cy.get(OnboardingLocator.introModal).should("be.visible"); cy.get(OnboardingLocator.checklistDatasourceBtn).click(); cy.get(OnboardingLocator.introModal).should("not.exist"); + EditorNavigation.ViaSidebar(SidebarButton.Pages); cy.get(".t--entity-name:contains(Page1)") .trigger("mouseover") .click({ force: true }); @@ -36,7 +41,7 @@ describe("FirstTimeUserOnboarding", function () { agHelper.GetNAssertContains(OnboardingLocator.checklistStatus, "0 of 5"); agHelper.AssertElementExist(OnboardingLocator.checklistDatasourceBtn); agHelper.GetNClick(OnboardingLocator.checklistDatasourceBtn); - agHelper.AssertElementVisibility(OnboardingLocator.datasourcePage); + agHelper.AssertElementVisibility(dataSources._newDatasourceContainer); agHelper.GetNClick(OnboardingLocator.datasourceMock); @@ -48,6 +53,7 @@ describe("FirstTimeUserOnboarding", function () { .realHover() .should("have.css", "cursor", "auto"); agHelper.GetNClick(OnboardingLocator.checklistActionBtn); + dataSources.navigateToDatasource("Movies"); agHelper.GetNClick(OnboardingLocator.createQuery); agHelper.Sleep(); @@ -117,7 +123,7 @@ describe("FirstTimeUserOnboarding", function () { .should("have.css", "cursor", "pointer"); cy.get(OnboardingLocator.checklistDatasourceBtn).click(); - cy.get(OnboardingLocator.datasourcePage).should("be.visible"); + cy.get(dataSources._newDatasourceContainer).should("be.visible"); cy.get(datasource.MongoDB).click(); dataSources.FillMongoDSForm(); cy.generateUUID().then((uid) => { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Onboarding/GuidedTour_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Onboarding/GuidedTour_spec.js index c5951f4c58..640c9d31d6 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Onboarding/GuidedTour_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Onboarding/GuidedTour_spec.js @@ -39,7 +39,7 @@ describe("excludeForAirgap", "Guided Tour", function () { dataSources.RunQuery(); cy.get(guidedTourLocators.successButton).click(); // Step 2: Select table widget - entityExplorer.SelectEntityByName("CustomersTable"); + cy.get(".t--entity-name").contains("CustomersTable").click({ force: true }); // Step 3: Add binding to the tableData property propPane.UpdatePropertyFieldValue( @@ -106,7 +106,7 @@ describe("excludeForAirgap", "Guided Tour", function () { cy.get(guidedTourLocators.successButton).click(); // Step 6: Drag and drop a widget cy.dragAndDropToCanvas("buttonwidget", { - x: 800, + x: 845, y: 750, }); cy.get(guidedTourLocators.successButton).click(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts index cc7c0eb1c9..208f07e299 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts @@ -9,6 +9,9 @@ import { import { expandLoadMoreOptions, OneClickBinding } from "./spec_utility"; import oneClickBindingLocator from "../../../../locators/OneClickBindingLocator"; import onboardingLocator from "../../../../locators/FirstTimeUserOnboarding.json"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; const oneClickBinding = new OneClickBinding(); @@ -67,9 +70,9 @@ describe("excludeForAirgap", "One click binding control", () => { oneClickBindingLocator.otherActionSelector("Connect new datasource"), ); - agHelper.AssertElementExist(onboardingLocator.datasourcePage); + agHelper.AssertElementExist(dataSources._newDatasourceContainer); - agHelper.GetNClick(onboardingLocator.datasourceBackBtn); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector); @@ -121,7 +124,7 @@ describe("excludeForAirgap", "One click binding control", () => { dataSources.SaveDatasource(); - entityExplorer.NavigateToSwitcher("Widgets"); + EditorNavigation.ViaSidebar(SidebarButton.Pages); agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector); @@ -149,15 +152,13 @@ describe("excludeForAirgap", "One click binding control", () => { dataSources.SaveDatasource(); - entityExplorer.NavigateToSwitcher("Widgets"); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); propPane.MoveToTab("Style"); propPane.MoveToTab("Content"); - entityExplorer.NavigateToSwitcher("Explorer"); - [1, 2, 3, 4, 5].forEach(() => { apiPage.CreateAndFillApi("http://www.example.com"); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/SelectWidget/mongoDB_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/SelectWidget/mongoDB_spec.ts index 966380d01d..5407cf0869 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/SelectWidget/mongoDB_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/SelectWidget/mongoDB_spec.ts @@ -11,6 +11,9 @@ import { import formWidgetsPage from "../../../../../locators/FormWidgets.json"; import widgetsPage from "../../../../../locators/Widgets.json"; import commonlocators from "../../../../../locators/commonlocators.json"; +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; const oneClickBinding = new OneClickBinding(); @@ -23,6 +26,7 @@ describe("Table widget one click binding feature", () => { dataSources.CreateDataSource("Mongo"); cy.get("@dsName").then((dsName) => { + EditorNavigation.ViaSidebar(SidebarButton.Pages); entityExplorer.NavigateToSwitcher("Widgets"); entityExplorer.SelectEntityByName("Select1", "Widgets"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/TableWidget/mongoDB_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/TableWidget/mongoDB_spec.ts index 4dc20458c0..16f5c15039 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/TableWidget/mongoDB_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/TableWidget/mongoDB_spec.ts @@ -8,6 +8,9 @@ import { assertHelper, } from "../../../../../support/Objects/ObjectsCore"; import { OneClickBinding } from "../spec_utility"; +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; const oneClickBinding = new OneClickBinding(); @@ -23,6 +26,7 @@ describe("one click binding mongodb datasource", function () { dataSources.CreateDataSource("Mongo"); cy.get("@dsName").then((dsName) => { + EditorNavigation.ViaSidebar(SidebarButton.Pages); entityExplorer.SelectEntityByName("Table1", "Widgets"); oneClickBinding.ChooseAndAssertForm(`${dsName}`, dsName, "netflix", { diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts index 5fbd3852f0..b26b0f5746 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts @@ -66,15 +66,7 @@ describe("Entity bottom bar", () => { _.debuggerHelper.AssertClosed(); //Verify if bottom bar opens on clicking debugger icon in datasource page. _.debuggerHelper.ClickDebuggerIcon(); - _.debuggerHelper.AssertOpen(PageType.DataSources); - //Verify if selected tab is errors in tab title. - _.debuggerHelper.AssertSelectedTab("Errors"); - //Verify if bottom bar is closed on clicking close icon in datasource page. - _.debuggerHelper.CloseBottomBar(); _.debuggerHelper.AssertClosed(); - //Verify if bottom bar opens on clicking debugger icon in datasource page. - _.debuggerHelper.ClickDebuggerIcon(); - _.debuggerHelper.AssertOpen(PageType.DataSources); }); it("excludeForAirgap", "5. Query bottom bar should be collapsable", () => { @@ -107,7 +99,7 @@ describe("Entity bottom bar", () => { _.debuggerHelper.AssertSelectedTab("Response"); // clean up _.dataSources.DeleteQuery("Query1"); - _.dataSources.DeleteDatasouceFromActiveTab(dbName); + _.dataSources.DeleteDatasourceFromWithinDS(dbName); }); }); @@ -138,7 +130,7 @@ describe("Entity bottom bar", () => { // clean up _.dataSources.DeleteQuery("Query1"); cy.get("@dsName").then(($dsName) => { - _.dataSources.DeleteDatasouceFromActiveTab($dsName as any); + _.dataSources.DeleteDatasourceFromWithinDS($dsName as any); }); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/GlobalSearch_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/GlobalSearch_spec.js index a175f5506a..29cc1c0125 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/GlobalSearch_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/GlobalSearch_spec.js @@ -1,4 +1,8 @@ /* eslint-disable cypress/no-unnecessary-waiting */ +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; + const commonlocators = require("../../../../locators/commonlocators.json"); const dsl = require("../../../../fixtures/MultipleWidgetDsl.json"); const globalSearchLocators = require("../../../../locators/GlobalSearch.json"); @@ -49,7 +53,6 @@ describe("GlobalSearch", function () { }); it("3. navigatesToApi", () => { - cy.NavigateToAPI_Panel(); cy.CreateAPI("SomeApi"); cy.get(commonlocators.globalSearchTrigger).click({ force: true }); @@ -136,6 +139,7 @@ describe("GlobalSearch", function () { }); it("7. Api actions should have API as prefix", () => { + EditorNavigation.ViaSidebar(SidebarButton.Pages); cy.get(globalSearchLocators.createNew).click({ force: true }); cy.get(globalSearchLocators.blankDatasource).first().click({ force: true }); cy.get(datasourceHomeLocators.createAuthApiDatasource).click(); @@ -148,6 +152,7 @@ describe("GlobalSearch", function () { cy.fillAuthenticatedAPIForm(); cy.saveDatasource(); + EditorNavigation.ViaSidebar(SidebarButton.Pages); cy.get(globalSearchLocators.createNew).click({ force: true }); cy.get(".ads-v2-menu__menu-item span:contains('omnibarApiDatasource')") .first() 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 33f286819d..d9537bcd80 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 @@ -49,8 +49,6 @@ describe("Undo/Redo functionality", function () { }); it("2. Checks undo/redo for Api pane", function () { - cy.NavigateToAPI_Panel(); - cy.log("Navigation to API Panel screen successful"); cy.CreateAPI("FirstAPI"); cy.get(`${apiwidget.resourceUrl} .CodeMirror-placeholder`).should( "have.text", diff --git a/app/client/cypress/e2e/Regression/ClientSide/Refactoring/Refactoring_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Refactoring/Refactoring_spec.ts index 95ef1bdfea..627dc4e7a3 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Refactoring/Refactoring_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Refactoring/Refactoring_spec.ts @@ -9,6 +9,9 @@ import { propPane, dataManager, } from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Validate JS Object Refactoring does not affect the comments & variables", () => { let dsName: any; @@ -41,6 +44,7 @@ describe("Validate JS Object Refactoring does not affect the comments & variable dataSources.CreateDataSource("MySql", true, false); cy.get("@dsName").then(($dsName) => { dsName = $dsName; + EditorNavigation.ViaSidebar(SidebarButton.Pages); //Selecting paintings table from MySQL DS //Initialize new JSObject with custom code diff --git a/app/client/cypress/e2e/Regression/ClientSide/SetProperty/WidgetPropertySetters2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/SetProperty/WidgetPropertySetters2_spec.ts index 111ff48607..8d7ab55ce4 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/SetProperty/WidgetPropertySetters2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/SetProperty/WidgetPropertySetters2_spec.ts @@ -96,6 +96,7 @@ describe("Widget Property Setters - Part II - Tc #2409", () => { expect(val).be.empty; }); agHelper.ClickButton("Submit"); + agHelper.Sleep(3000); //for the Api to run & new value to be set, for CI runs agHelper .GetText( locators._widgetInDeployed(draggableWidgets.INPUT_V2) + diff --git a/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/GeneralSettings_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/GeneralSettings_spec.ts index 10427aa2d2..0f7bc8f5aa 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/GeneralSettings_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/GeneralSettings_spec.ts @@ -12,7 +12,11 @@ describe("General Settings", () => { it("1. App name change updates URL", () => { _.appSettings.OpenAppSettings(); _.appSettings.GoToGeneralSettings(); - _.generalSettings.UpdateAppNameAndVerifyUrl(true, guid); + _.generalSettings.UpdateAppNameAndVerifyUrl({ + reset: true, + newAppName: guid, + restOfUrl: "/settings", + }); _.homePage.GetAppName().then((appName) => { _.deployMode.DeployApp(); _.appSettings.CheckUrl(appName as string, "Page1", undefined, false); @@ -28,7 +32,12 @@ describe("General Settings", () => { //App name allows special and accented character _.appSettings.OpenAppSettings(); _.appSettings.GoToGeneralSettings(); - _.generalSettings.UpdateAppNameAndVerifyUrl(true, guid + "!@#œ™¡", guid); + _.generalSettings.UpdateAppNameAndVerifyUrl({ + reset: true, + newAppName: guid + "!@#œ™¡", + verifyAppNameAs: guid, + restOfUrl: "/settings", + }); _.appSettings.ClosePane(); //Veirfy App name doesn't allow empty diff --git a/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/PageSettings_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/PageSettings_spec.ts index acde3460f8..8ea7e1a71a 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/PageSettings_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/SettingsPane/PageSettings_spec.ts @@ -4,7 +4,12 @@ describe("Page Settings", () => { it("1. Page name change updates URL", () => { _.appSettings.OpenAppSettings(); _.appSettings.GoToPageSettings("Page1"); - _.pageSettings.UpdatePageNameAndVerifyUrl("Page2", undefined, false); + _.pageSettings.UpdatePageNameAndVerifyUrl({ + newPageName: "Page2", + verifyPageNameAs: undefined, + reset: false, + restOfUrl: "/settings", + }); _.homePage.GetAppName().then((appName) => { _.deployMode.DeployApp(); _.appSettings.CheckUrl(appName as string, "Page2", undefined, false); @@ -16,7 +21,7 @@ describe("Page Settings", () => { it("2. Custom slug change updates URL", () => { _.appSettings.OpenAppSettings(); _.appSettings.GoToPageSettings("Page2"); - _.pageSettings.UpdateCustomSlugAndVerifyUrl("custom"); + _.pageSettings.UpdateCustomSlugAndVerifyUrl("custom", "/settings"); _.homePage.GetAppName().then((appName) => { _.deployMode.DeployApp(); _.appSettings.CheckUrl(appName as string, "Page2", "custom", false); @@ -46,7 +51,11 @@ describe("Page Settings", () => { // Page name allows accented character _.appSettings.OpenAppSettings(); _.appSettings.GoToPageSettings("Page3"); - _.pageSettings.UpdatePageNameAndVerifyUrl("Page3œßð", "Page3"); + _.pageSettings.UpdatePageNameAndVerifyUrl({ + newPageName: "Page3œßð", + verifyPageNameAs: "Page3", + restOfUrl: "/settings", + }); _.appSettings.ClosePane(); //Page name doesn't allow special character 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 8c1391574b..04a3ebd767 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 @@ -79,7 +79,7 @@ describe("excludeForAirgap", "Fork a template to an workspace", () => { .find(reconnectDatasourceLocators.ListItemIcon) .should("be.visible"); cy.get(reconnectDatasourceLocators.DatasourceList) - .find(reconnectDatasourceLocators.ListItemIcon, { + .find(reconnectDatasourceLocators.DatasourceTitle, { withinSubject: null, }) .first() diff --git a/app/client/cypress/e2e/Regression/ClientSide/VisualTests/DatasourcePageLayout_spec.js b/app/client/cypress/e2e/Regression/ClientSide/VisualTests/DatasourcePageLayout_spec.js index af4b55b064..a205f4501c 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/VisualTests/DatasourcePageLayout_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/VisualTests/DatasourcePageLayout_spec.js @@ -1,4 +1,7 @@ import * as _ from "../../../../support/Objects/ObjectsCore"; +import EditorNavigation, { + SidebarButton, +} from "../../../../support/Pages/EditorNavigation"; describe("Visual tests for datasources", () => { // for any changes in UI, update the screenshot in snapshot folder, to do so: @@ -13,10 +16,8 @@ describe("Visual tests for datasources", () => { const newWorkspaceName = interception.response.body.data.name; cy.CreateAppForWorkspace(newWorkspaceName, newWorkspaceName); }); - _.dataSources.NavigateToActiveTab(); - cy.get(".t--integrationsHomePage").matchImageSnapshot( - "emptydatasourcepage", - ); + EditorNavigation.ViaSidebar(SidebarButton.Data); + cy.get(".t--data-blank-state").matchImageSnapshot("emptydatasourcepage"); }); /* cy.NavigateToDatasourceEditor(); cy.wait(2000); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button_onClickAction_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button_onClickAction_spec.js index 82175f03a3..744651d94b 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button_onClickAction_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button_onClickAction_spec.js @@ -38,7 +38,6 @@ describe("Button Widget Functionality", function () { //creating an api and calling it from the onClickAction of the button widget. // Creating the api propPane.ClearActionField("onClick"); - cy.NavigateToAPI_Panel(); cy.CreateAPI("buttonApi"); cy.log("Creation of buttonApi Action successful"); cy.enterDatasourceAndPath( 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 ab44a2c03b..4e515ff717 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Chart/Custom3DChartSpec.ts @@ -9,12 +9,81 @@ describe("3D Custom EChart feature", function () { }); _.agHelper.RefreshPage(); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.CHART); + _.propPane.SelectPropertiesDropDown("Chart type", "Custom EChart"); + + cy.wait(1000); + cy.get(publicWidgetsPage.chartWidget).matchImageSnapshot("2DCustomECharts"); + _.propPane.UpdatePropertyFieldValue( "Custom ECharts Configuration", `{{${JSON.stringify(this.dataSet.Custom3DEChartConfig)}}}`, ); - _.deployMode.DeployApp(); + + cy.wait(1000); cy.get(publicWidgetsPage.chartWidget).matchImageSnapshot("3DCustomECharts"); + + _.entityExplorer.SelectEntityByName("Chart1", "Widgets"); + _.propPane.UpdatePropertyFieldValue( + "Custom ECharts Configuration", + `{{${JSON.stringify(this.dataSet.InvalidCustom3DEChartConfig)}}}`, + ); + + _.agHelper.AssertContains( + "Error in Chart Data/Configuration", + "exist", + _.locators._widgetInDeployed(_.draggableWidgets.CHART), + ); + + _.propPane.UpdatePropertyFieldValue( + "Custom ECharts Configuration", + `{{${JSON.stringify(this.dataSet.Custom3DEChartConfig)}}}`, + ); + + _.agHelper.AssertContains( + "Error in Chart Data/Configuration", + "not.exist", + _.locators._widgetInDeployed(_.draggableWidgets.CHART), + ); + + _.propPane.UpdatePropertyFieldValue( + "Custom ECharts Configuration", + `{{${JSON.stringify(this.dataSet.InvalidCustom3DEChartConfig)}}}`, + ); + + _.agHelper.AssertContains( + "Error in Chart Data/Configuration", + "exist", + _.locators._widgetInDeployed(_.draggableWidgets.CHART), + ); + + _.propPane.SelectPropertiesDropDown("Chart type", "Custom Fusion Charts"); + + cy.wait(1000); + cy.get(publicWidgetsPage.chartWidget).matchImageSnapshot("FusionCharts"); + + _.propPane.SelectPropertiesDropDown("Chart type", "Custom EChart"); + + _.agHelper.AssertContains( + "Error in Chart Data/Configuration", + "exist", + _.locators._widgetInDeployed(_.draggableWidgets.CHART), + ); + + _.propPane.UpdatePropertyFieldValue( + "Custom ECharts Configuration", + `{{${JSON.stringify(this.dataSet.Custom3DEChartConfig)}}}`, + ); + + _.agHelper.AssertContains( + "Error in Chart Data/Configuration", + "not.exist", + _.locators._widgetInDeployed(_.draggableWidgets.CHART), + ); + + cy.wait(1000); + cy.get(publicWidgetsPage.chartWidget).matchImageSnapshot( + "3DCustomECharts-2", + ); }); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Checkbox/CheckboxGroup_withQuery_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Checkbox/CheckboxGroup_withQuery_spec.js index 3a5b0f421d..891ab4fa42 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Checkbox/CheckboxGroup_withQuery_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Checkbox/CheckboxGroup_withQuery_spec.js @@ -1,3 +1,7 @@ +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; + const publish = require("../../../../../locators/publishWidgetspage.json"); const explorer = require("../../../../../locators/explorerlocators.json"); import * as _ from "../../../../../support/Objects/ObjectsCore"; @@ -9,6 +13,7 @@ describe("Checkbox Group Widget Functionality", function () { cy.get("@dsName").then(($dsName) => { dsName = $dsName; }); + EditorNavigation.ViaSidebar(SidebarButton.Pages); }); it("1. Check checkbox group with dynamic query", function () { diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/DropDownWidget_value_reset_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/DropDownWidget_value_reset_spec.js index 8e59099304..a74cd3f9eb 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/DropDownWidget_value_reset_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/DropDownWidget_value_reset_spec.js @@ -1,3 +1,7 @@ +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; + const commonlocators = require("../../../../../locators/commonlocators.json"); import * as _ from "../../../../../support/Objects/ObjectsCore"; @@ -15,7 +19,8 @@ describe("Dropdown Widget Check value does not reset on navigation", function () cy.wait(200); //Navigate - cy.NavigateToAPI_Panel(); + EditorNavigation.ViaSidebar(SidebarButton.Data); + EditorNavigation.ViaSidebar(SidebarButton.Pages); //Again navigate back to the widget _.entityExplorer.SelectEntityByName("Select3"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/Dropdown_onOptionChange_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/Dropdown_onOptionChange_spec.js index f06d14b406..11b4ca7386 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/Dropdown_onOptionChange_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Dropdown/Dropdown_onOptionChange_spec.js @@ -42,7 +42,6 @@ describe("Dropdown Widget", function () { it("2. Dropdown-Call-Api Validation", function () { //creating an api and calling it from the onOptionChangeAction of the Dropdown widget. // Creating the api - cy.NavigateToAPI_Panel(); cy.CreateAPI("dropdownApi"); cy.log("Creation of buttonApi Action successful"); cy.enterDatasourceAndPath( diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_RadioGroupField_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_RadioGroupField_spec.js index c4c8ca6669..2fd30f8306 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_RadioGroupField_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_RadioGroupField_spec.js @@ -1,10 +1,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const dslWithoutSchema = require("../../../../../fixtures/jsonFormDslWithoutSchema.json"); const fieldPrefix = ".t--jsonformfield"; -import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; -let agHelper = ObjectsRegistry.AggregateHelper; -let locators = ObjectsRegistry.CommonLocators; -let propPane = ObjectsRegistry.PropertyPane; +import { agHelper, propPane } from "../../../../../support/Objects/ObjectsCore"; function selectAndValidateOption(selector, option, expectedFormData) { // Select option Zero @@ -50,7 +47,7 @@ describe("JSONForm RadioGroup Field", () => { cy.closePropertyPane(); }); - it("accepts numeric options value", () => { + it("1. accepts numeric options value", () => { cy.openPropertyPane("jsonformwidget"); const schema = { binary: 1, @@ -76,7 +73,7 @@ describe("JSONForm RadioGroup Field", () => { cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Radio Group$/); clearOptionsProperty(); - cy.testJsontext("options", JSON.stringify(options)); + propPane.UpdatePropertyFieldValue("Options", JSON.stringify(options)); cy.wait(2000); @@ -99,7 +96,7 @@ describe("JSONForm RadioGroup Field", () => { }); }); - it("accepts string options value", () => { + it("2. accepts string options value", () => { cy.openPropertyPane("jsonformwidget"); const schema = { accept: "N", @@ -126,8 +123,7 @@ describe("JSONForm RadioGroup Field", () => { cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Radio Group$/); clearOptionsProperty(); - cy.testJsontext("options", JSON.stringify(options)); - + propPane.UpdatePropertyFieldValue("Options", JSON.stringify(options)); cy.wait(2000); // Validate initial form data diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_BasicServerSideData_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_BasicServerSideData_spec.js index b1c74122b0..967f8b8455 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_BasicServerSideData_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_BasicServerSideData_spec.js @@ -1,3 +1,7 @@ +import EditorNavigation, { + SidebarButton, +} from "../../../../../support/Pages/EditorNavigation"; + const publishLocators = require("../../../../../locators/publishWidgetspage.json"); const datasource = require("../../../../../locators/DatasourcesEditor.json"); const queryLocators = require("../../../../../locators/QueryEditor.json"); @@ -328,17 +332,10 @@ describe("List widget v2 - Basic server side data tests", () => { // Click on sample(mock) user database. cy.get(datasource.mockUserDatabase).click(); - _.dataSources.NavigateToActiveTab(); + EditorNavigation.ViaSidebar(SidebarButton.Data); // Choose the first data source which consists of users keyword & Click on the "New query +"" button - cy.get(`${datasource.datasourceCard}`) - .filter(":contains('Users')") - .first() - .within(() => { - cy.get(`${datasource.createQuery}`).click({ - force: true, - }); - }); + _.dataSources.CreateQueryFromActiveTab("Users"); // Click the editing field cy.get(".t--action-name-edit-field").click({ @@ -379,7 +376,7 @@ describe("List widget v2 - Basic server side data tests", () => { ); it( "airgap", - "6. no of items rendered should be equal to page size - airgap", + "7. no of items rendered should be equal to page size - airgap", () => { _.dataSources.CreateDataSource("Postgres"); cy.wait(1000); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_onItemClick_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_onItemClick_spec.js index 4cd81f10c6..4bf2176a19 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_onItemClick_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_onItemClick_spec.js @@ -82,7 +82,7 @@ describe("List widget v2 onItemClick", () => { entityExplorer.DragDropWidgetNVerify( draggableWidgets.INPUT_V2, - 250, + 150, 50, draggableWidgets.CONTAINER, ); @@ -94,7 +94,7 @@ describe("List widget v2 onItemClick", () => { entityExplorer.DragDropWidgetNVerify( draggableWidgets.SELECT, - 250, + 150, 50, draggableWidgets.CONTAINER, ); @@ -107,7 +107,7 @@ describe("List widget v2 onItemClick", () => { entityExplorer.DragDropWidgetNVerify( draggableWidgets.BUTTON, - 250, + 150, 50, draggableWidgets.CONTAINER, ); 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 d6dd991ffb..03c8b6a42b 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 @@ -27,7 +27,8 @@ describe("Modal Widget test cases", function () { //Verify that the Modal widget opens correctly when configured on a button click. agHelper.ClickButton("Submit"); - agHelper.AssertElementVisibility(locators._modal); + agHelper.WaitUntilEleAppear(locators._modal); + agHelper.AssertElementExist(locators._modal); //Verify that the Modal widget is closed and no longer visible on the screen on clicking the "X" button. agHelper.AssertElementVisibility( @@ -38,7 +39,8 @@ describe("Modal Widget test cases", function () { //Verify that clicking outside the Modal widget closes it as expected when Quick dismiss is enabled agHelper.ClickButton("Submit"); - agHelper.AssertElementVisibility(locators._modal); + agHelper.WaitUntilEleAppear(locators._modal); + agHelper.AssertElementExist(locators._modal); agHelper.ClickOutside(350, 150, false); agHelper.Sleep(); agHelper.AssertElementAbsence(locators._modal); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect3_spec.js index 4ce7f7921d..498cbc74b9 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Multiselect/MultiSelect3_spec.js @@ -29,7 +29,7 @@ describe("Dropdown Widget Functionality", function () { .find(widgetLocators.menuButton) .then(($menuButton) => { const outerWidth = Cypress.$($menuButton).outerWidth(); - expect(parseInt(outerWidth)).to.equal(147); + expect(parseInt(outerWidth)).to.equal(137); }); cy.get(formWidgetsPage.menuButtonWidget) .find(widgetLocators.menuButton) @@ -41,7 +41,7 @@ describe("Dropdown Widget Functionality", function () { cy.get(".menu-button-popover") .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(147); + expect(parseInt(width)).to.equal(137); }); // MultiSelect diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/StatboxDsl_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/StatboxDsl_spec.js index 9406c3c564..095f540dab 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/StatboxDsl_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/StatboxDsl_spec.js @@ -30,7 +30,7 @@ describe("Statbox Widget", function () { entityExplorer.SelectEntityByName("IconButton1", "Statbox1"); cy.get(".t--property-pane-section-general").then(() => { // changing the icon to arrow-up - cy.get(".bp3-button-text").first().click(); + cy.get(".bp3-button-text").first().click().wait(500); cy.get(".bp3-icon-arrow-up").click().wait(500); // opening modal from onClick action of icon button cy.createModal("Modal", "onClick"); 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 80ec9740b3..6872422d14 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 @@ -252,7 +252,7 @@ describe("Radio Widget test cases", function () { agHelper.AssertCSS( widgetLocators.radioWidgetLabelContainer, "width", - "59.765625px", + "55.859375px", ); agHelper.GetNClick(widgetLocators.selectWidgetWidthPlusBtn); agHelper.GetNClick(widgetLocators.selectWidgetWidthPlusBtn); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select2_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select2_Spec.ts index a589a0374a..1802411e87 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select2_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select2_Spec.ts @@ -44,7 +44,7 @@ describe("Select widget tests", function () { agHelper.AssertCSS( widgetLocators.selectWidgetLabelContainer, "width", - "59.765625px", + "55.859375px", ); agHelper.GetNClick(widgetLocators.selectWidgetWidthPlusBtn); agHelper.GetNClick(widgetLocators.selectWidgetWidthPlusBtn); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select3_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select3_Spec.ts index 52ad40d03c..0c300b0ad2 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select3_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Select/Select3_Spec.ts @@ -59,7 +59,7 @@ describe("Select widget tests", function () { deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.SELECT)); //Validate filtered data - agHelper.Sleep(3000); //Wait for widget to settle + agHelper.Sleep(6000); //Wait for widget to settle for CI failure agHelper.GetNClick(locators._widgetInDeployed(draggableWidgets.SELECT)); agHelper.TypeText(widgetLocators.selectWidgetFilter, "Ulf"); agHelper.Sleep(3000); //Wait for widget filter to settle for CI runs diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TreeSelect/Tree_Select_2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TreeSelect/Tree_Select_2_spec.ts index 797b3b9e2e..4cf7031850 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TreeSelect/Tree_Select_2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TreeSelect/Tree_Select_2_spec.ts @@ -160,10 +160,6 @@ describe("Tree Select widget Tests", function () { // Execute the query let postgresDatasourceName: any; dataSources.StartDataSourceRoutes(); - agHelper - .GetElement(locators._newDataSourceBtn) - .last() - .click({ force: true }); dataSources.NavigateToDSCreateNew(); dataSources.CreatePlugIn("PostgreSQL"); agHelper.GenerateUUID(); @@ -300,7 +296,7 @@ describe("Tree Select widget Tests", function () { propPane.UpdatePropertyFieldValue( "srcDoc", `
- + + + + + + +
+ + + + `; + + useEffect(() => { + window.addEventListener("message", onMessage); + return () => { + window.removeEventListener("message", onMessage); + }; + }); + + useEffect(() => { + let shouldUpdateOptions = true; + + const propsEqual = equal( + prevProps?.customEChartConfig, + props.customEChartConfig, + ); + + if (errorMsg) { + if (propsEqual) { + shouldUpdateOptions = false; + } else { + setErrorMsg(undefined); + setErrorStack(undefined); + } + } else { + if (propsEqual) { + shouldUpdateOptions = false; + } + } + + postMessageFn({ + options: chartOptions("CUSTOM_ECHART", props), + shouldUpdateOptions: shouldUpdateOptions, + shouldResize: true, + width: props.dimensions.componentWidth, + height: props.dimensions.componentHeight, + }); + }); + + return ( + <> + {errorMsg && ( + + )} + {!errorMsg && ( + <> + + {props.needsOverlay && } + + )} + + ); +} diff --git a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.test.ts b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.test.ts index 273457f63a..23c3fb3044 100644 --- a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.test.ts +++ b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.test.ts @@ -83,12 +83,12 @@ describe("EChartsConfigurationBuilder", () => { type: "scroll", show: true, }, - grid: { top: 100, bottom: 52, left: 52, show: false }, + grid: { top: 110, bottom: 52, left: 50, show: false }, title: { show: true, text: defaultProps.chartName, left: "center", - padding: [5, 50], + padding: [15, 50], textStyle: { fontFamily: "fontfamily", fontSize: 24, @@ -126,12 +126,12 @@ describe("EChartsConfigurationBuilder", () => { color: Colors.DOVE_GRAY2, overflow: "truncate", show: true, - width: 12, + width: 2, }, show: true, name: "yaxisname", nameLocation: "middle", - nameGap: 22, + nameGap: 20, nameTextStyle: { fontSize: 14, fontFamily: "fontfamily", @@ -235,7 +235,7 @@ describe("EChartsConfigurationBuilder", () => { expectedConfig.title = { text: "chart name", left: "center", - padding: [5, 50], + padding: [15, 50], show: true, textStyle: { fontFamily: "fontfamily", @@ -263,7 +263,7 @@ describe("EChartsConfigurationBuilder", () => { { text: "chart name", left: "center", - padding: [5, 50], + padding: [15, 50], show: true, textStyle: { fontFamily: "fontfamily", @@ -274,13 +274,13 @@ describe("EChartsConfigurationBuilder", () => { }, }, { - top: 80, + top: 90, left: 495, textAlign: "center", text: "series1", }, { - top: 80, + top: 90, left: 495, textAlign: "center", text: "series2", @@ -579,7 +579,7 @@ describe("EChartsConfigurationBuilder", () => { expectedConfig.series = [ { type: "pie", - top: 100, + top: 110, bottom: 30, name: "series1", encode: { diff --git a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts index cb5761dd5c..5643c7cfe3 100644 --- a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts +++ b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts @@ -11,7 +11,7 @@ import { Colors } from "constants/Colors"; import { EChartsLayoutBuilder } from "./LayoutBuilders/EChartsLayoutBuilder"; export class EChartsConfigurationBuilder { - fontFamily: string | undefined; + fontFamily: string = "Nunito Sans"; fontSize = 14; #seriesConfigurationForPieChart( @@ -115,10 +115,6 @@ export class EChartsConfigurationBuilder { return 0.3 * props.dimensions.componentHeight - 35; }; - #evaluateFontFamily(fontFamily: string | undefined) { - return fontFamily === "System Default" ? "inherit" : fontFamily; - } - #titleConfigForPiechart( props: ChartComponentProps, allSeriesData: AllChartData, @@ -159,7 +155,7 @@ export class EChartsConfigurationBuilder { const defaultTitleConfig = { text: props.chartName, show: layoutConfig.title.show, - padding: [5, 50], + padding: [15, 50], left: "center", textStyle: { fontFamily: this.fontFamily, @@ -339,7 +335,7 @@ export class EChartsConfigurationBuilder { allSeriesData: AllChartData, longestLabels: LongestLabelParams, ) { - this.fontFamily = this.#evaluateFontFamily(props.fontFamily); + this.fontFamily = props.fontFamily; const layoutBuilder = new EChartsLayoutBuilder({ allowScroll: props.allowScroll, widgetHeight: props.dimensions.componentHeight, diff --git a/app/client/src/widgets/ChartWidget/component/EChartsDatasetBuilder.ts b/app/client/src/widgets/ChartWidget/component/EChartsDatasetBuilder.ts index 6b614eb523..dfdac16cbf 100644 --- a/app/client/src/widgets/ChartWidget/component/EChartsDatasetBuilder.ts +++ b/app/client/src/widgets/ChartWidget/component/EChartsDatasetBuilder.ts @@ -37,7 +37,9 @@ export class EChartsDatasetBuilder { checkForLongestLabel(x: number | string, y: number | string) { const xString = x.toString(); - const yString = y.toString(); + + // This is needed to correctly calculate the width of the y label. + const yString = y.toLocaleString(); if (xString.length > this.maxXLabelLength) { this.maxXLabelLength = xString.length; diff --git a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.test.ts b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.test.ts index a691ccbe07..50700274dd 100644 --- a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.test.ts +++ b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.test.ts @@ -259,15 +259,15 @@ describe("layout configs", () => { height: 30, }, grid: { - top: 100, + top: 110, bottom: 113, - left: 53, + left: 51, }, yAxis: { show: true, - nameGap: 23, + nameGap: 21, axisLabel: { - width: 13, + width: 3, overflow: "truncate", }, }, diff --git a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.ts b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.ts index 57c19fb3c4..42c89953c8 100644 --- a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.ts +++ b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsLayoutBuilder.ts @@ -29,7 +29,7 @@ export class EChartsLayoutBuilder { scrollBarBottomOffset = 30; heightForLegend = 50; - heightForTitle = 50; + heightForTitle = 60; priorityOrderOfInclusion = ["legend", "title", "xAxis", "scrollBar"]; diff --git a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.test.ts b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.test.ts index f7142e1b50..81d53a6087 100644 --- a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.test.ts +++ b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.test.ts @@ -11,7 +11,7 @@ describe("EChartsYAxisLayoutBuilder", () => { longestLabel: { x: "x label", y: "123456" }, }); - expect(builder.maxWidthForLabels()).toEqual(16); + expect(builder.maxWidthForLabels()).toEqual(6); }); }); @@ -25,8 +25,8 @@ describe("EChartsYAxisLayoutBuilder", () => { }); expect(builder.minimumWidth).toEqual(150); - expect(builder.maxWidthForLabels()).toEqual(16); - expect(builder.widthForLabels()).toEqual(16); + expect(builder.maxWidthForLabels()).toEqual(6); + expect(builder.widthForLabels()).toEqual(6); }); it("if available space is lesser than label width, it returns available space", () => { @@ -38,8 +38,8 @@ describe("EChartsYAxisLayoutBuilder", () => { }); expect(builder.minimumWidth).toEqual(150); - expect(builder.maxWidthForLabels()).toEqual(16); - expect(builder.widthForLabels()).toEqual(10); + expect(builder.maxWidthForLabels()).toEqual(6); + expect(builder.widthForLabels()).toEqual(6); }); }); @@ -83,8 +83,8 @@ describe("EChartsYAxisLayoutBuilder", () => { expect(builder.minimumWidth).toEqual(150); expect(builder.showYAxisConfig()).toEqual(true); - expect(builder.labelsWidth).toEqual(10); - expect(builder.gridLeftOffset()).toEqual(50); + expect(builder.labelsWidth).toEqual(6); + expect(builder.gridLeftOffset()).toEqual(54); }); it("when y axis is not visible, offset is 5", () => { @@ -114,9 +114,9 @@ describe("EChartsYAxisLayoutBuilder", () => { const expectedOutput = { show: true, - nameGap: 20, + nameGap: 24, axisLabel: { - width: 10, + width: 6, overflow: "truncate", }, }; diff --git a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.ts b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.ts index 75dbf338ec..e7a87d637b 100644 --- a/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.ts +++ b/app/client/src/widgets/ChartWidget/component/LayoutBuilders/EChartsYAxisLayoutBuilder.ts @@ -16,13 +16,19 @@ export class EChartsYAxisLayoutBuilder { labelsWidth: number; nameGap: number; leftOffset: number; + paddingFromLabels = 10; + labelsPaddingFromXAxis = 8; + commaPadding = 10; + nameGapWidth = 30; constructor(props: YAxisLayoutBuilderParams) { this.props = props; this.labelsWidth = this.widthForLabels(); - this.nameGap = this.labelsWidth + 10; - this.leftOffset = this.nameGap + 30; + + this.nameGap = + this.labelsWidth + this.labelsPaddingFromXAxis + this.paddingFromLabels; + this.leftOffset = this.nameGap + this.nameGapWidth; } showYAxisConfig = () => { @@ -57,10 +63,12 @@ export class EChartsYAxisLayoutBuilder { maxWidthForLabels = () => { const longestLabelKey = labelKeyForChart("yAxis", this.props.chartType); + const labelWidthYAxis = getTextWidth( this.props.longestLabel[longestLabelKey], this.props.font, ); - return labelWidthYAxis + 10; + + return labelWidthYAxis; }; } diff --git a/app/client/src/widgets/ChartWidget/component/helpers.test.ts b/app/client/src/widgets/ChartWidget/component/helpers.test.ts index 1d222fe3c3..ebffaaed11 100644 --- a/app/client/src/widgets/ChartWidget/component/helpers.test.ts +++ b/app/client/src/widgets/ChartWidget/component/helpers.test.ts @@ -3,9 +3,6 @@ import { parseOnDataPointClickParams, parseOnDataPointClickForCustomEChart, parseOnDataPointClickForCustomFusionChart, - is3DChart, - EChartDisposalParams, - shouldDisposeEChartsInstance, } from "./helpers"; describe("parseOnDataPointClickParams", () => { @@ -116,187 +113,3 @@ describe("parseOnDataPointClickForCustomFusionChart", () => { expect(parsedEvent.seriesTitle).toBeUndefined(); }); }); - -describe("is3DChart", () => { - it("returns true if any of the 3D Chart config keys is present", () => { - const threeDChartKey = "globe"; - const config: Record = { - source: {}, - }; - - config[threeDChartKey] = {}; - expect(is3DChart(config)).toEqual(true); - }); - - it("returns true if any of the 3D series type is present in an array of series", () => { - const config: Record = { - source: {}, - series: [ - { - type: "line3D", - }, - ], - }; - - expect(is3DChart(config)).toEqual(true); - }); - - it("returns true if any of the 3D series type is present in single series", () => { - const config: Record = { - source: {}, - series: { - type: "line3D", - }, - }; - - expect(is3DChart(config)).toEqual(true); - }); - - it("returns false if none of the 3D fields is present", () => { - const config: Record = { - source: {}, - series: { - type: "line", - }, - }; - - expect(is3DChart(config)).toEqual(false); - }); - - it("returns false if none of the 3D fields is present and series config is null", () => { - const config: Record = { - source: {}, - series: null, - }; - - expect(is3DChart(config)).toEqual(false); - }); - - it("returns false if none of the 3D fields is present and series type is null", () => { - const config: Record = { - source: {}, - series: {}, - }; - - expect(is3DChart(config)).toEqual(false); - }); -}); - -describe("shouldDisposeEChartsInstance", () => { - const prevChartConfig = new EChartDisposalParams(); - const currentChartConfig = new EChartDisposalParams(); - - let propsEqual = false; - - describe("when previous chart type is basic chart", () => { - beforeEach(() => { - prevChartConfig.isBasicChart = true; - }); - it("returns true if current chart type is custom", () => { - currentChartConfig.isBasicChart = false; - expect( - shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }), - ).toEqual(true); - }); - - it("returns false if current chart type is basic", () => { - currentChartConfig.isBasicChart = true; - expect( - shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }), - ).toEqual(false); - }); - }); - - describe("when previous chart type is custom chart", () => { - beforeEach(() => { - prevChartConfig.isBasicChart = false; - }); - - it("returns true if current chart type is basic", () => { - currentChartConfig.isBasicChart = true; - expect( - shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }), - ).toEqual(true); - }); - - describe("when previous chart type is 2D", () => { - beforeEach(() => { - prevChartConfig.isCustom3DChart = false; - }); - - it("returns true if current chart type is 3D", () => { - currentChartConfig.isCustom3DChart = true; - const result = shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }); - expect(result).toEqual(true); - }); - - it("returns false if current chart type is 2D", () => { - currentChartConfig.isCustom3DChart = false; - const result = shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }); - expect(result).toEqual(true); - }); - }); - - describe("when previous chart type is 3D", () => { - beforeAll(() => { - prevChartConfig.isCustom3DChart = true; - }); - it("returns true if current chart type is 2D", () => { - currentChartConfig.isCustom3DChart = false; - const result = shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }); - expect(result).toEqual(true); - }); - - describe("when current chart type is 3D", () => { - beforeAll(() => { - currentChartConfig.isBasicChart = false; - currentChartConfig.isCustom3DChart = true; - }); - - it("returns true chart props (data and widget position) have changed", () => { - propsEqual = false; - const result = shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }); - expect(result).toEqual(true); - }); - - it("returns false if chart props (data and widget position) is same", () => { - propsEqual = true; - const result = shouldDisposeEChartsInstance({ - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }); - expect(result).toEqual(false); - }); - }); - }); - }); -}); diff --git a/app/client/src/widgets/ChartWidget/component/helpers.ts b/app/client/src/widgets/ChartWidget/component/helpers.ts index 90326256a2..eb3a08f81b 100644 --- a/app/client/src/widgets/ChartWidget/component/helpers.ts +++ b/app/client/src/widgets/ChartWidget/component/helpers.ts @@ -1,12 +1,9 @@ import { get } from "lodash"; import type { ChartType, ChartSelectedDataPoint } from "../constants"; -import { - THREE_D_CHART_CONFIGS, - THREE_D_CHART_SERIES_TYPES, -} from "../constants"; import { omit, cloneDeep } from "lodash"; import type { ChartComponentProps } from "."; -import equal from "fast-deep-equal/es6"; +import { EChartsDatasetBuilder } from "./EChartsDatasetBuilder"; +import { EChartsConfigurationBuilder } from "./EChartsConfigurationBuilder"; export const parseOnDataPointClickParams = (evt: any, chartType: ChartType) => { switch (chartType) { @@ -84,6 +81,7 @@ export const labelKeyForChart = ( export const getTextWidth = (text: string, font: string) => { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); + if (context) { context.font = font; const metrics = context.measureText(text); @@ -93,102 +91,48 @@ export const getTextWidth = (text: string, font: string) => { } }; -export class EChartDisposalParams { - isBasicChart = false; - isCustom3DChart = false; +export const getBasicEChartOptions = (props: ChartComponentProps) => { + const datasetBuilder = new EChartsDatasetBuilder( + props.chartType, + props.chartData, + ); + const dataset = datasetBuilder.datasetFromData(); + const echartsConfigurationBuilder = new EChartsConfigurationBuilder(); - constructor(isBasicChart = false, isCustom3DChart = false) { - this.isBasicChart = isBasicChart; - this.isCustom3DChart = isCustom3DChart; - } - - isCustomChart = () => { - return !this.isBasicChart; + const options = { + ...echartsConfigurationBuilder.prepareEChartConfig( + props, + datasetBuilder.filteredChartData, + datasetBuilder.longestDataLabels(), + ), + dataset: { + ...dataset, + }, }; + return options; +}; - isCustom2DChart = () => { - return !this.isCustom3DChart; - }; -} - -export const generateEChartInstanceDisposalParams = ( - prevProps: ChartComponentProps, - currentProps: ChartComponentProps, +export const chartOptions = ( + chartType: ChartType, + props: ChartComponentProps, ) => { - const prevChartConfig = new EChartDisposalParams( - isBasicEChart(prevProps.chartType), - is3DChart(prevProps.customEChartConfig), - ); - const currentChartConfig = new EChartDisposalParams( - isBasicEChart(currentProps.chartType), - is3DChart(currentProps.customEChartConfig), - ); - const propsEqual = equal(prevProps, currentProps); - return { - prevChart: prevChartConfig, - currentChart: currentChartConfig, - propsEqual, - }; -}; - -export const shouldDisposeEChartsInstance = (config: { - prevChart: EChartDisposalParams; - currentChart: EChartDisposalParams; - propsEqual: boolean; -}) => { - let shouldDispose = false; - const prevChart = config.prevChart; - const currentChart = config.currentChart; - - if (prevChart.isBasicChart) { - shouldDispose = currentChart.isCustomChart(); + if (isCustomEChart(chartType)) { + return props.customEChartConfig; + } else if (isBasicEChart(chartType)) { + return getBasicEChartOptions(props); } else { - // Previous chart type was custom - if (currentChart.isBasicChart) { - shouldDispose = true; - } else { - // current chart type is custom chart - if (prevChart.isCustom3DChart) { - if (currentChart.isCustom2DChart()) { - shouldDispose = true; - } else { - // check if props have changed or not - shouldDispose = !config.propsEqual; - } - } else { - // previous chart type is 2D - shouldDispose = currentChart.isCustom3DChart; - } - } - } - return shouldDispose; -}; - -export const is3DChart = (chartConfig: Record) => { - const chartConfigKeys = Object.keys(chartConfig); - - const threeDConfigPresent = chartConfigKeys.some((key) => - THREE_D_CHART_CONFIGS.includes(key), - ); - if (threeDConfigPresent) { - return true; - } - - const seriesConfig = chartConfig.series; - if (Array.isArray(seriesConfig)) { - return seriesConfig.some((series) => isSeriesConfig3D(series)); - } else { - return isSeriesConfig3D(seriesConfig); + return {}; } }; -const isSeriesConfig3D = (seriesConfig: unknown) => { - if (seriesConfig && typeof seriesConfig == "object") { - const seriesType = (seriesConfig as Record).type as string; - return THREE_D_CHART_SERIES_TYPES.includes(seriesType); - } else { - return false; - } +export const dataClickCallbackHelper = ( + params: echarts.ECElementEvent, + props: ChartComponentProps, + chartType: ChartType, +) => { + const dataPointClickParams = parseOnDataPointClickParams(params, chartType); + + props.onDataPointClick(dataPointClickParams); }; export const isBasicEChart = (type: ChartType) => { diff --git a/app/client/src/widgets/ChartWidget/component/index.test.tsx b/app/client/src/widgets/ChartWidget/component/index.test.tsx index 4dfcb2aa09..5e6fa00f88 100644 --- a/app/client/src/widgets/ChartWidget/component/index.test.tsx +++ b/app/client/src/widgets/ChartWidget/component/index.test.tsx @@ -14,6 +14,9 @@ import "@testing-library/jest-dom"; import userEvent from "@testing-library/user-event"; import { screen } from "@testing-library/react"; +import { Provider } from "react-redux"; +import configureMockStore from "redux-mock-store"; + let container: any; describe("Chart Widget", () => { @@ -72,9 +75,23 @@ describe("Chart Widget", () => { container = null; }); + const mockStore = configureMockStore(); + const store = mockStore({ + entities: { + canvasWidgets: {}, + }, + ui: { + widgetDragResize: { + selectedWidgets: [], + }, + }, + }); + it("1. renders the correct library for chart type", async () => { const { container, getByText, rerender } = render( - , + + + , ); const xAxisLabel = getByText("xaxisname"); @@ -91,7 +108,11 @@ describe("Chart Widget", () => { let props = { ...defaultProps }; props.chartType = "CUSTOM_FUSION_CHART"; - rerender(); + rerender( + + + , + ); echartsContainer = container.querySelector("#widgetIDechart-container"); expect(echartsContainer).not.toBeInTheDocument(); @@ -104,10 +125,14 @@ describe("Chart Widget", () => { props = JSON.parse(JSON.stringify(defaultProps)); props.chartType = "CUSTOM_ECHART"; - rerender(); + rerender( + + + , + ); - const bing = getByText("Bing"); - expect(bing).toBeInTheDocument(); + echartsContainer = container.querySelector("iframe"); + expect(echartsContainer).toBeInTheDocument(); }); it("2. successfully switches sequence of basic chart/custom fusion chart/basic chart/custom echart", async () => { @@ -116,7 +141,9 @@ describe("Chart Widget", () => { props.chartType = "AREA_CHART"; const { container, getByText, rerender } = render( - , + + + , ); let xAxisLabel = getByText("xaxisname"); @@ -134,7 +161,11 @@ describe("Chart Widget", () => { props = JSON.parse(JSON.stringify(defaultProps)); props.chartType = "CUSTOM_FUSION_CHART"; - rerender(); + rerender( + + + , + ); echartsContainer = container.querySelector("#widgetIDechart-container"); expect(echartsContainer).not.toBeInTheDocument(); @@ -148,7 +179,11 @@ describe("Chart Widget", () => { props = JSON.parse(JSON.stringify(defaultProps)); props.chartType = "AREA_CHART"; - rerender(); + rerender( + + + , + ); xAxisLabel = getByText("xaxisname"); expect(xAxisLabel).toBeInTheDocument(); @@ -160,10 +195,14 @@ describe("Chart Widget", () => { props = JSON.parse(JSON.stringify(defaultProps)); props.chartType = "CUSTOM_ECHART"; - rerender(); + rerender( + + + , + ); - const bing = getByText("Bing"); - expect(bing).toBeInTheDocument(); + echartsContainer = container.querySelector("iframe"); + expect(echartsContainer).toBeInTheDocument(); }); it("3. adds a click event when user adds a click callback", async () => { @@ -174,129 +213,15 @@ describe("Chart Widget", () => { mockCallback(point); }; - render(); + render( + + + , + ); expect(mockCallback.mock.calls.length).toEqual(0); const el = await screen.findByText("1000"); userEvent.click(el); expect(mockCallback.mock.calls.length).toEqual(1); }); - - it("4. check each chart type has their independent error showing up in the chart widget", () => { - let props = JSON.parse(JSON.stringify(defaultProps)); - props.chartType = "CUSTOM_ECHART"; - - const { container, getByText, queryByText, rerender } = render( - , - ); - - const bing = getByText("Bing"); - expect(bing).toBeInTheDocument(); - - // incorrect source key, thus incorrect echart configuration - props = JSON.parse(JSON.stringify(props)); - delete props.customEChartConfig.dataset.source; - props.customEChartConfig.dataset.soce = {}; - - rerender(); - - const errorTitle = getByText("Error in Chart Data/Configuration"); - expect(errorTitle).toBeInTheDocument(); - expect(queryByText("Bing")).toBeNull(); - - // change chart type to basic echart - props = JSON.parse(JSON.stringify(props)); - props.chartType = "LINE_CHART"; - - rerender(); - - expect(queryByText("Error in Chart Data/Configuration")).toBeNull(); - expect(queryByText("Bing")).toBeNull(); - expect(queryByText("xaxisname")).toBeInTheDocument(); - - // Check if updating props in basic charts is working - props = JSON.parse(JSON.stringify(props)); - props.chartType = "LINE_CHART"; - props.xAxisName = "xaxisname123"; - - rerender(); - - expect(queryByText("Error in Chart Data/Configuration")).toBeNull(); - expect(queryByText("Bing")).toBeNull(); - expect(queryByText("xaxisname")).not.toBeInTheDocument(); - expect(queryByText("xaxisname123")).toBeInTheDocument(); - - // switching back to custom echart should show the original error - props = JSON.parse(JSON.stringify(props)); - props.chartType = "CUSTOM_ECHART"; - - rerender(); - expect( - queryByText("Error in Chart Data/Configuration"), - ).toBeInTheDocument(); - - // Remove error from custom EChart, the chart should render without errors - props = JSON.parse(JSON.stringify(props)); - props.customEChartConfig.dataset.source = JSON.parse( - JSON.stringify((defaultProps.customEChartConfig.dataset as any).source), - ); - props.chartType = "CUSTOM_ECHART"; - - rerender(); - - expect( - queryByText("Error in Chart Data/Configuration"), - ).not.toBeInTheDocument(); - expect(queryByText("Bing")).toBeInTheDocument(); - - // Check if updating the props in custom ECharts is working now - props = JSON.parse(JSON.stringify(props)); - let updatedSource = JSON.parse( - JSON.stringify((props.customEChartConfig.dataset as any).source), - ); - updatedSource[2][0] = "Bing 123"; - props.customEChartConfig.dataset.source = updatedSource; - props.chartType = "CUSTOM_ECHART"; - - rerender(); - expect(queryByText("Bing 123")).toBeInTheDocument(); - - // Switch to custom fusion charts - props = JSON.parse(JSON.stringify(props)); - props.chartType = "CUSTOM_FUSION_CHART"; - - rerender(); - - expect( - queryByText("Error in Chart Data/Configuration"), - ).not.toBeInTheDocument(); - const fusionContainer = container.querySelector( - "#widgetIDcustom-fusion-chart-container", - ); - expect(fusionContainer).toBeInTheDocument(); - - // Switching back to custom echart should work. - props = JSON.parse(JSON.stringify(props)); - props.customEChartConfig.dataset.source = JSON.parse( - JSON.stringify((defaultProps.customEChartConfig.dataset as any).source), - ); - props.chartType = "CUSTOM_ECHART"; - - rerender(); - - expect(queryByText("Bing")).toBeInTheDocument(); - - // Updating props in custom echart should work. - props = JSON.parse(JSON.stringify(props)); - updatedSource = JSON.parse( - JSON.stringify((props.customEChartConfig.dataset as any).source), - ); - updatedSource[2][0] = "Bing 456"; - - props.customEChartConfig.dataset.source = updatedSource; - props.chartType = "CUSTOM_ECHART"; - - rerender(); - expect(queryByText("Bing 456")).toBeInTheDocument(); - }); }); diff --git a/app/client/src/widgets/ChartWidget/component/index.tsx b/app/client/src/widgets/ChartWidget/component/index.tsx index ed861051bb..0710b51704 100644 --- a/app/client/src/widgets/ChartWidget/component/index.tsx +++ b/app/client/src/widgets/ChartWidget/component/index.tsx @@ -1,7 +1,6 @@ import React from "react"; import styled from "styled-components"; import * as echarts from "echarts"; -import "echarts-gl"; import { invisible } from "constants/DefaultTheme"; import { getAppsmithConfigs } from "@appsmith/configs"; import type { @@ -17,18 +16,18 @@ import equal from "fast-deep-equal/es6"; import type { WidgetPositionProps } from "widgets/BaseWidget"; import { ChartErrorComponent } from "./ChartErrorComponent"; import { EChartsConfigurationBuilder } from "./EChartsConfigurationBuilder"; -import { EChartsDatasetBuilder } from "./EChartsDatasetBuilder"; -import { - generateEChartInstanceDisposalParams, - is3DChart, - isBasicEChart, - shouldDisposeEChartsInstance, -} from "./helpers"; +import { dataClickCallbackHelper, isBasicEChart } from "./helpers"; import { parseOnDataPointClickParams, isCustomEChart, isCustomFusionChart, + chartOptions, } from "./helpers"; + +import { CustomEChartIFrameComponent } from "./CustomEChartIFrameComponent"; +import type { AppState } from "@appsmith/reducers"; +import { connect } from "react-redux"; +import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors"; // Leaving this require here. Ref: https://stackoverflow.com/questions/41292559/could-not-find-a-declaration-file-for-module-module-name-path-to-module-nam/42505940#42505940 // FusionCharts comes with its own typings so there is no need to separately import them. But an import from fusioncharts/core still requires a declaration file. // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -86,7 +85,7 @@ export interface ChartComponentProps extends WidgetPositionProps { boxShadow?: string; primaryColor?: string; showDataPointLabel: boolean; - fontFamily?: string; + fontFamily: string; dimensions: { componentWidth: number; componentHeight: number; @@ -111,11 +110,12 @@ const CanvasContainer = styled.div< overflow: hidden; position: relative; ${(props) => (!props.isVisible ? invisible : "")}; - padding: 10px 0 0 0; }`; +export type ChartComponentConnectedProps = ReturnType & + ChartComponentProps; class ChartComponent extends React.Component< - ChartComponentProps, + ChartComponentConnectedProps, ChartComponentState > { fusionChartsInstance: any = null; @@ -123,16 +123,16 @@ class ChartComponent extends React.Component< customFusionChartContainerId = this.props.widgetId + "custom-fusion-chart-container"; + eChartsContainerId = this.props.widgetId + "echart-container"; eChartsHTMLContainer: HTMLElement | null = null; echartsConfigurationBuilder: EChartsConfigurationBuilder; echartConfiguration: Record = {}; - is3DChart = false; prevProps: ChartComponentProps; - constructor(props: ChartComponentProps) { + constructor(props: ChartComponentConnectedProps) { super(props); this.echartsConfigurationBuilder = new EChartsConfigurationBuilder(); this.prevProps = {} as ChartComponentProps; @@ -143,56 +143,10 @@ class ChartComponent extends React.Component< }; } - getBasicEChartOptions = () => { - const datasetBuilder = new EChartsDatasetBuilder( - this.props.chartType, - this.props.chartData, - ); - const dataset = datasetBuilder.datasetFromData(); - - const options = { - ...this.echartsConfigurationBuilder.prepareEChartConfig( - this.props, - datasetBuilder.filteredChartData, - datasetBuilder.longestDataLabels(), - ), - dataset: { - ...dataset, - }, - }; - return options; - }; - dataClickCallback = (params: echarts.ECElementEvent) => { - const dataPointClickParams = parseOnDataPointClickParams( - params, - this.state.chartType, - ); - - this.props.onDataPointClick(dataPointClickParams); + dataClickCallbackHelper(params, this.props, this.state.chartType); }; - disposeEChartsIfNeeded() { - if (this.echartsInstance?.isDisposed()) { - return; - } - - let shouldDisposeEcharts = true; - if (Object.keys(this.prevProps).length == 0) { - shouldDisposeEcharts = true; - } else { - const config = generateEChartInstanceDisposalParams( - this.prevProps, - this.props, - ); - shouldDisposeEcharts = shouldDisposeEChartsInstance(config); - } - this.prevProps = this.props; - if (shouldDisposeEcharts) { - this.echartsInstance?.dispose(); - } - } - initializeEchartsInstance = () => { this.eChartsHTMLContainer = document.getElementById( this.eChartsContainerId, @@ -201,15 +155,12 @@ class ChartComponent extends React.Component< return; } - this.disposeEChartsIfNeeded(); - this.is3DChart = is3DChart(this.props.customEChartConfig); - if (!this.echartsInstance || this.echartsInstance.isDisposed()) { this.echartsInstance = echarts.init( this.eChartsHTMLContainer, undefined, { - renderer: this.is3DChart ? "canvas" : "svg", + renderer: "svg", width: this.props.dimensions.componentWidth, height: this.props.dimensions.componentHeight, }, @@ -225,23 +176,8 @@ class ChartComponent extends React.Component< ); }; - getCustomEChartOptions = () => { - return this.props.customEChartConfig; - }; - shouldSetOptions(eChartOptions: any) { - if (equal(this.echartConfiguration, eChartOptions)) { - if (this.is3DChart) { - return ( - this.state.eChartsError == undefined || - this.state.eChartsError == null - ); - } else { - return false; - } - } else { - return true; - } + return !equal(this.echartConfiguration, eChartOptions); } renderECharts = () => { @@ -251,12 +187,10 @@ class ChartComponent extends React.Component< return; } - let eChartOptions: Record = {}; - if (isCustomEChart(this.state.chartType)) { - eChartOptions = this.getCustomEChartOptions(); - } else if (isBasicEChart(this.state.chartType)) { - eChartOptions = this.getBasicEChartOptions(); - } + const eChartOptions: Record = chartOptions( + this.state.chartType, + this.props, + ); try { if (this.shouldSetOptions(eChartOptions)) { @@ -302,6 +236,9 @@ class ChartComponent extends React.Component< if (this.state.chartType === "CUSTOM_FUSION_CHART") { this.disposeECharts(); this.renderFusionCharts(); + } else if (this.state.chartType == "CUSTOM_ECHART") { + this.disposeECharts(); + this.disposeFusionCharts(); } else { this.disposeFusionCharts(); this.renderECharts(); @@ -421,10 +358,14 @@ class ChartComponent extends React.Component< onClick={onClick} {...rest} > - {this.state.chartType !== "CUSTOM_FUSION_CHART" && ( + {isBasicEChart(this.state.chartType) && ( )} + {isCustomEChart(this.state.chartType) && ( + + )} + {this.state.chartType === "CUSTOM_FUSION_CHART" && ( )} @@ -437,4 +378,17 @@ class ChartComponent extends React.Component< } } -export default ChartComponent; +/** + * TODO: Balaji to refactor code to move out selected widget details to platform + */ +export const mapStateToProps = ( + state: AppState, + ownProps: ChartComponentProps, +) => { + return { + needsOverlay: + ownProps.widgetId !== getWidgetPropsForPropertyPane(state)?.widgetId, + }; +}; + +export default connect(mapStateToProps)(ChartComponent); diff --git a/app/client/src/widgets/ChartWidget/constants.ts b/app/client/src/widgets/ChartWidget/constants.ts index 014160ae1b..27aac52ca7 100644 --- a/app/client/src/widgets/ChartWidget/constants.ts +++ b/app/client/src/widgets/ChartWidget/constants.ts @@ -44,6 +44,33 @@ export interface ChartSelectedDataPoint { rawEventData?: Record; } +// export type IFrameChartWidgetEventTypes = "click-event" | "load-complete" | "error" + +export interface CustomEChartClickEventData { + event: echarts.ECElementEvent; // Record +} +export interface CustomEChartErrorData { + message: string; + stack: string; +} + +export interface CustomEChartIFrameMessageData { + options: Record; + shouldUpdateOptions: boolean; + shouldResize: boolean; + width: number; + height: number; +} + +export interface CustomEChartIFrameMessage { + type: "click-event" | "load-complete" | "error" | "update-options"; + data: + | CustomEChartClickEventData + | CustomEChartIFrameMessageData + | CustomEChartErrorData + | Record; +} + export const messages = { ErrorTitle: "Error in Chart Data/Configuration", MoreDetails: "More Details", @@ -64,29 +91,6 @@ export const CUSTOM_ECHART_FEATURE_FLAG = export const FUSION_CHART_DEPRECATION_FLAG = FEATURE_FLAG["deprecate_custom_fusioncharts_enabled"]; -export const THREE_D_CHART_CONFIGS = [ - "globe", - "geo3D", - "mapbox3D", - "grid3D", - "xAxis3D", - "yAxis3D", - "zAxis3D", -]; - -export const THREE_D_CHART_SERIES_TYPES = [ - "scatter3D", - "bar3D", - "line3D", - "lines3D", - "map3D", - "surface", - "polygons3D", - "scatterGL", - "graphGL", - "flowGL", -]; - export const CUSTOM_CHART_TYPES = [ "area2d", "bar2d", diff --git a/app/client/src/widgets/ChartWidget/widget/index.test.ts b/app/client/src/widgets/ChartWidget/widget/index.test.ts index 8f220a361f..d03ccd86b4 100644 --- a/app/client/src/widgets/ChartWidget/widget/index.test.ts +++ b/app/client/src/widgets/ChartWidget/widget/index.test.ts @@ -53,6 +53,10 @@ describe("emptyChartData", () => { renderMode: RenderModes.CANVAS, }; + describe("font family", () => { + expect(ChartWidget.fontFamily).toEqual("Nunito Sans"); + }); + describe("when chart type is basic ECharts", () => { const basicEChartProps = JSON.parse(JSON.stringify(defaultProps)); const basicEChartsType = "LINE_CHART"; diff --git a/app/client/src/widgets/ChartWidget/widget/index.tsx b/app/client/src/widgets/ChartWidget/widget/index.tsx index 8cb9ddcc6d..2e7dec911d 100644 --- a/app/client/src/widgets/ChartWidget/widget/index.tsx +++ b/app/client/src/widgets/ChartWidget/widget/index.tsx @@ -1,5 +1,4 @@ import React, { lazy, Suspense } from "react"; - import type { WidgetProps, WidgetState } from "widgets/BaseWidget"; import BaseWidget from "widgets/BaseWidget"; import Skeleton from "components/utils/Skeleton"; @@ -63,6 +62,7 @@ export const emptyChartData = (props: ChartWidgetProps) => { class ChartWidget extends BaseWidget { static type = "CHART_WIDGET"; + static fontFamily: string = "Nunito Sans"; static getConfig() { return { @@ -74,6 +74,12 @@ class ChartWidget extends BaseWidget { }; } + static getDependencyMap(): Record { + return { + customEChartConfig: ["chartType"], + }; + } + static getDefaults() { return { rows: 32, @@ -128,6 +134,7 @@ class ChartWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, @@ -239,7 +246,7 @@ class ChartWidget extends BaseWidget { customEChartConfig={this.props.customEChartConfig} customFusionChartConfig={this.props.customFusionChartConfig} dimensions={this.props} - fontFamily={this.props.fontFamily ?? "Nunito Sans"} + fontFamily={ChartWidget.fontFamily} hasOnDataPointClick={Boolean(this.props.onDataPointClick)} isLoading={this.props.isLoading} isVisible={this.props.isVisible} diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index e30a804679..e289d6ad2a 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -7,6 +7,7 @@ import { LABEL_ORIENTATION_COMPATIBLE_CHARTS, messages, } from "../constants"; +import type { WidgetProps } from "widgets/BaseWidget"; export const isLabelOrientationApplicableFor = (chartType: string) => LABEL_ORIENTATION_COMPATIBLE_CHARTS.includes(chartType); @@ -93,11 +94,18 @@ export const contentConfig = ( placeholderText: `Custom ECharts Configuration`, propertyName: "customEChartConfig", label: "Custom ECharts Configuration", - controlType: "INPUT_TEXT", + controlType: "WRAPPED_CODE_EDITOR", + controlConfig: { + wrapperCode: { + prefix: "{{ ((chartType) => ( ", + suffix: (widget: WidgetProps) => + `))(${widget.widgetName}.chartType); }}`, + }, + }, isBindProperty: true, isTriggerProperty: false, validation: { - type: ValidationTypes.OBJECT, + type: ValidationTypes.OBJECT_WITH_FUNCTION, params: { default: {}, }, diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index 55109f14f9..5fd4f41f9c 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -142,6 +142,7 @@ class CheckboxGroupWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index aa1235d24f..fa8d72cab5 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -107,6 +107,7 @@ class CheckboxWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/CodeScannerWidget/widget/index.tsx b/app/client/src/widgets/CodeScannerWidget/widget/index.tsx index 85a659a8ba..aa89541230 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/index.tsx +++ b/app/client/src/widgets/CodeScannerWidget/widget/index.tsx @@ -81,6 +81,7 @@ class CodeScannerWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 9411636cad..9d9f903ae4 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -173,6 +173,7 @@ export class ContainerWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index 37cbb47276..a7a5bee6e6 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -220,6 +220,7 @@ class CurrencyInputWidget extends BaseInputWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 72f1bf0689..942e9f237b 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -155,6 +155,7 @@ class DatePickerWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index ad48c7f6d7..f684631459 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -69,6 +69,7 @@ class DividerWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index 5174e0ca2c..c45f72c921 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -137,6 +137,7 @@ class DocumentViewerWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ExternalWidget/component/index.tsx b/app/client/src/widgets/ExternalWidget/component/index.tsx new file mode 100644 index 0000000000..32125a522b --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/component/index.tsx @@ -0,0 +1,123 @@ +import React, { useEffect, useRef } from "react"; +import styled from "styled-components"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-ignore +import script from "!!raw-loader!./script.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-ignore +import css from "!!raw-loader!./reset.css"; + +const StyledIframe = styled.iframe` + width: ${(props) => props.width}px; + height: ${(props) => props.height}px; +`; + +function ExternalComponent(props: any) { + const iframe = useRef(null); + + useEffect(() => { + const handler = (event: MessageEvent) => { + const iframeWindow = + iframe.current?.contentWindow || + iframe.current?.contentDocument?.defaultView; + + if (event.source === iframeWindow) { + const message = event.data; + + if (message.type === "UPDATE") { + props.update(message.data); + } else if (message.type === "EVENT") { + props.execute(message.data.eventName, message.data.contextObj); + } else if (message.type === "READY") { + iframe.current?.contentWindow?.postMessage( + { + type: "READY_ACK", + model: props.model, + dimensions: { + width: props.width, + height: props.height, + }, + }, + "*", + ); + } else if (message.type === "UPDATE_HEIGHT") { + const height = message.data.height; + + if (height) { + iframe.current!.style.height = `${height}px`; + } + } + } + }; + + window.addEventListener("message", handler, false); + + return () => window.removeEventListener("message", handler, false); + }, []); + + useEffect(() => { + if (iframe.current && iframe.current.contentWindow) { + iframe.current.contentWindow.postMessage( + { + type: "MODEL_UPDATE", + model: props.model, + }, + "*", + ); + } + }, [props.model]); + + useEffect(() => { + if (iframe.current && iframe.current.contentWindow) { + iframe.current.contentWindow.postMessage( + { + type: "UI_UPDATE", + dimensions: { + width: props.width, + height: props.height, + }, + }, + "*", + ); + } + }, [props.width, props.height]); + + const srcDoc = ` + + + + + + + + ${props.srcDoc.html} + + + + + `; + + return ( +
+ +
+ ); +} + +export interface ExternalComponentProps { + execute: (eventName: string, contextObj: Record) => void; + update: (data: Record) => void; + model: Record; + srcDoc: string; +} + +export default ExternalComponent; diff --git a/app/client/src/widgets/ExternalWidget/component/reset.css b/app/client/src/widgets/ExternalWidget/component/reset.css new file mode 100644 index 0000000000..45a05ecf85 --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/component/reset.css @@ -0,0 +1,129 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/app/client/src/widgets/ExternalWidget/component/script.js b/app/client/src/widgets/ExternalWidget/component/script.js new file mode 100644 index 0000000000..dfe47a0e58 --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/component/script.js @@ -0,0 +1,150 @@ +((global) => { + const modelSubscribers = []; + const uiSubscribers = []; + let onReady; + // const heightObserver = new ResizeObserver((entries) => { + // const height = entries[0].contentRect.height; + + // global.parent.postMessage( + // { + // type: "UPDATE_HEIGHT", + // data: { + // height, + // }, + // }, + // "*", + // ); + // }); + + global.addEventListener("message", (event) => { + if (event.data.model) { + window.appsmith.model = event.data.model; + } + if (event.data.dimensions) { + window.appsmith.ui = event.data.dimensions; + } + + switch (event.data.type) { + case "READY_ACK": + onReady && onReady(); + global.appsmith.modelProvider.subscribe( + generateAppsmithCssVariables("model"), + ); + global.appsmith.UIProvider.subscribe( + generateAppsmithCssVariables("ui"), + ); + + // heightObserver.observe(global.document.body); + break; + case "MODEL_UPDATE": + modelSubscribers.forEach((fn) => { + if (event.source === global.parent) { + fn(event.data.model); + } + }); + break; + case "UI_UPDATE": + uiSubscribers.forEach((fn) => { + if (event.source === global.parent) { + fn(event.data.dimensions); + } + }); + break; + } + }); + + global.appsmith = { + UIProvider: { + subscribe: (fn) => { + uiSubscribers.push(fn); + + fn(global.appsmith.ui); + + return () => { + const index = uiSubscribers.findIndex(fn); + + if (index > -1) { + uiSubscribers.splice(index, 1); + } + }; + }, + }, + modelProvider: { + subscribe: (fn) => { + modelSubscribers.push(fn); + + fn(global.appsmith.model); + + return () => { + const index = modelSubscribers.findIndex(fn); + + if (index > -1) { + modelSubscribers.splice(index, 1); + } + }; + }, + }, + updateModel: (obj) => { + global.parent.postMessage( + { + type: "UPDATE", + data: obj, + }, + "*", + ); + }, + triggerEvent: (eventName, contextObj) => { + global.parent.postMessage( + { + type: "EVENT", + data: { + eventName, + contextObj, + }, + }, + "*", + ); + }, + model: {}, + ui: {}, + onReady: (fn) => { + onReady = fn; + }, + // observeHeight: (element) => { + // heightObserver.disconnect(); + // heightObserver.observe(element); + // }, + }; + + window.addEventListener("load", () => { + global.parent.postMessage( + { + type: "READY", + }, + "*", + ); + }); + + const generateAppsmithCssVariables = (provider) => (source) => { + let cssTokens = document.getElementById(`appsmith-${provider}-css-tokens`); + + if (!cssTokens) { + cssTokens = document.createElement("style"); + cssTokens.id = `appsmith-${provider}-css-tokens`; + global.document.head.appendChild(cssTokens); + } + + const cssTokensContent = Object.keys(source).reduce((acc, key) => { + if (typeof source[key] === "string" || typeof source[key] === "number") { + return ` + ${acc} + --appsmith-${provider}-${key}: ${source[key]}; + `; + } else { + return acc; + } + }, ""); + + cssTokens.innerHTML = `:root {${cssTokensContent}}`; + }; +})(window); diff --git a/app/client/src/widgets/ExternalWidget/constants.ts b/app/client/src/widgets/ExternalWidget/constants.ts new file mode 100644 index 0000000000..76662d74ab --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/constants.ts @@ -0,0 +1,2 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const EXTERNAL_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/ExternalWidget/icon.svg b/app/client/src/widgets/ExternalWidget/icon.svg new file mode 100644 index 0000000000..8c9ceac2ec --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/client/src/widgets/ExternalWidget/index.ts b/app/client/src/widgets/ExternalWidget/index.ts new file mode 100644 index 0000000000..b668e51d06 --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/index.ts @@ -0,0 +1,3 @@ +import Widget from "./widget"; + +export default Widget; diff --git a/app/client/src/widgets/ExternalWidget/widget/index.tsx b/app/client/src/widgets/ExternalWidget/widget/index.tsx new file mode 100644 index 0000000000..7206341e68 --- /dev/null +++ b/app/client/src/widgets/ExternalWidget/widget/index.tsx @@ -0,0 +1,204 @@ +import React from "react"; + +import BaseWidget from "widgets/BaseWidget"; +import type { WidgetProps, WidgetState } from "widgets/BaseWidget"; + +import IconSVG from "../icon.svg"; + +import ExternalComponent from "../component"; + +import type { DerivedPropertiesMap } from "WidgetProvider/factory"; +import { WIDGET_TAGS } from "constants/WidgetConstants"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import type { OnButtonClickProps } from "components/propertyControls/ButtonControl"; +import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag"; + +class ExternalWidget extends BaseWidget { + static type = "EXTERNAL_WIDGET"; + + static getConfig() { + return { + name: "Custom Widget [alpha]", + hideCard: !super.getFeatureFlag( + FEATURE_FLAG.release_custom_widgets_enabled, + ), + iconSVG: IconSVG, + needsMeta: true, + isCanvas: false, + tags: [WIDGET_TAGS.DISPLAY], + }; + } + + static getDefaults() { + return { + widgetName: "CustomWidget", + rows: 50, + columns: 50, + version: 1, + events: [], + srcDoc: { + html: "", + js: "// no need to write window onLoad, it is handled by the widget", + css: "/* you can access your string properties of your model using `var(--appsmith-model-)`*/", + }, + }; + } + + static getPropertyPaneContentConfig() { + return [ + { + sectionName: "Data", + children: [ + { + propertyName: "defaultModel", + helpText: "The data that needs to be injected into the widget", + label: "Model", + controlType: "INPUT_TEXT", + placeholderText: "{}", + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.OBJECT, + }, + }, + // { + // propertyName: "isHosted", + // label: "is your component hosted somewhere?", + // controlType: "SWITCH", + // isBindProperty: false, + // isTriggerProperty: false, + // }, + { + propertyName: "srcDoc", + helpText: "Inline HTML to embed, overriding the src attribute", + label: "srcDoc", + controlType: "HTML_DOCUMENT_BUILDER", + placeholderText: "

Inline HTML

", + isBindProperty: true, + isTriggerProperty: false, + hidden: (widget: WidgetProps) => { + return widget.isHosted; + }, + dependencies: ["isHosted"], + evaluatedDependencies: ["defaultModel"], + }, + { + propertyName: "src", + helpText: "Url of the hosted widget", + label: "src", + controlType: "HTML_DOCUMENT_BUILDER", + placeholderText: "https://deployed.somewhere.com/mycomponent", + isBindProperty: false, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + }, + hidden: (widget: WidgetProps) => { + return !widget.isHosted; + }, + dependencies: ["isHosted"], + }, + ], + }, + { + sectionName: "Events", + hasDynamicProperties: true, + generateDynamicProperties: (widgetProps: WidgetProps) => { + return widgetProps.events.map((event: string) => ({ + propertyName: event, + label: event, + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + })); + }, + children: [ + { + propertyName: "generateFormButton", + label: "", + controlType: "BUTTON", + isJSConvertible: false, + isBindProperty: false, + buttonLabel: "Add Event", + onClick: ({ + batchUpdateProperties, + widgetProperties, + }: OnButtonClickProps) => { + const events = widgetProperties.events; + + const eventName = prompt("what is the event name"); + + batchUpdateProperties({ + events: [...events, eventName], + }); + }, + isTriggerProperty: false, + dependencies: ["events"], + }, + ], + }, + ]; + } + + static getPropertyPaneStyleConfig() { + return []; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return {}; + } + + static getDefaultPropertiesMap(): Record { + return { + model: "defaultModel", + }; + } + + static getMetaPropertiesMap(): Record { + return { + model: {}, + }; + } + + execute = (eventName: string) => { + if (this.props.hasOwnProperty(eventName)) { + const eventString = this.props[eventName]; + + super.executeAction({ + triggerPropertyName: eventName, + dynamicString: eventString, + event: { + type: EventType.ON_SUBMIT, + }, + }); + } + }; + + update = (data: Record) => { + this.props.updateWidgetMetaProperty("model", { + ...this.props.model, + ...data, + }); + }; + + getWidgetView() { + return ( + this.execute(eventName)} + height={this.props.componentHeight} + model={this.props.model} + srcDoc={this.props.srcDoc} + update={(data: any) => this.update(data)} + width={this.props.componentWidth} + /> + ); + } +} + +export interface ExternalWidgetProps extends WidgetProps { + something?: boolean; +} + +export default ExternalWidget; diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index baf7f65a53..8878045672 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -116,6 +116,7 @@ class FilePickerWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: { base: "360px" }, diff --git a/app/client/src/widgets/FormWidget/widget/index.tsx b/app/client/src/widgets/FormWidget/widget/index.tsx index f7a0af2c2c..96c1c81320 100644 --- a/app/client/src/widgets/FormWidget/widget/index.tsx +++ b/app/client/src/widgets/FormWidget/widget/index.tsx @@ -302,6 +302,7 @@ class FormWidget extends ContainerWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx index 135457ff97..d9ef454b09 100644 --- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx @@ -96,6 +96,7 @@ class IconButtonWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index 5209b650ea..bfb694bffd 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -93,6 +93,7 @@ class IframeWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index 3e54ef3a03..67aadb11a9 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -71,6 +71,7 @@ class ImageWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/InputWidgetV2/widget/index.tsx b/app/client/src/widgets/InputWidgetV2/widget/index.tsx index 37ff7fc45f..60b7b006c2 100644 --- a/app/client/src/widgets/InputWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/InputWidgetV2/widget/index.tsx @@ -364,6 +364,7 @@ class InputWidget extends BaseInputWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/JSONFormWidget/widget/index.tsx b/app/client/src/widgets/JSONFormWidget/widget/index.tsx index 9fc46a8f1d..f09956e242 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/index.tsx +++ b/app/client/src/widgets/JSONFormWidget/widget/index.tsx @@ -146,8 +146,6 @@ class JSONFormWidget extends BaseWidget< static type = "JSON_FORM_WIDGET"; - static preloadConfig = true; - static getConfig() { return { name: "JSON Form", @@ -321,6 +319,7 @@ class JSONFormWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ListWidget/widget/index.tsx b/app/client/src/widgets/ListWidget/widget/index.tsx index 46aa0faac9..32bd576fe2 100644 --- a/app/client/src/widgets/ListWidget/widget/index.tsx +++ b/app/client/src/widgets/ListWidget/widget/index.tsx @@ -519,6 +519,7 @@ class ListWidget extends BaseWidget, WidgetState> { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ListWidgetV2/widget/index.tsx b/app/client/src/widgets/ListWidgetV2/widget/index.tsx index 3eafa460c8..43ef659cdf 100644 --- a/app/client/src/widgets/ListWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/ListWidgetV2/widget/index.tsx @@ -207,6 +207,7 @@ class ListWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index 73f0bbc4c3..0ff8ea2171 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -136,6 +136,7 @@ class MapChartWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 7895b5c41a..016cc4257e 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -131,6 +131,7 @@ class MapWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx index 1e872c40e6..c38d57257c 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx @@ -104,6 +104,7 @@ class MenuButtonWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: { base: "360px" }, diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index b5520ce4fe..bd876c4327 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -158,6 +158,7 @@ class MultiSelectTreeWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 6c839c102a..ade1977558 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -135,6 +135,7 @@ class MultiSelectWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/NumberSliderWidget/widget/index.tsx b/app/client/src/widgets/NumberSliderWidget/widget/index.tsx index e5871ad23c..6d608ad5c9 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/index.tsx +++ b/app/client/src/widgets/NumberSliderWidget/widget/index.tsx @@ -116,6 +116,7 @@ class NumberSliderWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index 6aa4bb3998..4d0835638a 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -149,6 +149,7 @@ class PhoneInputWidget extends BaseInputWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 2da1663f97..c79b76b070 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -74,6 +74,7 @@ class ProgressWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index 62ad2f3d66..57e5ab5a3e 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -284,6 +284,7 @@ class RadioGroupWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/RangeSliderWidget/widget/index.tsx b/app/client/src/widgets/RangeSliderWidget/widget/index.tsx index 466bcc4e9c..3fb77a8181 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/index.tsx +++ b/app/client/src/widgets/RangeSliderWidget/widget/index.tsx @@ -133,6 +133,7 @@ class RangeSliderWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index ca5c467f9b..cfc32b98ea 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -178,6 +178,7 @@ class RateWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: (props: RateWidgetProps) => { let maxCount = props.maxCount; if (typeof maxCount !== "number") diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index 7b5e1f24c5..0728c5641e 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -129,6 +129,7 @@ class RichTextEditorWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 3bc833c2b6..d17dc7ac23 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -188,6 +188,7 @@ class SelectWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index dc0edd9550..7a1c7d8c94 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -147,6 +147,7 @@ class SingleSelectTreeWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 3091ba57c5..0ecef07329 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -306,6 +306,7 @@ class StatboxWidget extends ContainerWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index ece5f38a38..1ff1a0bdbb 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -109,6 +109,7 @@ class SwitchGroupWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index e2c6028ca0..2148ab92a4 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -103,6 +103,7 @@ class SwitchWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/TableWidget/widget/index.tsx b/app/client/src/widgets/TableWidget/widget/index.tsx index 29a6b6d450..0718619e78 100644 --- a/app/client/src/widgets/TableWidget/widget/index.tsx +++ b/app/client/src/widgets/TableWidget/widget/index.tsx @@ -95,8 +95,6 @@ const defaultFilter = [ class TableWidget extends BaseWidget { static type = "TABLE_WIDGET"; - static preloadConfig = true; - static getConfig() { return { name: "Table", @@ -321,6 +319,7 @@ class TableWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/TableWidgetV2/widget/index.tsx b/app/client/src/widgets/TableWidgetV2/widget/index.tsx index 31f1802169..6d0325d991 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/TableWidgetV2/widget/index.tsx @@ -167,8 +167,6 @@ class TableWidgetV2 extends BaseWidget { static type = "TABLE_WIDGET_V2"; - static preloadConfig = true; - static getConfig() { return { name: "Table", @@ -376,6 +374,7 @@ class TableWidgetV2 extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 6532d77ee7..865a66a64a 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -228,6 +228,7 @@ class TabsWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index 2aebec0719..78ee304424 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -136,6 +136,7 @@ class TextWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index e6218dc077..351b74935e 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -99,6 +99,7 @@ class VideoWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/index.ts b/app/client/src/widgets/index.ts index 71aa13d2de..bebbf6573c 100644 --- a/app/client/src/widgets/index.ts +++ b/app/client/src/widgets/index.ts @@ -63,9 +63,12 @@ import { WDSCheckboxWidget } from "./wds/WDSCheckboxWidget"; import { WDSIconButtonWidget } from "./wds/WDSIconButtonWidget"; import { WDSTextWidget } from "./wds/WDSTextWidget"; import type BaseWidget from "./BaseWidget"; +import ExternalWidget from "./ExternalWidget"; import { WDSTableWidget } from "./wds/WDSTableWidget"; +import { WDSCurrencyInputWidget } from "./wds/WDSCurrencyInputWidget"; import { WDSButtonGroupWidget } from "./wds/WDSButtonGroupWidget"; import { WDSCheckboxGroupWidget } from "./wds/WDSCheckboxGroupWidget"; +import { WDSSwitchWidget } from "./wds/WDSSwitchWidget"; const Widgets = [ CanvasWidget, @@ -117,14 +120,17 @@ const Widgets = [ CategorySliderWidget, CodeScannerWidget, ListWidgetV2, + ExternalWidget, WDSButtonWidget, WDSInputWidget, WDSCheckboxWidget, WDSIconButtonWidget, WDSTextWidget, WDSTableWidget, + WDSCurrencyInputWidget, WDSButtonGroupWidget, WDSCheckboxGroupWidget, + WDSSwitchWidget, //Deprecated Widgets InputWidget, diff --git a/app/client/src/widgets/wds/WDSBaseInputWidget/component/types.ts b/app/client/src/widgets/wds/WDSBaseInputWidget/component/types.ts index acedc03362..5926cde5cb 100644 --- a/app/client/src/widgets/wds/WDSBaseInputWidget/component/types.ts +++ b/app/client/src/widgets/wds/WDSBaseInputWidget/component/types.ts @@ -1,9 +1,14 @@ import type { ComponentProps } from "widgets/BaseComponent"; import type { TextInputProps } from "@design-system/widgets"; +export type KeyDownEvent = React.KeyboardEvent< + HTMLTextAreaElement | HTMLInputElement +>; + export interface BaseInputComponentProps extends ComponentProps { autoFocus?: boolean; autoComplete?: string; + onKeyDown?: (e: KeyDownEvent) => void; maxChars?: number; widgetId: string; diff --git a/app/client/src/widgets/wds/WDSBaseInputWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSBaseInputWidget/widget/index.tsx index ce501e5155..b67c1418f2 100644 --- a/app/client/src/widgets/wds/WDSBaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSBaseInputWidget/widget/index.tsx @@ -86,6 +86,7 @@ class WDSBaseInputWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSButtonWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSButtonWidget/widget/index.tsx index 1fff9c48c5..04975387fd 100644 --- a/app/client/src/widgets/wds/WDSButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSButtonWidget/widget/index.tsx @@ -99,6 +99,7 @@ class WDSButtonWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: { base: "360px" }, diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts index 3e259eae1a..1e3e41edda 100644 --- a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts @@ -1,6 +1,7 @@ import type { AnvilConfig } from "WidgetProvider/constants"; export const anvilConfig: AnvilConfig = { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSCheckboxWidget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSCheckboxWidget/config/anvilConfig.ts index 83eda12253..1e5907856f 100644 --- a/app/client/src/widgets/wds/WDSCheckboxWidget/config/anvilConfig.ts +++ b/app/client/src/widgets/wds/WDSCheckboxWidget/config/anvilConfig.ts @@ -1,6 +1,7 @@ import type { AnvilConfig } from "WidgetProvider/constants"; export const anvilConfig: AnvilConfig = { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/index.tsx b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/index.tsx new file mode 100644 index 0000000000..4825e2ac4b --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/index.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { Text, TextInput } from "@design-system/widgets"; +import type { CurrencyInputComponentProps } from "./types"; +import { CurrencyTypeOptions } from "constants/Currency"; + +export function CurrencyInputComponent(props: CurrencyInputComponentProps) { + const currency = CurrencyTypeOptions.find( + (option) => option.currency === props.currencyCode, + ); + + return ( + {currency?.symbol_native}} + validationState={props.validationStatus} + value={props.value} + /> + ); +} diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/types.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/types.ts new file mode 100644 index 0000000000..879b34c29a --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/types.ts @@ -0,0 +1,12 @@ +import type { CurrencyTypeOptions } from "constants/Currency"; +import type { BaseInputComponentProps } from "widgets/wds/WDSBaseInputWidget"; + +export interface CurrencyInputComponentProps extends BaseInputComponentProps { + currencyCode?: string; + noOfDecimals?: number; + allowCurrencyChange?: boolean; + decimals?: number; + onCurrencyChange: ( + code?: (typeof CurrencyTypeOptions)[number]["currency"], + ) => void; +} diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.test.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.test.ts new file mode 100644 index 0000000000..bb20a07612 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.test.ts @@ -0,0 +1,129 @@ +import { + getLocaleDecimalSeperator, + getLocaleThousandSeparator, +} from "widgets/WidgetUtils"; +import { + formatCurrencyNumber, + limitDecimalValue, + parseLocaleFormattedStringToNumber, +} from "./utilities"; + +let locale = "en-US"; + +jest.mock("utils/helpers", () => { + const originalModule = jest.requireActual("utils/helpers"); + return { + __esModule: true, + ...originalModule, + getLocale: () => { + return locale; + }, + }; +}); + +describe("Utilities - ", () => { + it("should test formatCurrencyNumber", () => { + //below values are in format of [no. of decimals, input, output] + [ + [0, "123", "123"], + [1, "123", "123"], + [2, "123", "123"], + [0, "123.12", "123"], + [1, "123.12", "123.1"], + [2, "123.12", "123.12"], + [2, "123456.12", "123,456.12"], + [1, "123456.12", "123,456.1"], + [0, "123456.12", "123,456"], + [0, "12345678", "12,345,678"], + [2, "12345678", "12,345,678"], + [2, "0.22", "0.22"], + [1, "0.22", "0.2"], + [0, "0.22", "0"], + [2, "0.22123123", "0.22"], + [1, "0.22123123", "0.2"], + [0, "0.22123123", "0"], + [0, "4", "4"], + [0, "4.9", "5"], + [0, "4.2", "4"], + [1, "4", "4"], + [1, "4.9", "4.9"], + [1, "4.99", "5.0"], + [1, "4.10", "4.1"], + [1, "4.12", "4.1"], + [2, "4", "4"], + [2, "4.90", "4.90"], + [2, "4.9", "4.90"], + [2, "4.99", "4.99"], + [2, "4.10", "4.10"], + [2, "4.1", "4.10"], + [2, "4.11", "4.11"], + [2, "4.119", "4.12"], + [2, "4.111", "4.11"], + [2, "4.999", "5.00"], + ].forEach((d) => { + expect(formatCurrencyNumber(d[0] as number, d[1] as string)).toBe(d[2]); + }); + }); + + it("should test limitDecimalValue", () => { + [ + [0, "123.12", "123"], + [1, "123.12", "123.1"], + [2, "123.12", "123.12"], + [2, "123456.12", "123456.12"], + [1, "123456.12", "123456.1"], + [0, "123456.12", "123456"], + [2, "0.22", "0.22"], + [1, "0.22", "0.2"], + [0, "0.22", "0"], + [2, "0.22123123", "0.22"], + [1, "0.22123123", "0.2"], + [0, "0.22123123", "0"], + ].forEach((d) => { + expect(limitDecimalValue(d[0] as number, d[1] as string)).toBe(d[2]); + }); + }); + + it("should test getLocaleDecimalSeperator", () => { + expect(getLocaleDecimalSeperator()).toBe("."); + locale = "en-IN"; + expect(getLocaleDecimalSeperator()).toBe("."); + locale = "hr-HR"; + expect(getLocaleDecimalSeperator()).toBe(","); + }); + + it("should test getLocaleThousandSeparator", () => { + locale = "en-US"; + expect(getLocaleThousandSeparator()).toBe(","); + locale = "en-IN"; + expect(getLocaleThousandSeparator()).toBe(","); + locale = "hr-HR"; + expect(getLocaleThousandSeparator()).toBe("."); + }); + + it("shoud test parseLocaleFormattedStringToNumber", () => { + locale = "en-US"; + [ + ["123", 123], + ["123.12", 123.12], + ["123,456.12", 123456.12], + ["123,456,789.12", 123456789.12], + ["0.22", 0.22], + ["0.22123123", 0.22123123], + ].forEach((d) => { + expect(parseLocaleFormattedStringToNumber(d[0] as string)).toBe(d[1]); + }); + + locale = "hr-HR"; + [ + ["123", 123], + ["123,12", 123.12], + ["123.456,12", 123456.12], + ["123.456.789,12", 123456789.12], + ["0,22", 0.22], + ["0,22123123", 0.22123123], + ].forEach((d) => { + expect(parseLocaleFormattedStringToNumber(d[0] as string)).toBe(d[1]); + }); + }); +}); diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.ts new file mode 100644 index 0000000000..fc86568bc1 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/component/utilities.ts @@ -0,0 +1,62 @@ +import { getLocale } from "utils/helpers"; +import { + getLocaleDecimalSeperator, + getLocaleThousandSeparator, +} from "widgets/WidgetUtils"; + +/* + Returns formatted value with maximum number of decimals based on decimalsInCurrency value + and add commas based on user's locale + for eg: + a) (2, 1235.456) will return 1,235.45 + b) (1, 1234.456) will return 1,234.4 +*/ +export const formatCurrencyNumber = (decimalsInCurrency = 0, value: string) => { + const fractionDigits = decimalsInCurrency || 0; + const hasDecimal = value.includes(getLocaleDecimalSeperator()); + const locale = getLocale(); + const formatter = new Intl.NumberFormat(locale, { + style: "decimal", + minimumFractionDigits: hasDecimal ? fractionDigits : 0, + maximumFractionDigits: hasDecimal ? fractionDigits : 0, + }); + + const parsedValue = parseLocaleFormattedStringToNumber(value); + return formatter.format(isNaN(parsedValue) ? 0 : parsedValue); +}; + +/* + Returns value in string format with maximum number of decimals based on decimalsInCurrency value + for eg: + a) (2, 1235.456) will return 1235.45 + b) (1, 1234.456) will return 1234.4 +*/ +export const limitDecimalValue = (decimals = 0, value = "") => { + const decimalSeperator = getLocaleDecimalSeperator(); + value = value.split(getLocaleThousandSeparator()).join(""); + switch (decimals) { + case 0: + return value.split(decimalSeperator).shift() || ""; + case 1: + case 2: + const decimalValueArray = value.split(decimalSeperator); + return ( + decimalValueArray[0] + + decimalSeperator + + decimalValueArray[1].slice(0, decimals) + ); + default: + return value; + } +}; + +/* + * Parses the locale formatted currency string to number + */ +export function parseLocaleFormattedStringToNumber(currencyString = "") { + return parseFloat( + currencyString + .replace(new RegExp("\\" + getLocaleThousandSeparator(), "g"), "") + .replace(new RegExp("\\" + getLocaleDecimalSeperator()), "."), + ); +} diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/constants.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/constants.ts new file mode 100644 index 0000000000..84e0aaa882 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/constants.ts @@ -0,0 +1,27 @@ +import { CurrencyTypeOptions } from "constants/Currency"; +import { countryToFlag } from "./widget/helpers"; + +const getCurrencyOptions = () => { + return CurrencyTypeOptions.map((item) => { + return { + leftElement: countryToFlag(item.code), + searchText: item.label, + label: `${item.currency} - ${item.currency_name}`, + value: item.currency, + id: item.symbol_native, + }; + }); +}; + +export const CurrencyDropdownOptions = getCurrencyOptions(); + +export const getDefaultCurrency = () => { + return { + code: "IN", + currency: "INR", + currency_name: "Indian Rupee", + label: "India", + phone: "91", + symbol_native: "₹", + }; +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/icon.svg b/app/client/src/widgets/wds/WDSCurrencyInputWidget/icon.svg new file mode 100644 index 0000000000..d723545078 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/index.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/index.ts new file mode 100644 index 0000000000..bd422433d3 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/index.ts @@ -0,0 +1,3 @@ +import { WDSCurrencyInputWidget } from "./widget"; + +export { WDSCurrencyInputWidget }; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/anvilConfig.ts new file mode 100644 index 0000000000..4e6e1dc076 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/anvilConfig.ts @@ -0,0 +1,11 @@ +import type { AnvilConfig } from "WidgetProvider/constants"; + +export const anvilConfig: AnvilConfig = { + widgetSize: { + maxHeight: {}, + maxWidth: {}, + minHeight: { base: "70px" }, + minWidth: { base: "120px" }, + }, + isLargeWidget: false, +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/autocompleteConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/autocompleteConfig.ts new file mode 100644 index 0000000000..40d635b616 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/autocompleteConfig.ts @@ -0,0 +1,28 @@ +import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; + +export const autocompleteConfig = { + "!doc": + "An input text field is used to capture a currency value. Inputs are used in forms and can have custom validations.", + "!url": "https://docs.appsmith.com/widget-reference/currency-input", + text: { + "!type": "string", + "!doc": "The formatted text value of the input", + "!url": "https://docs.appsmith.com/widget-reference/currency-input", + }, + value: { + "!type": "number", + "!doc": "The value of the input", + "!url": "https://docs.appsmith.com/widget-reference/currency-input", + }, + isValid: "bool", + isVisible: DefaultAutocompleteDefinitions.isVisible, + isDisabled: "bool", + countryCode: { + "!type": "string", + "!doc": "Selected country code for Currency", + }, + currencyCode: { + "!type": "string", + "!doc": "Selected Currency code", + }, +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/defaultsConfig.ts new file mode 100644 index 0000000000..b84476f89d --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/defaultsConfig.ts @@ -0,0 +1,18 @@ +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { WDSBaseInputWidget } from "widgets/wds/WDSBaseInputWidget"; +import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; + +import { getDefaultCurrency } from "../../constants"; + +export const defaultsConfig = { + ...WDSBaseInputWidget.getDefaults(), + widgetName: "CurrencyInput", + version: 1, + rows: 7, + allowCurrencyChange: false, + defaultCurrencyCode: getDefaultCurrency().currency, + decimals: 0, + showStepArrows: false, + responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: FILL_WIDGET_MIN_WIDTH, +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/featuresConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/featuresConfig.ts new file mode 100644 index 0000000000..83bb03e32e --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/featuresConfig.ts @@ -0,0 +1,9 @@ +import { DynamicHeight } from "utils/WidgetFeatures"; + +export const featuresConfig = { + dynamicHeight: { + sectionIndex: 3, + defaultValue: DynamicHeight.FIXED, + active: true, + }, +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/index.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/index.ts new file mode 100644 index 0000000000..08b08e53c6 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/index.ts @@ -0,0 +1,8 @@ +export * from "./propertyPaneConfig"; +export { metaConfig } from "./metaConfig"; +export { settersConfig } from "./settersConfig"; +export { defaultsConfig } from "./defaultsConfig"; +export { featuresConfig } from "./featuresConfig"; +export { autocompleteConfig } from "./autocompleteConfig"; +export { anvilConfig } from "./anvilConfig"; +export { propertyPaneContentConfig } from "./propertyPaneConfig"; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/metaConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/metaConfig.ts new file mode 100644 index 0000000000..0470ae711b --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/metaConfig.ts @@ -0,0 +1,11 @@ +import { WIDGET_TAGS } from "constants/WidgetConstants"; + +import IconSVG from "../../icon.svg"; + +export const metaConfig = { + name: "Currency Input", + iconSVG: IconSVG, + tags: [WIDGET_TAGS.INPUTS], + needsMeta: true, + searchTags: ["amount", "total"], +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/contentConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/contentConfig.ts new file mode 100644 index 0000000000..fe57ffeeb6 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/contentConfig.ts @@ -0,0 +1,120 @@ +import { CurrencyTypeOptions } from "constants/Currency"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; + +import * as validations from "./validations"; +import { countryToFlag } from "../../helpers"; + +export const propertyPaneContentConfig = [ + { + sectionName: "Data", + children: [ + { + helpText: + "Sets the default text of the widget. The text is updated if the default text changes", + propertyName: "defaultText", + label: "Default value", + controlType: "INPUT_TEXT", + placeholderText: "100", + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.FUNCTION, + params: { + fn: validations.defaultValueValidation, + expected: { + type: "number", + example: `100`, + autocompleteDataType: AutocompleteDataType.STRING, + }, + }, + }, + dependencies: ["decimals"], + }, + { + helpText: "Changes the type of currency", + propertyName: "defaultCurrencyCode", + label: "Currency", + enableSearch: true, + dropdownHeight: "156px", + controlType: "DROP_DOWN", + searchPlaceholderText: "Search by code or name", + options: CurrencyTypeOptions.map((item) => { + return { + leftElement: countryToFlag(item.code), + searchText: item.label, + label: `${item.currency} - ${item.currency_name}`, + value: item.currency, + id: item.symbol_native, + }; + }), + virtual: true, + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + }, + }, + { + propertyName: "allowCurrencyChange", + label: "Allow currency change", + helpText: "Search by currency or country", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + helpText: "No. of decimals in currency input", + propertyName: "decimals", + label: "Decimals allowed", + controlType: "DROP_DOWN", + options: [ + { + label: "0", + value: 0, + }, + { + label: "1", + value: 1, + }, + { + label: "2", + value: 2, + }, + ], + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.NUMBER, + params: { + min: 0, + max: 2, + }, + }, + }, + ], + }, + { + sectionName: "Label", + children: [], + }, + { + sectionName: "Validation", + children: [ + { + propertyName: "isRequired", + label: "Required", + helpText: "Makes input to the widget mandatory", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/index.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/index.ts new file mode 100644 index 0000000000..7f43d3bde5 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/index.ts @@ -0,0 +1 @@ +export { propertyPaneContentConfig } from "./contentConfig"; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/defaultValueValidation.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/defaultValueValidation.ts new file mode 100644 index 0000000000..7a2fd36767 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/defaultValueValidation.ts @@ -0,0 +1,104 @@ +import type { ValidationResponse } from "constants/WidgetValidation"; + +import type { CurrencyInputWidgetProps } from "../../../types"; + +export function defaultValueValidation( + value: any, + props: CurrencyInputWidgetProps, + _?: any, +): ValidationResponse { + const NUMBER_ERROR_MESSAGE = { + name: "TypeError", + message: "This value must be number", + }; + const DECIMAL_SEPARATOR_ERROR_MESSAGE = { + name: "ValidationError", + message: "Please use . as the decimal separator for default values.", + }; + const EMPTY_ERROR_MESSAGE = { + name: "", + message: "", + }; + const localeLang = navigator.languages?.[0] || "en-US"; + + function getLocaleDecimalSeperator() { + return Intl.NumberFormat(localeLang) + .format(1.1) + .replace(/\p{Number}/gu, ""); + } + const decimalSeperator = getLocaleDecimalSeperator(); + const defaultDecimalSeperator = "."; + if (_.isObject(value)) { + return { + isValid: false, + parsed: JSON.stringify(value, null, 2), + messages: [NUMBER_ERROR_MESSAGE], + }; + } + + if (_.isBoolean(value) || _.isUndefined(value) || _.isNull(value)) { + return { + isValid: false, + parsed: value, + messages: [NUMBER_ERROR_MESSAGE], + }; + } + + let parsed: any = Number(value); + let isValid, messages; + + if (_.isString(value) && value.trim() === "") { + /* + * When value is empty string + */ + isValid = true; + messages = [EMPTY_ERROR_MESSAGE]; + parsed = undefined; + } else if (!Number.isFinite(parsed)) { + /* + * When parsed value is not a finite numer + */ + isValid = false; + parsed = undefined; + + /** + * Check whether value contains the locale decimal separator apart from "." + * We only allow "." as a decimal separator inside default value + */ + if ( + String(value).indexOf(defaultDecimalSeperator) === -1 && + String(value).indexOf(decimalSeperator) > 0 + ) { + messages = [DECIMAL_SEPARATOR_ERROR_MESSAGE]; + } else { + messages = [NUMBER_ERROR_MESSAGE]; + } + } else { + /* + * When parsed value is a Number + */ + + // Check whether value is honoring the decimals property + if (parsed !== Number(parsed.toFixed(props.decimals))) { + isValid = false; + messages = [ + { + name: "RangeError", + message: + "No. of decimals are higher than the decimals field set. Please update the default or the decimals field", + }, + ]; + } else { + isValid = true; + messages = [EMPTY_ERROR_MESSAGE]; + } + + parsed = String(parsed); + } + + return { + isValid, + parsed, + messages, + }; +} diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/index.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/index.ts new file mode 100644 index 0000000000..edeb38be1a --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/propertyPaneConfig/validations/index.ts @@ -0,0 +1 @@ +export { defaultValueValidation } from "./defaultValueValidation"; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/settersConfig.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/settersConfig.ts new file mode 100644 index 0000000000..479d2b27e3 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/config/settersConfig.ts @@ -0,0 +1,21 @@ +export const settersConfig = { + __setters: { + setVisibility: { + path: "isVisible", + type: "boolean", + }, + setDisabled: { + path: "isDisabled", + type: "boolean", + }, + setRequired: { + path: "isRequired", + type: "boolean", + }, + setValue: { + path: "defaultText", + type: "string", + accessor: "text", + }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.js b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.js new file mode 100644 index 0000000000..154ae98f72 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.js @@ -0,0 +1,90 @@ +/* eslint-disable @typescript-eslint/no-unused-vars*/ +export default { + isValid: (props, moment, _) => { + let hasValidValue, value; + try { + value = Number(props.value); + hasValidValue = Number.isFinite(value); + } catch (e) { + return false; + } + + if (!props.isRequired && (props.text === "" || props.text === undefined)) { + return true; + } + if (props.isRequired && !hasValidValue) { + return false; + } + + if (typeof props.validation === "boolean" && !props.validation) { + return false; + } + + let parsedRegex = null; + if (props.regex) { + /* + * break up the regexp pattern into 4 parts: given regex, regex prefix , regex pattern, regex flags + * Example /test/i will be split into ["/test/gi", "/", "test", "gi"] + */ + const regexParts = props.regex.match(/(\/?)(.+)\\1([a-z]*)/i); + + if (!regexParts) { + parsedRegex = new RegExp(props.regex); + } else { + if ( + regexParts[3] && + !/^(?!.*?(.).*?\\1)[gmisuy]+$/.test(regexParts[3]) + ) { + parsedRegex = RegExp(props.regex); + } else { + /* + * if we have a regex flags, use it to form regexp + */ + parsedRegex = new RegExp(regexParts[2], regexParts[3]); + } + } + } + if (parsedRegex) { + return parsedRegex.test(props.text); + } else { + return hasValidValue; + } + }, + // + value: (props, moment, _) => { + const text = props.text; + + function getLocale() { + return navigator.languages?.[0] || "en-US"; + } + + function getLocaleDecimalSeperator() { + return Intl.NumberFormat(getLocale()) + .format(1.1) + .replace(/\p{Number}/gu, ""); + } + + function getLocaleThousandSeparator() { + return Intl.NumberFormat(getLocale()) + .format(11111) + .replace(/\p{Number}/gu, ""); + } + + if (text) { + const parsed = parseFloat( + text + .replace(new RegExp(`[${getLocaleThousandSeparator()}]`, "g"), "") + .replace(new RegExp(`[${getLocaleDecimalSeperator()}]`), "."), + ); + + if (Number.isNaN(parsed)) { + parsed = undefined; + } + + return parsed; + } else { + return undefined; + } + }, + // +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.test.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.test.ts new file mode 100644 index 0000000000..4014c07987 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/derived.test.ts @@ -0,0 +1,65 @@ +import derivedProperty from "./derived"; + +describe("Derived property - ", () => { + describe("isValid property", () => { + it("should test isRequired", () => { + let isValid = derivedProperty.isValid({ + text: undefined, + isRequired: false, + }); + + expect(isValid).toBeTruthy(); + + isValid = derivedProperty.isValid({ + text: undefined, + isRequired: true, + }); + + expect(isValid).toBeFalsy(); + + isValid = derivedProperty.isValid({ + value: 100, + text: "100", + isRequired: true, + }); + + expect(isValid).toBeTruthy(); + }); + + it("should test validation", () => { + let isValid = derivedProperty.isValid({ + value: 100, + text: "100", + validation: false, + }); + + expect(isValid).toBeFalsy(); + + isValid = derivedProperty.isValid({ + value: 100, + text: "100", + validation: true, + }); + + expect(isValid).toBeTruthy(); + }); + + it("should test regex validation", () => { + let isValid = derivedProperty.isValid({ + value: 100, + text: "100", + regex: "^100$", + }); + + expect(isValid).toBeTruthy(); + + isValid = derivedProperty.isValid({ + value: 101, + text: "101", + regex: "^100$", + }); + + expect(isValid).toBeFalsy(); + }); + }); +}); diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.test.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.test.ts new file mode 100644 index 0000000000..b2258ee480 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.test.ts @@ -0,0 +1,21 @@ +import { countryToFlag } from "./helpers"; + +describe("countryToFlag", () => { + it("should test countryToFlag", () => { + [ + ["IN", "🇮🇳"], + ["in", "🇮🇳"], + ["US", "🇺🇸"], + ].forEach((d) => { + expect(countryToFlag(d[0])).toBe(d[1]); + }); + String.fromCodePoint = undefined as any; + [ + ["IN", "IN"], + ["in", "in"], + ["US", "US"], + ].forEach((d) => { + expect(countryToFlag(d[0])).toBe(d[1]); + }); + }); +}); diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.ts new file mode 100644 index 0000000000..4b6b9982ac --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/helpers.ts @@ -0,0 +1,45 @@ +import { + createMessage, + FIELD_REQUIRED_ERROR, +} from "@appsmith/constants/messages"; +import { CurrencyTypeOptions } from "constants/Currency"; + +export function getCountryCodeFromCurrencyCode( + currencyCode?: (typeof CurrencyTypeOptions)[number]["currency"], +) { + const option = CurrencyTypeOptions.find( + (option) => option.currency === currencyCode, + ); + + if (option) return option.code; + + return ""; +} + +export function validateInput(props: any) { + const value = props.text ?? ""; + const isInvalid = "isValid" in props && !props.isValid && !!props.isDirty; + + const conditionalProps: any = {}; + + conditionalProps.errorMessage = props.errorMessage; + + if (props.isRequired && value.length === 0) { + conditionalProps.errorMessage = createMessage(FIELD_REQUIRED_ERROR); + } + + return { + validattionStatus: isInvalid ? "invalid" : undefined, + errorMessage: isInvalid ? conditionalProps.errorMessage : undefined, + } as const; +} + +export const countryToFlag = (isoCode: string) => { + return typeof String.fromCodePoint !== "undefined" + ? isoCode + .toUpperCase() + .replace(/./g, (char) => + String.fromCodePoint(char.charCodeAt(0) + 127397), + ) + : isoCode; +}; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.test.tsx b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.test.tsx new file mode 100644 index 0000000000..db4c4ef010 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.test.tsx @@ -0,0 +1,54 @@ +import type { CurrencyInputWidgetProps } from "./types"; +import { defaultValueValidation } from "./config/propertyPaneConfig/validations"; +import _ from "lodash"; + +describe("defaultValueValidation", () => { + let result: any; + + it("should validate defaulttext", () => { + result = defaultValueValidation("100", {} as CurrencyInputWidgetProps, _); + + expect(result).toEqual({ + isValid: true, + parsed: "100", + messages: [{ name: "", message: "" }], + }); + + result = defaultValueValidation("test", {} as CurrencyInputWidgetProps, _); + + expect(result).toEqual({ + isValid: false, + parsed: undefined, + messages: [ + { + name: "TypeError", + message: "This value must be number", + }, + ], + }); + + result = defaultValueValidation("", {} as CurrencyInputWidgetProps, _); + + expect(result).toEqual({ + isValid: true, + parsed: undefined, + messages: [{ name: "", message: "" }], + }); + }); + + it("should validate defaulttext with object value", () => { + const value = {}; + result = defaultValueValidation(value, {} as CurrencyInputWidgetProps, _); + + expect(result).toEqual({ + isValid: false, + parsed: JSON.stringify(value, null, 2), + messages: [ + { + name: "TypeError", + message: "This value must be number", + }, + ], + }); + }); +}); diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx new file mode 100644 index 0000000000..11c9073cc9 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx @@ -0,0 +1,296 @@ +import _ from "lodash"; +import React from "react"; +import log from "loglevel"; +import * as Sentry from "@sentry/react"; +import type { WidgetState } from "widgets/BaseWidget"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; + +import { CurrencyInputComponent } from "../component"; +import derivedProperties from "./parsedDerivedProperties"; +import { + formatCurrencyNumber, + limitDecimalValue, +} from "../component/utilities"; +import { getLocale, mergeWidgetConfig } from "utils/helpers"; +import { + getLocaleDecimalSeperator, + getLocaleThousandSeparator, +} from "widgets/WidgetUtils"; +import type { SetterConfig, Stylesheet } from "entities/AppTheming"; +import type { + AnvilConfig, + AutocompletionDefinitions, +} from "WidgetProvider/constants"; +import { + anvilConfig, + autocompleteConfig, + defaultsConfig, + featuresConfig, + metaConfig, + settersConfig, + propertyPaneContentConfig, +} from "./config"; +import type { CurrencyInputWidgetProps } from "./types"; +import { WDSBaseInputWidget } from "widgets/wds/WDSBaseInputWidget"; +import { getCountryCodeFromCurrencyCode, validateInput } from "./helpers"; +import type { KeyDownEvent } from "widgets/wds/WDSBaseInputWidget/component/types"; + +class WDSCurrencyInputWidget extends WDSBaseInputWidget< + CurrencyInputWidgetProps, + WidgetState +> { + static type = "WDS_CURRENCY_INPUT_WIDGET"; + + static getConfig() { + return metaConfig; + } + + static getFeatures() { + return featuresConfig; + } + + static getDefaults() { + return defaultsConfig; + } + + static getAutoLayoutConfig() { + return {}; + } + + static getAnvilConfig(): AnvilConfig | null { + return anvilConfig; + } + + static getAutocompleteDefinitions(): AutocompletionDefinitions { + return autocompleteConfig; + } + + static getSetterConfig(): SetterConfig { + return settersConfig; + } + + static getPropertyPaneContentConfig() { + return mergeWidgetConfig( + propertyPaneContentConfig, + super.getPropertyPaneContentConfig(), + ); + } + + static getPropertyPaneStyleConfig() { + return super.getPropertyPaneStyleConfig(); + } + + static getDerivedPropertiesMap() { + return { + isValid: `{{(()=>{${derivedProperties.isValid}})()}}`, + value: `{{(()=>{${derivedProperties.value}})()}}`, + }; + } + + static getMetaPropertiesMap(): Record { + return _.merge(super.getMetaPropertiesMap(), { + text: undefined, + currencyCode: undefined, + }); + } + + static getDefaultPropertiesMap(): Record { + return _.merge(super.getDefaultPropertiesMap(), { + currencyCode: "defaultCurrencyCode", + }); + } + + static getStylesheetConfig(): Stylesheet { + return {}; + } + + componentDidMount() { + this.formatText(); + } + + componentDidUpdate(prevProps: CurrencyInputWidgetProps) { + if ( + prevProps.text !== this.props.text && + !this.props.isFocused && + this.props.text === String(this.props.defaultText) + ) { + this.formatText(); + } + // If defaultText property has changed, reset isDirty to false + if ( + this.props.defaultText !== prevProps.defaultText && + this.props.isDirty + ) { + this.props.updateWidgetMetaProperty("isDirty", false); + } + + if ( + this.props.currencyCode === this.props.defaultCurrencyCode && + prevProps.currencyCode !== this.props.currencyCode + ) { + this.onCurrencyChange(this.props.currencyCode); + } + } + + onValueChange = (value: string) => { + let formattedValue = ""; + const decimalSeperator = getLocaleDecimalSeperator(); + + try { + if (value && value.includes(decimalSeperator)) { + formattedValue = limitDecimalValue(this.props.decimals, value); + } else { + formattedValue = value; + } + } catch (e) { + formattedValue = value; + log.error(e); + Sentry.captureException(e); + } + + this.props.updateWidgetMetaProperty("text", String(formattedValue), { + triggerPropertyName: "onTextChanged", + dynamicString: this.props.onTextChanged, + event: { + type: EventType.ON_TEXT_CHANGE, + }, + }); + + if (!this.props.isDirty) { + this.props.updateWidgetMetaProperty("isDirty", true); + } + }; + + onFocusChange = (isFocused?: boolean) => { + try { + if (isFocused) { + const text = this.props.text || ""; + const deFormattedValue = text.replace( + new RegExp("\\" + getLocaleThousandSeparator(), "g"), + "", + ); + this.props.updateWidgetMetaProperty("text", deFormattedValue); + this.props.updateWidgetMetaProperty("isFocused", isFocused, { + triggerPropertyName: "onFocus", + dynamicString: this.props.onFocus, + event: { + type: EventType.ON_FOCUS, + }, + }); + } else { + if (this.props.text) { + const formattedValue = formatCurrencyNumber( + this.props.decimals, + this.props.text, + ); + this.props.updateWidgetMetaProperty("text", formattedValue); + } + this.props.updateWidgetMetaProperty("isFocused", isFocused, { + triggerPropertyName: "onBlur", + dynamicString: this.props.onBlur, + event: { + type: EventType.ON_BLUR, + }, + }); + } + } catch (e) { + log.error(e); + Sentry.captureException(e); + this.props.updateWidgetMetaProperty("text", this.props.text); + } + + super.onFocusChange(!!isFocused); + }; + + onCurrencyChange = ( + currencyCode?: Parameters[0], + ) => { + const countryCode = getCountryCodeFromCurrencyCode(currencyCode); + + this.props.updateWidgetMetaProperty("countryCode", countryCode); + this.props.updateWidgetMetaProperty("currencyCode", currencyCode); + }; + + onKeyDown = (e: KeyDownEvent) => { + // don't allow entering anything other than numbers. but allow backspace, arrows delete, tab, enter + if ( + !( + (e.key >= "0" && e.key <= "9") || + // allow . or comma if decimals are allowed + (this.props.decimals && + (e.key === getLocaleDecimalSeperator() || + e.key === getLocaleThousandSeparator())) || + (e.key >= "0" && e.key <= "9" && e.code.includes("Numpad")) || + e.key === "Backspace" || + e.key === "Tab" || + e.key === "Enter" || + e.key === "ArrowUp" || + e.key === "ArrowDown" || + e.key === "ArrowLeft" || + e.key === "ArrowRight" || + e.key === "Delete" || + e.ctrlKey || + e.metaKey || + e.altKey + ) + ) { + e.preventDefault(); + } + + super.onKeyDown(e); + }; + + isTextFormatted = () => { + return this.props.text.includes(getLocaleThousandSeparator()); + }; + + formatText() { + if (!!this.props.text && !this.isTextFormatted()) { + try { + const floatVal = parseFloat(this.props.text); + + const formattedValue = Intl.NumberFormat(getLocale(), { + style: "decimal", + minimumFractionDigits: this.props.decimals, + maximumFractionDigits: this.props.decimals, + }).format(floatVal); + + this.props.updateWidgetMetaProperty("text", formattedValue); + } catch (e) { + log.error(e); + Sentry.captureException(e); + } + } + } + + getWidgetView() { + const value = this.props.text ?? ""; + + const validation = validateInput(this.props); + + return ( + + ); + } +} + +export { WDSCurrencyInputWidget }; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/parsedDerivedProperties.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/parsedDerivedProperties.ts new file mode 100644 index 0000000000..124afba6cb --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/parsedDerivedProperties.ts @@ -0,0 +1,34 @@ +// @ts-expect-error: Types are not available +import widgetPropertyFns from "!!raw-loader!./derived.js"; + +// TODO(abhinav): +// Add unit test cases +// Handle edge cases +// Error out on wrong values +const derivedProperties: any = {}; +// const regex = /(\w+):\s?\(props\)\s?=>\s?{([\w\W]*?)},/gim; +const regex = + /(\w+):\s?\(props, moment, _\)\s?=>\s?{([\w\W\n]*?)},\n?\s+?\/\//gim; + +let m; +while ((m = regex.exec(widgetPropertyFns)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++; + } + let key = ""; + // The result can be accessed through the `m`-variable. + m.forEach((match, groupIndex) => { + if (groupIndex === 1) { + key = match; + } + if (groupIndex === 2) { + derivedProperties[key] = match + .trim() + .replace(/\n/g, "") + .replace(/props\./g, "this."); + } + }); +} + +export default derivedProperties; diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/types.ts b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/types.ts new file mode 100644 index 0000000000..fa307c6099 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/types.ts @@ -0,0 +1,11 @@ +import type { CurrencyTypeOptions } from "constants/Currency"; +import type { BaseInputWidgetProps } from "widgets/wds/WDSBaseInputWidget"; + +export interface CurrencyInputWidgetProps extends BaseInputWidgetProps { + countryCode?: string; + currencyCode?: (typeof CurrencyTypeOptions)[number]["currency"]; + noOfDecimals?: number; + allowCurrencyChange?: boolean; + decimals?: number; + defaultText?: number; +} diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx index d50c5c97d2..592e406644 100644 --- a/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx @@ -42,6 +42,7 @@ class WDSIconButtonWidget extends BaseWidget< static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSInputWidget/component/types.ts b/app/client/src/widgets/wds/WDSInputWidget/component/types.ts index b57be8aeee..4556c3fc5d 100644 --- a/app/client/src/widgets/wds/WDSInputWidget/component/types.ts +++ b/app/client/src/widgets/wds/WDSInputWidget/component/types.ts @@ -2,7 +2,6 @@ import type { BaseInputComponentProps } from "../../WDSBaseInputWidget"; import type { INPUT_TYPES } from "../constants"; import type { IconName } from "@blueprintjs/icons"; -import type { KeyDownEvent } from "../widget/types"; export type InputType = (typeof INPUT_TYPES)[keyof typeof INPUT_TYPES]; @@ -15,5 +14,4 @@ export interface InputComponentProps extends BaseInputComponentProps { autoComplete?: string; iconAlign?: "left" | "right"; iconName?: IconName; - onKeyDown?: (e: KeyDownEvent) => void; } diff --git a/app/client/src/widgets/wds/WDSInputWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSInputWidget/widget/index.tsx index a21f2f45e3..005c43cbd1 100644 --- a/app/client/src/widgets/wds/WDSInputWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSInputWidget/widget/index.tsx @@ -14,6 +14,7 @@ import type { } from "WidgetProvider/constants"; import InputComponent from "../component"; import { INPUT_TYPES } from "../constants"; +import type { InputWidgetProps } from "./types"; import { mergeWidgetConfig } from "utils/helpers"; import { parseText, validateInput } from "./helper"; import { DynamicHeight } from "utils/WidgetFeatures"; @@ -22,13 +23,13 @@ import type { SetterConfig } from "entities/AppTheming"; import { WIDGET_TAGS } from "constants/WidgetConstants"; import derivedProperties from "./parsedDerivedProperties"; import { WDSBaseInputWidget } from "../../WDSBaseInputWidget"; -import type { InputWidgetProps, KeyDownEvent } from "./types"; import type { DerivedPropertiesMap } from "WidgetProvider/factory"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; import type { BaseInputWidgetProps } from "../../WDSBaseInputWidget"; -import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import type { KeyDownEvent } from "widgets/wds/WDSBaseInputWidget/component/types"; class WDSInputWidget extends WDSBaseInputWidget { static getConfig() { @@ -111,6 +112,7 @@ class WDSInputWidget extends WDSBaseInputWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSInputWidget/widget/types.ts b/app/client/src/widgets/wds/WDSInputWidget/widget/types.ts index 2e0743cd44..9f5396ffa4 100644 --- a/app/client/src/widgets/wds/WDSInputWidget/widget/types.ts +++ b/app/client/src/widgets/wds/WDSInputWidget/widget/types.ts @@ -25,7 +25,3 @@ export interface Validation { errorMessage?: string; validationStatus?: InputComponentProps["validationStatus"]; } - -export type KeyDownEvent = React.KeyboardEvent< - HTMLTextAreaElement | HTMLInputElement ->; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/anvilConfig.ts new file mode 100644 index 0000000000..1e5907856f --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/anvilConfig.ts @@ -0,0 +1,11 @@ +import type { AnvilConfig } from "WidgetProvider/constants"; + +export const anvilConfig: AnvilConfig = { + isLargeWidget: false, + widgetSize: { + maxHeight: {}, + maxWidth: {}, + minHeight: { base: "40px" }, + minWidth: { base: "120px" }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/autocompleteConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/autocompleteConfig.ts new file mode 100644 index 0000000000..ed51fd82e4 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/autocompleteConfig.ts @@ -0,0 +1,10 @@ +import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; + +export const autocompleteConfig = { + "!doc": + "Switch is a simple UI widget you can use when you want users to make a binary choice", + "!url": "https://docs.appsmith.com/widget-reference/switch", + isVisible: DefaultAutocompleteDefinitions.isVisible, + isSwitchedOn: "bool", + isDisabled: "bool", +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/defaultsConfig.ts new file mode 100644 index 0000000000..e6c96a3fbb --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/defaultsConfig.ts @@ -0,0 +1,14 @@ +import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; + +export const defaultsConfig = { + label: "Label", + rows: 4, + columns: 12, + defaultSwitchState: true, + widgetName: "Switch", + labelPosition: "left", + version: 1, + isDisabled: false, + animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/featuresConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/featuresConfig.ts new file mode 100644 index 0000000000..5edea989f1 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/featuresConfig.ts @@ -0,0 +1,6 @@ +export const featuresConfig = { + dynamicHeight: { + sectionIndex: 1, + active: true, + }, +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/index.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/index.ts new file mode 100644 index 0000000000..a4244ad200 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/index.ts @@ -0,0 +1,8 @@ +export * from "./propertyPaneConfig"; +export { metaConfig } from "./metaConfig"; +export { settersConfig } from "./settersConfig"; +export { methodsConfig } from "./methodsConfig"; +export { defaultsConfig } from "./defaultsConfig"; +export { featuresConfig } from "./featuresConfig"; +export { autocompleteConfig } from "./autocompleteConfig"; +export { anvilConfig } from "./anvilConfig"; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/metaConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/metaConfig.ts new file mode 100644 index 0000000000..bcba624fea --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/metaConfig.ts @@ -0,0 +1,10 @@ +import IconSVG from "../icon.svg"; +import { WIDGET_TAGS } from "constants/WidgetConstants"; + +export const metaConfig = { + name: "Switch", + iconSVG: IconSVG, + tags: [WIDGET_TAGS.TOGGLES], + needsMeta: true, + searchTags: ["boolean"], +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/methodsConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/methodsConfig.ts new file mode 100644 index 0000000000..112e9d60b3 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/methodsConfig.ts @@ -0,0 +1,18 @@ +import type { + PropertyUpdates, + SnipingModeProperty, +} from "WidgetProvider/constants"; + +export const methodsConfig = { + getSnipingModeUpdates: ( + propValueMap: SnipingModeProperty, + ): PropertyUpdates[] => { + return [ + { + propertyPath: "defaultSwitchState", + propertyValue: propValueMap.data, + isDynamicPropertyPath: true, + }, + ]; + }, +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/contentConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/contentConfig.ts new file mode 100644 index 0000000000..e55c6902b2 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/contentConfig.ts @@ -0,0 +1,95 @@ +import { ValidationTypes } from "constants/WidgetValidation"; + +export const propertyPaneContentConfig = [ + { + sectionName: "Label", + children: [ + { + propertyName: "label", + label: "Text", + controlType: "INPUT_TEXT", + helpText: "Displays a label next to the widget", + placeholderText: "Enable Option", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + helpText: "Sets the label position of the widget", + propertyName: "labelPosition", + label: "Position", + controlType: "ICON_TABS", + fullWidth: true, + options: [ + { label: "Left", value: "left" }, + { label: "Right", value: "right" }, + ], + defaultValue: "left", + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ], + }, + { + sectionName: "General", + children: [ + { + propertyName: "defaultSwitchState", + label: "Default state", + helpText: + "On / Off the Switch by default. Changes to the default selection update the widget state", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "isVisible", + label: "Visible", + helpText: "Controls the visibility of the widget", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "isDisabled", + label: "Disabled", + controlType: "SWITCH", + helpText: "Disables input to this widget", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "animateLoading", + label: "Animate loading", + controlType: "SWITCH", + helpText: "Controls the loading of the widget", + defaultValue: true, + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + ], + }, + { + sectionName: "Events", + children: [ + { + helpText: "when the switch state is changed", + propertyName: "onChange", + label: "onChange", + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/index.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/index.ts new file mode 100644 index 0000000000..7f43d3bde5 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/index.ts @@ -0,0 +1 @@ +export { propertyPaneContentConfig } from "./contentConfig"; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/styleConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/styleConfig.ts new file mode 100644 index 0000000000..a7666dd337 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/propertyPaneConfig/styleConfig.ts @@ -0,0 +1 @@ +export const propertyPaneStyleConfig = []; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/config/settersConfig.ts b/app/client/src/widgets/wds/WDSSwitchWidget/config/settersConfig.ts new file mode 100644 index 0000000000..311222978f --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/config/settersConfig.ts @@ -0,0 +1,25 @@ +export const settersConfig = { + __setters: { + setVisibility: { + path: "isVisible", + type: "boolean", + }, + setDisabled: { + path: "isDisabled", + type: "boolean", + }, + setRequired: { + path: "isRequired", + type: "boolean", + }, + setValue: { + path: "defaultSwitchState", + type: "boolean", + accessor: "isSwitchedOn", + }, + setColor: { + path: "accentColor", + type: "string", + }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/icon.svg b/app/client/src/widgets/wds/WDSSwitchWidget/icon.svg new file mode 100644 index 0000000000..7956f14c73 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/index.ts b/app/client/src/widgets/wds/WDSSwitchWidget/index.ts new file mode 100644 index 0000000000..9eb1fc2f72 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/index.ts @@ -0,0 +1,3 @@ +import { WDSSwitchWidget } from "./widget"; + +export { WDSSwitchWidget }; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSSwitchWidget/widget/index.tsx new file mode 100644 index 0000000000..5184816ef8 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/widget/index.tsx @@ -0,0 +1,115 @@ +import React from "react"; +import { Switch } from "@design-system/widgets"; +import type { SetterConfig } from "entities/AppTheming"; +import type { DerivedPropertiesMap } from "WidgetProvider/factory"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; + +import * as config from "./../config"; +import BaseWidget from "widgets/BaseWidget"; +import type { SwitchWidgetProps } from "./types"; +import type { WidgetState } from "widgets/BaseWidget"; +import type { AnvilConfig } from "WidgetProvider/constants"; + +class WDSSwitchWidget extends BaseWidget { + static type = "WDS_SWITCH_WIDGET"; + + static getConfig() { + return config.metaConfig; + } + + static getFeatures() { + return config.featuresConfig; + } + + static getDefaults() { + return config.defaultsConfig; + } + + static getMethods() { + return config.methodsConfig; + } + + static getAutoLayoutConfig() { + return {}; + } + + static getAnvilConfig(): AnvilConfig | null { + return config.anvilConfig; + } + + static getAutocompleteDefinitions() { + return config.autocompleteConfig; + } + + static getSetterConfig(): SetterConfig { + return config.settersConfig; + } + + static getPropertyPaneContentConfig() { + return config.propertyPaneContentConfig; + } + + static getPropertyPaneStyleConfig() { + return []; + } + + static getDefaultPropertiesMap(): Record { + return { + isSwitchedOn: "defaultSwitchState", + }; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + value: `{{!!this.isSwitchedOn}}`, + }; + } + + static getMetaPropertiesMap(): Record { + return { + isSwitchedOn: undefined, + isDirty: false, + }; + } + + componentDidUpdate(prevProps: SwitchWidgetProps) { + if ( + this.props.defaultSwitchState !== prevProps.defaultSwitchState && + this.props.isDirty + ) { + this.props.updateWidgetMetaProperty("isDirty", false); + } + } + + onChange = (isSwitchedOn: boolean) => { + if (!this.props.isDirty) { + this.props.updateWidgetMetaProperty("isDirty", true); + } + + this.props.updateWidgetMetaProperty("isSwitchedOn", isSwitchedOn, { + triggerPropertyName: "onChange", + dynamicString: this.props.onChange, + event: { + type: EventType.ON_SWITCH_CHANGE, + }, + }); + }; + + getWidgetView() { + return ( + + {this.props.label} + + ); + } +} + +export { WDSSwitchWidget }; diff --git a/app/client/src/widgets/wds/WDSSwitchWidget/widget/types.ts b/app/client/src/widgets/wds/WDSSwitchWidget/widget/types.ts new file mode 100644 index 0000000000..95efd43755 --- /dev/null +++ b/app/client/src/widgets/wds/WDSSwitchWidget/widget/types.ts @@ -0,0 +1,11 @@ +import type { WidgetProps } from "widgets/BaseWidget"; + +export interface SwitchWidgetProps extends WidgetProps { + label: string; + defaultSwitchState: boolean; + isSwitchedOn?: boolean; + isDisabled?: boolean; + onChange?: string; + isRequired?: boolean; + labelPosition?: "left" | "right"; +} diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/Constants.ts b/app/client/src/widgets/wds/WDSTableWidget/component/Constants.ts index 96f6337d4d..df90a199f9 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/Constants.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/component/Constants.ts @@ -27,6 +27,7 @@ export interface TableSizes { EDIT_ICON_TOP: number; ROW_VIRTUAL_OFFSET: number; VERTICAL_EDITOR_PADDING: number; + EDITABLE_CELL_HEIGHT: number; } export enum CompactModeTypes { @@ -63,6 +64,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { VERTICAL_EDITOR_PADDING: 0, EDIT_ICON_TOP: 10, ROW_VIRTUAL_OFFSET: 3, + EDITABLE_CELL_HEIGHT: 30, }, [CompactModeTypes.SHORT]: { COLUMN_HEADER_HEIGHT: 32, @@ -73,6 +75,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { VERTICAL_EDITOR_PADDING: 0, EDIT_ICON_TOP: 5, ROW_VIRTUAL_OFFSET: 1, + EDITABLE_CELL_HEIGHT: 20, }, [CompactModeTypes.TALL]: { COLUMN_HEADER_HEIGHT: 32, @@ -83,6 +86,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { VERTICAL_EDITOR_PADDING: 16, EDIT_ICON_TOP: 21, ROW_VIRTUAL_OFFSET: 3, + EDITABLE_CELL_HEIGHT: 30, }, }; @@ -192,6 +196,13 @@ export interface DateCellProperties { timePrecision?: TimePrecision; } +export interface CurrencyCellProperties { + currencyCode: string; + decimals: number; + thousandSeparator: boolean; + notation: Intl.NumberFormatOptions["notation"]; +} + export interface BaseCellProperties { horizontalAlignment?: CellAlignment; verticalAlignment?: VerticalAlignment; @@ -217,6 +228,7 @@ export interface CellLayoutProperties SelectCellProperties, ImageCellProperties, DateCellProperties, + CurrencyCellProperties, BaseCellProperties {} export interface TableColumnMetaProps { @@ -224,6 +236,7 @@ export interface TableColumnMetaProps { format?: string; inputFormat?: string; type: ColumnTypes; + decimals?: number; } export enum StickyType { @@ -330,11 +343,19 @@ export interface EditActionColumnProperties { selectOptions?: DropdownOption[] | DropdownOption[][]; } +export interface CurrencyColumnProperties { + currencyCode?: string; + decimals?: number; + thousandSeparator?: boolean; + notation?: Intl.NumberFormatOptions["notation"]; +} + export interface ColumnProperties extends ColumnBaseProperties, ColumnStyleProperties, DateColumnProperties, ColumnEditabilityProperties, + CurrencyColumnProperties, EditActionColumnProperties { allowSameOptionsInNewRow?: boolean; newRowSelectOptions?: DropdownOption[]; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/AutoToolTipComponent.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/AutoToolTipComponent.tsx index e9f665f4c1..522ba2a58b 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/AutoToolTipComponent.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/AutoToolTipComponent.tsx @@ -3,7 +3,7 @@ import { Tooltip } from "@blueprintjs/core"; import { CellWrapper, TooltipContentWrapper } from "../TableStyledWrappers"; import type { CellAlignment, VerticalAlignment } from "../Constants"; import styled from "styled-components"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { importSvg } from "design-system-old"; const OpenNewTabIcon = importSvg( diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/Button.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/Button.tsx index 8b8667ffb9..6267ea0cd6 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/Button.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/Button.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { ActionWrapper } from "../TableStyledWrappers"; import { BaseButton } from "widgets/ButtonWidget/component"; -import type { ButtonColumnActions } from "widgets/TableWidgetV2/constants"; +import type { ButtonColumnActions } from "widgets/wds/WDSTableWidget/constants"; import styled from "styled-components"; const StyledButton = styled(BaseButton)<{ diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/ButtonCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/ButtonCell.tsx index a4bfc928ea..2af13fb8ae 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/ButtonCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/ButtonCell.tsx @@ -4,7 +4,7 @@ import { CellWrapper } from "../TableStyledWrappers"; import type { BaseCellComponentProps } from "../Constants"; import { TABLE_SIZES } from "../Constants"; import { Button } from "./Button"; -import type { ButtonColumnActions } from "widgets/TableWidgetV2/constants"; +import type { ButtonColumnActions } from "widgets/wds/WDSTableWidget/constants"; import styled from "styled-components"; const StyledButton = styled(Button)<{ compactMode: string }>` diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/DateCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/DateCell.tsx index 9379eb1729..a60ae8f6a1 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/DateCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/DateCell.tsx @@ -9,7 +9,7 @@ import DateComponent from "widgets/DatePickerWidget2/component"; import { TimePrecision } from "widgets/DatePickerWidget2/constants"; import type { RenderDefaultPropsType } from "./PlainTextCell"; import styled from "styled-components"; -import { EditableCellActions } from "widgets/TableWidgetV2/constants"; +import { EditableCellActions } from "widgets/wds/WDSTableWidget/constants"; import { ISO_DATE_FORMAT } from "constants/WidgetValidation"; import moment from "moment"; import { BasicCell } from "./BasicCell"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/EditActionsCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/EditActionsCell.tsx index f268882926..5e65c9b162 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/EditActionsCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/EditActionsCell.tsx @@ -1,7 +1,7 @@ import React, { memo } from "react"; import type { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import type { ButtonColumnActions } from "widgets/TableWidgetV2/constants"; -import { EditableCellActions } from "widgets/TableWidgetV2/constants"; +import type { ButtonColumnActions } from "widgets/wds/WDSTableWidget/constants"; +import { EditableCellActions } from "widgets/wds/WDSTableWidget/constants"; import { Button } from "./Button"; import type { BaseCellComponentProps } from "../Constants"; import { CellWrapper } from "../TableStyledWrappers"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/HeaderCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/HeaderCell.tsx index 136e307ad3..56809cb60a 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/HeaderCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/HeaderCell.tsx @@ -20,7 +20,7 @@ import { StickyType, } from "../Constants"; import { TooltipContentWrapper } from "../TableStyledWrappers"; -import { isColumnTypeEditable } from "widgets/TableWidgetV2/widget/utilities"; +import { isColumnTypeEditable } from "widgets/wds/WDSTableWidget/widget/utilities"; import { Popover2 } from "@blueprintjs/popover2"; import { MenuDivider } from "@design-system/widgets-old"; import { importRemixIcon, importSvg } from "@design-system/widgets-old"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/InlineCellEditor.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/InlineCellEditor.tsx index cca2c292d7..e853050095 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/InlineCellEditor.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/InlineCellEditor.tsx @@ -1,12 +1,26 @@ import { Colors } from "constants/Colors"; import { isNil } from "lodash"; -import React, { useCallback, useLayoutEffect, useRef, useState } from "react"; +import React, { + useCallback, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; import styled from "styled-components"; +import type { InputHTMLType } from "widgets/BaseInputWidget/component"; import BaseInputComponent from "widgets/BaseInputWidget/component"; import { InputTypes } from "widgets/BaseInputWidget/constants"; -import type { EditableCell } from "widgets/TableWidgetV2/constants"; +import type { EditableCell } from "widgets/wds/WDSTableWidget/constants"; import type { VerticalAlignment } from "../Constants"; import { EDITABLE_CELL_PADDING_OFFSET, TABLE_SIZES } from "../Constants"; +import { + getLocaleDecimalSeperator, + getLocaleThousandSeparator, +} from "widgets/WidgetUtils"; +import { limitDecimalValue } from "widgets/CurrencyInputWidget/component/utilities"; +import * as Sentry from "@sentry/react"; +import { getLocale } from "utils/helpers"; const FOCUS_CLASS = "has-focus"; @@ -72,6 +86,15 @@ const Wrapper = styled.div<{ min-height: 34px; font-size: ${(props) => props.textSize}; } + + .currency-change-dropdown-trigger { + border: none; + height: ${(props) => + TABLE_SIZES[props.compactMode].EDITABLE_CELL_HEIGHT}px; + padding: 0 0 0 5px; + margin-right: 0; + } + .bp3-button-group.bp3-vertical { display: none; } @@ -99,10 +122,28 @@ const Wrapper = styled.div<{ } `; +function convertToNumber(inputValue: string) { + inputValue = inputValue.replace( + new RegExp(`[${getLocaleDecimalSeperator()}]`), + ".", + ); + + const parsedValue = Number(inputValue); + + if (isNaN(parsedValue) || inputValue.trim() === "" || isNil(inputValue)) { + return null; + } else if (Number.isFinite(parsedValue)) { + return parsedValue; + } else { + return null; + } +} + interface InlineEditorPropsType { accentColor: string; compactMode: string; - inputType: InputTypes.TEXT | InputTypes.NUMBER; + inputType: InputTypes; + inputHTMLType: InputHTMLType; multiline: boolean; onChange: (value: EditableCell["value"], inputValue: string) => void; onDiscard: () => void; @@ -116,13 +157,16 @@ interface InlineEditorPropsType { widgetId: string; paddedInput: boolean; autoFocus: boolean; + additionalProps: Record; } export function InlineCellEditor({ accentColor, + additionalProps = {}, allowCellWrapping, autoFocus, compactMode, + inputHTMLType, inputType = InputTypes.TEXT, isEditableCellValid, multiline, @@ -173,16 +217,21 @@ export function InlineCellEditor({ let value: EditableCell["value"] = inputValue; if (inputType === InputTypes.NUMBER) { - const parsedValue = Number(inputValue); + value = convertToNumber(inputValue); + } else if (inputType === InputTypes.CURRENCY) { + const decimalSeperator = getLocaleDecimalSeperator(); - if ( - isNaN(parsedValue) || - inputValue.trim() === "" || - isNil(inputValue) - ) { - value = null; - } else if (Number.isFinite(parsedValue)) { - value = parsedValue; + try { + if (inputValue && inputValue.includes(decimalSeperator)) { + inputValue = limitDecimalValue( + additionalProps.decimals as number, + inputValue, + ); + } + + value = convertToNumber(inputValue); + } catch (e) { + Sentry.captureException(e); } } @@ -201,6 +250,20 @@ export function InlineCellEditor({ } }, [multiline]); + const parsedValue = useMemo(() => { + if (inputType === InputTypes.CURRENCY && typeof value === "number") { + return Intl.NumberFormat(getLocale(), { + style: "decimal", + minimumFractionDigits: additionalProps.decimals as number, + maximumFractionDigits: additionalProps.decimals as number, + }) + .format(value) + .replaceAll(getLocaleThousandSeparator(), ""); + } else { + return value; + } + }, [value]); + return ( ); diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.test.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.test.tsx index da956d8e1c..533b1070ba 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.test.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.test.tsx @@ -1,5 +1,5 @@ import { getCellText } from "./PlainTextCell"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; describe("DefaultRendere - ", () => { describe("getCellText", () => { diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.tsx index 94d7e9a398..75d6cd2254 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/PlainTextCell.tsx @@ -4,17 +4,23 @@ import { isNumber, isNil } from "lodash"; import type { BaseCellComponentProps, VerticalAlignment } from "../Constants"; import { ALIGN_ITEMS } from "../Constants"; -import type { EditableCell } from "widgets/TableWidgetV2/constants"; +import type { EditableCell } from "widgets/wds/WDSTableWidget/constants"; import { ColumnTypes, EditableCellActions, -} from "widgets/TableWidgetV2/constants"; +} from "widgets/wds/WDSTableWidget/constants"; import { InputTypes } from "widgets/BaseInputWidget/constants"; import { CELL_WRAPPER_LINE_HEIGHT } from "../TableStyledWrappers"; import { BasicCell } from "./BasicCell"; import { InlineCellEditor } from "./InlineCellEditor"; import styled from "styled-components"; import fastdom from "fastdom"; +import CurrencyTypeDropdown, { + CurrencyDropdownOptions, +} from "widgets/CurrencyInputWidget/component/CurrencyCodeDropdown"; +import { getLocale } from "utils/helpers"; +import * as Sentry from "@sentry/react"; +import { getLocaleThousandSeparator } from "widgets/WidgetUtils"; const Container = styled.div<{ isCellEditMode?: boolean; @@ -58,6 +64,13 @@ export type RenderDefaultPropsType = BaseCellComponentProps & { isNewRow: boolean; }; +export interface RenderCurrencyPropsType { + currencyCode?: string; + decimals?: number; + notation?: Intl.NumberFormatOptions["notation"]; + thousandSeparator?: boolean; +} + interface editPropertyType { alias: string; onSubmitString: string; @@ -73,6 +86,8 @@ export function getCellText( if (value && columnType === ColumnTypes.URL && displayText) { text = displayText; + } else if (columnType === ColumnTypes.CURRENCY) { + text = value ?? ""; } else if (!isNil(value) && (!isNumber(value) || !isNaN(value))) { text = (value as string).toString(); } else { @@ -89,7 +104,9 @@ function getContentHeight(ref: RefObject) { ); } -function PlainTextCell(props: RenderDefaultPropsType & editPropertyType) { +function PlainTextCell( + props: RenderDefaultPropsType & editPropertyType & RenderCurrencyPropsType, +) { const { accentColor, alias, @@ -97,6 +114,8 @@ function PlainTextCell(props: RenderDefaultPropsType & editPropertyType) { cellBackground, columnType, compactMode, + currencyCode, + decimals, disabledEditIcon, disabledEditIconMessage, displayText, @@ -110,12 +129,14 @@ function PlainTextCell(props: RenderDefaultPropsType & editPropertyType) { isEditableCellValid, isHidden, isNewRow, + notation, onCellTextChange, onSubmitString, rowIndex, tableWidth, textColor, textSize, + thousandSeparator, toggleCellEditMode, validationErrorMessage, verticalAlignment, @@ -174,18 +195,82 @@ function PlainTextCell(props: RenderDefaultPropsType & editPropertyType) { } }, [value, isCellEditMode]); + const currency = useMemo( + () => CurrencyDropdownOptions.find((d) => d.value === currencyCode), + [currencyCode], + ); + + const formattedValue = useMemo(() => { + if (columnType === ColumnTypes.CURRENCY) { + try { + const floatVal = parseFloat(value); + + if (isNaN(floatVal)) { + return value; + } else { + let formattedValue = Intl.NumberFormat(getLocale(), { + style: "decimal", + minimumFractionDigits: decimals, + maximumFractionDigits: decimals, + notation: notation, + }).format(floatVal); + + if (!thousandSeparator) { + formattedValue = formattedValue.replaceAll( + getLocaleThousandSeparator(), + "", + ); + } + + return currency?.id + " " + formattedValue; + } + } catch (e) { + Sentry.captureException(e); + return value; + } + } else { + return value; + } + }, [value, decimals, currencyCode, notation, thousandSeparator, columnType]); + if (isCellEditMode) { + const [inputType, inputHTMLType]: [InputTypes, "TEXT" | "NUMBER"] = (() => { + switch (columnType) { + case ColumnTypes.NUMBER: + return [InputTypes.NUMBER, "NUMBER"]; + case ColumnTypes.CURRENCY: + return [InputTypes.CURRENCY, "NUMBER"]; + default: + return [InputTypes.TEXT, "TEXT"]; + } + })(); + + const additionalProps: Record = {}; + + if (inputType === InputTypes.CURRENCY) { + additionalProps.inputHTMLType = "NUMBER"; + additionalProps.leftIcon = ( + + ); + additionalProps.shouldUseLocale = true; + additionalProps.decimals = decimals; + } + editor = ( {editor} diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/SelectCell.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/SelectCell.tsx index 89c9a1fe15..6c2e742245 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/SelectCell.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/SelectCell.tsx @@ -5,7 +5,7 @@ import type { DropdownOption } from "widgets/SelectWidget/constants"; import type { BaseCellComponentProps } from "../Constants"; import { EDITABLE_CELL_PADDING_OFFSET, TABLE_SIZES } from "../Constants"; import { CellWrapper } from "../TableStyledWrappers"; -import type { EditableCellActions } from "widgets/TableWidgetV2/constants"; +import type { EditableCellActions } from "widgets/wds/WDSTableWidget/constants"; import { BasicCell } from "./BasicCell"; import { useCallback } from "react"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/menuButtonTableComponent.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/menuButtonTableComponent.tsx index bd3d9dcdf0..b83bb8776f 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/menuButtonTableComponent.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/cellComponents/menuButtonTableComponent.tsx @@ -26,7 +26,7 @@ import { Colors } from "constants/Colors"; import { getBooleanPropertyValue, getPropertyValue, -} from "widgets/TableWidgetV2/widget/utilities"; +} from "widgets/wds/WDSTableWidget/widget/utilities"; import type { ThemeProp } from "WidgetProvider/constants"; import type { ConfigureMenuItems, diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/header/TableColumnHeader.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/header/TableColumnHeader.tsx index c3e051bdd5..ec993dc179 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/header/TableColumnHeader.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/header/TableColumnHeader.tsx @@ -1,5 +1,5 @@ import React, { memo } from "react"; -import { getDragHandlers } from "widgets/TableWidgetV2/widget/utilities"; +import { getDragHandlers } from "widgets/wds/WDSTableWidget/widget/utilities"; import { HeaderCell } from "../cellComponents/HeaderCell"; import type { ReactTableColumnProps } from "../Constants"; import { StickyType } from "../Constants"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/component/header/actions/filter/FilterPaneContent.tsx b/app/client/src/widgets/wds/WDSTableWidget/component/header/actions/filter/FilterPaneContent.tsx index c27d1c556f..3f3393efa5 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/component/header/actions/filter/FilterPaneContent.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/component/header/actions/filter/FilterPaneContent.tsx @@ -22,7 +22,7 @@ import { cloneDeep } from "lodash"; import { ColumnTypes, FilterableColumnTypes, -} from "widgets/TableWidgetV2/constants"; +} from "widgets/wds/WDSTableWidget/constants"; import { generateReactKey } from "utils/generators"; import { importRemixIcon } from "design-system-old"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/constants.ts b/app/client/src/widgets/wds/WDSTableWidget/constants.ts index 7510ec1293..f7715da949 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/constants.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/constants.ts @@ -142,6 +142,7 @@ export enum ColumnTypes { EDIT_ACTIONS = "editActions", CHECKBOX = "checkbox", SWITCH = "switch", + CURRENCY = "currency", } export enum ReadOnlyColumnTypes { diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/derived.js b/app/client/src/widgets/wds/WDSTableWidget/widget/derived.js index 2f2ed87ff8..3975ab8a3d 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/derived.js +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/derived.js @@ -247,8 +247,10 @@ export default { Object.values(existingColumns).forEach((column) => { /* guard to not allow columns without id */ if (column.id) { - column.isAscOrder = column.id === sortByColumn ? isAscOrder : undefined; - columns.push(column); + columns.push({ + ...column, + isAscOrder: column.id === sortByColumn ? isAscOrder : undefined, + }); } }); @@ -348,6 +350,7 @@ export default { } else { switch (columnType) { case "number": + case "currency": return sortByOrder( Number(a[sortByColumnOriginalId]) > Number(b[sortByColumnOriginalId]), @@ -771,7 +774,7 @@ export default { }; let editableColumns = []; - const validatableColumns = ["text", "number"]; + const validatableColumns = ["text", "number", "currency"]; if (props.isAddRowInProgress) { Object.values(props.primaryColumns) @@ -822,6 +825,7 @@ export default { /* Column type related validations */ switch (editedColumn.columnType) { case "number": + case "currency": if ( !_.isNil(validation.min) && validation.min !== "" && diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSTableWidget/widget/index.tsx index 2cc64df75b..e49d3e51b3 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/index.tsx @@ -377,6 +377,7 @@ export class WDSTableWidget extends BaseWidget { static getAnvilConfig(): AnvilConfig | null { return { + isLargeWidget: true, widgetSize: { maxHeight: {}, maxWidth: {}, @@ -2489,6 +2490,8 @@ export class WDSTableWidget extends BaseWidget { cellBackground={cellProperties.cellBackground} columnType={column.columnType} compactMode={compactMode} + currencyCode={cellProperties.currencyCode} + decimals={cellProperties.decimals} disabledEditIcon={ shouldDisableEdit || this.props.isAddRowInProgress } @@ -2504,12 +2507,14 @@ export class WDSTableWidget extends BaseWidget { isEditableCellValid={this.isColumnCellValid(alias)} isHidden={isHidden} isNewRow={isNewRow} + notation={cellProperties.notation} onCellTextChange={this.onCellTextChange} onSubmitString={props.cell.column.columnProperties.onSubmit} rowIndex={rowIndex} tableWidth={this.props.componentWidth} textColor={cellProperties.textColor} textSize={cellProperties.textSize} + thousandSeparator={cellProperties.thousandSeparator} toggleCellEditMode={this.toggleCellEditMode} validationErrorMessage={validationErrorMessage} value={props.cell.value} diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Alignment.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Alignment.ts index b789db3a3b..781a0d3c0f 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Alignment.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Alignment.ts @@ -1,7 +1,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { hideByColumnType } from "../../propertyUtils"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; +import { hideByColumnType } from "../../propertyUtils"; export default { sectionName: "Alignment", @@ -53,6 +53,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, @@ -98,6 +99,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Basic.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Basic.ts index 59a1417d15..7b2e6622d2 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Basic.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Basic.ts @@ -1,6 +1,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ICON_NAMES } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes, ICON_NAMES } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, hideByMenuItemsSource, @@ -12,7 +12,6 @@ import { IconNames } from "@blueprintjs/icons"; import { MenuItemsSource } from "widgets/MenuButtonWidget/constants"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import configureMenuItemsConfig from "./childPanels/configureMenuItemsConfig"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; export default { sectionName: "Basic", diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/BorderAndShadow.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/BorderAndShadow.ts index dc7239da03..0bacee2d36 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/BorderAndShadow.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/BorderAndShadow.ts @@ -1,10 +1,10 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, removeBoxShadowColorProp, } from "../../propertyUtils"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; export default { sectionName: "Border and shadow", diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Color.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Color.ts index d95c4bb0fa..1b6b9b0360 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Color.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Color.ts @@ -1,7 +1,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { hideByColumnType } from "../../propertyUtils"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; +import { hideByColumnType } from "../../propertyUtils"; export default { sectionName: "Color", @@ -100,6 +100,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ]); }, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Data.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Data.ts index 956bf36f02..b2689a9c2f 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Data.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Data.ts @@ -1,19 +1,23 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { DateInputFormat } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { + ColumnTypes, + DateInputFormat, +} from "widgets/wds/WDSTableWidget/constants"; import { get } from "lodash"; import { getBasePropertyPath, hideByColumnType, showByColumnType, uniqueColumnAliasValidation, + updateCurrencyDefaultValues, updateMenuItemsSource, updateNumberColumnTypeTextAlignment, updateThemeStylesheetsInColumns, } from "../../propertyUtils"; import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; +import { CurrencyDropdownOptions } from "widgets/CurrencyInputWidget/component/CurrencyCodeDropdown"; export default { sectionName: "Data", @@ -33,6 +37,10 @@ export default { label: "Checkbox", value: ColumnTypes.CHECKBOX, }, + { + label: "Currency", + value: ColumnTypes.CURRENCY, + }, { label: "Date", value: ColumnTypes.DATE, @@ -78,6 +86,7 @@ export default { updateNumberColumnTypeTextAlignment, updateThemeStylesheetsInColumns, updateMenuItemsSource, + updateCurrencyDefaultValues, ]), dependencies: ["primaryColumns", "columnOrder", "childStylesheet"], isBindProperty: false, @@ -109,6 +118,7 @@ export default { ColumnTypes.DATE, ColumnTypes.IMAGE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.TEXT, ColumnTypes.VIDEO, ColumnTypes.URL, @@ -164,6 +174,7 @@ export default { ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, ColumnTypes.SELECT, + ColumnTypes.CURRENCY, ]); }, dependencies: ["primaryColumns", "columnOrder"], @@ -430,5 +441,116 @@ export default { }, isTriggerProperty: false, }, + { + helpText: "Changes the type of currency", + propertyName: "currencyCode", + label: "Currency", + enableSearch: true, + dropdownHeight: "156px", + controlType: "DROP_DOWN", + searchPlaceholderText: "Search by code or name", + options: CurrencyDropdownOptions, + virtual: true, + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + default: "USD", + required: true, + allowedValues: CurrencyDropdownOptions.map((option) => option.value), + }, + }, + hidden: (props: TableWidgetProps, propertyPath: string) => { + const baseProperty = getBasePropertyPath(propertyPath); + const columnType = get(props, `${baseProperty}.columnType`, ""); + return columnType !== ColumnTypes.CURRENCY; + }, + dependencies: ["primaryColumns", "columnType"], + }, + { + helpText: "No. of decimals in currency input", + propertyName: "decimals", + label: "Decimals allowed", + controlType: "DROP_DOWN", + options: [ + { + label: "0", + value: 0, + }, + { + label: "1", + value: 1, + }, + { + label: "2", + value: 2, + }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.NUMBER, + params: { + min: 0, + max: 2, + default: 0, + required: true, + }, + }, + hidden: (props: TableWidgetProps, propertyPath: string) => { + const baseProperty = getBasePropertyPath(propertyPath); + const columnType = get(props, `${baseProperty}.columnType`, ""); + return columnType !== ColumnTypes.CURRENCY; + }, + dependencies: ["primaryColumns", "columnType"], + }, + { + propertyName: "thousandSeparator", + helpText: "formats the currency with a thousand separator", + label: "Thousand separator", + controlType: "SWITCH", + dependencies: ["primaryColumns", "columnType"], + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + hidden: (props: TableWidgetProps, propertyPath: string) => { + const baseProperty = getBasePropertyPath(propertyPath); + const columnType = get(props, `${baseProperty}.columnType`, ""); + return columnType !== ColumnTypes.CURRENCY; + }, + }, + { + propertyName: "notation", + helpText: "Displays the currency in standard or compact notation", + label: "Notation", + controlType: "DROP_DOWN", + options: [ + { + label: "Standard", + value: "standard", + }, + { + label: "Compact", + value: "compact", + }, + ], + dependencies: ["primaryColumns", "columnType"], + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { default: "standard", allowedValues: ["standard", "compact"] }, + }, + hidden: (props: TableWidgetProps, propertyPath: string) => { + const baseProperty = getBasePropertyPath(propertyPath); + const columnType = get(props, `${baseProperty}.columnType`, ""); + return columnType !== ColumnTypes.CURRENCY; + }, + }, ], }; diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DateProperties.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DateProperties.ts index 578e4ff8e3..e6a925e4b9 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DateProperties.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DateProperties.ts @@ -1,6 +1,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { get } from "lodash"; import { allowedFirstDayOfWeekRange } from "../../propertyUtils"; import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DiscardButtonproperties.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DiscardButtonproperties.ts index d36af93ba6..cfed9da58d 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DiscardButtonproperties.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/DiscardButtonproperties.ts @@ -1,10 +1,10 @@ import { get } from "lodash"; import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, getBasePropertyPath } from "../../propertyUtils"; import { ButtonVariantTypes } from "components/constants"; import { ICON_NAMES } from "WidgetProvider/constants"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; export default { sectionName: "Discard Button", diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Events.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Events.ts index a5f3361afb..b4a2064a4e 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Events.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Events.ts @@ -1,4 +1,5 @@ -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { get } from "lodash"; import { getBasePropertyPath, @@ -6,7 +7,6 @@ import { hideByColumnType, getColumnPath, } from "../../propertyUtils"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; export default { sectionName: "Events", @@ -20,6 +20,7 @@ export default { !( columnType === ColumnTypes.TEXT || columnType === ColumnTypes.NUMBER || + columnType === ColumnTypes.CURRENCY || columnType === ColumnTypes.CHECKBOX || columnType === ColumnTypes.SWITCH || columnType === ColumnTypes.SELECT || @@ -56,7 +57,9 @@ export default { const isEditable = get(props, `${baseProperty}.isEditable`, ""); return ( !( - columnType === ColumnTypes.TEXT || columnType === ColumnTypes.NUMBER + columnType === ColumnTypes.TEXT || + columnType === ColumnTypes.NUMBER || + columnType === ColumnTypes.CURRENCY ) || !isEditable ); }, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/General.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/General.ts index 576cfdb1d3..5482befbac 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/General.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/General.ts @@ -1,5 +1,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { get } from "lodash"; import { getBasePropertyPath, @@ -11,8 +12,7 @@ import { import { isColumnTypeEditable } from "../../utilities"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; import { ButtonVariantTypes } from "components/constants"; -import { StickyType } from "widgets/TableWidgetV2/component/Constants"; -import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; +import { StickyType } from "widgets/wds/WDSTableWidget/component/Constants"; export default { sectionName: "General", diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Icon.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Icon.ts index 71d1db85ee..ee1c04f99b 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Icon.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Icon.ts @@ -1,6 +1,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ICON_NAMES } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ICON_NAMES } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, updateIconAlignment } from "../../propertyUtils"; import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/SaveButtonProperties.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/SaveButtonProperties.ts index e6a17fd0f1..54fac8d27a 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/SaveButtonProperties.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/SaveButtonProperties.ts @@ -1,6 +1,6 @@ import { get } from "lodash"; import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, getBasePropertyPath } from "../../propertyUtils"; import { ButtonVariantTypes } from "components/constants"; import { ICON_NAMES } from "WidgetProvider/constants"; diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Select.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Select.ts index c0868afa8c..450820142f 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Select.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Select.ts @@ -1,6 +1,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { get } from "lodash"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { getBasePropertyPath, hideByColumnType, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/TextFormatting.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/TextFormatting.ts index f9572f6102..cfcd154626 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/TextFormatting.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/TextFormatting.ts @@ -1,5 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, showByColumnType } from "../../propertyUtils"; import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; @@ -57,6 +57,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ]); }, @@ -96,6 +97,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ]); }, @@ -140,6 +142,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, @@ -186,6 +189,7 @@ export default { ColumnTypes.TEXT, ColumnTypes.DATE, ColumnTypes.NUMBER, + ColumnTypes.CURRENCY, ColumnTypes.URL, ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validation.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validation.ts index fa3533e445..9606a8826c 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validation.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validation.ts @@ -1,4 +1,4 @@ -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { get } from "lodash"; import { hideByColumnType } from "../../propertyUtils"; import commonValidations from "./Validations/Common"; @@ -16,7 +16,12 @@ export default { hideByColumnType( props, propertyPath, - [ColumnTypes.TEXT, ColumnTypes.NUMBER, ColumnTypes.DATE], + [ + ColumnTypes.TEXT, + ColumnTypes.NUMBER, + ColumnTypes.DATE, + ColumnTypes.CURRENCY, + ], true, ) ); diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Common.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Common.ts index f2aca64ab1..1c03396538 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Common.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Common.ts @@ -1,10 +1,10 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { showByColumnType, getColumnPath, -} from "widgets/TableWidgetV2/widget/propertyUtils"; +} from "widgets/wds/WDSTableWidget/widget/propertyUtils"; export default [ { diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Date.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Date.ts index e6fbd37eee..81543987ac 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Date.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Date.ts @@ -1,9 +1,9 @@ -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { getColumnPath, hideByColumnType, -} from "widgets/TableWidgetV2/widget/propertyUtils"; +} from "widgets/wds/WDSTableWidget/widget/propertyUtils"; export default [ { diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Number.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Number.ts index b8d69d1859..3eb9db6f14 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Number.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/Validations/Number.ts @@ -1,10 +1,10 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; -import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; +import { ColumnTypes } from "widgets/wds/WDSTableWidget/constants"; import { hideByColumnType, getColumnPath, -} from "widgets/TableWidgetV2/widget/propertyUtils"; +} from "widgets/wds/WDSTableWidget/widget/propertyUtils"; export default [ { @@ -21,7 +21,12 @@ export default [ }, hidden: (props: TableWidgetProps, propertyPath: string) => { const path = getColumnPath(propertyPath); - return hideByColumnType(props, path, [ColumnTypes.NUMBER], true); + return hideByColumnType( + props, + path, + [ColumnTypes.NUMBER, ColumnTypes.CURRENCY], + true, + ); }, dependencies: ["primaryColumns"], }, @@ -39,7 +44,12 @@ export default [ }, hidden: (props: TableWidgetProps, propertyPath: string) => { const path = getColumnPath(propertyPath); - return hideByColumnType(props, path, [ColumnTypes.NUMBER], true); + return hideByColumnType( + props, + path, + [ColumnTypes.NUMBER, ColumnTypes.CURRENCY], + true, + ); }, dependencies: ["primaryColumns"], }, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/childPanels/configureMenuItemsConfig.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/childPanels/configureMenuItemsConfig.ts index 3c7dc14dfa..30414e721e 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/childPanels/configureMenuItemsConfig.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/PanelConfig/childPanels/configureMenuItemsConfig.ts @@ -7,8 +7,8 @@ import { iconNamesForEachRowValidation, iconPositionForEachRowValidation, textForEachRowValidation, -} from "widgets/TableWidgetV2/widget/propertyUtils"; -import { getSourceDataAndCaluclateKeysForEventAutoComplete } from "widgets/TableWidgetV2/widget/utilities"; +} from "widgets/wds/WDSTableWidget/widget/propertyUtils"; +import { getSourceDataAndCaluclateKeysForEventAutoComplete } from "widgets/wds/WDSTableWidget/widget/utilities"; export default { editableTitle: false, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/contentConfig.ts index 5e7d4a26f9..bf37c13ebb 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyConfig/contentConfig.ts @@ -6,9 +6,9 @@ import type { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; -import type { TableWidgetProps } from "widgets/TableWidgetV2/constants"; +import type { TableWidgetProps } from "widgets/wds/WDSTableWidget/constants"; import { ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING } from "../../constants"; -import { InlineEditingSaveOptions } from "widgets/TableWidgetV2/constants"; +import { InlineEditingSaveOptions } from "widgets/wds/WDSTableWidget/constants"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; import { tableDataValidation, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyUtils.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyUtils.ts index dbd9b3dcca..9a5d4ba9c0 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/propertyUtils.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/propertyUtils.ts @@ -774,6 +774,50 @@ export const updateMenuItemsSource = ( return propertiesToUpdate?.length ? propertiesToUpdate : undefined; }; +export const updateCurrencyDefaultValues = ( + props: TableWidgetProps, + propertyPath: string, + propertyValue: unknown, +): Array<{ propertyPath: string; propertyValue: unknown }> | undefined => { + const propertiesToUpdate: Array<{ + propertyPath: string; + propertyValue: unknown; + }> = []; + const baseProperty = getBasePropertyPath(propertyPath); + + if (propertyValue === ColumnTypes.CURRENCY) { + if (!get(props, `${baseProperty}.currencyCode`)) { + propertiesToUpdate.push({ + propertyPath: `${baseProperty}.currencyCode`, + propertyValue: "USD", + }); + } + + if (get(props, `${baseProperty}.decimals`) === undefined) { + propertiesToUpdate.push({ + propertyPath: `${baseProperty}.decimals`, + propertyValue: 0, + }); + } + + if (get(props, `${baseProperty}.notation`) === undefined) { + propertiesToUpdate.push({ + propertyPath: `${baseProperty}.notation`, + propertyValue: "standard", + }); + } + + if (get(props, `${baseProperty}.thousandSeparator`) === undefined) { + propertiesToUpdate.push({ + propertyPath: `${baseProperty}.thousandSeparator`, + propertyValue: true, + }); + } + } + + return propertiesToUpdate?.length ? propertiesToUpdate : undefined; +}; + export function selectColumnOptionsValidation( value: unknown, props: TableWidgetProps, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/reactTableUtils/getColumnsPureFn.tsx b/app/client/src/widgets/wds/WDSTableWidget/widget/reactTableUtils/getColumnsPureFn.tsx index 21019f627a..def9cef913 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/reactTableUtils/getColumnsPureFn.tsx +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/reactTableUtils/getColumnsPureFn.tsx @@ -56,6 +56,7 @@ export const getColumnsPureFn: getColumns = ( type: column.columnType, format: column.outputFormat || "", inputFormat: column.inputFormat || "", + decimals: column.decimals || 0, }, columnProperties: column, Cell: renderCell, diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.test.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.test.ts index cd4d9b2968..f8a36f503f 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.test.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.test.ts @@ -2,6 +2,7 @@ import type { ColumnProperties, TableStyles } from "../component/Constants"; import { StickyType } from "../component/Constants"; import { ColumnTypes } from "../constants"; import { + convertNumToCompactString, escapeString, generateNewColumnOrderFromStickyValue, getAllTableColumnKeys, @@ -2671,3 +2672,22 @@ describe("getSelectOptions", () => { ]); }); }); + +describe("convertNumToCompactString", () => { + it("formats a number in thousands (K)", () => { + expect(convertNumToCompactString(5000)).toBe("5.0K"); + expect(convertNumToCompactString(9999)).toBe("10.0K"); // Rounding + expect(convertNumToCompactString(123456)).toBe("123.5K"); // Rounding with precision + }); + + it("formats a number in millions (M)", () => { + expect(convertNumToCompactString(1500000)).toBe("1.5M"); + expect(convertNumToCompactString(9999999)).toBe("10.0M"); // Rounding + expect(convertNumToCompactString(123456789)).toBe("123.5M"); // Rounding with precision + }); + + it("formats a number less than 1000 as is", () => { + expect(convertNumToCompactString(42)).toBe("42"); + expect(convertNumToCompactString(999)).toBe("999"); + }); +}); diff --git a/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.ts b/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.ts index b20841cd51..cae3e18557 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/widget/utilities.ts @@ -221,6 +221,10 @@ export function getDefaultColumnProperties( )}"]))}}`, sticky: StickyType.NONE, validation: {}, + currencyCode: "USD", + decimals: 0, + thousandSeparator: true, + notation: "standard" as Intl.NumberFormatOptions["notation"], }; return columnProps; @@ -508,6 +512,14 @@ export const getCellProperties = ( rowIndex, true, ), + currencyCode: getPropertyValue( + columnProperties.currencyCode, + rowIndex, + true, + ), + decimals: columnProperties.decimals, + thousandSeparator: !!columnProperties.thousandSeparator, + notation: columnProperties.notation, } as CellLayoutProperties; } return {} as CellLayoutProperties; @@ -520,6 +532,7 @@ const EdtiableColumnTypes: string[] = [ ColumnTypes.CHECKBOX, ColumnTypes.SWITCH, ColumnTypes.DATE, + ColumnTypes.CURRENCY, ]; export function isColumnTypeEditable(columnType: string) { @@ -1102,3 +1115,13 @@ export const getSelectOptions = ( return getArrayPropertyValue(columnProperties.selectOptions, rowIndex); } }; + +export function convertNumToCompactString(num: number) { + if (num >= 1e6) { + return (num / 1e6).toFixed(1) + "M"; + } else if (num >= 1e3) { + return (num / 1e3).toFixed(1) + "K"; + } else { + return num.toString(); + } +} diff --git a/app/client/src/widgets/wds/WDSTextWidget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSTextWidget/config/anvilConfig.ts index 83eda12253..1e5907856f 100644 --- a/app/client/src/widgets/wds/WDSTextWidget/config/anvilConfig.ts +++ b/app/client/src/widgets/wds/WDSTextWidget/config/anvilConfig.ts @@ -1,6 +1,7 @@ import type { AnvilConfig } from "WidgetProvider/constants"; export const anvilConfig: AnvilConfig = { + isLargeWidget: false, widgetSize: { maxHeight: {}, maxWidth: {}, diff --git a/app/client/src/widgets/wds/WDSTextWidget/config/propertyPaneConfig/styleConfig.ts b/app/client/src/widgets/wds/WDSTextWidget/config/propertyPaneConfig/styleConfig.ts index 45002e2e8b..19e6627e2d 100644 --- a/app/client/src/widgets/wds/WDSTextWidget/config/propertyPaneConfig/styleConfig.ts +++ b/app/client/src/widgets/wds/WDSTextWidget/config/propertyPaneConfig/styleConfig.ts @@ -81,10 +81,6 @@ export const propertyPaneStyleConfig = [ label: "Warning", value: "warning", }, - { - label: "Heading", - value: "heading", - }, ], isJSConvertible: true, isBindProperty: true, diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index c421a9782d..bebb1792dc 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -242,7 +242,11 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { !isPreviewMode; widgetProps.mainCanvasWidth = mainCanvasWidth; - if (layoutSystemType !== LayoutSystemTypes.ANVIL) { + if (layoutSystemType === LayoutSystemTypes.ANVIL) { + if (shouldCollapseWidgetInViewOrPreviewMode) { + return null; + } + } else { // We don't render invisible widgets in view mode if (shouldCollapseWidgetInViewOrPreviewMode) { // This flag (isMetaWidget) is used to prevent the Auto height saga from updating diff --git a/app/client/src/workers/Evaluation/helpers.test.ts b/app/client/src/workers/Evaluation/helpers.test.ts new file mode 100644 index 0000000000..3e367b01e6 --- /dev/null +++ b/app/client/src/workers/Evaluation/helpers.test.ts @@ -0,0 +1,95 @@ +import { fn_keys, stringifyFnsInObject } from "./helpers"; + +describe("stringifyFnsInObject", () => { + it("includes full path of key having a function in the parent object", () => { + const obj = { + key1: "value", + key2: { + key3: { + fnKey: () => {}, + }, + }, + }; + const result = stringifyFnsInObject(obj); + + expect(result[fn_keys]).toEqual(["key2.key3.fnKey"]); + expect(result).toEqual({ + __fn_keys__: ["key2.key3.fnKey"], + key1: "value", + key2: { + key3: { + fnKey: "() => { }", + }, + }, + }); + }); + + it("includes an array index if a function is present inside an array", () => { + const obj = { + key1: "value", + key2: { + key3: { + key4: ["string1", () => {}, "string3"], + }, + }, + }; + const result = stringifyFnsInObject(obj); + + expect(result[fn_keys]).toEqual(["key2.key3.key4.[1]"]); + expect(result).toEqual({ + __fn_keys__: ["key2.key3.key4.[1]"], + key1: "value", + key2: { + key3: { + key4: ["string1", "() => { }", "string3"], + }, + }, + }); + }); + + it("includes an array index if a function is present inside a nested object inside an array", () => { + const obj = { + key1: "value", + key2: { + key3: { + key4: ["string1", { key5: () => {}, key6: "value" }, "string3"], + }, + }, + }; + const result = stringifyFnsInObject(obj); + + expect(result[fn_keys]).toEqual(["key2.key3.key4.[1].key5"]); + expect(result).toEqual({ + __fn_keys__: ["key2.key3.key4.[1].key5"], + key1: "value", + key2: { + key3: { + key4: ["string1", { key5: "() => { }", key6: "value" }, "string3"], + }, + }, + }); + }); + + it("includes a nested array index if a function is present inside a nested array inside an array", () => { + const obj = { + key1: "value", + key2: { + key3: { + key4: ["string1", [() => {}], "string3"], + }, + }, + }; + const result = stringifyFnsInObject(obj); + + expect(result[fn_keys]).toEqual(["key2.key3.key4.[1].[0]"]); + expect(result).toEqual({ + __fn_keys__: ["key2.key3.key4.[1].[0]"], + key1: "value", + key2: { + key3: { + key4: ["string1", ["() => { }"], "string3"], + }, + }, + }); + }); +}); diff --git a/app/client/src/workers/Evaluation/helpers.ts b/app/client/src/workers/Evaluation/helpers.ts index 4915b92456..9fa55e4bae 100644 --- a/app/client/src/workers/Evaluation/helpers.ts +++ b/app/client/src/workers/Evaluation/helpers.ts @@ -3,10 +3,12 @@ import type { Diff } from "deep-diff"; import { diff } from "deep-diff"; import type { DataTree } from "entities/DataTree/dataTreeTypes"; import equal from "fast-deep-equal"; -import { get, isNumber, isObject } from "lodash"; +import { get, isNumber, isObject, set } from "lodash"; import { isMoment } from "moment"; import { EvalErrorTypes } from "utils/DynamicBindingUtils"; +export const fn_keys: string = "__fn_keys__"; + export interface DiffReferenceState { kind: "referenceState"; path: any[]; @@ -94,6 +96,71 @@ const generateWithKey = (basePath: any, key: any) => { }; }; +export const stringifyFnsInObject = ( + userObject: Record, +): Record => { + const paths: string[] = parseFunctionsInObject(userObject); + const fnStrings: string[] = []; + + for (const path of paths) { + const fnValue: any = get(userObject, path); + fnStrings.push(fnValue.toString()); + } + + const output = JSON.parse(JSON.stringify(userObject)); + for (const [index, parsedFnString] of fnStrings.entries()) { + set(output, paths[index], parsedFnString); + } + + output[fn_keys] = paths; + return output; +}; + +const constructPath = (existingPath: string, suffix: string): string => { + if (existingPath.length > 0) { + return `${existingPath}.${suffix}`; + } else { + return suffix; + } +}; + +const parseFunctionsInObject = ( + userObject: Record, + paths: string[] = [], + path: string = "", +): string[] => { + if (Array.isArray(userObject)) { + for (let i = 0; i < userObject.length; i++) { + const arrayValue = userObject[i]; + if (typeof arrayValue == "function") { + paths.push(constructPath(path, `[${i}]`)); + } else if (typeof arrayValue == "object") { + parseFunctionsInObject( + arrayValue, + paths, + constructPath(path, `[${i}]`), + ); + } + } + } else { + const keys = Object.keys(userObject); + for (const key of keys) { + const value = userObject[key]; + if (typeof value == "function") { + paths.push(constructPath(path, key)); + } else if (typeof value == "object") { + parseFunctionsInObject( + value as Record, + paths, + constructPath(path, key), + ); + } + } + } + + return paths; +}; + const isLargeCollection = (val: any) => { if (!Array.isArray(val)) return false; const rowSize = !isObject(val[0]) ? 1 : Object.keys(val[0]).length; diff --git a/app/client/src/workers/Evaluation/validations.ts b/app/client/src/workers/Evaluation/validations.ts index 837d3991d9..c3e02e8a7e 100644 --- a/app/client/src/workers/Evaluation/validations.ts +++ b/app/client/src/workers/Evaluation/validations.ts @@ -19,7 +19,11 @@ import type { ValidationConfig } from "constants/PropertyControlConstants"; import getIsSafeURL from "utils/validation/getIsSafeURL"; import * as log from "loglevel"; -import { countOccurrences, findDuplicateIndex } from "./helpers"; +import { + countOccurrences, + findDuplicateIndex, + stringifyFnsInObject, +} from "./helpers"; export const UNDEFINED_VALIDATION = "UNDEFINED_VALIDATION"; export const VALIDATION_ERROR_COUNT_THRESHOLD = 10; @@ -360,6 +364,7 @@ export function getExpectedType(config?: ValidationConfig): string | undefined { return validationType; case ValidationTypes.OBJECT: + case ValidationTypes.OBJECT_WITH_FUNCTION: let objectType = "Object"; if (config.params?.allowedKeys) { objectType = "{"; @@ -1248,6 +1253,37 @@ export const VALIDATORS: Record = { parsed: resultValue, }; }, + [ValidationTypes.OBJECT_WITH_FUNCTION]: ( + config: ValidationConfig, + value: any, + props: Record, + propertyPath: string, + ): ValidationResponse => { + const invalidResponse = { + isValid: true, + parsed: {}, + messages: [ + { + name: "TypeError", + message: `${WIDGET_TYPE_VALIDATION_ERROR} ${getExpectedType(config)}`, + }, + ], + }; + + const { isValid, messages, parsed } = VALIDATORS[ValidationTypes.OBJECT]( + config, + value, + props, + propertyPath, + ); + + if (!isValid) { + return { isValid, messages, parsed }; // return the expected type + } else { + return { isValid, messages, parsed: stringifyFnsInObject(parsed) }; + } + }, + [ValidationTypes.UNION]: ( config: ValidationConfig, value: unknown, diff --git a/app/client/src/workers/common/JSLibrary/index.ts b/app/client/src/workers/common/JSLibrary/index.ts index 9f23b48d71..bf40677204 100644 --- a/app/client/src/workers/common/JSLibrary/index.ts +++ b/app/client/src/workers/common/JSLibrary/index.ts @@ -7,6 +7,7 @@ export interface JSLibrary { name: string; accessor: string[]; url?: string; + id?: string; } export const defaultLibraries: JSLibrary[] = [ diff --git a/app/client/src/workers/common/JSLibrary/ternDefinitionGenerator.ts b/app/client/src/workers/common/JSLibrary/ternDefinitionGenerator.ts index 1a5e416a50..d90e567aba 100644 --- a/app/client/src/workers/common/JSLibrary/ternDefinitionGenerator.ts +++ b/app/client/src/workers/common/JSLibrary/ternDefinitionGenerator.ts @@ -19,6 +19,13 @@ export function getTernDocType(obj: any) { } } +export function typeToTernType(type: string) { + if (type === "boolean") return "bool"; + if (type === "undefined") return "?"; + if (type === "function") return "fn()"; + return "string"; +} + const ignoredKeys = [ "constructor", "WINDOW", diff --git a/app/client/test/__mocks__/CodeMirrorEditorMock.ts b/app/client/test/__mocks__/CodeMirrorEditorMock.ts index 852b49b909..d417712980 100644 --- a/app/client/test/__mocks__/CodeMirrorEditorMock.ts +++ b/app/client/test/__mocks__/CodeMirrorEditorMock.ts @@ -18,6 +18,7 @@ export const MockCodemirrorEditor = { getRange: jest.fn(), getDoc: jest.fn(), getTokenAt: jest.fn(), + getMode: jest.fn(), constructor: MockCodemirrorNamespace, }; diff --git a/app/client/test/testCommon.ts b/app/client/test/testCommon.ts index 512ab20be4..525940a30e 100644 --- a/app/client/test/testCommon.ts +++ b/app/client/test/testCommon.ts @@ -6,7 +6,7 @@ import { APP_MODE } from "entities/App"; import { useDispatch } from "react-redux"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { createSelector } from "reselect"; -import { getCanvasWidgetsPayload } from "sagas/PageSagas"; +import { getCanvasWidgetsPayload } from "@appsmith/sagas/PageSagas"; import { getCanvasWidgets } from "@appsmith/selectors/entitiesSelector"; import { editorInitializer } from "utils/editor/EditorUtils"; import { extractCurrentDSL } from "utils/WidgetPropsUtils"; diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 1a8f0a58ff..ef1ee00598 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -185,6 +185,682 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 9fdb3e837fc54119b017ea34fd0a6d71d2c88075d99e1e818a5158e0ad30ced67ddbcc423a11ceeef6cc465ab5ffd91830acab516470b48237ca7abd51be9642 + languageName: node + linkType: hard + +"@aws-crypto/crc32c@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32c@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 0a116b5d1c5b09a3dde65aab04a07b32f543e87b68f2d175081e3f4a1a17502343f223d691dd883ace1ddce65cd40093673e7c7415dcd99062202ba87ffb4038 + languageName: node + linkType: hard + +"@aws-crypto/ie11-detection@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/ie11-detection@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 299b2ddd46eddac1f2d54d91386ceb37af81aef8a800669281c73d634ed17fd855dcfb8b3157f2879344b93a2666a6d602550eb84b71e4d7868100ad6da8f803 + languageName: node + linkType: hard + +"@aws-crypto/sha1-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha1-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: 78c379e105a0c4e7b2ed745dffd8f55054d7dde8b350b61de682bbc3cd081a50e2f87861954fa9cd53c7ea711ebca1ca0137b14cb36483efc971f60f573cf129 + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/sha256-js": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: ca89456bf508db2e08060a7f656460db97ac9a15b11e39d6fa7665e2b156508a1758695bff8e82d0a00178d6ac5c36f35eb4bcfac2e48621265224ca14a19bd2 + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:3.0.0, @aws-crypto/sha256-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-js@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 644ded32ea310237811afae873d3c7320739cb6f6cc39dced9c94801379e68e5ee2cca0c34f0384793fa9e750a7e0a5e2468f95754bd08e6fd72ab833c8fe23c + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 35479a1558db9e9a521df6877a99f95670e972c602f2a0349303477e5d638a5baf569fb037c853710e382086e6fd77e8ed58d3fb9b49f6e1186a9d26ce7be006 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/util@npm:3.0.0" + dependencies: + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: d29d5545048721aae3d60b236708535059733019a105f8a64b4e4a8eab7cf8dde1546dc56bff7de20d36140a4d1f0f4693e639c5732a7059273a7b1e56354776 + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/client-s3@npm:3.441.0" + dependencies: + "@aws-crypto/sha1-browser": 3.0.0 + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.441.0 + "@aws-sdk/core": 3.441.0 + "@aws-sdk/credential-provider-node": 3.441.0 + "@aws-sdk/middleware-bucket-endpoint": 3.433.0 + "@aws-sdk/middleware-expect-continue": 3.433.0 + "@aws-sdk/middleware-flexible-checksums": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-location-constraint": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-sdk-s3": 3.440.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-ssec": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.438.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/signature-v4-multi-region": 3.437.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.438.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.437.0 + "@aws-sdk/xml-builder": 3.310.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/eventstream-serde-browser": ^2.0.12 + "@smithy/eventstream-serde-config-resolver": ^2.0.12 + "@smithy/eventstream-serde-node": ^2.0.12 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-blob-browser": ^2.0.12 + "@smithy/hash-node": ^2.0.12 + "@smithy/hash-stream-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/md5-js": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-endpoints": ^1.0.2 + "@smithy/util-retry": ^2.0.5 + "@smithy/util-stream": ^2.0.17 + "@smithy/util-utf8": ^2.0.0 + "@smithy/util-waiter": ^2.0.12 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: bac08b8fc9025184b28403bda5aea24ab3e722531c525dab32cfc12683c6e6e1bb23c9eae802ca24355134ccb918e2d0fc3cc3c730575fe570bf2af4fca6deeb + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/client-sso@npm:3.441.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/core": 3.441.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.438.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.438.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.437.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-endpoints": ^1.0.2 + "@smithy/util-retry": ^2.0.5 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: e901c2619d0a264029a84016cabf3dd7ffc24b8b7d6c44037f0c463e059d14d652873db4ec6a8a786a7cee2c9ea5b2aa81f50001793afbc6a176faedc06905ec + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/client-sts@npm:3.441.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/core": 3.441.0 + "@aws-sdk/credential-provider-node": 3.441.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-sdk-sts": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.438.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.438.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.437.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-endpoints": ^1.0.2 + "@smithy/util-retry": ^2.0.5 + "@smithy/util-utf8": ^2.0.0 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 3da7c3777f1f6d890534bf7a68ff29497dee42612ff82874ba7d8359bdb1fdf3aacaad9194dd55e0a767443b20f7189a2318547fad7d9c37335ae377a290960e + languageName: node + linkType: hard + +"@aws-sdk/core@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/core@npm:3.441.0" + dependencies: + "@smithy/smithy-client": ^2.1.12 + checksum: 9cf5046e16ac3e871cd3bd4f8c434ed508d6282b9da2ed806eb2c998410e2d6bfeb32c2f65419228803c56f14797ef5a772de81484943b83d4f1cb8a89bfdaf2 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: bc8d2afb35245d1c4aea85d0a2fb56ab85b7a48ddf92d90fc7351c871e8fb90622d6662e066a0a0cf6f493a94f8aba24061f663450bafeec6a70cd6e6af07e29 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.441.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.433.0 + "@aws-sdk/credential-provider-process": 3.433.0 + "@aws-sdk/credential-provider-sso": 3.441.0 + "@aws-sdk/credential-provider-web-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: e3b9bc635bd8e5e4b6866eb49b78d0d7a447038970ea318e8d41d184a4a704dafd8f527180c36a2a6bfb9a48c20f03b947c31b5e520752780153f2a27dab775a + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.441.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.433.0 + "@aws-sdk/credential-provider-ini": 3.441.0 + "@aws-sdk/credential-provider-process": 3.433.0 + "@aws-sdk/credential-provider-sso": 3.441.0 + "@aws-sdk/credential-provider-web-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 0b1de9e98daf4d2723a37c302ca6e29ee9a9c400ea349dcd9dd9397908f8a019540bb6c0adfb2d9045bce8f6edb39db0ece82de80ff56426ae2b4d98495d764f + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 42c04f294744a7d2b066b6a9e77f785eb391f49335963d25f87fb09d4b2d9a6acf78dfde7e3b4aca1bfca5eb6d799c557d5800846d8c055a27d5a047e023ba35 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.441.0" + dependencies: + "@aws-sdk/client-sso": 3.441.0 + "@aws-sdk/token-providers": 3.438.0 + "@aws-sdk/types": 3.433.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: e0043bd2179f998750a779cf808f1b2bc609a3685e2697ba078c8f64093f6721f522d331e2911b1e6f5215522456827f69764dc2224d6038e7bb42e29445b879 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: a0a76fb939da1f3a221927a8d4707f9f554ab27649cecbe84fb8f99264009c88aa10cf13324013fc0efc62edd450d60fe39525d7b9715b95ef7ae14374ce82d3 + languageName: node + linkType: hard + +"@aws-sdk/lib-storage@npm:^3.441.0": + version: 3.441.0 + resolution: "@aws-sdk/lib-storage@npm:3.441.0" + dependencies: + "@smithy/abort-controller": ^2.0.1 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/smithy-client": ^2.1.12 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: ^2.5.0 + peerDependencies: + "@aws-sdk/client-s3": ^3.0.0 + checksum: dd091d46c77f401426b99c78882d7db3cf5068850b12b865d110dff8a55d3d0abdaf6eadff870091b4fa1d77420f316e092fbd00a1517e2933b2c10ca01c6a3e + languageName: node + linkType: hard + +"@aws-sdk/middleware-bucket-endpoint@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-arn-parser": 3.310.0 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + "@smithy/util-config-provider": ^2.0.0 + tslib: ^2.5.0 + checksum: ee9e3d93b680b53402d3a0c945dd7778c2c043f9d50d265074ced28f06b715548272e4fb3349c797454b9b95af96e33a379008590ec827b40de127f339892329 + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: dcf3b671f5db6e2bfaeceee711a47342025188ce86eacaade44694b16af35f63f5d0b3dac8c41553b30652c71347ecb219c072c2881a92148e0420a8e6553ddb + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.433.0" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@aws-crypto/crc32c": 3.0.0 + "@aws-sdk/types": 3.433.0 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: 06e4925aa78645aa0b3c795f042dd12199cda5e13dd1571465d232e4557ab1cddd6cf329696a1269b85cc922a3686acae0fd5968444707b13a21966e0e5aa7fc + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: b9a2b1b8c1eceaad9db2c30a38007e131ea4d67b936b1cfa8727cc20ae9a3f95975e24c0d5267c77b05c8c8811bfb8ede83d9f8d4bb8eb9726f03c6e5f21345a + languageName: node + linkType: hard + +"@aws-sdk/middleware-location-constraint@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: c364e34a346c97bce551004aedf439ee8e175600f5b8ac2ef86e58263999b19d418ace1799f5beac6bd45302955823db9dc575c00f699c7177dd07510cebfb9b + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-logger@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 4184122eb5e519e4be2f3e70b3b328488ec861e7e9f586e5589fc7395b759e1bf79a5657f96f3dc13d9b0dcf9a0f0040703ac78e0dc736407319ec6d05b01a64 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 49ba0e4b87a911aa834ae4aa22d395258d4a6f1441c780f9f1356b4cb6bb023cecfb5d551f285f11c1968ee930804acf251c0e8b5fdcd9a8544e9177f1675812 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-s3@npm:3.440.0": + version: 3.440.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.440.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-arn-parser": 3.310.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 25513ee79320aedf29bea164b614d302dd7ef7767e025753638f941def166e5c1d9f0a4d687bdf05377d78267bc8e513909915c6bb0da343a9553217e7b750ca + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-sts@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.433.0" + dependencies: + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 116b8c1bff74828cbbae69e84c380c0643c45a7b66ea57731f68aa618b189af01a43931c0a82b2a20f67bc8dd7cec1228ebd65c87e620b06a9b5b3c0673d77a3 + languageName: node + linkType: hard + +"@aws-sdk/middleware-signing@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-signing@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.4.0 + "@smithy/util-middleware": ^2.0.5 + tslib: ^2.5.0 + checksum: a55defd93fa78e613df223668807c314d6c30e299859743c7ffac94da0340703ff93eccf3940cb216add60c475f6334ccbddb484e322c88416111e0e3aef19b5 + languageName: node + linkType: hard + +"@aws-sdk/middleware-ssec@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: e1e1c631686f4a89cf02520361533a007d98a3c9faa868d1462bf8b287e77fd86da896dc7610be37cf8cd66fa672280035dc2169fa15ce1a8debb28fff3cb949 + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.438.0": + version: 3.438.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.438.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.438.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 9fa28d029fb41c982a9e90a00ee811d77c3cf0887d872b2388fc5dedb3d3e192d5a2bbedb7a29f8b488101e8ce0f4de10ef5e2237fcdbcaf6506726324c832f5 + languageName: node + linkType: hard + +"@aws-sdk/region-config-resolver@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.433.0" + dependencies: + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 + "@smithy/util-config-provider": ^2.0.0 + "@smithy/util-middleware": ^2.0.5 + tslib: ^2.5.0 + checksum: 80a80707c2c991c16e6a52bde426704337b119d89cdedd70af72a7c52d2ee285a6cdcd355e45cb630e6d2dc3a7f57749b3276b9fff851d57c57916ef5ee2616f + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.437.0": + version: 3.437.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.437.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 94a442ccaad2c58b5a05eb7573a49da3110c7836a4e56e3790a37c3a124981b625b1df79ec6f806e71d72e0e26347c4c1800136417c121ce3130bc573cd86c7a + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.438.0": + version: 3.438.0 + resolution: "@aws-sdk/token-providers@npm:3.438.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.438.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.438.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.437.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-endpoints": ^1.0.2 + "@smithy/util-retry": ^2.0.5 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: 2171e910d29c814362a058f8c9447e444b8aff44a3f82f0b527dab13fe70ce1034d72739c64c1891c01aeedf25662dc049ca8e903c2b07d61cf2b0a930b9d811 + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.433.0, @aws-sdk/types@npm:^3.222.0": + version: 3.433.0 + resolution: "@aws-sdk/types@npm:3.433.0" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: f7460897bee2835b06cd957853b17eb4eb4fe1e7f66f6ca97e2fc0c642ff7093011d73cbde64f097cdcc462f1f72c3e0980472c716eefd656c61dac95e7e060d + languageName: node + linkType: hard + +"@aws-sdk/util-arn-parser@npm:3.310.0": + version: 3.310.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.310.0" + dependencies: + tslib: ^2.5.0 + checksum: faac1e10f8bb6c2fe5fee82bcb7ce56c2b37ae9ffdb2b78b0746a7a06005eaa5ea747a0a10eaf490c1c4907ecc327e1c94a600e26a069e023e54b8d63c031e96 + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.438.0": + version: 3.438.0 + resolution: "@aws-sdk/util-endpoints@npm:3.438.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/util-endpoints": ^1.0.2 + tslib: ^2.5.0 + checksum: d88756a35fc5a9830d682bafce8874e1cdb4cc59909a5f54ef99d985b248e0f0d99b1d70a560974802289040abfec03e92038f2bf7efdddd83b0d810c692509f + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.310.0 + resolution: "@aws-sdk/util-locate-window@npm:3.310.0" + dependencies: + tslib: ^2.5.0 + checksum: d552ce5f0f836ecb13d7920ae650552c56706f26a5e8abf894ba471e18775a3791869bda95269153735bac9d211efc3ba78ea01c34428c3fed4318ac693a08bc + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: ca762fdf65f0b17832dd6f9d1e48e3c57d54cb79e1ae26fa882a7c13cae2e14b138ec07d4ef766b40c17ec558f1cfd9c1d9ecf9ccb369472abdef79adc1e3189 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.437.0": + version: 3.437.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.437.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: d9840b5c8595865c0a955fba80dbbb46f7e5bdc3899868393d7062dfaaf9921608dd2a09b7a28286c8213652a1bed13d1aed4781c0d5b4323edd72cc3b124ba8 + languageName: node + linkType: hard + +"@aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.259.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0" + dependencies: + tslib: ^2.3.1 + checksum: b6a1e580da1c9b62c749814182a7649a748ca4253edb4063aa521df97d25b76eae3359eb1680b86f71aac668e05cc05c514379bca39ebf4ba998ae4348412da8 + languageName: node + linkType: hard + +"@aws-sdk/xml-builder@npm:3.310.0": + version: 3.310.0 + resolution: "@aws-sdk/xml-builder@npm:3.310.0" + dependencies: + tslib: ^2.5.0 + checksum: fc17fd8f68470702d947948ada46097bdddecafdc68fa57bf584320e92748e8ef0372a51999d3ab7902ba4f62c2dbfbdec2dba1180fca19bb5127bad1ef0e48b + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.10, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.8.3": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" @@ -4957,6 +5633,30 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-dismissable-layer@npm:1.0.5": + version: 1.0.5 + resolution: "@radix-ui/react-dismissable-layer@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-escape-keydown": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: e73cf4bd3763f4d55b1bea7486a9700384d7d94dc00b1d5a75e222b2f1e4f32bc667a206ca4ed3baaaf7424dce7a239afd0ba59a6f0d89c3462c4e6e8d029a04 + languageName: node + linkType: hard + "@radix-ui/react-dropdown-menu@npm:^2.0.4": version: 2.0.4 resolution: "@radix-ui/react-dropdown-menu@npm:2.0.4" @@ -5039,6 +5739,34 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-hover-card@npm:^1.0.7": + version: 1.0.7 + resolution: "@radix-ui/react-hover-card@npm:1.0.7" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.5 + "@radix-ui/react-popper": 1.1.3 + "@radix-ui/react-portal": 1.0.4 + "@radix-ui/react-presence": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 812c348d8331348774b0460cd9058fdb34e0a4e167cc3ab7350d60d0ac374c673e8159573919da299f58860b8eeb9d43c21ccb679cf6db70f5db0386359871ef + languageName: node + linkType: hard + "@radix-ui/react-id@npm:1.0.0": version: 1.0.0 resolution: "@radix-ui/react-id@npm:1.0.0" @@ -5182,6 +5910,35 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-popper@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/react-popper@npm:1.1.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@floating-ui/react-dom": ^2.0.0 + "@radix-ui/react-arrow": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.0.1 + "@radix-ui/react-use-rect": 1.0.1 + "@radix-ui/react-use-size": 1.0.1 + "@radix-ui/rect": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: b18a15958623f9222b6ed3e24b9fbcc2ba67b8df5a5272412f261de1592b3f05002af1c8b94c065830c3c74267ce00cf6c1d70d4d507ec92ba639501f98aa348 + languageName: node + linkType: hard + "@radix-ui/react-portal@npm:1.0.2": version: 1.0.2 resolution: "@radix-ui/react-portal@npm:1.0.2" @@ -5215,6 +5972,26 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-portal@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-portal@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: c4cf35e2f26a89703189d0eef3ceeeb706ae0832e98e558730a5e929ca7c72c7cb510413a24eca94c7732f8d659a1e81942bec7b90540cb73ce9e4885d040b64 + languageName: node + linkType: hard + "@radix-ui/react-presence@npm:1.0.0": version: 1.0.0 resolution: "@radix-ui/react-presence@npm:1.0.0" @@ -6871,6 +7648,551 @@ __metadata: languageName: node linkType: hard +"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/abort-controller@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 187bbe7819271de99c8218d0df08d7b56131a7563e1822ef3142ecdad258201c9cc792e222d59145f6f59f6260e3c4ae2ef09b76370daa393797fad1b3d56551 + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader-native@npm:2.0.0" + dependencies: + "@smithy/util-base64": ^2.0.0 + tslib: ^2.5.0 + checksum: 5f656dbc4913ab8312b6e687938f534a2ed28e749335560c21a6975f691630ede80afc4a81007078692da4eaa91839ae0a6e65dc39f3faf4423538f5d9bef37b + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: a47e5298f0b28e25eaa5825ea9737718f0e2b7cf0f03a49cca186eb5544dd20ac91a2d92069f9805e40e5f3ab34d32f8091853518672fdbca009411179dbeb2a + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/config-resolver@npm:2.0.16" + dependencies: + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 + "@smithy/util-config-provider": ^2.0.0 + "@smithy/util-middleware": ^2.0.5 + tslib: ^2.5.0 + checksum: d92948bc42e59c451ff0cf5cf803b6cb13c664dd920d43c0f5a647193c93aa3634fa88391e85dad1c159f535432bfdd7653de8450599b4170e4adced2c8c9850 + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/credential-provider-imds@npm:2.0.18" + dependencies: + "@smithy/node-config-provider": ^2.1.3 + "@smithy/property-provider": ^2.0.13 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + tslib: ^2.5.0 + checksum: 12e4a436429b140a2d85e34842d9deb42d7507fe3d3b26070f45f484bf8ecba9ac4fe3f9deb87252f3f6e5ae31d19c9e61147079c69716c2f4bcd0aa4d2c73b8 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-codec@npm:2.0.12" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@smithy/types": ^2.4.0 + "@smithy/util-hex-encoding": ^2.0.0 + tslib: ^2.5.0 + checksum: 38e457645512d06e9b74bdb8b33df8b712e96b97e59b7cd51c9d31686ba71b7f4e094615dedcca7a1790fdb7e52f3e0791af7d7b66ca46e0556544827a311d5b + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-browser@npm:2.0.12" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 685d9d874e019d62cacac4d98c19ffbd8496c68efa0968f43f93cbcf3bcaa0db2c5ae060d0550c50bd24a6b1a15ea2b94ce7fed121733bb060dd536b7e618ff6 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 1fbed5f1b1c5fb8830d9940e2d8d56e1c33dd3ce5e5a79f259f0dacaa8ec6dfa4203163b63e707769e4153d1d17680cbf195690b596a44da6f43a62f66bad1aa + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-node@npm:2.0.12" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 541f57903daa13d78b09b23ac74a6643e8260b4c9afe9375344ccc347c62fdc1fc0c162f763f733b7bd46f8ceb240890cfc89f786bd49efd57cf43d74c9b3f6b + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-universal@npm:2.0.12" + dependencies: + "@smithy/eventstream-codec": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: fea8ad03da25f92b0f3a0b20398a410bbf264aad6318b2cea9c8740cd86b1b130f3b52a07fb2b25e82b19eb44d60ec3770b17667a6842d404548e200a085ead9 + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^2.2.4": + version: 2.2.4 + resolution: "@smithy/fetch-http-handler@npm:2.2.4" + dependencies: + "@smithy/protocol-http": ^3.0.8 + "@smithy/querystring-builder": ^2.0.12 + "@smithy/types": ^2.4.0 + "@smithy/util-base64": ^2.0.0 + tslib: ^2.5.0 + checksum: 37b9dfdd35ff4a997de07f3aacdaf4acb3881b3586b3c2bbf27f163066a241d54ce471fe100353e2bea3f3cd71ec8ef57a0a1f78f897e11c9166f75b06902cfc + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-blob-browser@npm:2.0.12" + dependencies: + "@smithy/chunked-blob-reader": ^2.0.0 + "@smithy/chunked-blob-reader-native": ^2.0.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 212dd0200020c13c98efaea4544d81acf286ecebf6b8751b7205797da7b0282b17df1e85385525a479c7d3a1f7fd17100f8083974fb33e220e084f310b86f578 + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-node@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: e2b36a60c812fb716091ea06d205113cdee9ba4dfdd608bb1723e635f9bd53c4f8a9bd038f2c6fb369a91beee3189123925e2543ee373b81a77d62e71170523c + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-stream-node@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: 83b395ad6e529a23f82ca006597b08e5e83cf35e92b6813624cb8735632f7271e13249ffc687d6c21dbabccec92fc73fcf747e7dd7096d6d913a33d1e6842c7d + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/invalid-dependency@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 3b8a218ad67d3eca06d1646f21e52bf7704449fec714a0c113ab5db100605b05b37b12facd00b92df1203d5bec66ff4ed5e763691ac7c098b85854f194eefb58 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/is-array-buffer@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: 6d101cf509a7818667f42d297894f88f86ef41d3cc9d02eae38bbe5e69b16edf83b8e67eb691964d859a16a4e39db1aad323d83f6ae55ae4512a14ff6406c02d + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/md5-js@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: c6b90d31d89ff386d13b8ecad7aeb2d63fd6b534f0954745b34690fdb4b2520f228769c4ef2967a476a2cd5d6de0151be2998714c5ba1fde2253976012b18fba + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^2.0.14": + version: 2.0.14 + resolution: "@smithy/middleware-content-length@npm:2.0.14" + dependencies: + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: ff289f3c7ec4dbf53297e5968196444a387ddd3e67cb8426e40cadc096e7a5127e30315520761aa53a98daecfde0e6ecc195a722d4b31b7662f63b3286474224 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^2.1.3": + version: 2.1.3 + resolution: "@smithy/middleware-endpoint@npm:2.1.3" + dependencies: + "@smithy/middleware-serde": ^2.0.12 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/shared-ini-file-loader": ^2.2.2 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-middleware": ^2.0.5 + tslib: ^2.5.0 + checksum: 62dfcb031bccb575a33f04ca8d684634eb03585530b28ffe759242dc13fef7e11755673d3d7d1be15a90f933f579614bc78d83dad0747e3bf344c60cb2212d92 + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/middleware-retry@npm:2.0.18" + dependencies: + "@smithy/node-config-provider": ^2.1.3 + "@smithy/protocol-http": ^3.0.8 + "@smithy/service-error-classification": ^2.0.5 + "@smithy/types": ^2.4.0 + "@smithy/util-middleware": ^2.0.5 + "@smithy/util-retry": ^2.0.5 + tslib: ^2.5.0 + uuid: ^8.3.2 + checksum: 7372232d35fbff0f770e4ec608940c81a776040971556e3a328980ebcceb9f9469eb09e5d6014811c42759c77653ded4cbbccc21b7c26f3405c7299062a523b3 + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/middleware-serde@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 5e8b04511c017bcadbf1a6efc6c71588586cabaa130df10562a74159d128e56965581799e80a0645557bab03df8bea187b21cb1fd536e17cf73148e5b678925f + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^2.0.6": + version: 2.0.6 + resolution: "@smithy/middleware-stack@npm:2.0.6" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 3626b71364b83d091751cd6ad7f7bc655a1746f970c63ea3205c2bc171a596a734394d556fcf66f1458b8151fe54cab5bf774ee66b4d40c3dd9d9e7d9114f905 + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^2.1.3": + version: 2.1.3 + resolution: "@smithy/node-config-provider@npm:2.1.3" + dependencies: + "@smithy/property-provider": ^2.0.13 + "@smithy/shared-ini-file-loader": ^2.2.2 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 22e188fbc099616e50661afb0decb88ba67b396a1fbed122ad2a857a2a9e4a80d34a68d793cca6cb9e34a299ca1cde2bf3b9ab2b97b733bae838852acec080c5 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^2.1.8": + version: 2.1.8 + resolution: "@smithy/node-http-handler@npm:2.1.8" + dependencies: + "@smithy/abort-controller": ^2.0.12 + "@smithy/protocol-http": ^3.0.8 + "@smithy/querystring-builder": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 17e51b8c0b2dc7dcf7e32bc2cbd836220f86355b4d630f0b94fad4ed79dfa737b4ecbb7c72752b59e6849ca342c4a3ade89846e0276d986a72d25ed280ce3a8c + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.0.13": + version: 2.0.13 + resolution: "@smithy/property-provider@npm:2.0.13" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 62443ec94d4dafaa0c2f285957264b3b548fd5a164ebd1ef02e4286c55d3e07e4d22d695fc2857ad0b1e406d01bf27271e9d7c3c05465638da0226ae4305d3d7 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^3.0.8": + version: 3.0.8 + resolution: "@smithy/protocol-http@npm:3.0.8" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: deb4f7d863bcc67724555b3a1ffb8e605a3df63cde9f40234813f072184bb68f5c33388c1934f56576b08a877bb8c9c0bfb849deb0526b55a9410678040fa019 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/querystring-builder@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + "@smithy/util-uri-escape": ^2.0.0 + tslib: ^2.5.0 + checksum: d7d0608ac14d8ccd2b418743fc91be9c77b75a302a7552f666a81454fa1764e2162fb2c2f7655cf24045ae44416252362111b9612ea9759dbc1f27f75a71aa42 + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/querystring-parser@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 889dad387fda7db289d0360cbc38901d2c726d164c56915c76ee125bb8059f8a86e28442841000112c3b8a5a3c7701da391f961350969ea5242c6cdf55f296cf + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^2.0.5": + version: 2.0.5 + resolution: "@smithy/service-error-classification@npm:2.0.5" + dependencies: + "@smithy/types": ^2.4.0 + checksum: cd4b9fcc5cd940035ca4f3e832f8480d75eb81c90501bdb5c9295c5fd26487ca2e2f3d3efa9a322faeaedf10d6d8324327cd3341fc05d38f8605006ad836abaa + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^2.0.6, @smithy/shared-ini-file-loader@npm:^2.2.2": + version: 2.2.2 + resolution: "@smithy/shared-ini-file-loader@npm:2.2.2" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 851b1ed096609a3c860aebdf7110629783e4824a246d96b10a262426bb90aa4eb2e0370ff489dec48c1dcbd812d95bd3208d785f34c22c2f20249a36bf5ea762 + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^2.0.0": + version: 2.0.12 + resolution: "@smithy/signature-v4@npm:2.0.12" + dependencies: + "@smithy/eventstream-codec": ^2.0.12 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/types": ^2.4.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-middleware": ^2.0.5 + "@smithy/util-uri-escape": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: e786146c65cc6c748c0699db0a082b589bc332a1db9461e0ca8a3e5465712639ec02a352f31f5099f1fc0ee75d956a21a5927ec9079ae6152e220cb2cba14f9d + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^2.1.12": + version: 2.1.12 + resolution: "@smithy/smithy-client@npm:2.1.12" + dependencies: + "@smithy/middleware-stack": ^2.0.6 + "@smithy/types": ^2.4.0 + "@smithy/util-stream": ^2.0.17 + tslib: ^2.5.0 + checksum: 9e2944a9c753511777468ec40a3295e5351d08349258a57b70dfc9a96e882efed6075eb7fd3c0494fa07279bdefdfad2e5aecf7930685c656131a97d56aae209 + languageName: node + linkType: hard + +"@smithy/types@npm:^2.4.0": + version: 2.4.0 + resolution: "@smithy/types@npm:2.4.0" + dependencies: + tslib: ^2.5.0 + checksum: 936690f8ba9323c05a1046102f83d7ed76c5c2f2405ca22e8bfed8d66a5ba12d74a187c10d93b085d6822b98edaec7b6309a4401f036099bf239a0bf5cdcf00d + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/url-parser@npm:2.0.12" + dependencies: + "@smithy/querystring-parser": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 40324cee758137342573e9f7bf685bc7c3f8284ff2f15d3c68a244dacf26f62cd92b234f220ddfc2963038ef766dd73c3f70642c592a49bd10432c5432fb1ab6 + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-base64@npm:2.0.0" + dependencies: + "@smithy/util-buffer-from": ^2.0.0 + tslib: ^2.5.0 + checksum: 52124a684dfac853288acd2a0ffff02559c21bf7faaa3db58a914e4acb4b1f7925fd48593e7545db87f8f962250824d1249dc8be645ecbd2c1dd1728cfe1069b + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-body-length-browser@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: 4bccdd857bd24c9dcb6e9f2d5be03d59415f9a94d660ec7b3efb45e9aa04017f34c387368f176f24233a071af3b7a2b5f8236a2f5a83bfc884d24dfcc341e836 + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^2.1.0": + version: 2.1.0 + resolution: "@smithy/util-body-length-node@npm:2.1.0" + dependencies: + tslib: ^2.5.0 + checksum: e4635251898f12e1825f2848e0b7cc9d01ec6635b3f1f71b790734bb702b88e795f6c539d42d95472dad00e50e9ff13fcf396791092b131e5834069cb8f52ed0 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-buffer-from@npm:2.0.0" + dependencies: + "@smithy/is-array-buffer": ^2.0.0 + tslib: ^2.5.0 + checksum: d33cbf3e488d23390c88705ddae71b08de7a87b6453e38b508cd37a22a02e8b5be9f0cd46c1347b496c3977a815a7399b18840544ecdc4cce8cf3dcd0f5bb009 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-config-provider@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: cdc34db5b42658a7c98652ddb2e35b31e0d76f22a051d71724927999a53467fb38fe6dcf228585544bc168cbd54ded3913e14cbc33c947d3c8a45ca518a9b7b0 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/util-defaults-mode-browser@npm:2.0.16" + dependencies: + "@smithy/property-provider": ^2.0.13 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: 8dae0256e89c13ab7bcd791fe336124adc17d95401ceb7152784a809ed9ba09a639573c1ce2bf32b12964f7181aeb2cdfc283d820301f2b3a82ef4906fe83280 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^2.0.21": + version: 2.0.21 + resolution: "@smithy/util-defaults-mode-node@npm:2.0.21" + dependencies: + "@smithy/config-resolver": ^2.0.16 + "@smithy/credential-provider-imds": ^2.0.18 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/property-provider": ^2.0.13 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: ce2643ad99181b91b4eb00f2b2b34d12ff006ac1770333ae62541cfc7b98b873e233933d483d7bb0a443a8155debd94731a1df0f4cc572e6cc5ddbf97416e2d7 + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^1.0.2": + version: 1.0.2 + resolution: "@smithy/util-endpoints@npm:1.0.2" + dependencies: + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 11b98c4897b275f1c7e456671356cf3f8f61743a8788891c82fcfed682a8a5bfbf83bbdbc72d471dc26467b66299c1df6921daec15d0024b43ca4638f18ed856 + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-hex-encoding@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: 884373e089d909e3c9805bdb78f367d1f3612e4e1e6d8f0263cc82a8b9689eddc0bc80b8b58aa711bd5b48d9cb124f9996906c172e951c9dac78984459e831cf + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^2.0.5": + version: 2.0.5 + resolution: "@smithy/util-middleware@npm:2.0.5" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 9d001723e7472c0d78619320235f66d1de42f16e13d1189697f8e447d05643047ab97965525b147eaafbb0e169563ecb5b806da2d02bd4ce0b652b72df4d9131 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^2.0.5": + version: 2.0.5 + resolution: "@smithy/util-retry@npm:2.0.5" + dependencies: + "@smithy/service-error-classification": ^2.0.5 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: e7169b458a9c194104e16014b2829deddb9ee4175fd17bd933d0ab9ec9df065cf23816b605eafb6604da1111e3280c5fea4da98dd8ec5f5f3e1c30e166119808 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/util-stream@npm:2.0.17" + dependencies: + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/types": ^2.4.0 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.5.0 + checksum: acd68f7b092fdf3560f5d88f3f81d1bfab4c634f8b7acd8eca1993c8ce789d9652d23048c9e891a42dd12dd71e7a9756b9879ae95fccd1cd92f7ad8204c97d68 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-uri-escape@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: d201cee524ece997c406902463b5ea0b72599994f7b3ac1d923d5645497e9ef93126d146016f13dd4afafe33b9a3e92faf4e023cf0af510b270c1b9ce3d78da8 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-utf8@npm:2.0.0" + dependencies: + "@smithy/util-buffer-from": ^2.0.0 + tslib: ^2.5.0 + checksum: bc8cda84f85b513380a61352635b306ae50d3b92974454db32835b39bbaa38150332b89346098ba9dea2e0002e2963fcbdd622bc9b3eec7b7ea8fa3f8c7ce737 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/util-waiter@npm:2.0.12" + dependencies: + "@smithy/abort-controller": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: af35c36a58585472aae9e06ea000a113110f22bed179687213336a014b002deb867cb094f9cb01bc43856235df05517baf08009b3b929a48b48f964c426c1ffc + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.0 resolution: "@socket.io/component-emitter@npm:3.1.0" @@ -10925,6 +12247,8 @@ __metadata: version: 0.0.0-use.local resolution: "appsmith@workspace:." dependencies: + "@aws-sdk/client-s3": ^3.441.0 + "@aws-sdk/lib-storage": ^3.441.0 "@babel/helper-create-regexp-features-plugin": ^7.18.6 "@babel/helper-string-parser": ^7.19.4 "@blueprintjs/core": ^3.43.0 @@ -11029,7 +12353,6 @@ __metadata: algoliasearch: ^4.2.0 assert-never: ^1.2.1 astring: ^1.7.5 - aws-sdk: ^2.1443.0 axios: ^0.27.2 babel-plugin-lodash: ^3.3.4 babel-plugin-module-resolver: ^4.1.0 @@ -11061,13 +12384,12 @@ __metadata: cypress-xpath: ^1.6.0 dayjs: ^1.10.6 deep-diff: ^1.0.2 - design-system: "npm:@appsmithorg/design-system@2.1.26" + design-system: "npm:@appsmithorg/design-system@2.1.27" design-system-old: "npm:@appsmithorg/design-system-old@1.1.14" diff: ^5.0.0 dotenv: ^8.1.0 downloadjs: ^1.4.7 echarts: ^5.4.2 - echarts-gl: ^2.0.9 eslint: ^8.51.0 eslint-config-prettier: ^9.0.0 eslint-import-resolver-babel-module: ^5.3.2 @@ -11673,24 +12995,6 @@ __metadata: languageName: node linkType: hard -"aws-sdk@npm:^2.1443.0": - version: 2.1443.0 - resolution: "aws-sdk@npm:2.1443.0" - dependencies: - buffer: 4.9.2 - events: 1.1.1 - ieee754: 1.1.13 - jmespath: 0.16.0 - querystring: 0.2.0 - sax: 1.2.1 - url: 0.10.3 - util: ^0.12.4 - uuid: 8.0.0 - xml2js: 0.5.0 - checksum: 494124cded48679ec449bfa1cee54db969141639414b71fd984d519f5cc4904b614a023339ad17f58f89d5ca45e038d9bc89b22bdd4e5b02b138eb0d800162f7 - languageName: node - linkType: hard - "aws-sign2@npm:~0.7.0": version: 0.7.0 resolution: "aws-sign2@npm:0.7.0" @@ -12339,6 +13643,13 @@ __metadata: languageName: node linkType: hard +"bowser@npm:^2.11.0": + version: 2.11.0 + resolution: "bowser@npm:2.11.0" + checksum: 29c3f01f22e703fa6644fc3b684307442df4240b6e10f6cfe1b61c6ca5721073189ca97cdeedb376081148c8518e33b1d818a57f781d70b0b70e1f31fb48814f + languageName: node + linkType: hard + "boxen@npm:^5.0.0": version: 5.1.2 resolution: "boxen@npm:5.1.2" @@ -12687,14 +13998,13 @@ __metadata: languageName: node linkType: hard -"buffer@npm:4.9.2": - version: 4.9.2 - resolution: "buffer@npm:4.9.2" +"buffer@npm:5.6.0": + version: 5.6.0 + resolution: "buffer@npm:5.6.0" dependencies: base64-js: ^1.0.2 ieee754: ^1.1.4 - isarray: ^1.0.0 - checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3 + checksum: d659494c5032dd39d03d2912e64179cc44c6340e7e9d1f68d3840e7ab4559989fbce92b4950174593c38d05268224235ba404f0878775cab2a616b6dcad9c23e languageName: node linkType: hard @@ -13192,13 +14502,6 @@ __metadata: languageName: node linkType: hard -"claygl@npm:^1.2.1": - version: 1.3.0 - resolution: "claygl@npm:1.3.0" - checksum: cbf3c4788b426ecc183e0fc5f7e3f88fae15d616ebf39467cb719c1f78efbef8bbe92f8def22c927a9d3cbb2a3ae6d1194755edae96d5f623b4dca0b78bdefa8 - languageName: node - linkType: hard - "clean-css@npm:^5.2.2": version: 5.3.0 resolution: "clean-css@npm:5.3.0" @@ -15148,12 +16451,13 @@ __metadata: languageName: node linkType: hard -"design-system@npm:@appsmithorg/design-system@2.1.26": - version: 2.1.26 - resolution: "@appsmithorg/design-system@npm:2.1.26" +"design-system@npm:@appsmithorg/design-system@2.1.27": + version: 2.1.27 + resolution: "@appsmithorg/design-system@npm:2.1.27" dependencies: "@radix-ui/react-dialog": ^1.0.2 "@radix-ui/react-dropdown-menu": ^2.0.4 + "@radix-ui/react-hover-card": ^1.0.7 "@radix-ui/react-popover": ^1.0.6 "@radix-ui/react-tabs": ^1.0.2 "@react-aria/button": ^3.7.0 @@ -15179,7 +16483,7 @@ __metadata: react-dom: ^17.0.2 react-router-dom: ^5.0.0 styled-components: ^5.3.6 - checksum: afc3448a9edfbd1f149982acc2fe878fcce19ecd214ee20ac0810efa0932980c6be85ac5497b63f596b3f7c6872a7fc25159d20aa932343c9afffa00dfa6cdcd + checksum: 52db562d85a04da3d5623127e73f110f65202946060b65af82ac79378feb37eb4edb85219282345b89a4dee7c1cfdec61b5ae23549b982db73a09d916be679e2 languageName: node linkType: hard @@ -15709,18 +17013,6 @@ __metadata: languageName: node linkType: hard -"echarts-gl@npm:^2.0.9": - version: 2.0.9 - resolution: "echarts-gl@npm:2.0.9" - dependencies: - claygl: ^1.2.1 - zrender: ^5.1.1 - peerDependencies: - echarts: ^5.1.2 - checksum: d07dfeb093c5ca05a671317a8f4f4be7fffaa12fb91d485ab19c65d740bbf3c08b02b57c4d7b772bc3ce025184843f1a44ba8bdfc3d48257a46684a5d10fb941 - languageName: node - linkType: hard - "echarts@npm:^5.4.2": version: 5.4.2 resolution: "echarts@npm:5.4.2" @@ -16907,7 +18199,14 @@ __metadata: languageName: node linkType: hard -"events@npm:1.1.1, events@npm:^1.1.1": +"events@npm:3.3.0, events@npm:^3.0.0, events@npm:^3.2.0, events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 + languageName: node + linkType: hard + +"events@npm:^1.1.1": version: 1.1.1 resolution: "events@npm:1.1.1" checksum: 40431eb005cc4c57861b93d44c2981a49e7feb99df84cf551baed299ceea4444edf7744733f6a6667e942af687359b1f4a87ec1ec4f21d5127dac48a782039b9 @@ -16921,13 +18220,6 @@ __metadata: languageName: node linkType: hard -"events@npm:^3.0.0, events@npm:^3.2.0, events@npm:^3.3.0": - version: 3.3.0 - resolution: "events@npm:3.3.0" - checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 - languageName: node - linkType: hard - "evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3": version: 1.0.3 resolution: "evp_bytestokey@npm:1.0.3" @@ -17253,6 +18545,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.2.5": + version: 4.2.5 + resolution: "fast-xml-parser@npm:4.2.5" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d32b22005504eeb207249bf40dc82d0994b5bb9ca9dcc731d335a1f425e47fe085b3cace3cf9d32172dd1a5544193c49e8615ca95b4bf95a4a4920a226b06d80 + languageName: node + linkType: hard + "fastdom@npm:^1.0.11": version: 1.0.11 resolution: "fastdom@npm:1.0.11" @@ -19104,13 +20407,6 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:1.1.13": - version: 1.1.13 - resolution: "ieee754@npm:1.1.13" - checksum: 102df1ba662e316e6160f7ce29c7c7fa3e04f2014c288336c5a9ff40bbcc2a27d209fa2a81ebfb33f28b1941021343d30e9ad8ee85a2d61f79f5936c35edc33d - languageName: node - linkType: hard - "ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" @@ -20044,13 +21340,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:^1.0.0, isarray@npm:~1.0.0": - version: 1.0.0 - resolution: "isarray@npm:1.0.0" - checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -20058,6 +21347,13 @@ __metadata: languageName: node linkType: hard +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab + languageName: node + linkType: hard + "isbinaryfile@npm:^4.0.8": version: 4.0.10 resolution: "isbinaryfile@npm:4.0.10" @@ -21289,13 +22585,6 @@ __metadata: languageName: node linkType: hard -"jmespath@npm:0.16.0": - version: 0.16.0 - resolution: "jmespath@npm:0.16.0" - checksum: 2d602493a1e4addfd1350ac8c9d54b1b03ed09e305fd863bab84a4ee1f52868cf939dd1a08c5cdea29ce9ba8f86875ebb458b6ed45dab3e1c3f2694503fb2fd9 - languageName: node - linkType: hard - "joi@npm:^17.3.0": version: 17.9.2 resolution: "joi@npm:17.9.2" @@ -26143,13 +27432,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - "punycode@npm:^1.3.2, punycode@npm:^1.4.1": version: 1.4.1 resolution: "punycode@npm:1.4.1" @@ -26269,13 +27551,6 @@ __metadata: languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 - languageName: node - linkType: hard - "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -28675,20 +29950,6 @@ __metadata: languageName: node linkType: hard -"sax@npm:1.2.1": - version: 1.2.1 - resolution: "sax@npm:1.2.1" - checksum: 8dca7d5e1cd7d612f98ac50bdf0b9f63fbc964b85f0c4e2eb271f8b9b47fd3bf344c4d6a592e69ecf726d1485ca62cd8a52e603bbc332d18a66af25a9a1045ad - languageName: node - linkType: hard - -"sax@npm:>=0.6.0, sax@npm:~1.2.4": - version: 1.2.4 - resolution: "sax@npm:1.2.4" - checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe - languageName: node - linkType: hard - "sax@npm:~1.1.1": version: 1.1.6 resolution: "sax@npm:1.1.6" @@ -28696,6 +29957,13 @@ __metadata: languageName: node linkType: hard +"sax@npm:~1.2.4": + version: 1.2.4 + resolution: "sax@npm:1.2.4" + checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe + languageName: node + linkType: hard + "saxes@npm:^5.0.1": version: 5.0.1 resolution: "saxes@npm:5.0.1" @@ -29626,6 +30894,16 @@ __metadata: languageName: node linkType: hard +"stream-browserify@npm:3.0.0, stream-browserify@npm:^3.0.0": + version: 3.0.0 + resolution: "stream-browserify@npm:3.0.0" + dependencies: + inherits: ~2.0.4 + readable-stream: ^3.5.0 + checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921 + languageName: node + linkType: hard + "stream-browserify@npm:^2.0.0": version: 2.0.2 resolution: "stream-browserify@npm:2.0.2" @@ -29636,16 +30914,6 @@ __metadata: languageName: node linkType: hard -"stream-browserify@npm:^3.0.0": - version: 3.0.0 - resolution: "stream-browserify@npm:3.0.0" - dependencies: - inherits: ~2.0.4 - readable-stream: ^3.5.0 - checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921 - languageName: node - linkType: hard - "stream-combiner2@npm:^1.1.1": version: 1.1.1 resolution: "stream-combiner2@npm:1.1.1" @@ -29996,6 +31264,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "style-loader@npm:^3.3.1": version: 3.3.1 resolution: "style-loader@npm:3.3.1" @@ -31054,7 +32329,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:^1.11.1, tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd @@ -31651,16 +32926,6 @@ __metadata: languageName: node linkType: hard -"url@npm:0.10.3": - version: 0.10.3 - resolution: "url@npm:0.10.3" - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 7b83ddb106c27bf9bde8629ccbe8d26e9db789c8cda5aa7db72ca2c6f9b8a88a5adf206f3e10db78e6e2d042b327c45db34c7010c1bf0d9908936a17a2b57d05 - languageName: node - linkType: hard - "url@npm:~0.11.0": version: 0.11.1 resolution: "url@npm:0.11.1" @@ -31822,15 +33087,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:8.0.0": - version: 8.0.0 - resolution: "uuid@npm:8.0.0" - bin: - uuid: dist/bin/uuid - checksum: 56d4e23aa7ac26fa2db6bd1778db34cb8c9f5a10df1770a27167874bf6705fc8f14a4ac414af58a0d96c7653b2bd4848510b29d1c2ef8c91ccb17429c1872b5e - languageName: node - linkType: hard - "uuid@npm:^3.2.1": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -32952,16 +34208,6 @@ __metadata: languageName: node linkType: hard -"xml2js@npm:0.5.0": - version: 0.5.0 - resolution: "xml2js@npm:0.5.0" - dependencies: - sax: ">=0.6.0" - xmlbuilder: ~11.0.0 - checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea - languageName: node - linkType: hard - "xmlbuilder@npm:^10.0.0": version: 10.1.1 resolution: "xmlbuilder@npm:10.1.1" @@ -32976,13 +34222,6 @@ __metadata: languageName: node linkType: hard -"xmlbuilder@npm:~11.0.0": - version: 11.0.1 - resolution: "xmlbuilder@npm:11.0.1" - checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0 - languageName: node - linkType: hard - "xmlchars@npm:^2.2.0": version: 2.2.0 resolution: "xmlchars@npm:2.2.0" @@ -33225,12 +34464,3 @@ __metadata: checksum: ae89584fbe4256217e500a74183fd5870a2bed7e7b3234cd78fcbc0a1a82da8449596b2019105927069531b567ecf94425fb171abd6cedcbf517e52fdf438722 languageName: node linkType: hard - -"zrender@npm:^5.1.1": - version: 5.4.4 - resolution: "zrender@npm:5.4.4" - dependencies: - tslib: 2.3.0 - checksum: 4b317346af8eca38e62ba029239c3a13e97eac4fa15b3ddadbae23442d8b373f0e937c255dee8080d6bb2fc79c9da54f1106415586ed8942bd8bc684b3890ea9 - languageName: node - linkType: hard diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/AnalyticsEvents.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/AnalyticsEvents.java index 744496623c..79ddf8159f 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/AnalyticsEvents.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/AnalyticsEvents.java @@ -86,7 +86,9 @@ public enum AnalyticsEvents { GIT_STALE_FILE_LOCK_DELETED, SERVER_SETUP_COMPLETE("server_setup_complete"), - PARTIAL_IMPORT; + PARTIAL_IMPORT, + + PARTIAL_EXPORT; private final String eventName; diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginError.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginError.java index 5aad875c65..802f016235 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginError.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginError.java @@ -107,6 +107,15 @@ public enum AppsmithPluginError implements BasePluginError { ErrorType.AUTHENTICATION_ERROR, "{0}", "{1}"), + PLUGIN_DATASOURCE_ERROR( + 400, + AppsmithPluginErrorCode.PLUGIN_DATASOURCE_ERROR.getCode(), + "Error with datasource request. Please check datasource configuration.", + AppsmithErrorAction.DEFAULT, + "Datasource error", + ErrorType.BAD_REQUEST, + "{0}", + "{1}"), PLUGIN_IN_MEMORY_FILTERING_ERROR( 500, AppsmithPluginErrorCode.PLUGIN_IN_MEMORY_FILTERING_ERROR.getCode(), diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginErrorCode.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginErrorCode.java index 4a9ba3f733..7e6465e4ab 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginErrorCode.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/exceptions/pluginExceptions/AppsmithPluginErrorCode.java @@ -17,6 +17,7 @@ public enum AppsmithPluginErrorCode { PLUGIN_DATASOURCE_TIMEOUT_ERROR("PE-DSE-5004", "Timed out when connecting to datasource"), PLUGIN_QUERY_TIMEOUT_ERROR("PE-QRY-5000", "Timed out on query execution"), PLUGIN_AUTHENTICATION_ERROR("PE-ATH-5000", "Datasource authentication error"), + PLUGIN_DATASOURCE_ERROR("PE-DSE-4000", "Datasource error"), PLUGIN_UQI_WHERE_CONDITION_UNKNOWN("PE-UQI-5000", "Where condition could not be parsed"), GENERIC_STALE_CONNECTION("PE-STC-5000", "Secondary stale connection error"), PLUGIN_EXECUTE_ARGUMENT_ERROR("PE-ARG-5000", "Wrong arguments provided"), diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/CreatorContextType.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/CreatorContextType.java new file mode 100644 index 0000000000..67c2687693 --- /dev/null +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/CreatorContextType.java @@ -0,0 +1,6 @@ +package com.appsmith.external.models; + +public enum CreatorContextType { + PAGE, + MODULE +} diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ce/ActionCE_DTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ce/ActionCE_DTO.java index 9bbc40ac69..c60e5b05aa 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ce/ActionCE_DTO.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ce/ActionCE_DTO.java @@ -7,6 +7,7 @@ import com.appsmith.external.helpers.Identifiable; import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.ActionProvider; import com.appsmith.external.models.AnalyticsInfo; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.Documentation; @@ -75,6 +76,9 @@ public class ActionCE_DTO implements Identifiable, Executable { @JsonView(Views.Public.class) String pageId; + @JsonView(Views.Public.class) + CreatorContextType contextType; + @JsonView(Views.Public.class) String collectionId; diff --git a/app/server/appsmith-plugins/mongoPlugin/pom.xml b/app/server/appsmith-plugins/mongoPlugin/pom.xml index 6e5c8345ba..d355dcf266 100644 --- a/app/server/appsmith-plugins/mongoPlugin/pom.xml +++ b/app/server/appsmith-plugins/mongoPlugin/pom.xml @@ -14,4 +14,20 @@ mongoPlugin + + + + org.testcontainers + mongodb + 1.19.1 + test + + + junit + junit + + + + + diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginDatasourceTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginDatasourceTest.java index cc804950f6..e46c9e774e 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginDatasourceTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginDatasourceTest.java @@ -18,7 +18,7 @@ import com.mongodb.reactivestreams.client.MongoClient; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Mono; @@ -47,6 +47,7 @@ import static com.external.plugins.utils.DatasourceUtils.extractInfoFromConnecti import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; @@ -66,11 +67,11 @@ public class MongoPluginDatasourceTest { @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); } @@ -99,10 +100,7 @@ public class MongoPluginDatasourceTest { Mono dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig); StepVerifier.create(dsConnectionMono) - .assertNext(obj -> { - MongoClient client = obj; - assertNotNull(client); - }) + .assertNext(Assertions::assertNotNull) .verifyComplete(); } @@ -268,7 +266,7 @@ public class MongoPluginDatasourceTest { .assertNext(invalids -> { String expectedError = "Appsmith server has failed to fetch SSL configuration from datasource " + "configuration form. Please reach out to Appsmith customer support to resolve this."; - assertTrue(invalids.stream().anyMatch(error -> expectedError.equals(error))); + assertTrue(invalids.stream().anyMatch(expectedError::equals)); }) .verifyComplete(); } @@ -420,8 +418,8 @@ public class MongoPluginDatasourceTest { + "&minpoolsize=0"; Map extractedInfo = extractInfoFromConnectionStringURI(uri, MONGO_URI_REGEX); assertEquals("mongodb://", extractedInfo.get(KEY_URI_HEAD)); - assertEquals(null, extractedInfo.get(KEY_USERNAME)); - assertEquals(null, extractedInfo.get(KEY_PASSWORD)); + assertNull(extractedInfo.get(KEY_USERNAME)); + assertNull(extractedInfo.get(KEY_PASSWORD)); assertEquals("localhost:28017", extractedInfo.get(KEY_HOST_PORT)); assertEquals("mongo_samples", extractedInfo.get(KEY_URI_DEFAULT_DBNAME)); assertEquals("w=majority&retrywrites=true&authsource=admin&minpoolsize=0", extractedInfo.get(KEY_URI_TAIL)); @@ -433,8 +431,8 @@ public class MongoPluginDatasourceTest { + "&minpoolsize=0"; Map extractedInfo = extractInfoFromConnectionStringURI(uri, MONGO_URI_REGEX); assertEquals("mongodb://", extractedInfo.get(KEY_URI_HEAD)); - assertEquals(null, extractedInfo.get(KEY_USERNAME)); - assertEquals(null, extractedInfo.get(KEY_PASSWORD)); + assertNull(extractedInfo.get(KEY_USERNAME)); + assertNull(extractedInfo.get(KEY_PASSWORD)); assertEquals("localhost:28017", extractedInfo.get(KEY_HOST_PORT)); assertEquals("mongo_samples", extractedInfo.get(KEY_URI_DEFAULT_DBNAME)); assertEquals("w=majority&retrywrites=true&authsource=admin&minpoolsize=0", extractedInfo.get(KEY_URI_TAIL)); diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginErrorsTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginErrorsTest.java index 43beac875b..feb91e5441 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginErrorsTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginErrorsTest.java @@ -14,16 +14,14 @@ import com.appsmith.external.models.Property; import com.appsmith.external.models.SSLDetails; import com.external.plugins.exceptions.MongoPluginError; import com.external.plugins.exceptions.MongoPluginErrorMessages; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.mongodb.MongoCommandException; import com.mongodb.MongoSecurityException; import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; import com.mongodb.reactivestreams.client.MongoDatabase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Mono; @@ -70,19 +68,15 @@ public class MongoPluginErrorsTest { private static String address; private static Integer port; - private JsonNode value; - private static MongoClient mongoClient; @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); - String uri = "mongodb://" + address + ":" + port; - mongoClient = MongoClients.create(uri); } private DatasourceConfiguration createDatasourceConfiguration() { diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginFormsTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginFormsTest.java index 9619c186f7..00b84f9859 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginFormsTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginFormsTest.java @@ -21,7 +21,7 @@ import com.mongodb.reactivestreams.client.MongoCollection; import org.bson.Document; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Flux; @@ -73,11 +73,11 @@ public class MongoPluginFormsTest { @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); String uri = "mongodb://" + address + ":" + port; mongoClient = MongoClients.create(uri); diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginQueriesTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginQueriesTest.java index aee5e2c636..fb02c1ca90 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginQueriesTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginQueriesTest.java @@ -18,10 +18,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Mono; @@ -71,18 +70,15 @@ public class MongoPluginQueriesTest { private static String address; private static Integer port; private JsonNode value; - private static MongoClient mongoClient; @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); - String uri = "mongodb://" + address + ":" + port; - mongoClient = MongoClients.create(uri); } private DatasourceConfiguration createDatasourceConfiguration() { diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginRegexTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginRegexTest.java index 782bf80fb4..b290feaed5 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginRegexTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginRegexTest.java @@ -9,13 +9,11 @@ import com.appsmith.external.models.Endpoint; import com.appsmith.external.models.Param; import com.appsmith.external.models.ParsedDataType; import com.appsmith.external.models.SSLDetails; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Mono; @@ -44,19 +42,15 @@ public class MongoPluginRegexTest { private static String address; private static Integer port; - private JsonNode value; - private static MongoClient mongoClient; @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); - String uri = "mongodb://" + address + ":" + port; - mongoClient = MongoClients.create(uri); } private DatasourceConfiguration createDatasourceConfiguration() { diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginStaleConnTest.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginStaleConnTest.java index 18b6c0a98c..e99624f152 100644 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginStaleConnTest.java +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginStaleConnTest.java @@ -8,14 +8,12 @@ import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceStructure; import com.appsmith.external.models.Endpoint; import com.appsmith.external.models.SSLDetails; -import com.fasterxml.jackson.databind.JsonNode; import com.mongodb.MongoSocketWriteException; import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; import com.mongodb.reactivestreams.client.MongoDatabase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import reactor.core.publisher.Mono; @@ -44,19 +42,15 @@ public class MongoPluginStaleConnTest { private static String address; private static Integer port; - private JsonNode value; - private static MongoClient mongoClient; @SuppressWarnings("rawtypes") @Container - public static GenericContainer mongoContainer = new MongoTestContainer(); + public static MongoDBContainer mongoContainer = MongoTestDBContainerManager.getMongoDBForTest(); @BeforeAll public static void setUp() { - address = mongoContainer.getContainerIpAddress(); + address = mongoContainer.getHost(); port = mongoContainer.getFirstMappedPort(); - String uri = "mongodb://" + address + ":" + port; - mongoClient = MongoClients.create(uri); } private DatasourceConfiguration createDatasourceConfiguration() { diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestContainer.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestContainer.java deleted file mode 100644 index 7893b993a4..0000000000 --- a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestContainer.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.external.plugins; - -import com.github.dockerjava.api.command.InspectContainerResponse; -import com.mongodb.DBRef; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; -import com.mongodb.reactivestreams.client.MongoCollection; -import org.bson.Document; -import org.bson.types.BSONTimestamp; -import org.bson.types.Decimal128; -import org.testcontainers.containers.GenericContainer; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.math.BigDecimal; -import java.sql.Date; -import java.time.LocalDate; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -public class MongoTestContainer extends GenericContainer { - - private static MongoClient mongoClient; - - public MongoTestContainer() { - super(CompletableFuture.completedFuture("mongo:4.4")); - addExposedPorts(27017); - } - - /* - * this is overridden to prepare Mongo with sample dataset after the test container is started - */ - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo) { - - String uri = "mongodb://" + getHost() + ":" + getFirstMappedPort(); - mongoClient = MongoClients.create(uri); - - Flux.from(mongoClient.getDatabase("test").listCollectionNames()) - .collectList() - .flatMap(collectionNamesList -> { - if (collectionNamesList.size() == 0) { - final MongoCollection usersCollection = - mongoClient.getDatabase("test").getCollection("users"); - Mono.from(usersCollection.insertMany(List.of( - new Document(Map.of( - "name", - "Cierra Vega", - "gender", - "F", - "age", - 20, - "luckyNumber", - 987654321L, - "dob", - LocalDate.of(2018, 12, 31), - "netWorth", - new BigDecimal("123456.789012"), - "updatedByCommand", - false)), - new Document(Map.of( - "name", - "Alden Cantrell", - "gender", - "M", - "age", - 30, - "dob", - new Date(0), - "netWorth", - Decimal128.parse("123456.789012"), - "updatedByCommand", - false, - "aLong", - 9_000_000_000_000_000_000L, - "ts", - new BSONTimestamp(1421006159, 4))), - new Document(Map.of("name", "Kierra Gentry", "gender", "F", "age", 40))))) - .block(); - - final MongoCollection addressCollection = - mongoClient.getDatabase("test").getCollection("address"); - Mono.from(addressCollection.insertMany(List.of( - new Document(Map.of( - "user", new DBRef("test", "users", "1"), - "street", "First Street", - "city", "Line One", - "state", "UP")), - new Document(Map.of( - "user", new DBRef("AAA", "BBB", "2000"), - "street", "Second Street", - "city", "Line Two", - "state", "UP"))))) - .block(); - - final MongoCollection teamCollection = - mongoClient.getDatabase("test").getCollection("teams"); - Mono.from(teamCollection.insertMany(List.of( - new Document(Map.of( - "name", "Noisy Neighbours 2", - "goals_allowed", "20", - "goals_forwarded", "41", - "goal_difference", "+21", - "xGD", "-2.5", - "best_scoreline", "5-2")), - new Document(Map.of( - "name", "Red Side of the city", - "goals_allowed", "35", - "goals_forwarded", "28", - "goal_difference", "-7", - "xGD", "+3.6", - "best_scoreline", "8-3"))))) - .block(); - } - return Mono.empty(); - }) - .block(); - } -} diff --git a/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestDBContainerManager.java b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestDBContainerManager.java new file mode 100644 index 0000000000..f5bcd5dcb5 --- /dev/null +++ b/app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoTestDBContainerManager.java @@ -0,0 +1,116 @@ +package com.external.plugins; + +import com.mongodb.DBRef; +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; +import com.mongodb.reactivestreams.client.MongoCollection; +import org.bson.Document; +import org.bson.types.BSONTimestamp; +import org.bson.types.Decimal128; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.utility.DockerImageName; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.math.BigDecimal; +import java.sql.Date; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +public class MongoTestDBContainerManager { + public static final String MONGO_DOCKER_HUB_CONTAINER = "mongo:4.4"; + + public static MongoDBContainer getMongoDBForTest() { + MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse(MONGO_DOCKER_HUB_CONTAINER)); + mongoDBContainer.start(); + initialSetup(mongoDBContainer); + return mongoDBContainer; + } + + private static void initialSetup(MongoDBContainer mongoDBContainer) { + String uri = "mongodb://" + mongoDBContainer.getHost() + ":" + mongoDBContainer.getFirstMappedPort(); + try (MongoClient mongoClient = MongoClients.create(uri)) { + + Flux.from(mongoClient.getDatabase("test").listCollectionNames()) + .collectList() + .flatMap(collectionNamesList -> { + if (collectionNamesList.isEmpty()) { + final MongoCollection usersCollection = + mongoClient.getDatabase("test").getCollection("users"); + Mono.from(usersCollection.insertMany(List.of( + new Document(Map.of( + "name", + "Cierra Vega", + "gender", + "F", + "age", + 20, + "luckyNumber", + 987654321L, + "dob", + LocalDate.of(2018, 12, 31), + "netWorth", + new BigDecimal("123456.789012"), + "updatedByCommand", + false)), + new Document(Map.of( + "name", + "Alden Cantrell", + "gender", + "M", + "age", + 30, + "dob", + new Date(0), + "netWorth", + Decimal128.parse("123456.789012"), + "updatedByCommand", + false, + "aLong", + 9_000_000_000_000_000_000L, + "ts", + new BSONTimestamp(1421006159, 4))), + new Document(Map.of("name", "Kierra Gentry", "gender", "F", "age", 40))))) + .block(); + + final MongoCollection addressCollection = + mongoClient.getDatabase("test").getCollection("address"); + Mono.from(addressCollection.insertMany(List.of( + new Document(Map.of( + "user", new DBRef("test", "users", "1"), + "street", "First Street", + "city", "Line One", + "state", "UP")), + new Document(Map.of( + "user", new DBRef("AAA", "BBB", "2000"), + "street", "Second Street", + "city", "Line Two", + "state", "UP"))))) + .block(); + + final MongoCollection teamCollection = + mongoClient.getDatabase("test").getCollection("teams"); + Mono.from(teamCollection.insertMany(List.of( + new Document(Map.of( + "name", "Noisy Neighbours 2", + "goals_allowed", "20", + "goals_forwarded", "41", + "goal_difference", "+21", + "xGD", "-2.5", + "best_scoreline", "5-2")), + new Document(Map.of( + "name", "Red Side of the city", + "goals_allowed", "35", + "goals_forwarded", "28", + "goal_difference", "-7", + "xGD", "+3.6", + "best_scoreline", "8-3"))))) + .block(); + } + return Mono.empty(); + }) + .block(); + } + } +} diff --git a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/OpenAiPlugin.java b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/OpenAiPlugin.java index ace6f1967e..47b4241f44 100644 --- a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/OpenAiPlugin.java +++ b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/OpenAiPlugin.java @@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j; import org.json.JSONArray; import org.json.JSONObject; import org.pf4j.PluginWrapper; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatusCode; import org.springframework.web.reactive.function.BodyInserters; @@ -105,12 +104,22 @@ public class OpenAiPlugin extends BasePlugin { return RequestUtils.makeRequest(httpMethod, uri, bearerTokenAuth, BodyInserters.fromValue(openAIRequestDTO)) .flatMap(responseEntity -> { HttpStatusCode statusCode = responseEntity.getStatusCode(); - HttpHeaders headers = responseEntity.getHeaders(); ActionExecutionResult actionExecutionResult = new ActionExecutionResult(); actionExecutionResult.setRequest(actionExecutionRequest); actionExecutionResult.setStatusCode(statusCode.toString()); + if (HttpStatusCode.valueOf(401).isSameCodeAs(statusCode)) { + actionExecutionResult.setIsExecutionSuccess(false); + String errorMessage = ""; + if (responseEntity.getBody() != null && responseEntity.getBody().length > 0) { + errorMessage = new String(responseEntity.getBody()); + } + actionExecutionResult.setErrorInfo(new AppsmithPluginException( + AppsmithPluginError.PLUGIN_AUTHENTICATION_ERROR, errorMessage)); + return Mono.just(actionExecutionResult); + } + if (statusCode.is4xxClientError()) { actionExecutionResult.setIsExecutionSuccess(false); String errorMessage = ""; @@ -118,7 +127,7 @@ public class OpenAiPlugin extends BasePlugin { errorMessage = new String(responseEntity.getBody()); } actionExecutionResult.setErrorInfo(new AppsmithPluginException( - AppsmithPluginError.PLUGIN_AUTHENTICATION_ERROR, errorMessage)); + AppsmithPluginError.PLUGIN_DATASOURCE_ERROR, errorMessage)); return Mono.just(actionExecutionResult); } diff --git a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/commands/VisionCommand.java b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/commands/VisionCommand.java index 802e161692..31e0e31537 100644 --- a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/commands/VisionCommand.java +++ b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/commands/VisionCommand.java @@ -31,6 +31,7 @@ import java.util.Map; import java.util.regex.Pattern; import static com.external.plugins.constants.OpenAIConstants.CONTENT; +import static com.external.plugins.constants.OpenAIConstants.DEFAULT_MAX_TOKEN; import static com.external.plugins.constants.OpenAIConstants.ID; import static com.external.plugins.constants.OpenAIConstants.IMAGE_TYPE; import static com.external.plugins.constants.OpenAIConstants.LABEL; @@ -178,18 +179,16 @@ public class VisionCommand implements OpenAICommand { } private int getMaxTokenFromFormData(Map formData) { - int defaultMaxToken = 1000; - String maxTokenAsString = RequestUtils.extractValueFromFormData(formData, MAX_TOKENS); if (!StringUtils.hasText(maxTokenAsString)) { - return defaultMaxToken; + return DEFAULT_MAX_TOKEN; } try { return Integer.parseInt(maxTokenAsString); } catch (IllegalArgumentException illegalArgumentException) { - return defaultMaxToken; + return DEFAULT_MAX_TOKEN; } catch (Exception exception) { throw new AppsmithPluginException( AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, diff --git a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/constants/OpenAIConstants.java b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/constants/OpenAIConstants.java index 45d596c827..7a3f7069f4 100644 --- a/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/constants/OpenAIConstants.java +++ b/app/server/appsmith-plugins/openAiPlugin/src/main/java/com/external/plugins/constants/OpenAIConstants.java @@ -42,6 +42,7 @@ public class OpenAIConstants { // Other constants public static final String BODY = "body"; + public static final Integer DEFAULT_MAX_TOKEN = 16; public static final ExchangeStrategies EXCHANGE_STRATEGIES = ExchangeStrategies.builder() .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(/* 10MB */ 10 * 1024 * 1024)) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCE.java index d09f4c0090..fd6f04a770 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCE.java @@ -1,6 +1,7 @@ package com.appsmith.server.actioncollections.base; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.ActionCollection; import com.appsmith.server.domains.NewPage; @@ -68,4 +69,7 @@ public interface ActionCollectionServiceCE extends CrudService findAllActionCollectionsByContextIdAndContextTypeAndViewMode( + String contextId, CreatorContextType contextType, AclPermission permission, boolean viewMode); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCEImpl.java index 35210774e5..390324d2b1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/actioncollections/base/ActionCollectionServiceCEImpl.java @@ -1,6 +1,7 @@ package com.appsmith.server.actioncollections.base; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.Policy; import com.appsmith.server.acl.AclPermission; @@ -620,4 +621,15 @@ public class ActionCollectionServiceCEImpl extends BaseService findAllActionCollectionsByContextIdAndContextTypeAndViewMode( + String contextId, CreatorContextType contextType, AclPermission permission, boolean viewMode) { + if (viewMode) { + return repository.findAllPublishedActionCollectionsByContextIdAndContextType( + contextId, contextType, permission); + } + return repository.findAllUnpublishedActionCollectionsByContextIdAndContextType( + contextId, contextType, permission); + } } 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 c79e1be902..2118aef793 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 @@ -10,9 +10,9 @@ import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ActionCollectionDTO; import com.appsmith.server.dtos.ApplicationJson; +import com.appsmith.server.dtos.ImportActionCollectionResultDTO; import com.appsmith.server.dtos.ImportingMetaDTO; import com.appsmith.server.dtos.MappedImportableResourcesDTO; -import com.appsmith.server.dtos.ce.ImportActionCollectionResultDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.DefaultResourcesUtils; 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 19176a7798..16c73fd8db 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 @@ -15,6 +15,7 @@ public class FieldNameCE { public static final String UPDATED_AT = "updatedAt"; public static final String CURL_CODE = "curlCode"; public static final String PLUGIN_TYPE = "pluginType"; + public static final String PACKAGE_NAME = "packageName"; public static final String COLLECTION_ID = "collectionId"; public static final String ACTION_ID = "actionId"; public static String WORKSPACE = "workspace"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ApplicationTemplateControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ApplicationTemplateControllerCE.java index d1b78ce802..535e0f2721 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ApplicationTemplateControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ApplicationTemplateControllerCE.java @@ -74,14 +74,6 @@ public class ApplicationTemplateControllerCE { .map(importedApp -> new ResponseDTO<>(HttpStatus.OK.value(), importedApp, null)); } - @JsonView(Views.Public.class) - @GetMapping("recent") - public Mono>> getRecentlyUsedTemplates() { - return applicationTemplateService - .getRecentlyUsedTemplates() - .map(templates -> new ResponseDTO<>(HttpStatus.OK.value(), templates, null)); - } - @JsonView(Views.Public.class) @PostMapping("{templateId}/merge/{applicationId}/{organizationId}") public Mono> mergeTemplateWithApplication( diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/DatasourceControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/DatasourceControllerCE.java index 2ecf28c4f5..c45ab1ad2a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/DatasourceControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/DatasourceControllerCE.java @@ -158,13 +158,14 @@ public class DatasourceControllerCE { public Mono getTokenRequestUrl( @PathVariable String datasourceId, @PathVariable String pageId, - ServerWebExchange serverWebExchange, - @RequestParam String environmentId) { + @RequestParam String environmentId, + @RequestParam(name = FieldName.BRANCH_NAME, required = false) String branchName, + ServerWebExchange serverWebExchange) { log.debug( "Going to retrieve token request URL for datasource with id: {} and page id: {}", datasourceId, pageId); return authenticationService .getAuthorizationCodeURLForGenericOAuth2( - datasourceId, environmentId, pageId, serverWebExchange.getRequest()) + datasourceId, environmentId, pageId, branchName, serverWebExchange.getRequest()) .flatMap(url -> { serverWebExchange.getResponse().setStatusCode(HttpStatus.FOUND); serverWebExchange.getResponse().getHeaders().setLocation(URI.create(url)); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java index 4e1a09e621..6fa59cf2c5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java @@ -12,6 +12,7 @@ import com.appsmith.server.domains.GitApplicationMetadata; import com.appsmith.server.domains.GitAuth; import com.appsmith.server.domains.GitProfile; import com.appsmith.server.dtos.ApplicationImportDTO; +import com.appsmith.server.dtos.BranchProtectionRequestDTO; import com.appsmith.server.dtos.GitCommitDTO; import com.appsmith.server.dtos.GitConnectDTO; import com.appsmith.server.dtos.GitDeployKeyDTO; @@ -19,7 +20,6 @@ import com.appsmith.server.dtos.GitDocsDTO; import com.appsmith.server.dtos.GitMergeDTO; import com.appsmith.server.dtos.GitPullDTO; import com.appsmith.server.dtos.ResponseDTO; -import com.appsmith.server.dtos.ce.BranchProtectionRequestDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.GitDeployKeyGenerator; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/LayoutControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/LayoutControllerCE.java index ad6be0997b..a26de03c94 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/LayoutControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/LayoutControllerCE.java @@ -8,7 +8,7 @@ import com.appsmith.server.dtos.EntityType; import com.appsmith.server.dtos.LayoutDTO; import com.appsmith.server.dtos.RefactorEntityNameDTO; import com.appsmith.server.dtos.ResponseDTO; -import com.appsmith.server.dtos.ce.UpdateMultiplePageLayoutDTO; +import com.appsmith.server.dtos.UpdateMultiplePageLayoutDTO; import com.appsmith.server.refactors.applications.RefactoringSolution; import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutService; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ProductFeatureAlertControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ProductFeatureAlertControllerCE.java index 19b7885215..48ece64f5c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ProductFeatureAlertControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ProductFeatureAlertControllerCE.java @@ -2,8 +2,8 @@ package com.appsmith.server.controllers.ce; import com.appsmith.external.views.Views; import com.appsmith.server.constants.Url; +import com.appsmith.server.dtos.ProductAlertResponseDTO; import com.appsmith.server.dtos.ResponseDTO; -import com.appsmith.server.dtos.ce.ProductAlertResponseDTO; import com.appsmith.server.services.ProductAlertService; import com.fasterxml.jackson.annotation.JsonView; import org.springframework.http.HttpStatus; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java index 12ffb3c160..f00285d479 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java @@ -198,6 +198,10 @@ public class Application extends BaseDomain { @JsonView(Views.Public.class) Boolean isCommunityTemplate; + /* Template title of the template from which this app was forked, if any */ + @JsonView(Views.Public.class) + String forkedFromTemplateTitle; + @JsonView(Views.Internal.class) @Deprecated String defaultPermissionGroup; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/UserData.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/UserData.java index 2d0d7a0908..b9382105d3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/UserData.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/UserData.java @@ -72,6 +72,7 @@ public class UserData extends BaseDomain { Map userClaims; // list of template ids that were recently forked by the user + @Deprecated @JsonView(Views.Public.class) private List recentlyUsedTemplateIds; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/BranchProtectionRequestDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/BranchProtectionRequestDTO.java similarity index 83% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/BranchProtectionRequestDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/BranchProtectionRequestDTO.java index 5334eb3b4f..6c44126a9a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/BranchProtectionRequestDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/BranchProtectionRequestDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import jakarta.validation.constraints.NotNull; import lombok.Data; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/EntityType.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/EntityType.java index 4d6e35b204..38d0ee8e24 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/EntityType.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/EntityType.java @@ -4,5 +4,8 @@ public enum EntityType { WIDGET, JS_ACTION, ACTION, - JS_OBJECT + JS_OBJECT, + + // EE-only entities + MODULE_INSTANCE, } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesRequestDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesRequestDTO.java similarity index 86% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesRequestDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesRequestDTO.java index c6d430b033..f18672b49a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesRequestDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesRequestDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import lombok.Getter; import lombok.Setter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesResponseDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesResponseDTO.java similarity index 81% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesResponseDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesResponseDTO.java index 242d9834e9..17bd775b78 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/FeaturesResponseDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FeaturesResponseDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import lombok.Getter; import lombok.Setter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionCollectionResultDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionCollectionResultDTO.java similarity index 96% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionCollectionResultDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionCollectionResultDTO.java index cf62eb8233..27bb8b0a96 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionCollectionResultDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionCollectionResultDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import com.appsmith.server.domains.ActionCollection; import lombok.Getter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionResultDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionResultDTO.java similarity index 98% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionResultDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionResultDTO.java index 458e598930..f09706fce1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportActionResultDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportActionResultDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import com.appsmith.server.domains.NewAction; import lombok.Getter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportedActionAndCollectionMapsDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportedActionAndCollectionMapsDTO.java similarity index 89% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportedActionAndCollectionMapsDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportedActionAndCollectionMapsDTO.java index cb6c5f3c70..40931d3bf3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ImportedActionAndCollectionMapsDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ImportedActionAndCollectionMapsDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import lombok.Getter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertMessageApplicabilityContext.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertMessageApplicabilityContext.java similarity index 70% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertMessageApplicabilityContext.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertMessageApplicabilityContext.java index 9b1d50022f..f723266c3f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertMessageApplicabilityContext.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertMessageApplicabilityContext.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; public enum ProductAlertMessageApplicabilityContext { COMMON_CONFIG, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertResponseDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertResponseDTO.java similarity index 96% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertResponseDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertResponseDTO.java index fd08de9779..ea1bddadc5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ProductAlertResponseDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ProductAlertResponseDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RefactorEntityNameDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RefactorEntityNameDTO.java index 5fcd9ffbbf..379dbfd0ae 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RefactorEntityNameDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RefactorEntityNameDTO.java @@ -1,29 +1,5 @@ package com.appsmith.server.dtos; -import com.appsmith.external.views.Views; -import com.fasterxml.jackson.annotation.JsonView; -import lombok.Getter; -import lombok.Setter; +import com.appsmith.server.dtos.ce.RefactorEntityNameCE_DTO; -@Getter -@Setter -public class RefactorEntityNameDTO { - String pageId; - String layoutId; - String oldName; - String newName; - - @JsonView(Views.Internal.class) - EntityType entityType; - - String actionId; - String actionCollectionId; - String collectionName; - ActionCollectionDTO actionCollection; - - @JsonView(Views.Internal.class) - String oldFullyQualifiedName; - - @JsonView(Views.Internal.class) - String newFullyQualifiedName; -} +public class RefactorEntityNameDTO extends RefactorEntityNameCE_DTO {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UncommittedChangesDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UncommittedChangesDTO.java similarity index 91% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UncommittedChangesDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UncommittedChangesDTO.java index c4b0a27355..25a07ad6ed 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UncommittedChangesDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UncommittedChangesDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import lombok.Data; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UpdateMultiplePageLayoutDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UpdateMultiplePageLayoutDTO.java similarity index 93% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UpdateMultiplePageLayoutDTO.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UpdateMultiplePageLayoutDTO.java index a28b4818ea..ee21c95e0d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UpdateMultiplePageLayoutDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UpdateMultiplePageLayoutDTO.java @@ -1,4 +1,4 @@ -package com.appsmith.server.dtos.ce; +package com.appsmith.server.dtos; import com.appsmith.server.domains.Layout; import jakarta.validation.Valid; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserUpdateDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserUpdateDTO.java index d48dd720ac..c66d89ba14 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserUpdateDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserUpdateDTO.java @@ -1,10 +1,10 @@ package com.appsmith.server.dtos; -import com.appsmith.server.dtos.ce.UserUpdateDTO_CE; +import com.appsmith.server.dtos.ce.UserUpdateCE_DTO; import lombok.Data; /** * Includes **only** those fields that can be updated for a user, via an API call. */ @Data -public class UserUpdateDTO extends UserUpdateDTO_CE {} +public class UserUpdateDTO extends UserUpdateCE_DTO {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ActionCollectionCE_DTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ActionCollectionCE_DTO.java index 9976638a46..c482972775 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ActionCollectionCE_DTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/ActionCollectionCE_DTO.java @@ -2,6 +2,7 @@ package com.appsmith.server.dtos.ce; import com.appsmith.external.exceptions.ErrorDTO; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.JSValue; import com.appsmith.external.models.PluginType; @@ -50,6 +51,9 @@ public class ActionCollectionCE_DTO { @JsonView(Views.Public.class) String pageId; + @JsonView(Views.Public.class) + CreatorContextType contextType; + // This field will only be populated if this collection is bound to one plugin (eg: JS) @JsonView(Views.Public.class) String pluginId; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/MappedImportableResourcesCE_DTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/MappedImportableResourcesCE_DTO.java index fe81e0522b..5b2fb78816 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/MappedImportableResourcesCE_DTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/MappedImportableResourcesCE_DTO.java @@ -2,6 +2,9 @@ package com.appsmith.server.dtos.ce; import com.appsmith.server.domains.NewPage; import com.appsmith.server.dtos.CustomJSLibApplicationDTO; +import com.appsmith.server.dtos.ImportActionCollectionResultDTO; +import com.appsmith.server.dtos.ImportActionResultDTO; +import com.appsmith.server.dtos.ImportedActionAndCollectionMapsDTO; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/RefactorEntityNameCE_DTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/RefactorEntityNameCE_DTO.java new file mode 100644 index 0000000000..85017a15e4 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/RefactorEntityNameCE_DTO.java @@ -0,0 +1,31 @@ +package com.appsmith.server.dtos.ce; + +import com.appsmith.external.views.Views; +import com.appsmith.server.dtos.ActionCollectionDTO; +import com.appsmith.server.dtos.EntityType; +import com.fasterxml.jackson.annotation.JsonView; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class RefactorEntityNameCE_DTO { + String pageId; + String layoutId; + String oldName; + String newName; + + @JsonView(Views.Internal.class) + EntityType entityType; + + String actionId; + String actionCollectionId; + String collectionName; + ActionCollectionDTO actionCollection; + + @JsonView(Views.Internal.class) + String oldFullyQualifiedName; + + @JsonView(Views.Internal.class) + String newFullyQualifiedName; +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateDTO_CE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateCE_DTO.java similarity index 94% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateDTO_CE.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateCE_DTO.java index b9a5c6f628..be2ae09ee4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateDTO_CE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ce/UserUpdateCE_DTO.java @@ -6,7 +6,7 @@ import lombok.Data; * Includes **only** those fields that can be updated for a user, via an API call. */ @Data -public class UserUpdateDTO_CE { +public class UserUpdateCE_DTO { private String name; private String role; 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 7058e16a81..debdd9841d 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 @@ -200,10 +200,11 @@ public enum AppsmithError { + " \"message\" : \"Binding path in the widget not found. Please reach out to Appsmith customer support to resolve this.\"," + " \"widgetName\" : \"{1}\"," + " \"widgetId\" : \"{2}\"," - + " \"pageId\" : \"{4}\"," + + " \"creatorId\" : \"{4}\"," + " \"layoutId\" : \"{5}\"," + " \"errorDetail\" : \"{8}\"," - + " \"dynamicBinding\" : {6}", + + " \"dynamicBinding\" : {6}," + + " \"creatorType\" : \"{9}\"", AppsmithErrorAction.LOG_EXTERNALLY, "Invalid dynamic binding reference", ErrorType.BAD_REQUEST, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java index 71b299359d..cfbe2e0e5d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java @@ -233,13 +233,11 @@ public class GlobalExceptionHandler { @ExceptionHandler @ResponseBody public Mono> catchPluginException(AppsmithPluginException e, ServerWebExchange exchange) { - AppsmithError appsmithError = AppsmithError.INTERNAL_SERVER_ERROR; - exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode())); + exchange.getResponse().setStatusCode(HttpStatus.resolve(e.getHttpStatus())); doLog(e); String urlPath = exchange.getRequest().getPath().toString(); ResponseDTO response = new ResponseDTO<>( - appsmithError.getHttpErrorCode(), - new ErrorDTO(appsmithError.getAppErrorCode(), e.getMessage(), e.getErrorType(), e.getTitle())); + e.getHttpStatus(), new ErrorDTO(e.getAppErrorCode(), e.getErrorType(), e.getMessage(), e.getTitle())); return getResponseDTOMono(urlPath, response); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCEImpl.java index 5e9f655d10..d392f5e87b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceCEImpl.java @@ -1,5 +1,6 @@ package com.appsmith.server.exports.internal; +import com.appsmith.external.constants.AnalyticsEvents; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.DatasourceStorage; import com.appsmith.server.acl.AclPermission; @@ -11,6 +12,7 @@ import com.appsmith.server.domains.Application; import com.appsmith.server.domains.CustomJSLib; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.Plugin; +import com.appsmith.server.domains.User; import com.appsmith.server.dtos.ApplicationJson; import com.appsmith.server.dtos.ExportingMetaDTO; import com.appsmith.server.dtos.MappedExportableResourcesDTO; @@ -22,15 +24,17 @@ import com.appsmith.server.jslibs.base.CustomJSLibService; import com.appsmith.server.migrations.JsonSchemaVersions; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.solutions.ApplicationPermission; -import com.google.gson.Gson; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import reactor.core.publisher.Mono; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static com.appsmith.server.constants.ResourceModes.EDIT; @@ -50,7 +54,8 @@ public class PartialExportServiceCEImpl implements PartialExportServiceCE { private final ExportableService pluginExportableService; private final ExportableService newActionExportableService; private final ExportableService actionCollectionExportableService; - private final Gson gson; + private final SessionUserService sessionUserService; + private final AnalyticsService analyticsService; @Override public Mono getPartialExportResources( @@ -158,12 +163,26 @@ public class PartialExportServiceCEImpl implements PartialExportServiceCE { applicationJson, SerialiseApplicationObjective.SHARE); } - return Mono.just(applicationJson); + return Mono.just(applicationJson).zipWith(sessionUserService.getCurrentUser()); }) - .map(exportedJson -> { + .flatMap(tuple -> { + ApplicationJson applicationJson1 = tuple.getT1(); + Application application = applicationJson1.getExportedApplication(); + applicationJson.setWidgets(partialExportFileDTO.getWidget()); applicationJson.setExportedApplication(null); - return applicationJson; + + User user = tuple.getT2(); + final Map eventData = Map.of(FieldName.APPLICATION, application); + + final Map data = Map.of( + FieldName.APPLICATION_ID, application.getId(), + FieldName.WORKSPACE_ID, application.getWorkspaceId(), + FieldName.EVENT_DATA, eventData); + + return analyticsService + .sendEvent(AnalyticsEvents.PARTIAL_EXPORT.getEventName(), user.getUsername(), data) + .thenReturn(applicationJson); }); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceImpl.java index f01b3fb04e..8ab40ce103 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exports/internal/PartialExportServiceImpl.java @@ -9,9 +9,10 @@ import com.appsmith.server.exports.exportable.ExportableService; import com.appsmith.server.jslibs.base.CustomJSLibService; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.solutions.ApplicationPermission; -import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -29,7 +30,8 @@ public class PartialExportServiceImpl extends PartialExportServiceCEImpl impleme ExportableService pluginExportableService, ExportableService newActionExportableService, ExportableService actionCollectionExportableService, - Gson gson) { + SessionUserService sessionUserService, + AnalyticsService analyticsService) { super( applicationService, applicationPermission, @@ -41,6 +43,7 @@ public class PartialExportServiceImpl extends PartialExportServiceCEImpl impleme pluginExportableService, newActionExportableService, actionCollectionExportableService, - gson); + sessionUserService, + analyticsService); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java index b8742b34a2..26d16a3b90 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java @@ -29,6 +29,7 @@ public enum FeatureFlagEnum { release_embed_hide_share_settings_enabled, ab_mock_mongo_schema_enabled, rollout_datasource_test_rate_limit_enabled, + ab_onboarding_flow_start_with_data_dev_only_enabled, // Add EE flags below this line, to avoid conflicts. } 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 ed4b5a5e32..2dff04c4f6 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 @@ -338,10 +338,10 @@ public class ImportApplicationServiceCEImpl implements ImportApplicationServiceC */ private String validateApplicationJson(ApplicationJson importedDoc) { String errorField = ""; - if (CollectionUtils.isEmpty(importedDoc.getPageList())) { - errorField = FieldName.PAGE_LIST; - } else if (importedDoc.getExportedApplication() == null) { + if (importedDoc.getExportedApplication() == null) { errorField = FieldName.APPLICATION; + } else if (CollectionUtils.isEmpty(importedDoc.getPageList())) { + errorField = FieldName.PAGE_LIST; } else if (importedDoc.getActionList() == null) { errorField = FieldName.ACTIONS; } else if (importedDoc.getDatasourceList() == null) { @@ -493,6 +493,13 @@ public class ImportApplicationServiceCEImpl implements ImportApplicationServiceC String errorField = validateApplicationJson(importedDoc); if (!errorField.isEmpty()) { log.error("Error in importing application. Field {} is missing", errorField); + if (errorField.equals(FieldName.APPLICATION)) { + return Mono.error( + new AppsmithException( + AppsmithError.VALIDATION_FAILURE, + "Field '" + errorField + + "' Sorry! Seems like you've imported a page-level json instead of an application. Please use the import within the page.")); + } return Mono.error(new AppsmithException( AppsmithError.VALIDATION_FAILURE, "Field '" + errorField + "' is missing in the JSON.")); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCEImpl.java index 63af9fb0dc..c69dc5d542 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceCEImpl.java @@ -10,6 +10,7 @@ import com.appsmith.server.domains.CustomJSLib; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Plugin; +import com.appsmith.server.domains.User; import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ApplicationJson; import com.appsmith.server.dtos.ImportingMetaDTO; @@ -22,22 +23,21 @@ import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.WorkspacePermission; -import com.google.gson.Gson; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.codec.multipart.Part; import org.springframework.transaction.reactive.TransactionalOperator; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -54,7 +54,7 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { private final ApplicationPermission applicationPermission; private final PagePermission pagePermission; private final ActionPermission actionPermission; - private final Gson gson; + private final SessionUserService sessionUserService; private final TransactionalOperator transactionalOperator; private final PermissionGroupRepository permissionGroupRepository; private final ImportableService pluginImportableService; @@ -68,22 +68,14 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { @Override public Mono importResourceInPage( String workspaceId, String applicationId, String pageId, String branchName, Part file) { - /* - 1. Get branchedPageId from pageId and branchName - 2. Get Application Mono - 3. Prepare the Meta DTO's - 4. Get plugin data - 5. Import datasources - 6. Import customJsLib - 7. Import actions - 8. Import actionCollection - */ MappedImportableResourcesDTO mappedImportableResourcesDTO = new MappedImportableResourcesDTO(); Mono branchedPageIdMono = newPageService.findBranchedPageId(branchName, pageId, AclPermission.MANAGE_PAGES); + Mono currUserMono = sessionUserService.getCurrentUser(); + // Extract file and get App Json Mono partiallyImportedAppMono = importApplicationService .extractApplicationJson(file) @@ -144,21 +136,30 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { importedApplicationMono, applicationJson)) .thenReturn("done") - .then(Mono.defer(() -> { + .flatMap(result -> { Application application = applicationJson.getExportedApplication(); + // Keep existing JS Libs and add the imported ones + application + .getUnpublishedCustomJSLibs() + .addAll(new HashSet<>(mappedImportableResourcesDTO.getInstalledJsLibsList())); + if (mappedImportableResourcesDTO.getActionResultDTO() == null) { + return applicationService.update(application.getId(), application); + } return newActionImportableService .updateImportedEntities( application, importingMetaDTO, mappedImportableResourcesDTO) .then(newPageImportableService.updateImportedEntities( application, importingMetaDTO, mappedImportableResourcesDTO)) - .flatMap( - newPage -> applicationService.update(application.getId(), application)); - })); + .thenReturn(application); + }); }) + .flatMap(application -> applicationService.update(application.getId(), application)) .as(transactionalOperator::transactional); // Send Analytics event - return partiallyImportedAppMono.flatMap(application -> { + return partiallyImportedAppMono.zipWith(currUserMono).flatMap(tuple -> { + Application application = tuple.getT1(); + User user = tuple.getT2(); final Map eventData = Map.of(FieldName.APPLICATION, application); final Map data = Map.of( @@ -166,7 +167,9 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { FieldName.WORKSPACE_ID, application.getWorkspaceId(), FieldName.EVENT_DATA, eventData); - return analyticsService.sendObjectEvent(AnalyticsEvents.PARTIAL_IMPORT, application, data); + return analyticsService + .sendEvent(AnalyticsEvents.PARTIAL_IMPORT.getEventName(), user.getUsername(), data) + .thenReturn(application); }); } @@ -194,7 +197,7 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { Mono workspaceMono, Mono importedApplicationMono, ApplicationJson applicationJson) { - Mono customJSLibMono = pluginImportableService.importEntities( + Mono pluginMono = pluginImportableService.importEntities( importingMetaDTO, mappedImportableResourcesDTO, workspaceMono, @@ -211,8 +214,7 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { Mono customJsLibMono = customJSLibImportableService.importEntities( importingMetaDTO, mappedImportableResourcesDTO, null, null, applicationJson); - return Flux.merge(List.of(customJsLibMono, datasourceMono, customJSLibMono)) - .then(); + return pluginMono.then(datasourceMono).then(customJsLibMono).then(); } private Mono getActionAndActionCollectionImport( @@ -235,7 +237,7 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { importedApplicationMono, applicationJson); - return Flux.merge(List.of(actionMono, actionCollectionMono)).then(); + return actionMono.then(actionCollectionMono).then(); } private Mono paneNameMapForActionAndActionCollectionInAppJson( @@ -250,6 +252,10 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { pageNameMap.put(pageName, newPage); mappedImportableResourcesDTO.setPageNameMap(pageNameMap); + if (applicationJson.getActionList() == null) { + return Mono.just(pageName); + } + applicationJson.getActionList().forEach(action -> { action.getPublishedAction().setPageId(pageName); action.getUnpublishedAction().setPageId(pageName); @@ -266,6 +272,9 @@ public class PartialImportServiceCEImpl implements PartialImportServiceCE { action.setGitSyncId(null); }); + if (applicationJson.getActionCollectionList() == null) { + return Mono.just(pageName); + } applicationJson.getActionCollectionList().forEach(actionCollection -> { actionCollection.getPublishedCollection().setPageId(pageName); actionCollection.getUnpublishedCollection().setPageId(pageName); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceImpl.java index 1db60b46d2..c6b06ac1a6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/imports/internal/PartialImportServiceImpl.java @@ -11,13 +11,13 @@ import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.WorkspacePermission; -import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; @@ -38,7 +38,7 @@ public class PartialImportServiceImpl extends PartialImportServiceCEImpl impleme ApplicationPermission applicationPermission, PagePermission pagePermission, ActionPermission actionPermission, - Gson gson, + SessionUserService sessionUserService, TransactionalOperator transactionalOperator, PermissionGroupRepository permissionGroupRepository, ImportableService pluginImportableService, @@ -58,7 +58,7 @@ public class PartialImportServiceImpl extends PartialImportServiceCEImpl impleme applicationPermission, pagePermission, actionPermission, - gson, + sessionUserService, transactionalOperator, permissionGroupRepository, pluginImportableService, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration034ChangeOpenAIIntegrationDocumentationLink.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration034ChangeOpenAIIntegrationDocumentationLink.java new file mode 100644 index 0000000000..72fa85f927 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration034ChangeOpenAIIntegrationDocumentationLink.java @@ -0,0 +1,41 @@ +package com.appsmith.server.migrations.db.ce; + +import com.appsmith.external.constants.PluginConstants; +import com.appsmith.server.constants.ce.FieldNameCE; +import com.appsmith.server.domains.Plugin; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +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; + +@Slf4j +@ChangeUnit(order = "034", id = "change-open-ai-integration-documentation-link", author = " ") +public class Migration034ChangeOpenAIIntegrationDocumentationLink { + + private final MongoTemplate mongoTemplate; + + public Migration034ChangeOpenAIIntegrationDocumentationLink(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void changeDocumentationLink() { + Query pluginFindQuery = new Query(); + + pluginFindQuery.addCriteria( + Criteria.where(FieldNameCE.PACKAGE_NAME).is(PluginConstants.PackageName.OPEN_AI_PLUGIN)); + Plugin openAiPlugin = mongoTemplate.findOne(pluginFindQuery, Plugin.class); + if (openAiPlugin == null) { + log.debug("OpenAI plugin not found while trying to update the documentation link"); + return; + } + openAiPlugin.setDocumentationLink("https://docs.appsmith.com/connect-data/reference/open-ai"); + mongoTemplate.save(openAiPlugin); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java index 0391928779..7a14fe0514 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java @@ -1,17 +1,18 @@ package com.appsmith.server.newactions.base; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Executable; import com.appsmith.external.models.MustacheBindingToken; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.NewPage; import com.appsmith.server.dtos.ActionViewDTO; +import com.appsmith.server.dtos.ImportActionCollectionResultDTO; +import com.appsmith.server.dtos.ImportActionResultDTO; +import com.appsmith.server.dtos.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.dtos.LayoutExecutableUpdateDTO; import com.appsmith.server.dtos.PluginTypeAndCountDTO; -import com.appsmith.server.dtos.ce.ImportActionCollectionResultDTO; -import com.appsmith.server.dtos.ce.ImportActionResultDTO; -import com.appsmith.server.dtos.ce.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.services.CrudService; import com.mongodb.bulk.BulkWriteResult; import org.springframework.data.domain.Sort; @@ -136,4 +137,11 @@ public interface NewActionServiceCE extends CrudService { Flux countActionsByPluginType(String applicationId); Flux findByListOfPageIds(List unpublishedPages, Optional optionalPermission); + + Flux findAllActionsByContextIdAndContextTypeAndViewMode( + String contextId, + CreatorContextType contextType, + AclPermission permission, + boolean viewMode, + boolean includeJs); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java index 298a561035..97534fedac 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java @@ -6,6 +6,7 @@ import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.ActionDTO; import com.appsmith.external.models.ActionProvider; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DefaultResources; @@ -30,11 +31,11 @@ import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Page; import com.appsmith.server.domains.Plugin; import com.appsmith.server.dtos.ActionViewDTO; +import com.appsmith.server.dtos.ImportActionCollectionResultDTO; +import com.appsmith.server.dtos.ImportActionResultDTO; +import com.appsmith.server.dtos.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.dtos.LayoutExecutableUpdateDTO; import com.appsmith.server.dtos.PluginTypeAndCountDTO; -import com.appsmith.server.dtos.ce.ImportActionCollectionResultDTO; -import com.appsmith.server.dtos.ce.ImportActionResultDTO; -import com.appsmith.server.dtos.ce.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.PluginExecutorHelper; @@ -317,8 +318,7 @@ public class NewActionServiceCEImpl extends BaseService unpublishedPages, Optional optionalPermission) { return repository.findByListOfPageIds(unpublishedPages, optionalPermission); } + + @Override + public Flux findAllActionsByContextIdAndContextTypeAndViewMode( + String contextId, + CreatorContextType contextType, + AclPermission permission, + boolean viewMode, + boolean includeJs) { + if (viewMode) { + return repository.findAllPublishedActionsByContextIdAndContextType( + contextId, contextType, permission, includeJs); + } + return repository.findAllUnpublishedActionsByContextIdAndContextType( + contextId, contextType, permission, includeJs); + } } 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 8bb7be2457..07cda3c385 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 @@ -11,10 +11,10 @@ import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ApplicationJson; +import com.appsmith.server.dtos.ImportActionCollectionResultDTO; +import com.appsmith.server.dtos.ImportActionResultDTO; import com.appsmith.server.dtos.ImportingMetaDTO; import com.appsmith.server.dtos.MappedImportableResourcesDTO; -import com.appsmith.server.dtos.ce.ImportActionCollectionResultDTO; -import com.appsmith.server.dtos.ce.ImportActionResultDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.ce.ImportApplicationPermissionProvider; @@ -70,7 +70,7 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE importedNewActionList = applicationJson.getActionList(); - Mono> importedNewActionMono = Mono.just(importedNewActionList); + Mono> importedNewActionMono = Mono.justOrEmpty(importedNewActionList); if (Boolean.TRUE.equals(importingMetaDTO.getAppendToApp())) { importedNewActionMono = importedNewActionMono.map(importedNewActionList1 -> { List importedNewPages = mappedImportableResourcesDTO.getPageNameMap().values().stream() diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceCEImpl.java deleted file mode 100644 index 87819addff..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceCEImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.appsmith.server.newactions.onpageload; - -import com.appsmith.external.models.ActionDTO; -import com.appsmith.external.models.Executable; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.onpageload.executables.ExecutableOnPageLoadServiceCE; -import com.appsmith.server.solutions.ActionPermission; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Slf4j -@RequiredArgsConstructor -public class ActionOnPageLoadServiceCEImpl implements ExecutableOnPageLoadServiceCE { - - private final NewActionService newActionService; - private final ActionPermission actionPermission; - - @Override - public Flux getAllExecutablesByPageIdFlux(String pageId) { - return newActionService - .findByPageIdAndViewMode(pageId, false, actionPermission.getEditPermission()) - .flatMap(newAction -> newActionService.generateActionByViewMode(newAction, false)) - .map(actionDTO -> (Executable) actionDTO) - .cache(); - } - - @Override - public Mono fillSelfReferencingPaths(ActionDTO executable) { - return newActionService.fillSelfReferencingDataPaths(executable).map(actionDTO -> actionDTO); - } - - @Override - public Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(String pageId) { - return newActionService - .findUnpublishedOnLoadActionsExplicitSetByUserInPage(pageId) - .flatMap(newAction -> newActionService.generateActionByViewMode(newAction, false)); - } - - @Override - public Mono updateUnpublishedExecutable(String id, Executable executable) { - return newActionService - .updateUnpublishedAction(id, (ActionDTO) executable) - .map(updated -> updated); - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceImpl.java deleted file mode 100644 index 4fed6b5390..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/onpageload/ActionOnPageLoadServiceImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.appsmith.server.newactions.onpageload; - -import com.appsmith.external.models.ActionDTO; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.onpageload.executables.ExecutableOnPageLoadService; -import com.appsmith.server.solutions.ActionPermission; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -@Slf4j -@Service -public class ActionOnPageLoadServiceImpl extends ActionOnPageLoadServiceCEImpl - implements ExecutableOnPageLoadService { - public ActionOnPageLoadServiceImpl(NewActionService newActionService, ActionPermission actionPermission) { - super(newActionService, actionPermission); - } -} 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 809b308a75..e4d88a17a1 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 @@ -9,10 +9,10 @@ import com.appsmith.server.domains.ApplicationPage; import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ApplicationJson; +import com.appsmith.server.dtos.ImportActionResultDTO; +import com.appsmith.server.dtos.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.dtos.ImportingMetaDTO; import com.appsmith.server.dtos.MappedImportableResourcesDTO; -import com.appsmith.server.dtos.ce.ImportActionResultDTO; -import com.appsmith.server.dtos.ce.ImportedActionAndCollectionMapsDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.DefaultResourcesUtils; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceCEImpl.java new file mode 100644 index 0000000000..b173931d22 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceCEImpl.java @@ -0,0 +1,108 @@ +package com.appsmith.server.newpages.onload; + +import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.Executable; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.domains.Layout; +import com.appsmith.server.domains.NewPage; +import com.appsmith.server.dtos.PageDTO; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.newactions.base.NewActionService; +import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.onload.executables.ExecutableOnLoadServiceCE; +import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.PagePermission; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class ExecutableOnPageLoadServiceCEImpl implements ExecutableOnLoadServiceCE { + + private final NewActionService newActionService; + private final NewPageService newPageService; + private final ApplicationService applicationService; + + private final ActionPermission actionPermission; + private final PagePermission pagePermission; + + @Override + public Flux getAllExecutablesByCreatorIdFlux(String creatorId) { + return newActionService + .findByPageIdAndViewMode(creatorId, false, actionPermission.getEditPermission()) + .flatMap(newAction -> newActionService.generateActionByViewMode(newAction, false)) + .map(actionDTO -> (Executable) actionDTO) + .cache(); + } + + @Override + public Mono fillSelfReferencingPaths(Executable executable) { + return newActionService + .fillSelfReferencingDataPaths((ActionDTO) executable) + .map(actionDTO -> actionDTO); + } + + @Override + public Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(String creatorId) { + return newActionService + .findUnpublishedOnLoadActionsExplicitSetByUserInPage(creatorId) + .flatMap(newAction -> newActionService.generateActionByViewMode(newAction, false)); + } + + @Override + public Mono updateUnpublishedExecutable(String id, Executable executable) { + return newActionService + .updateUnpublishedAction(id, (ActionDTO) executable) + .map(updated -> updated); + } + + @Override + public Mono findAndUpdateLayout(String creatorId, String layoutId, Layout layout) { + Mono pageDTOMono = newPageService + .findByIdAndLayoutsId(creatorId, layoutId, pagePermission.getEditPermission(), false) + .switchIfEmpty(Mono.error(new AppsmithException( + AppsmithError.ACL_NO_RESOURCE_FOUND, + FieldName.PAGE_ID + " or " + FieldName.LAYOUT_ID, + creatorId + ", " + layoutId))); + + return pageDTOMono + .flatMap(page -> { + List layoutList = page.getLayouts(); + // Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the + // layoutId here. + for (Layout storedLayout : layoutList) { + if (storedLayout.getId().equals(layoutId)) { + // Now that all the on load actions have been computed, set the vertices, edges, actions in + // DSL in the layout for re-use to avoid computing DAG unnecessarily. + + BeanUtils.copyProperties(layout, storedLayout); + storedLayout.setId(layoutId); + + break; + } + } + page.setLayouts(layoutList); + return applicationService + .saveLastEditInformation(page.getApplicationId()) + .then(newPageService.saveUnpublishedPage(page)); + }) + .flatMap(page -> { + List layoutList = page.getLayouts(); + for (Layout storedLayout : layoutList) { + if (storedLayout.getId().equals(layoutId)) { + return Mono.just(storedLayout); + } + } + return Mono.empty(); + }); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceImpl.java new file mode 100644 index 0000000000..18f09af14f --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/onload/ExecutableOnPageLoadServiceImpl.java @@ -0,0 +1,25 @@ +package com.appsmith.server.newpages.onload; + +import com.appsmith.server.domains.NewPage; +import com.appsmith.server.newactions.base.NewActionService; +import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.onload.executables.ExecutableOnLoadService; +import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.PagePermission; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class ExecutableOnPageLoadServiceImpl extends ExecutableOnPageLoadServiceCEImpl + implements ExecutableOnLoadService { + public ExecutableOnPageLoadServiceImpl( + NewActionService newActionService, + NewPageService newPageService, + ApplicationService applicationService, + ActionPermission actionPermission, + PagePermission pagePermission) { + super(newActionService, newPageService, applicationService, actionPermission, pagePermission); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadService.java new file mode 100644 index 0000000000..7c380a27d8 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadService.java @@ -0,0 +1,5 @@ +package com.appsmith.server.onload.executables; + +import com.appsmith.external.models.BaseDomain; + +public interface ExecutableOnLoadService extends ExecutableOnLoadServiceCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadServiceCE.java new file mode 100644 index 0000000000..e951fcf617 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/executables/ExecutableOnLoadServiceCE.java @@ -0,0 +1,20 @@ +package com.appsmith.server.onload.executables; + +import com.appsmith.external.models.BaseDomain; +import com.appsmith.external.models.Executable; +import com.appsmith.server.domains.Layout; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface ExecutableOnLoadServiceCE { + + Flux getAllExecutablesByCreatorIdFlux(String creatorId); + + Mono fillSelfReferencingPaths(Executable executable); + + Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(String creatorId); + + Mono updateUnpublishedExecutable(String id, Executable executable); + + Mono findAndUpdateLayout(String creatorId, String layoutId, Layout layout); +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtil.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtil.java new file mode 100644 index 0000000000..50024413e8 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtil.java @@ -0,0 +1,3 @@ +package com.appsmith.server.onload.internal; + +public interface OnLoadExecutablesUtil extends OnLoadExecutablesUtilCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCE.java similarity index 73% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCE.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCE.java index 87fdd30779..3237ee77d0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCE.java @@ -1,32 +1,35 @@ -package com.appsmith.server.onpageload.internal; +package com.appsmith.server.onload.internal; import com.appsmith.external.dtos.DslExecutableDTO; import com.appsmith.external.dtos.LayoutExecutableUpdateDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Executable; import com.appsmith.server.domains.ExecutableDependencyEdge; +import com.appsmith.server.domains.Layout; import reactor.core.publisher.Mono; import java.util.List; import java.util.Map; import java.util.Set; -public interface PageLoadExecutablesUtilCE { +public interface OnLoadExecutablesUtilCE { Mono>> findAllOnLoadExecutables( - String pageId, + String creatorId, Integer evaluatedVersion, Set widgetNames, Set edges, Map> widgetDynamicBindingsMap, List flatPageLoadExecutables, - Set executablesUsedInDSL); + Set executablesUsedInDSL, + CreatorContextType creatorType); /** * !!!WARNING!!! This function edits the parameters executableUpdatesRef and messagesRef which are eventually returned back to * the caller with the updates values. * * @param onLoadExecutables : All the actions which have been found to be on page load - * @param pageId + * @param creatorId * @param executableUpdatesRef : Empty array list which would be set in this function with all the page actions whose * execute on load setting has changed (whether flipped from true to false, or vice versa) * @param messagesRef : Empty array list which would be set in this function with all the messagesRef that should be @@ -35,7 +38,10 @@ public interface PageLoadExecutablesUtilCE { */ Mono updateExecutablesExecuteOnLoad( List onLoadExecutables, - String pageId, + String creatorId, List executableUpdatesRef, - List messagesRef); + List messagesRef, + CreatorContextType creatorType); + + Mono findAndUpdateLayout(String creatorId, CreatorContextType creatorType, String layoutId, Layout layout); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCEImpl.java similarity index 94% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCEImpl.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCEImpl.java index 681b506f05..035b4f6119 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilCEImpl.java @@ -1,17 +1,20 @@ -package com.appsmith.server.onpageload.internal; +package com.appsmith.server.onload.internal; import com.appsmith.external.dtos.DslExecutableDTO; import com.appsmith.external.dtos.LayoutExecutableUpdateDTO; import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.EntityDependencyNode; import com.appsmith.external.models.EntityReferenceType; import com.appsmith.external.models.Executable; import com.appsmith.external.models.Property; import com.appsmith.server.domains.ExecutableDependencyEdge; +import com.appsmith.server.domains.Layout; +import com.appsmith.server.domains.NewPage; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.server.onpageload.executables.ExecutableOnPageLoadService; +import com.appsmith.server.onload.executables.ExecutableOnLoadService; import com.appsmith.server.services.AstService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -48,11 +51,11 @@ import static java.lang.Boolean.TRUE; @Slf4j @RequiredArgsConstructor -public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE { +public class OnLoadExecutablesUtilCEImpl implements OnLoadExecutablesUtilCE { private final AstService astService; private final ObjectMapper objectMapper; - private final ExecutableOnPageLoadService actionExecutableOnPageLoadService; + private final ExecutableOnLoadService pageExecutableOnLoadService; /** * The following regex finds the immediate parent of an entity path. @@ -75,7 +78,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE * !!!WARNING!!! : This function edits the parameters edges, executablesUsedInDSL and flatPageLoadExecutables * and the same are used by the caller function for further processing. * - * @param pageId : Argument used for fetching executables in this page + * @param creatorId : Argument used for fetching executables in this page * @param evaluatedVersion : Depending on the evaluated version, the way the AST parsing logic picks entities in the dynamic binding will change * @param widgetNames : Set of widget names which SHOULD have been populated before calling this function. * @param edgesRef : Set where this function adds all the relationships (dependencies) between executables @@ -92,22 +95,22 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE * in the list. */ public Mono>> findAllOnLoadExecutables( - String pageId, + String creatorId, Integer evaluatedVersion, Set widgetNames, Set edgesRef, Map> widgetDynamicBindingsMap, List flatPageLoadExecutablesRef, - Set executablesUsedInDSLRef) { + Set executablesUsedInDSLRef, + CreatorContextType creatorType) { - Set onPageLoadExecutableSetRef = new HashSet<>(); + Set onLoadExecutableSetRef = new HashSet<>(); Set explicitUserSetOnLoadExecutablesRef = new HashSet<>(); Set bindingsFromExecutablesRef = ConcurrentHashMap.newKeySet(); // Function `extractAndSetExecutableBindingsInGraphEdges` updates this map to keep a track of all the - // executables which - // have been discovered while walking the executables to ensure that we don't end up in a recursive infinite - // loop + // executables which have been discovered while walking the executables to ensure that we don't end up in a + // recursive infinite loop // in case of a cyclical relationship between executables (and not specific paths) and helps us exit at the // appropriate junction. // e.g : Consider the following relationships : @@ -116,13 +119,13 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // In the above case, the two executables depend on each other without there being a real cyclical dependency. Map executablesFoundDuringWalkRef = new HashMap<>(); - Flux allExecutablesByPageIdFlux = getAllExecutablesByPageIdFlux(pageId); + Flux allExecutablesByCreatorIdFlux = getAllExecutablesByCreatorIdFlux(creatorId, creatorType); - Mono> executableNameToExecutableMapMono = allExecutablesByPageIdFlux + Mono> executableNameToExecutableMapMono = allExecutablesByCreatorIdFlux .collectMap(Executable::getValidName, executable -> executable) .cache(); - Mono> executablesInPageMono = allExecutablesByPageIdFlux + Mono> executablesInCreatorContextMono = allExecutablesByCreatorIdFlux .map(Executable::getValidName) .collect(Collectors.toSet()) .cache(); @@ -147,17 +150,17 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE Mono> createAllEdgesForPageMono = directlyReferencedExecutablesToGraphMono // Add dependencies of all on page load executables set by the user in the graph .flatMap(updatedEdges -> addExplicitUserSetOnLoadExecutablesToGraph( - pageId, + creatorId, updatedEdges, explicitUserSetOnLoadExecutablesRef, executablesFoundDuringWalkRef, bindingsFromExecutablesRef, executableNameToExecutableMapMono, executableBindingsInDslRef, - evaluatedVersion)) + evaluatedVersion, + creatorType)) // For all the executables found so far, recursively walk the dynamic bindings of the executables to - // find more - // relationships with other executables (& widgets) + // find more relationships with other executables (& widgets) .flatMap(updatedEdges -> recursivelyAddExecutablesAndTheirDependentsToGraphFromBindings( updatedEdges, executablesFoundDuringWalkRef, @@ -165,7 +168,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE executableNameToExecutableMapMono, evaluatedVersion)) // At last, add all the widget relationships to the graph as well. - .zipWith(executablesInPageMono) + .zipWith(executablesInCreatorContextMono) .flatMap(tuple -> { Set updatedEdges = tuple.getT1(); return addWidgetRelationshipToGraph(updatedEdges, widgetDynamicBindingsMap, evaluatedVersion); @@ -173,7 +176,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // Create a graph given edges Mono> createGraphMono = Mono.zip( - executablesInPageMono, createAllEdgesForPageMono) + executablesInCreatorContextMono, createAllEdgesForPageMono) .map(tuple -> { Set allExecutables = tuple.getT1(); Set updatedEdges = tuple.getT2(); @@ -190,7 +193,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE return computeOnPageLoadExecutablesSchedulingOrder( graph, - onPageLoadExecutableSetRef, + onLoadExecutableSetRef, executableNameToExecutableMap, explicitUserSetOnLoadExecutablesRef); }) @@ -199,15 +202,15 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // This scenario would happen if an explicitly turned on for page load executable does not have any // relationships in the page with any widgets/executables. Set pageLoadExecutableNames = new HashSet<>(); - pageLoadExecutableNames.addAll(onPageLoadExecutableSetRef); + pageLoadExecutableNames.addAll(onLoadExecutableSetRef); pageLoadExecutableNames.addAll(explicitUserSetOnLoadExecutablesRef); - pageLoadExecutableNames.removeAll(onPageLoadExecutableSetRef); + pageLoadExecutableNames.removeAll(onLoadExecutableSetRef); // If any of the explicitly set on page load executables havent been added yet, add them to the 0th // set // of executables set since no relationships were found with any other appsmith entity if (!pageLoadExecutableNames.isEmpty()) { - onPageLoadExecutableSetRef.addAll(pageLoadExecutableNames); + onLoadExecutableSetRef.addAll(pageLoadExecutableNames); // In case there are no page load executables, initialize the 0th set of page load executables // list. @@ -224,7 +227,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // Transform the schedule order into client feasible DTO Mono>> computeCompletePageLoadExecutableScheduleMono = filterAndTransformSchedulingOrderToDTO( - onPageLoadExecutableSetRef, + onLoadExecutableSetRef, executableNameToExecutableMapMono, computeOnPageLoadScheduleNamesMono) .cache(); @@ -234,7 +237,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE Mono> flatPageLoadExecutablesMono = computeCompletePageLoadExecutableScheduleMono .then(executableNameToExecutableMapMono) .map(executableMap -> { - onPageLoadExecutableSetRef.stream() + onLoadExecutableSetRef.stream() .forEach(executableName -> flatPageLoadExecutablesRef.add(executableMap.get(executableName))); return flatPageLoadExecutablesRef; @@ -246,17 +249,18 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE @Override public Mono updateExecutablesExecuteOnLoad( List onLoadExecutables, - String pageId, + String creatorId, List executableUpdatesRef, - List messagesRef) { + List messagesRef, + CreatorContextType creatorType) { List toUpdateExecutables = new ArrayList<>(); // Fetch all the actions which exist in this page. - Flux pageExecutablesFlux = - this.getAllExecutablesByPageIdFlux(pageId).cache(); + Flux creatorContextExecutablesFlux = + this.getAllExecutablesByCreatorIdFlux(creatorId, creatorType).cache(); // Before we update the actions, fetch all the actions which are currently set to execute on load. - Mono> existingOnPageLoadExecutablesMono = pageExecutablesFlux + Mono> existingOnLoadExecutablesMono = creatorContextExecutablesFlux .flatMap(executable -> { if (TRUE.equals(executable.getExecuteOnLoad())) { return Mono.just(executable); @@ -265,26 +269,26 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE }) .collectList(); - return existingOnPageLoadExecutablesMono - .zipWith(pageExecutablesFlux.collectList()) + return existingOnLoadExecutablesMono + .zipWith(creatorContextExecutablesFlux.collectList()) .flatMap(tuple -> { - List existingOnPageLoadExecutables = tuple.getT1(); - List pageExecutables = tuple.getT2(); + List existingOnLoadExecutables = tuple.getT1(); + List creatorContextExecutables = tuple.getT2(); // There are no actions in this page. No need to proceed further since no actions would get updated - if (pageExecutables.isEmpty()) { + if (creatorContextExecutables.isEmpty()) { return Mono.just(FALSE); } // No actions require an update if no actions have been found as page load actions as well as // existing on load page actions are empty - if (existingOnPageLoadExecutables.isEmpty() + if (existingOnLoadExecutables.isEmpty() && (onLoadExecutables == null || onLoadExecutables.isEmpty())) { return Mono.just(FALSE); } // Extract names of existing page load actions and new page load actions for quick lookup. - Set existingOnPageLoadExecutableNames = existingOnPageLoadExecutables.stream() + Set existingOnLoadExecutableNames = existingOnLoadExecutables.stream() .map(Executable::getValidName) .collect(Collectors.toSet()); @@ -294,15 +298,15 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // Calculate the actions which would need to be updated from execute on load TRUE to FALSE. Set turnedOffExecutableNames = new HashSet<>(); - turnedOffExecutableNames.addAll(existingOnPageLoadExecutableNames); + turnedOffExecutableNames.addAll(existingOnLoadExecutableNames); turnedOffExecutableNames.removeAll(newOnLoadExecutableNames); // Calculate the actions which would need to be updated from execute on load FALSE to TRUE Set turnedOnExecutableNames = new HashSet<>(); turnedOnExecutableNames.addAll(newOnLoadExecutableNames); - turnedOnExecutableNames.removeAll(existingOnPageLoadExecutableNames); + turnedOnExecutableNames.removeAll(existingOnLoadExecutableNames); - for (Executable executable : pageExecutables) { + for (Executable executable : creatorContextExecutables) { String executableName = executable.getValidName(); // If a user has ever set execute on load, this field can not be changed automatically. It has @@ -331,11 +335,11 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE // Add newly turned on page actions to report back to the caller executableUpdatesRef.addAll( - addExecutableUpdatesForExecutableNames(pageExecutables, turnedOnExecutableNames)); + addExecutableUpdatesForExecutableNames(creatorContextExecutables, turnedOnExecutableNames)); // Add newly turned off page actions to report back to the caller - executableUpdatesRef.addAll( - addExecutableUpdatesForExecutableNames(pageExecutables, turnedOffExecutableNames)); + executableUpdatesRef.addAll(addExecutableUpdatesForExecutableNames( + creatorContextExecutables, turnedOffExecutableNames)); // Now add messagesRef that would eventually be displayed to the developer user informing them // about the action setting change. @@ -356,23 +360,29 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE }); } + @Override + public Mono findAndUpdateLayout( + String creatorId, CreatorContextType creatorType, String layoutId, Layout layout) { + return pageExecutableOnLoadService.findAndUpdateLayout(creatorId, layoutId, layout); + } + private Mono updateUnpublishedExecutable(String id, Executable executable) { if (executable instanceof ActionDTO actionDTO) { - return actionExecutableOnPageLoadService.updateUnpublishedExecutable(id, actionDTO); + return pageExecutableOnLoadService.updateUnpublishedExecutable(id, actionDTO); } else return Mono.just(executable); } private List addExecutableUpdatesForExecutableNames( - List pageExecutables, Set updatedExecutableNames) { + List executables, Set updatedExecutableNames) { - return pageExecutables.stream() - .filter(pageExecutable -> updatedExecutableNames.contains(pageExecutable.getValidName())) + return executables.stream() + .filter(executable -> updatedExecutableNames.contains(executable.getValidName())) .map(Executable::createLayoutExecutableUpdateDTO) .collect(Collectors.toList()); } - protected Flux getAllExecutablesByPageIdFlux(String pageId) { - return actionExecutableOnPageLoadService.getAllExecutablesByPageIdFlux(pageId); + protected Flux getAllExecutablesByCreatorIdFlux(String creatorId, CreatorContextType creatorType) { + return pageExecutableOnLoadService.getAllExecutablesByCreatorIdFlux(creatorId); } /** @@ -613,7 +623,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE protected Mono fillSelfReferencingPaths(T executable) { if (executable instanceof ActionDTO actionDTO) { - return actionExecutableOnPageLoadService.fillSelfReferencingPaths(actionDTO); + return pageExecutableOnLoadService.fillSelfReferencingPaths(actionDTO); } else return Mono.just(executable); } @@ -902,7 +912,7 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE * !!! WARNING !!! : This function updates the set `explicitUserSetOnLoadExecutables` and adds the names of all such * executables found in this function. * - * @param pageId + * @param creatorId * @param edges * @param explicitUserSetOnLoadExecutables * @param executablesFoundDuringWalkRef @@ -910,17 +920,18 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE * @return */ private Mono> addExplicitUserSetOnLoadExecutablesToGraph( - String pageId, + String creatorId, Set edges, Set explicitUserSetOnLoadExecutables, Map executablesFoundDuringWalkRef, Set bindingsFromExecutablesRef, Mono> executableNameToExecutableMapMono, Set executableBindingsInDsl, - int evalVersion) { + int evalVersion, + CreatorContextType creatorType) { // First fetch all the executables which have been tagged as on load by the user explicitly. - return getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(pageId) + return getUnpublishedOnLoadExecutablesExplicitSetByUserInCreatorContextFlux(creatorId, creatorType) .flatMap(this::fillSelfReferencingPaths) // Add the vertices and edges to the graph for these executables .flatMap(executable -> { @@ -941,8 +952,9 @@ public class PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtilCE .thenReturn(edges); } - protected Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(String pageId) { - return actionExecutableOnPageLoadService.getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(pageId); + protected Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInCreatorContextFlux( + String creatorId, CreatorContextType creatorType) { + return pageExecutableOnLoadService.getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(creatorId); } /** diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilImpl.java new file mode 100644 index 0000000000..fd04d51519 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/onload/internal/OnLoadExecutablesUtilImpl.java @@ -0,0 +1,20 @@ +package com.appsmith.server.onload.internal; + +import com.appsmith.server.domains.NewPage; +import com.appsmith.server.onload.executables.ExecutableOnLoadService; +import com.appsmith.server.services.AstService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class OnLoadExecutablesUtilImpl extends OnLoadExecutablesUtilCEImpl implements OnLoadExecutablesUtil { + + public OnLoadExecutablesUtilImpl( + AstService astService, + ObjectMapper objectMapper, + ExecutableOnLoadService pageExecutableOnLoadService) { + super(astService, objectMapper, pageExecutableOnLoadService); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadService.java deleted file mode 100644 index 1208180f9a..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.appsmith.server.onpageload.executables; - -import com.appsmith.external.models.Executable; - -public interface ExecutableOnPageLoadService extends ExecutableOnPageLoadServiceCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadServiceCE.java deleted file mode 100644 index fbbb07005a..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/executables/ExecutableOnPageLoadServiceCE.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.appsmith.server.onpageload.executables; - -import com.appsmith.external.models.Executable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -public interface ExecutableOnPageLoadServiceCE { - - Flux getAllExecutablesByPageIdFlux(String pageId); - - Mono fillSelfReferencingPaths(T executable); - - Flux getUnpublishedOnLoadExecutablesExplicitSetByUserInPageFlux(String pageId); - - Mono updateUnpublishedExecutable(String id, Executable executable); -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtil.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtil.java deleted file mode 100644 index d6b824c4c7..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtil.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.appsmith.server.onpageload.internal; - -public interface PageLoadExecutablesUtil extends PageLoadExecutablesUtilCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilImpl.java deleted file mode 100644 index 5b1471e43f..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/onpageload/internal/PageLoadExecutablesUtilImpl.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.appsmith.server.onpageload.internal; - -import com.appsmith.external.models.ActionDTO; -import com.appsmith.server.onpageload.executables.ExecutableOnPageLoadService; -import com.appsmith.server.services.AstService; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class PageLoadExecutablesUtilImpl extends PageLoadExecutablesUtilCEImpl implements PageLoadExecutablesUtil { - - public PageLoadExecutablesUtilImpl( - AstService astService, - ObjectMapper objectMapper, - ExecutableOnPageLoadService actionExecutableOnPageLoadService) { - super(astService, objectMapper, actionExecutableOnPageLoadService); - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/refactors/applications/RefactoringSolutionCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/refactors/applications/RefactoringSolutionCEImpl.java index 3fdc4861ec..d693d49216 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/refactors/applications/RefactoringSolutionCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/refactors/applications/RefactoringSolutionCEImpl.java @@ -201,6 +201,7 @@ public class RefactoringSolutionCEImpl implements RefactoringSolutionCE { case JS_ACTION -> jsActionEntityRefactoringService; case ACTION -> newActionEntityRefactoringService; case JS_OBJECT -> actionCollectionEntityRefactoringService; + default -> null; }; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCE.java index 0db6f97ecf..6e31c8b72a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCE.java @@ -1,5 +1,6 @@ package com.appsmith.server.repositories.ce; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.ActionCollection; import com.appsmith.server.repositories.AppsmithRepository; @@ -54,4 +55,10 @@ public interface CustomActionCollectionRepositoryCE extends AppsmithRepository> bulkUpdate(List actionCollections); Flux findAllByApplicationIds(List applicationIds, List includeFields); + + Flux findAllUnpublishedActionCollectionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission); + + Flux findAllPublishedActionCollectionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCEImpl.java index 4934ae66fa..2af549e481 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomActionCollectionRepositoryCEImpl.java @@ -1,5 +1,6 @@ package com.appsmith.server.repositories.ce; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.QBranchAwareDomain; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.constants.FieldName; @@ -269,4 +270,28 @@ public class CustomActionCollectionRepositoryCEImpl extends BaseAppsmithReposito Criteria applicationCriteria = Criteria.where(FieldName.APPLICATION_ID).in(applicationIds); return queryAll(List.of(applicationCriteria), includeFields, null, null, NO_RECORD_LIMIT); } + + @Override + public Flux findAllUnpublishedActionCollectionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission) { + String contextIdPath = fieldName(QActionCollection.actionCollection.unpublishedCollection) + "." + + fieldName(QActionCollection.actionCollection.unpublishedCollection.pageId); + String contextTypePath = fieldName(QActionCollection.actionCollection.unpublishedCollection) + "." + + fieldName(QActionCollection.actionCollection.unpublishedCollection.contextType); + Criteria contextIdAndContextTypeCriteria = + where(contextIdPath).is(contextId).and(contextTypePath).is(contextType); + return queryAll(List.of(contextIdAndContextTypeCriteria), Optional.of(permission)); + } + + @Override + public Flux findAllPublishedActionCollectionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission) { + String contextIdPath = fieldName(QActionCollection.actionCollection.publishedCollection) + "." + + fieldName(QActionCollection.actionCollection.publishedCollection.pageId); + String contextTypePath = fieldName(QActionCollection.actionCollection.publishedCollection) + "." + + fieldName(QActionCollection.actionCollection.publishedCollection.contextType); + Criteria contextIdAndContextTypeCriteria = + where(contextIdPath).is(contextId).and(contextTypePath).is(contextType); + return queryAll(List.of(contextIdAndContextTypeCriteria), Optional.of(permission)); + } } 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 c3bda9cc5a..1ad0f9d01b 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 @@ -1,5 +1,6 @@ package com.appsmith.server.repositories.ce; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.NewAction; import com.appsmith.server.dtos.PluginTypeAndCountDTO; @@ -84,4 +85,10 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository countActionsByPluginType(String applicationId); Flux findAllByApplicationIdsWithoutPermission(List applicationIds, List includeFields); + + Flux findAllUnpublishedActionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission, boolean includeJs); + + Flux findAllPublishedActionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission, boolean includeJs); } 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 fa09372d18..e263413b50 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 @@ -1,5 +1,6 @@ package com.appsmith.server.repositories.ce; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.PluginType; import com.appsmith.external.models.QActionConfiguration; import com.appsmith.external.models.QBranchAwareDomain; @@ -642,4 +643,59 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl< Criteria applicationCriteria = Criteria.where(FieldName.APPLICATION_ID).in(applicationIds); return queryAll(List.of(applicationCriteria), includeFields, null, null, NO_RECORD_LIMIT); } + + @Override + public Flux findAllUnpublishedActionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission, boolean includeJs) { + List criteriaList = new ArrayList<>(); + + String contextIdPath = fieldName(QNewAction.newAction.unpublishedAction) + "." + + fieldName(QNewAction.newAction.unpublishedAction.pageId); + String contextTypePath = fieldName(QNewAction.newAction.unpublishedAction) + "." + + fieldName(QNewAction.newAction.unpublishedAction.contextType); + Criteria contextIdAndContextTypeCriteria = + where(contextIdPath).is(contextId).and(contextTypePath).is(contextType); + + criteriaList.add(contextIdAndContextTypeCriteria); + + Criteria jsInclusionOrExclusionCriteria; + if (includeJs) { + jsInclusionOrExclusionCriteria = + where(fieldName(QNewAction.newAction.pluginType)).is(PluginType.JS); + } else { + jsInclusionOrExclusionCriteria = + where(fieldName(QNewAction.newAction.pluginType)).ne(PluginType.JS); + } + + criteriaList.add(jsInclusionOrExclusionCriteria); + + return queryAll(List.of(contextIdAndContextTypeCriteria), Optional.of(permission)); + } + + @Override + public Flux findAllPublishedActionsByContextIdAndContextType( + String contextId, CreatorContextType contextType, AclPermission permission, boolean includeJs) { + List criteriaList = new ArrayList<>(); + String contextIdPath = fieldName(QNewAction.newAction.publishedAction) + "." + + fieldName(QNewAction.newAction.publishedAction.pageId); + String contextTypePath = fieldName(QNewAction.newAction.publishedAction) + "." + + fieldName(QNewAction.newAction.publishedAction.contextType); + Criteria contextIdAndContextTypeCriteria = + where(contextIdPath).is(contextId).and(contextTypePath).is(contextType); + + criteriaList.add(contextIdAndContextTypeCriteria); + + Criteria jsInclusionOrExclusionCriteria; + if (includeJs) { + jsInclusionOrExclusionCriteria = + where(fieldName(QNewAction.newAction.pluginType)).is(PluginType.JS); + } else { + jsInclusionOrExclusionCriteria = + where(fieldName(QNewAction.newAction.pluginType)).ne(PluginType.JS); + } + + criteriaList.add(jsInclusionOrExclusionCriteria); + + return queryAll(List.of(contextIdAndContextTypeCriteria), Optional.of(permission)); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/LayoutActionServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/LayoutActionServiceImpl.java index 2ecbd42872..58b448d9c5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/LayoutActionServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/LayoutActionServiceImpl.java @@ -5,7 +5,7 @@ import com.appsmith.server.datasources.base.DatasourceService; import com.appsmith.server.helpers.ResponseUtils; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; -import com.appsmith.server.onpageload.internal.PageLoadExecutablesUtil; +import com.appsmith.server.onload.internal.OnLoadExecutablesUtil; import com.appsmith.server.services.ce.LayoutActionServiceCEImpl; import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.PagePermission; @@ -22,7 +22,7 @@ public class LayoutActionServiceImpl extends LayoutActionServiceCEImpl implement AnalyticsService analyticsService, NewPageService newPageService, NewActionService newActionService, - PageLoadExecutablesUtil pageLoadActionsUtil, + OnLoadExecutablesUtil pageLoadActionsUtil, SessionUserService sessionUserService, ActionCollectionService actionCollectionService, CollectionService collectionService, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCE.java index 3b40b2d738..44e359c217 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationTemplateServiceCE.java @@ -16,8 +16,6 @@ public interface ApplicationTemplateServiceCE { Flux getSimilarTemplates(String templateId, MultiValueMap params); - Mono> getRecentlyUsedTemplates(); - Mono getTemplateDetails(String templateId); Mono importApplicationFromTemplate(String templateId, String workspaceId); 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 107a4c79ae..176dcb6f90 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 @@ -6,7 +6,6 @@ import com.appsmith.server.configurations.CloudServicesConfig; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationMode; -import com.appsmith.server.domains.UserData; import com.appsmith.server.dtos.*; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; @@ -41,7 +40,6 @@ import reactor.core.publisher.Mono; import java.lang.reflect.Type; import java.time.Instant; -import java.util.Comparator; import java.util.List; import java.util.Map; @@ -130,24 +128,7 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ return clientResponse.createException().flatMapMany(Flux::error); } }) - .collectList() - .zipWith(userDataService.getForCurrentUser()) - .map(objects -> { - List applicationTemplateList = objects.getT1(); - UserData userData = objects.getT2(); - List recentlyUsedTemplateIds = userData.getRecentlyUsedTemplateIds(); - if (!CollectionUtils.isEmpty(recentlyUsedTemplateIds)) { - applicationTemplateList.sort(Comparator.comparingInt(o -> { - int index = recentlyUsedTemplateIds.indexOf(o.getId()); - if (index < 0) { - // template not in recent list, return a large value so that it's sorted out to the end - index = Integer.MAX_VALUE; - } - return index; - })); - } - return applicationTemplateList; - }); + .collectList(); } @Override @@ -171,9 +152,13 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ private Mono getApplicationJsonFromTemplate(String templateId) { final String baseUrl = cloudServicesConfig.getBaseUrl(); final String templateUrl = baseUrl + "/api/v1/app-templates/" + templateId + "/application"; - /* using a custom url builder factory because default builder always encodes URL. - It's expected that the appDataUrl is already encoded, so we don't need to encode that again. - Encoding an encoded URL will not work and end up resulting a 404 error */ + /* + * using a custom url builder factory because default builder always encodes + * URL. + * It's expected that the appDataUrl is already encoded, so we don't need to + * encode that again. + * Encoding an encoded URL will not work and end up resulting a 404 error + */ final int size = 4 * 1024 * 1024; // 4 MB final ExchangeStrategies strategies = ExchangeStrategies.builder() .codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size)) @@ -204,8 +189,15 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ @Override public Mono importApplicationFromTemplate(String templateId, String workspaceId) { return getApplicationJsonFromTemplate(templateId) - .flatMap(applicationJson -> - importApplicationService.importNewApplicationInWorkspaceFromJson(workspaceId, applicationJson)) + .flatMap(applicationJson -> Mono.zip( + importApplicationService.importNewApplicationInWorkspaceFromJson(workspaceId, applicationJson), + Mono.just(applicationJson.getExportedApplication().getName()))) + .flatMap(tuple -> { + Application application = tuple.getT1(); + String templateTitle = tuple.getT2(); + application.setForkedFromTemplateTitle(templateTitle); + return applicationService.save(application).thenReturn(application); + }) .flatMap(application -> importApplicationService.getApplicationImportDTO( application.getId(), application.getWorkspaceId(), application)) .flatMap(applicationImportDTO -> { @@ -228,17 +220,6 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ }); } - @Override - public Mono> getRecentlyUsedTemplates() { - return userDataService.getForCurrentUser().flatMap(userData -> { - List templateIds = userData.getRecentlyUsedTemplateIds(); - if (!CollectionUtils.isEmpty(templateIds)) { - return getActiveTemplates(templateIds); - } - return Mono.empty(); - }); - } - @Override public Mono getFilters() { final String baseUrl = cloudServicesConfig.getBaseUrl(); @@ -265,12 +246,17 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ } /** - * Merge Template API is slow today because it needs to communicate with ImportExport Service, CloudService and/or serialise and de-serialise the - * application. This process takes time and the client may cancel the request. This leads to the flow getting stopped + * Merge Template API is slow today because it needs to communicate with + * ImportExport Service, CloudService and/or serialise and de-serialise the + * application. This process takes time and the client may cancel the request. + * This leads to the flow getting stopped * midway producing corrupted states. - * We use the synchronous sink to ensure that even though the client may have cancelled the flow, git operations should - * proceed uninterrupted and whenever the user refreshes the page, we will have the sane state. Synchronous sink does - * not take subscription cancellations into account. This means that even if the subscriber has cancelled its + * We use the synchronous sink to ensure that even though the client may have + * cancelled the flow, git operations should + * proceed uninterrupted and whenever the user refreshes the page, we will have + * the sane state. Synchronous sink does + * not take subscription cancellations into account. This means that even if the + * subscriber has cancelled its * subscription, the create method still generates its event. */ @Override diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCE.java index 50e1333f49..ab1f5c756a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCE.java @@ -1,8 +1,8 @@ package com.appsmith.server.services.ce; import com.appsmith.server.domains.User; -import com.appsmith.server.dtos.ce.FeaturesRequestDTO; -import com.appsmith.server.dtos.ce.FeaturesResponseDTO; +import com.appsmith.server.dtos.FeaturesRequestDTO; +import com.appsmith.server.dtos.FeaturesResponseDTO; import com.appsmith.server.featureflags.CachedFeatures; import com.appsmith.server.featureflags.CachedFlags; import reactor.core.publisher.Mono; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCEImpl.java index 45aab7808c..369aa3f1f1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CacheableFeatureFlagHelperCEImpl.java @@ -6,9 +6,9 @@ import com.appsmith.server.configurations.CloudServicesConfig; import com.appsmith.server.configurations.CommonConfig; import com.appsmith.server.domains.Tenant; import com.appsmith.server.domains.User; +import com.appsmith.server.dtos.FeaturesRequestDTO; +import com.appsmith.server.dtos.FeaturesResponseDTO; import com.appsmith.server.dtos.ResponseDTO; -import com.appsmith.server.dtos.ce.FeaturesRequestDTO; -import com.appsmith.server.dtos.ce.FeaturesResponseDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.featureflags.CachedFeatures; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCE.java index 72c9380c0f..b68a98fb9e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCE.java @@ -5,7 +5,7 @@ import com.appsmith.external.models.ActionDTO; import com.appsmith.server.domains.Layout; import com.appsmith.server.dtos.ActionMoveDTO; import com.appsmith.server.dtos.LayoutDTO; -import com.appsmith.server.dtos.ce.UpdateMultiplePageLayoutDTO; +import com.appsmith.server.dtos.UpdateMultiplePageLayoutDTO; import net.minidev.json.JSONObject; import reactor.core.publisher.Mono; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java index e71ce5e6fb..fe3e234fbf 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java @@ -9,6 +9,7 @@ import com.appsmith.external.helpers.AppsmithEventContext; import com.appsmith.external.helpers.AppsmithEventContextType; import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.Executable; @@ -25,8 +26,7 @@ import com.appsmith.server.domains.User; import com.appsmith.server.dtos.ActionCollectionDTO; import com.appsmith.server.dtos.ActionMoveDTO; import com.appsmith.server.dtos.LayoutDTO; -import com.appsmith.server.dtos.PageDTO; -import com.appsmith.server.dtos.ce.UpdateMultiplePageLayoutDTO; +import com.appsmith.server.dtos.UpdateMultiplePageLayoutDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.DefaultResourcesUtils; @@ -34,7 +34,7 @@ import com.appsmith.server.helpers.ResponseUtils; import com.appsmith.server.helpers.WidgetSpecificUtils; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; -import com.appsmith.server.onpageload.internal.PageLoadExecutablesUtil; +import com.appsmith.server.onload.internal.OnLoadExecutablesUtil; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; import com.appsmith.server.services.CollectionService; @@ -46,7 +46,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONObject; -import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -81,7 +80,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { private final AnalyticsService analyticsService; private final NewPageService newPageService; private final NewActionService newActionService; - private final PageLoadExecutablesUtil pageLoadActionsUtil; + private final OnLoadExecutablesUtil onLoadExecutablesUtil; private final SessionUserService sessionUserService; private final ActionCollectionService actionCollectionService; private final CollectionService collectionService; @@ -280,7 +279,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { * @param dsl * @param widgetNames * @param widgetDynamicBindingsMap - * @param pageId + * @param creatorId * @param layoutId * @param escapedWidgetNames * @return @@ -289,9 +288,10 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { JSONObject dsl, Set widgetNames, Map> widgetDynamicBindingsMap, - String pageId, + String creatorId, String layoutId, - Set escapedWidgetNames) + Set escapedWidgetNames, + CreatorContextType creatorType) throws AppsmithException { if (dsl.get(FieldName.WIDGET_NAME) == null) { // This isn't a valid widget configuration. No need to traverse this. @@ -345,11 +345,12 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { widgetName, widgetId, fieldPath, - pageId, + creatorId, layoutId, oldParent, nextKey, - "Index out of bounds for list"); + "Index out of bounds for list", + creatorType); } } else { throw new AppsmithException( @@ -358,11 +359,12 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { widgetName, widgetId, fieldPath, - pageId, + creatorId, layoutId, oldParent, nextKey, - "Child of list is not in an indexed path"); + "Child of list is not in an indexed path", + creatorType); } } // After updating the parent, check for the types @@ -373,11 +375,12 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { widgetName, widgetId, fieldPath, - pageId, + creatorId, layoutId, oldParent, nextKey, - "New element is null"); + "New element is null", + creatorType); } else if (parent instanceof String) { // If we get String value, then this is a leaf node isLeafNode = true; @@ -396,11 +399,12 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { widgetName, widgetId, fieldPath, - pageId, + creatorId, layoutId, bindingAsString, nextKey, - "Binding path has no mustache bindings"); + "Binding path has no mustache bindings", + creatorType); } catch (JsonProcessingException e) { throw new AppsmithException(AppsmithError.JSON_PROCESSING_ERROR, parent); } @@ -437,7 +441,13 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { if (!CollectionUtils.isEmpty(data)) { object.putAll(data); JSONObject child = extractAllWidgetNamesAndDynamicBindingsFromDSL( - object, widgetNames, widgetDynamicBindingsMap, pageId, layoutId, escapedWidgetNames); + object, + widgetNames, + widgetDynamicBindingsMap, + creatorId, + layoutId, + escapedWidgetNames, + creatorType); newChildren.add(child); } } @@ -656,8 +666,13 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { } private Mono sendUpdateLayoutAnalyticsEvent( - String pageId, String layoutId, JSONObject dsl, boolean isSuccess, Throwable error) { - return Mono.zip(sessionUserService.getCurrentUser(), newPageService.getById(pageId)) + String creatorId, + String layoutId, + JSONObject dsl, + boolean isSuccess, + Throwable error, + CreatorContextType creatorType) { + return Mono.zip(sessionUserService.getCurrentUser(), newPageService.getById(creatorId)) .flatMap(tuple -> { User t1 = tuple.getT1(); NewPage t2 = tuple.getT2(); @@ -667,8 +682,10 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { t1.getUsername(), "appId", t2.getApplicationId(), - "pageId", - pageId, + "creatorId", + creatorId, + "creatorType", + creatorType, "layoutId", layoutId, "isSuccessfulExecution", @@ -686,7 +703,12 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { }); } - private Mono updateLayoutDsl(String pageId, String layoutId, Layout layout, Integer evaluatedVersion) { + private Mono updateLayoutDsl( + String creatorId, + String layoutId, + Layout layout, + Integer evaluatedVersion, + CreatorContextType creatorType) { JSONObject dsl = layout.getDsl(); if (dsl == null) { // There is no DSL here. No need to process anything. Return as is. @@ -698,9 +720,9 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { Set escapedWidgetNames = new HashSet<>(); try { dsl = extractAllWidgetNamesAndDynamicBindingsFromDSL( - dsl, widgetNames, widgetDynamicBindingsMap, pageId, layoutId, escapedWidgetNames); + dsl, widgetNames, widgetDynamicBindingsMap, creatorId, layoutId, escapedWidgetNames, creatorType); } catch (Throwable t) { - return sendUpdateLayoutAnalyticsEvent(pageId, layoutId, dsl, false, t) + return sendUpdateLayoutAnalyticsEvent(creatorId, layoutId, dsl, false, t, creatorType) .then(Mono.error(t)); } @@ -710,30 +732,31 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { layout.setMongoEscapedWidgetNames(escapedWidgetNames); } - Set actionNames = new HashSet<>(); + Set executableNames = new HashSet<>(); Set edges = new HashSet<>(); Set executablesUsedInDSL = new HashSet<>(); - List flatmapPageLoadExecutables = new ArrayList<>(); + List flatmapOnLoadExecutables = new ArrayList<>(); List executableUpdatesRef = new ArrayList<>(); List messagesRef = new ArrayList<>(); - AtomicReference validOnPageLoadExecutables = new AtomicReference<>(Boolean.TRUE); + AtomicReference validOnLoadExecutables = new AtomicReference<>(Boolean.TRUE); // setting the layoutOnLoadActionActionErrors to empty to remove the existing errors before new DAG calculation. layout.setLayoutOnLoadActionErrors(new ArrayList<>()); - Mono>> allOnLoadExecutablesMono = pageLoadActionsUtil + Mono>> allOnLoadExecutablesMono = onLoadExecutablesUtil .findAllOnLoadExecutables( - pageId, + creatorId, evaluatedVersion, widgetNames, edges, widgetDynamicBindingsMap, - flatmapPageLoadExecutables, - executablesUsedInDSL) + flatmapOnLoadExecutables, + executablesUsedInDSL, + creatorType) .onErrorResume(AppsmithException.class, error -> { log.info(error.getMessage()); - validOnPageLoadExecutables.set(FALSE); + validOnLoadExecutables.set(FALSE); layout.setLayoutOnLoadActionErrors(List.of(new ErrorDTO( error.getAppErrorCode(), error.getErrorType(), @@ -749,62 +772,26 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { .flatMap(allOnLoadExecutables -> { // If there has been an error (e.g. cyclical dependency), then don't update any actions. // This is so that unnecessary updates don't happen to actions while the page is in invalid state. - if (!validOnPageLoadExecutables.get()) { + if (!validOnLoadExecutables.get()) { return Mono.just(allOnLoadExecutables); } - // Update these actions to be executed on load, unless the user has touched the executeOnLoad + // Update these executables to be executed on load, unless the user has touched the executeOnLoad // setting for this - return pageLoadActionsUtil + return onLoadExecutablesUtil .updateExecutablesExecuteOnLoad( - flatmapPageLoadExecutables, pageId, executableUpdatesRef, messagesRef) + flatmapOnLoadExecutables, creatorId, executableUpdatesRef, messagesRef, creatorType) .thenReturn(allOnLoadExecutables); }) - .zipWith(newPageService - .findByIdAndLayoutsId(pageId, layoutId, pagePermission.getEditPermission(), false) - .switchIfEmpty(Mono.error(new AppsmithException( - AppsmithError.ACL_NO_RESOURCE_FOUND, - FieldName.PAGE_ID + " or " + FieldName.LAYOUT_ID, - pageId + ", " + layoutId)))) - // Now update the page layout with the page load actions and the graph. - .flatMap(tuple -> { - List> onLoadActions = tuple.getT1(); - PageDTO page = tuple.getT2(); + // Now update the page layout with the page load executables and the graph. + .flatMap(onLoadExecutables -> { + layout.setLayoutOnLoadActions(onLoadExecutables); + layout.setAllOnPageLoadActionNames(executableNames); + layout.setActionsUsedInDynamicBindings(executablesUsedInDSL); + // The below field is to ensure that we record if the page load actions computation was + // valid when last stored in the database. + layout.setValidOnPageLoadActions(validOnLoadExecutables.get()); - List layoutList = page.getLayouts(); - // Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the - // layoutId here. - for (Layout storedLayout : layoutList) { - if (storedLayout.getId().equals(layoutId)) { - // Now that all the on load actions have been computed, set the vertices, edges, actions in - // DSL - // in the layout for re-use to avoid computing DAG unnecessarily. - layout.setLayoutOnLoadActions(onLoadActions); - layout.setAllOnPageLoadActionNames(actionNames); - layout.setActionsUsedInDynamicBindings(executablesUsedInDSL); - // The below field is to ensure that we record if the page load actions computation was - // valid - // when last stored in the database. - layout.setValidOnPageLoadActions(validOnPageLoadExecutables.get()); - - BeanUtils.copyProperties(layout, storedLayout); - storedLayout.setId(layoutId); - - break; - } - } - page.setLayouts(layoutList); - return applicationService - .saveLastEditInformation(page.getApplicationId()) - .then(newPageService.saveUnpublishedPage(page)); - }) - .flatMap(page -> { - List layoutList = page.getLayouts(); - for (Layout storedLayout : layoutList) { - if (storedLayout.getId().equals(layoutId)) { - return Mono.just(storedLayout); - } - } - return Mono.empty(); + return onLoadExecutablesUtil.findAndUpdateLayout(creatorId, creatorType, layoutId, layout); }) .map(savedLayout -> { savedLayout.setDsl(this.unescapeMongoSpecialCharacters(savedLayout)); @@ -815,7 +802,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { layoutDTO.setActionUpdates(executableUpdatesRef); layoutDTO.setMessages(messagesRef); - return sendUpdateLayoutAnalyticsEvent(pageId, layoutId, finalDsl, true, null) + return sendUpdateLayoutAnalyticsEvent(creatorId, layoutId, finalDsl, true, null, creatorType) .thenReturn(layoutDTO); }); } @@ -831,7 +818,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE { if (evaluationVersion == null) { evaluationVersion = EVALUATION_VERSION; } - return updateLayoutDsl(pageId, layoutId, layout, evaluationVersion); + return updateLayoutDsl(pageId, layoutId, layout, evaluationVersion, CreatorContextType.PAGE); }); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCE.java index bac91542a2..783ce21706 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCE.java @@ -1,6 +1,6 @@ package com.appsmith.server.services.ce; -import com.appsmith.server.dtos.ce.ProductAlertResponseDTO; +import com.appsmith.server.dtos.ProductAlertResponseDTO; import reactor.core.publisher.Mono; import java.util.List; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCEImpl.java index 634987bdeb..f034595494 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ProductAlertServiceCEImpl.java @@ -1,7 +1,7 @@ package com.appsmith.server.services.ce; import com.appsmith.server.configurations.CommonConfig; -import com.appsmith.server.dtos.ce.ProductAlertResponseDTO; +import com.appsmith.server.dtos.ProductAlertResponseDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCE.java index d2578b2b49..f3e347a599 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCE.java @@ -42,8 +42,6 @@ public interface UserDataServiceCE { Mono updateLastUsedAppAndWorkspaceList(Application application); - Mono addTemplateIdToLastUsedList(String templateId); - Mono> getFeatureFlagsForCurrentUser(); Mono removeRecentWorkspaceAndApps(String userId, String workspaceId); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCEImpl.java index fcc8a81501..411e4a32e4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserDataServiceCEImpl.java @@ -274,16 +274,6 @@ public class UserDataServiceCEImpl extends BaseService addTemplateIdToLastUsedList(String templateId) { - return this.getForCurrentUser().flatMap(userData -> { - // set recently used template ids - userData.setRecentlyUsedTemplateIds( - addIdToRecentList(userData.getRecentlyUsedTemplateIds(), templateId, 5)); - return repository.save(userData); - }); - } - private List addIdToRecentList(List srcIdList, String newId, int maxSize) { if (srcIdList == null) { srcIdList = new ArrayList<>(); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCE.java index c8f7aedec6..915af5877c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCE.java @@ -19,7 +19,7 @@ public interface AuthenticationServiceCE { * @return a url String to continue the authorization flow */ Mono getAuthorizationCodeURLForGenericOAuth2( - String datasourceId, String environmentId, String pageId, ServerHttpRequest httpRequest); + String datasourceId, String environmentId, String pageId, String branchName, ServerHttpRequest httpRequest); /** * This is the method that handles callback for generic OAuth2. We will be retrieving and storing token information here diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCEImpl.java index 48d87dd24f..573589ab9a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/AuthenticationServiceCEImpl.java @@ -101,7 +101,11 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { * @return a url String to continue the authorization flow */ public Mono getAuthorizationCodeURLForGenericOAuth2( - String datasourceId, String environmentId, String pageId, ServerHttpRequest httpRequest) { + String datasourceId, + String environmentId, + String pageId, + String branchName, + ServerHttpRequest httpRequest) { // This is the only database access that is controlled by ACL // The rest of the queries in this flow will not have context information @@ -130,6 +134,9 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { OAuth2 oAuth2 = (OAuth2) datasourceStorage.getDatasourceConfiguration().getAuthentication(); final String redirectUri = redirectHelper.getRedirectDomain(httpRequest.getHeaders()); + final String state = StringUtils.hasText(branchName) + ? String.join(",", pageId, datasourceId, trueEnvironmentId, redirectUri, branchName) + : String.join(",", pageId, datasourceId, trueEnvironmentId, redirectUri); // Adding basic uri components UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString( oAuth2.getAuthorizationUrl()) @@ -138,7 +145,7 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { .queryParam(REDIRECT_URI, redirectUri + Url.DATASOURCE_URL + "/authorize") // The state is used internally to calculate the redirect url when returning control to the // client - .queryParam(STATE, String.join(",", pageId, datasourceId, trueEnvironmentId, redirectUri)); + .queryParam(STATE, state); // Adding optional scope parameter if (oAuth2.getScope() != null && !oAuth2.getScope().isEmpty()) { uriComponentsBuilder.queryParam( @@ -196,7 +203,7 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS))) .flatMap(localState -> { String[] splitStates = localState.split(","); - if (splitStates.length != 4) { + if (splitStates.length < 4) { return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS)); } else return datasourceService @@ -324,13 +331,14 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { final String datasourceId = splitState[1]; final String environmentId = splitState[2]; final String redirectOrigin = splitState[3]; + final String branchName = splitState.length == 5 ? splitState[4] : null; String response = SUCCESS; if (error != null) { response = error; } final String responseStatus = response; return newPageService - .getById(pageId) + .findByIdAndBranchName(pageId, branchName) .map(newPage -> redirectOrigin + Entity.SLASH + Entity.APPLICATIONS + Entity.SLASH + newPage.getApplicationId() + Entity.SLASH + Entity.PAGES @@ -339,7 +347,9 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE { + Entity.SLASH + Entity.DATASOURCE + Entity.SLASH + datasourceId + "?response_status=" - + responseStatus + "&view_mode=true") + + responseStatus + + "&view_mode=true" + + (StringUtils.hasText(branchName) ? "&branch=" + branchName : "")) .onErrorResume(e -> Mono.just(redirectOrigin + Entity.SLASH + Entity.APPLICATIONS + "?response_status=" + responseStatus + "&view_mode=true")); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/MockCacheableFeatureFlagHelper.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/MockCacheableFeatureFlagHelper.java index 54915fdb26..ef95c5800b 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/MockCacheableFeatureFlagHelper.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/MockCacheableFeatureFlagHelper.java @@ -3,8 +3,8 @@ package com.appsmith.server.helpers; import com.appsmith.caching.annotations.Cache; import com.appsmith.caching.annotations.CacheEvict; import com.appsmith.server.domains.User; -import com.appsmith.server.dtos.ce.FeaturesRequestDTO; -import com.appsmith.server.dtos.ce.FeaturesResponseDTO; +import com.appsmith.server.dtos.FeaturesRequestDTO; +import com.appsmith.server.dtos.FeaturesResponseDTO; import com.appsmith.server.featureflags.CachedFeatures; import com.appsmith.server.featureflags.CachedFlags; import com.appsmith.server.services.CacheableFeatureFlagHelper; 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 6b9f492f79..8835628d6d 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 @@ -1,7 +1,6 @@ package com.appsmith.server.services; import com.appsmith.server.configurations.CloudServicesConfig; -import com.appsmith.server.domains.UserData; import com.appsmith.server.dtos.ApplicationTemplate; import com.appsmith.server.dtos.PageNameIdDTO; import com.appsmith.server.exports.internal.ExportApplicationService; @@ -13,7 +12,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import mockwebserver3.MockResponse; import mockwebserver3.MockWebServer; -import mockwebserver3.RecordedRequest; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.AfterAll; @@ -117,56 +115,15 @@ public class ApplicationTemplateServiceUnitTest { .setBody(objectMapper.writeValueAsString(List.of(templateOne, templateTwo, templateThree))) .addHeader("Content-Type", "application/json")); - // mock the user data to set second template as recently used - UserData mockUserData = new UserData(); - mockUserData.setRecentlyUsedTemplateIds(List.of("id-two")); - Mockito.when(userDataService.getForCurrentUser()).thenReturn(Mono.just(mockUserData)); - Mono> templateListMono = applicationTemplateService.getActiveTemplates(null); StepVerifier.create(templateListMono) .assertNext(applicationTemplates -> { assertThat(applicationTemplates.size()).isEqualTo(3); - assertThat(applicationTemplates.get(0).getId()).isEqualTo("id-two"); // second one should come first }) .verifyComplete(); } - @Test - public void getRecentlyUsedTemplates_WhenNoRecentTemplate_ReturnsEmpty() { - // mock the user data to that has no recent template - Mockito.when(userDataService.getForCurrentUser()).thenReturn(Mono.just(new UserData())); - - StepVerifier.create(applicationTemplateService.getRecentlyUsedTemplates()) - .verifyComplete(); - } - - @Test - public void getRecentlyUsedTemplates_WhenRecentTemplatesExist_ReturnsTemplates() - throws InterruptedException, JsonProcessingException { - // mock the user data to set recently used template ids - UserData mockUserData = new UserData(); - mockUserData.setRecentlyUsedTemplateIds(List.of("id-one", "id-two")); - Mockito.when(userDataService.getForCurrentUser()).thenReturn(Mono.just(mockUserData)); - - // mock the server to return a template when it's called - mockCloudServices.enqueue(new MockResponse() - .setBody(objectMapper.writeValueAsString(List.of(create("id-one", "First template")))) - .addHeader("Content-Type", "application/json")); - - // make sure we've received the response returned by the mockCloudServices - StepVerifier.create(applicationTemplateService.getRecentlyUsedTemplates()) - .assertNext( - applicationTemplates -> assertThat(applicationTemplates).hasSize(1)) - .verifyComplete(); - - // verify that mockCloudServices was called with the query param id i.e. id=id-one&id=id-two - RecordedRequest recordedRequest = mockCloudServices.takeRequest(); - assert recordedRequest.getRequestUrl() != null; - List queryParameterValues = recordedRequest.getRequestUrl().queryParameterValues("id"); - assertThat(queryParameterValues).containsExactly("id-one", "id-two"); - } - @Test public void get_WhenPageMetaDataExists_PageMetaDataParsedProperly() throws JsonProcessingException { JSONObject jsonObject = new JSONObject(); @@ -188,11 +145,6 @@ public class ApplicationTemplateServiceUnitTest { mockCloudServices.enqueue( new MockResponse().setBody(templates.toString()).addHeader("Content-Type", "application/json")); - // mock the user data to set recently used template ids - UserData mockUserData = new UserData(); - mockUserData.setRecentlyUsedTemplateIds(List.of()); - Mockito.when(userDataService.getForCurrentUser()).thenReturn(Mono.just(mockUserData)); - // make sure we've received the response returned by the mockCloudServices StepVerifier.create(applicationTemplateService.getActiveTemplates(null)) .assertNext(applicationTemplates -> { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutActionServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutActionServiceTest.java index cbea4d0087..de5f566b71 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutActionServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutActionServiceTest.java @@ -22,7 +22,7 @@ import com.appsmith.server.dtos.EntityType; import com.appsmith.server.dtos.LayoutDTO; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.dtos.RefactorEntityNameDTO; -import com.appsmith.server.dtos.ce.UpdateMultiplePageLayoutDTO; +import com.appsmith.server.dtos.UpdateMultiplePageLayoutDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exports.internal.ExportApplicationService; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java index 316732de03..efc8ccc1bc 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java @@ -3,6 +3,7 @@ package com.appsmith.server.services; import com.appsmith.external.dtos.DslExecutableDTO; import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.ActionDTO; +import com.appsmith.external.models.CreatorContextType; import com.appsmith.external.models.Datasource; import com.appsmith.external.models.PluginType; import com.appsmith.external.models.Property; @@ -21,6 +22,7 @@ import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.PluginRepository; import com.appsmith.server.solutions.ApplicationPermission; import com.fasterxml.jackson.core.JsonProcessingException; @@ -94,6 +96,12 @@ public class LayoutServiceTest { @Autowired ApplicationPermission applicationPermission; + @Autowired + SessionUserService sessionUserService; + + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + @MockBean PluginExecutorHelper pluginExecutorHelper; @@ -108,14 +116,26 @@ public class LayoutServiceTest { @BeforeEach public void setup() { + User currentUser = sessionUserService.getCurrentUser().block(); purgeAllPages(); User apiUser = userService.findByEmail("api_user").block(); Workspace toCreate = new Workspace(); toCreate.setName("LayoutServiceTest"); + Set beforeCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingWorkspace); Workspace workspace = workspaceService.create(toCreate, apiUser, Boolean.FALSE).block(); workspaceId = workspace.getId(); + Set afterCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); + + log.info("Workspace ID: {}", workspaceId); + log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", workspace.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); datasource = new Datasource(); datasource.setName("Default Database"); @@ -199,15 +219,13 @@ public class LayoutServiceTest { } private Mono createPage(Application app, PageDTO page) { - return newPageService - .findByNameAndViewMode(page.getName(), AclPermission.READ_PAGES, false) - .switchIfEmpty(applicationPageService - .createApplication(app, workspaceId) - .map(application -> { - page.setApplicationId(application.getId()); - return page; - }) - .flatMap(applicationPageService::createPage)); + return applicationPageService + .createApplication(app, workspaceId) + .map(application -> { + page.setApplicationId(application.getId()); + return page; + }) + .flatMap(applicationPageService::createPage); } @Test @@ -554,21 +572,27 @@ public class LayoutServiceTest { Layout newLayout = new Layout(); JSONObject obj = new JSONObject(Map.of( - "widgetName", "testWidget", - "key", "value-updated", - "another", "Hello people of the {{input1.text}} planet!", - "dynamicGet", "some dynamic {{\"anIgnoredAction.data:\" + aGetAction.data}}", + "widgetName", + "testWidget", + "key", + "value-updated", + "another", + "Hello people of the {{input1.text}} planet!", + "dynamicGet", + "some dynamic {{\"anIgnoredAction.data:\" + aGetAction.data}}", "dynamicPost", - "some dynamic {{\n" + "(function(ignoredAction1){\n" - + "\tlet a = ignoredAction1.data\n" - + "\tlet ignoredAction2 = { data: \"nothing\" }\n" - + "\tlet b = ignoredAction2.data\n" - + "\tlet c = \"ignoredAction3.data\"\n" - + "\t// ignoredAction4.data\n" - + "\treturn aPostAction.data\n" - + "})(anotherPostAction.data)}}", - "dynamicPostWithAutoExec", "some dynamic {{aPostActionWithAutoExec.data}}", - "dynamicDelete", "some dynamic {{aDeleteAction.data}}")); + "some dynamic {{\n" + "(function(ignoredAction1){\n" + + "\tlet a = ignoredAction1.data\n" + + "\tlet ignoredAction2 = { data: \"nothing\" }\n" + + "\tlet b = ignoredAction2.data\n" + + "\tlet c = \"ignoredAction3.data\"\n" + + "\t// ignoredAction4.data\n" + + "\treturn aPostAction.data\n" + + "})(anotherPostAction.data)}}", + "dynamicPostWithAutoExec", + "some dynamic {{aPostActionWithAutoExec.data}}", + "dynamicDelete", + "some dynamic {{aDeleteAction.data}}")); obj.putAll(Map.of( "collection1Key", "some dynamic {{Collection.anAsyncCollectionActionWithoutCall.data}}", "collection2Key", "some dynamic {{Collection.aSyncCollectionActionWithoutCall.data}}", @@ -1286,7 +1310,8 @@ public class LayoutServiceTest { layoutId.get(), oldParent, "dynamicGet_IncorrectKey", - "New element is null")); + "New element is null", + CreatorContextType.PAGE)); return true; }) .verify(); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ThemeServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ThemeServiceTest.java index b0bb162ae8..d9d315ecf2 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ThemeServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ThemeServiceTest.java @@ -13,12 +13,14 @@ import com.appsmith.server.dtos.UpdatePermissionGroupDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.repositories.ThemeRepository; import com.appsmith.server.repositories.UserRepository; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.UserAndAccessManagementService; import com.appsmith.server.themes.base.ThemeService; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,6 +38,7 @@ import reactor.util.function.Tuples; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.UUID; import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS; @@ -48,6 +51,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest @ExtendWith(SpringExtension.class) +@Slf4j public class ThemeServiceTest { @Autowired @@ -91,6 +95,12 @@ public class ThemeServiceTest { @Autowired PermissionGroupService permissionGroupService; + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + + @Autowired + SessionUserService sessionUserService; + @BeforeEach public void setup() { Workspace workspace = new Workspace(); @@ -111,13 +121,29 @@ public class ThemeServiceTest { } private Application createApplication() { + User currentUser = sessionUserService.getCurrentUser().block(); + Set beforeCreatingApplication = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingApplication); Application application = new Application(); application.setName("ThemeTest_" + UUID.randomUUID()); application.setWorkspaceId(this.workspace.getId()); - applicationPageService + Application createdApplication = applicationPageService .createApplication(application, this.workspace.getId()) .block(); - return application; + + Set afterCreatingApplication = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating Application: {}", afterCreatingApplication); + + log.info("Workspace ID: {}", this.workspace.getId()); + log.info("Workspace Role Ids: {}", this.workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", this.workspace.getPolicies()); + log.info("Application ID: {}", createdApplication.getId()); + log.info("Policies for created Application: {}", createdApplication.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); + + return createdApplication; } public void replaceApiUserWithAnotherUserInWorkspace() { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java index 53b5014ec5..f2d08098e1 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java @@ -325,81 +325,6 @@ public class UserDataServiceTest { .verifyComplete(); } - @Test - @WithUserDetails(value = "api_user") - public void addTemplateIdToLastUsedList_WhenListIsEmpty_templateIdPrepended() { - final Mono saveMono = userDataService - .getForCurrentUser() - .flatMap(userData -> { - // set recently used template ids to null - userData.setRecentlyUsedTemplateIds(null); - return userDataRepository.save(userData); - }) - .then(userDataService.addTemplateIdToLastUsedList("123456")); - - StepVerifier.create(saveMono) - .assertNext(userData -> { - assertEquals(1, userData.getRecentlyUsedTemplateIds().size()); - assertEquals("123456", userData.getRecentlyUsedTemplateIds().get(0)); - }) - .verifyComplete(); - } - - @Test - @WithUserDetails(value = "api_user") - public void addTemplateIdToLastUsedList_WhenListIsNotEmpty_templateIdPrepended() { - final Mono resultMono = userDataService - .getForCurrentUser() - .flatMap(userData -> { - // Set an initial list of template ids to the current user. - userData.setRecentlyUsedTemplateIds(Arrays.asList("123", "456")); - return userDataRepository.save(userData); - }) - .flatMap(userData -> { - // Now check whether a new template id is put at first. - String newTemplateId = "456"; - return userDataService.addTemplateIdToLastUsedList(newTemplateId); - }); - - StepVerifier.create(resultMono) - .assertNext(userData -> { - assertEquals(2, userData.getRecentlyUsedTemplateIds().size()); - assertEquals("456", userData.getRecentlyUsedTemplateIds().get(0)); - assertEquals("123", userData.getRecentlyUsedTemplateIds().get(1)); - }) - .verifyComplete(); - } - - @Test - @WithUserDetails(value = "api_user") - public void addTemplateIdToLastUsedList_TooManyRecentIds_ListsAreTruncated() { - String newTemplateId = "new-template-id"; - - final Mono resultMono = userDataService - .getForCurrentUser() - .flatMap(userData -> { - // Set an initial list of 12 template ids to the current user - userData.setRecentlyUsedTemplateIds(new ArrayList<>()); - for (int i = 1; i <= 12; i++) { - userData.getRecentlyUsedTemplateIds().add("template-" + i); - } - return userDataRepository.save(userData); - }) - .flatMap(userData -> { - // Now check whether a new template id is put at first. - return userDataService.addTemplateIdToLastUsedList(newTemplateId); - }); - - StepVerifier.create(resultMono) - .assertNext(userData -> { - // org id list should be truncated to 10 - assertThat(userData.getRecentlyUsedTemplateIds().size()).isEqualTo(5); - assertThat(userData.getRecentlyUsedTemplateIds().get(0)).isEqualTo(newTemplateId); - assertThat(userData.getRecentlyUsedTemplateIds().get(4)).isEqualTo("template-4"); - }) - .verifyComplete(); - } - @Test @WithUserDetails(value = "api_user") public void deleteProfilePhotot_WhenExists_RemovedFromAssetAndUserData() { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ApplicationServiceCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ApplicationServiceCETest.java index 98f9862ae4..02f88cc3ce 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ApplicationServiceCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ApplicationServiceCETest.java @@ -54,6 +54,7 @@ import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.plugins.base.PluginService; import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.AssetRepository; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.NewPageRepository; import com.appsmith.server.repositories.PermissionGroupRepository; @@ -266,6 +267,9 @@ public class ApplicationServiceCETest { @Autowired UserAndAccessManagementService userAndAccessManagementService; + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + String workspaceId; String defaultEnvironmentId; @@ -299,6 +303,9 @@ public class ApplicationServiceCETest { Workspace toCreate = new Workspace(); toCreate.setName("ApplicationServiceTest"); + Set beforeCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingWorkspace); Workspace workspace = workspaceService.create(toCreate, apiUser, Boolean.FALSE).block(); workspaceId = workspace.getId(); @@ -366,6 +373,14 @@ public class ApplicationServiceCETest { datasource1.setDatasourceStorages(storages1); testDatasource1 = datasourceService.create(datasource1).block(); + Set afterCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); + + log.info("Workspace ID: {}", workspaceId); + log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", workspace.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); } @AfterEach diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/FeatureFlagServiceCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/FeatureFlagServiceCETest.java index 78592c79a9..31e105de9c 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/FeatureFlagServiceCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/FeatureFlagServiceCETest.java @@ -2,7 +2,7 @@ package com.appsmith.server.services.ce; import com.appsmith.caching.components.CacheManager; import com.appsmith.server.domains.User; -import com.appsmith.server.dtos.ce.FeaturesResponseDTO; +import com.appsmith.server.dtos.FeaturesResponseDTO; import com.appsmith.server.featureflags.CachedFeatures; import com.appsmith.server.featureflags.CachedFlags; import com.appsmith.server.featureflags.FeatureFlagEnum; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java index 68ed489ba8..bcb796db5b 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java @@ -50,12 +50,14 @@ import com.appsmith.server.migrations.JsonSchemaVersions; import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.PluginRepository; import com.appsmith.server.repositories.WorkspaceRepository; import com.appsmith.server.services.ApplicationPageService; import com.appsmith.server.services.ApplicationService; import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutCollectionService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ApplicationPermission; @@ -213,13 +215,22 @@ public class GitServiceCETest { @Autowired WorkspacePermission workspacePermission; + @Autowired + SessionUserService sessionUserService; + + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + @BeforeEach public void setup() throws IOException, GitAPIException { - + User currentUser = sessionUserService.getCurrentUser().block(); User apiUser = userService.findByEmail("api_user").block(); Workspace toCreate = new Workspace(); toCreate.setName("Git Service Test"); + Set beforeCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingWorkspace); Workspace workspace = workspaceService.create(toCreate, apiUser, Boolean.FALSE).block(); workspaceId = workspace.getId(); @@ -236,6 +247,15 @@ public class GitServiceCETest { // applicationPermission = new ApplicationPermissionImpl(); testUserProfile.setAuthorEmail("test@email.com"); testUserProfile.setAuthorName("testUser"); + + Set afterCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); + + log.info("Workspace ID: {}", workspaceId); + log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", workspace.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); } @AfterEach diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ProductAlertServiceCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ProductAlertServiceCEImplTest.java index fbc40e8332..3c54884d52 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ProductAlertServiceCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ProductAlertServiceCEImplTest.java @@ -1,7 +1,7 @@ package com.appsmith.server.services.ce; import com.appsmith.server.configurations.CommonConfig; -import com.appsmith.server.dtos.ce.ProductAlertResponseDTO; +import com.appsmith.server.dtos.ProductAlertResponseDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/AuthenticationServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/AuthenticationServiceTest.java index 065d8994d9..933528d264 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/AuthenticationServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/AuthenticationServiceTest.java @@ -109,7 +109,7 @@ public class AuthenticationServiceTest { @WithUserDetails(value = "api_user") public void testGetAuthorizationCodeURL_missingDatasource() { Mono authorizationCodeUrlMono = authenticationService.getAuthorizationCodeURLForGenericOAuth2( - "invalidId", FieldName.UNUSED_ENVIRONMENT_ID, "irrelevantPageId", null); + "invalidId", FieldName.UNUSED_ENVIRONMENT_ID, "irrelevantPageId", null, null); StepVerifier.create(authorizationCodeUrlMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -150,7 +150,7 @@ public class AuthenticationServiceTest { Mono authorizationCodeUrlMono = datasourceMono .map(BaseDomain::getId) .flatMap(datasourceId -> authenticationService.getAuthorizationCodeURLForGenericOAuth2( - datasourceId, defaultEnvironmentId, "irrelevantPageId", null)); + datasourceId, defaultEnvironmentId, "irrelevantPageId", null, null)); StepVerifier.create(authorizationCodeUrlMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -228,7 +228,7 @@ public class AuthenticationServiceTest { final String datasourceId1 = datasourceMono.map(BaseDomain::getId).block(); Mono authorizationCodeUrlMono = authenticationService.getAuthorizationCodeURLForGenericOAuth2( - datasourceId1, defaultEnvironmentId, pageDto.getId(), httpRequest); + datasourceId1, defaultEnvironmentId, pageDto.getId(), null, httpRequest); StepVerifier.create(authorizationCodeUrlMono) .assertNext(url -> { @@ -248,4 +248,98 @@ public class AuthenticationServiceTest { }) .verifyComplete(); } + + @Test + @WithUserDetails(value = "api_user") + public void testGetAuthorizationCodeURL_validDatasourceAndBranchName() { + LinkedMultiValueMap mockHeaders = new LinkedMultiValueMap<>(1); + mockHeaders.add(HttpHeaders.REFERER, "https://mock.origin.com/source/uri"); + + MockServerHttpRequest httpRequest = + MockServerHttpRequest.get("").headers(mockHeaders).build(); + + Workspace testWorkspace = new Workspace(); + testWorkspace.setName("Test-Workspace-OAuth2-git-redirection"); + testWorkspace = workspaceService.create(testWorkspace).block(); + String workspaceId = testWorkspace == null ? "" : testWorkspace.getId(); + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())) + .thenReturn(Mono.just(new MockPluginExecutor())); + + String defaultEnvironmentId = workspaceService + .getDefaultEnvironmentId(workspaceId, environmentPermission.getExecutePermission()) + .block(); + + PageDTO testPage = new PageDTO(); + testPage.setName("Test-Page-oauth2-git-redirection"); + + Application newApp = new Application(); + newApp.setName(UUID.randomUUID().toString()); + Application application = + applicationPageService.createApplication(newApp, workspaceId).block(); + + testPage.setApplicationId(application.getId()); + + PageDTO pageDTO = applicationPageService.createPage(testPage).block(); + + Mono pluginMono = pluginService.findByName("Installed Plugin Name"); + // install plugin + pluginMono + .flatMap(plugin -> pluginService.installPlugin(PluginWorkspaceDTO.builder() + .pluginId(plugin.getId()) + .workspaceId(workspaceId) + .status(WorkspacePluginStatus.FREE) + .build())) + .block(); + Datasource datasource = new Datasource(); + datasource.setName("Valid datasource for OAuth2"); + datasource.setWorkspaceId(workspaceId); + DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setUrl("http://test.com"); + OAuth2 authenticationDTO = new OAuth2(); + authenticationDTO.setClientId("ClientId"); + authenticationDTO.setClientSecret("ClientSecret"); + authenticationDTO.setAuthorizationUrl("AuthorizationURL"); + authenticationDTO.setAccessTokenUrl("AccessTokenURL"); + authenticationDTO.setScope(Set.of("Scope1", "Scope2")); + authenticationDTO.setCustomAuthenticationParameters(Set.of(new Property("key", "value"))); + datasourceConfiguration.setAuthentication(authenticationDTO); + datasource.setDatasourceConfiguration(datasourceConfiguration); + + HashMap storages = new HashMap<>(); + datasource.setDatasourceStorages(storages); + storages.put( + defaultEnvironmentId, new DatasourceStorageDTO(null, defaultEnvironmentId, datasourceConfiguration)); + + Mono datasourceMono = pluginMono + .map(plugin -> { + datasource.setPluginId(plugin.getId()); + return datasource; + }) + .flatMap(datasourceService::create) + .cache(); + + final String datasourceId = datasourceMono.map(BaseDomain::getId).block(); + + Mono authorizationCodeUrlMono = authenticationService.getAuthorizationCodeURLForGenericOAuth2( + datasourceId, null, pageDTO.getId(), "testBranch", httpRequest); + + StepVerifier.create(authorizationCodeUrlMono) + .assertNext(url -> { + assertThat(url) + .matches(Pattern.compile("AuthorizationURL" + "\\?client_id=ClientId" + + "&response_type=code" + + "&redirect_uri=https://mock.origin.com/api/v1/datasources/authorize" + + "&state=" + + String.join( + ",", + pageDTO.getId(), + datasourceId, + defaultEnvironmentId, + "https://mock.origin.com", + "testBranch") + + "&scope=Scope\\d%20Scope\\d" + + "&key=value")); + }) + .verifyComplete(); + } } 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 a4e14f57f8..f58fb74b06 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 @@ -33,6 +33,7 @@ import com.appsmith.server.domains.NewPage; import com.appsmith.server.domains.PermissionGroup; import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.Theme; +import com.appsmith.server.domains.User; import com.appsmith.server.domains.Workspace; import com.appsmith.server.dtos.ActionCollectionDTO; import com.appsmith.server.dtos.ApplicationAccessDTO; @@ -55,6 +56,7 @@ import com.appsmith.server.newactions.base.NewActionService; import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.plugins.base.PluginService; import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.repositories.PluginRepository; import com.appsmith.server.repositories.ThemeRepository; @@ -63,6 +65,7 @@ import com.appsmith.server.services.ApplicationService; import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutCollectionService; import com.appsmith.server.services.PermissionGroupService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.WorkspaceService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -214,6 +217,12 @@ public class ImportApplicationServiceTests { @SpyBean PluginService pluginService; + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + + @Autowired + SessionUserService sessionUserService; + @BeforeEach public void setup() { Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())) @@ -222,6 +231,10 @@ public class ImportApplicationServiceTests { if (Boolean.TRUE.equals(isSetupDone)) { return; } + User currentUser = sessionUserService.getCurrentUser().block(); + Set beforeCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingWorkspace); installedPlugin = pluginRepository.findByPackageName("installed-plugin").block(); Workspace workspace = new Workspace(); workspace.setName("Import-Export-Test-Workspace"); @@ -230,6 +243,14 @@ public class ImportApplicationServiceTests { defaultEnvironmentId = workspaceService .getDefaultEnvironmentId(workspaceId, environmentPermission.getExecutePermission()) .block(); + Set afterCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); + + log.info("Workspace ID: {}", workspaceId); + log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", workspace.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); Application testApplication = new Application(); testApplication.setName("Export-Application-Test-Application"); @@ -878,11 +899,14 @@ public class ImportApplicationServiceTests { importApplicationService.extractFileAndSaveApplication(workspaceId, filePart); StepVerifier.create(resultMono) - .expectErrorMatches(throwable -> throwable instanceof AppsmithException - && throwable - .getMessage() - .equals(AppsmithError.VALIDATION_FAILURE.getMessage( - "Field '" + FieldName.APPLICATION + "' is missing in the JSON."))) + .expectErrorMatches( + throwable -> throwable instanceof AppsmithException + && throwable + .getMessage() + .equals( + AppsmithError.VALIDATION_FAILURE.getMessage( + "Field '" + FieldName.APPLICATION + + "' Sorry! Seems like you've imported a page-level json instead of an application. Please use the import within the page."))) .verify(); } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java index 4726ae2bff..0a07db5ccf 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java @@ -36,6 +36,7 @@ import com.appsmith.server.helpers.WidgetSuggestionHelper; import com.appsmith.server.imports.internal.ImportApplicationService; import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.plugins.base.PluginService; +import com.appsmith.server.repositories.CacheableRepositoryHelper; import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.PermissionGroupRepository; import com.appsmith.server.repositories.PluginRepository; @@ -46,6 +47,7 @@ import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutService; import com.appsmith.server.services.MockDataService; import com.appsmith.server.services.PermissionGroupService; +import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ActionExecutionSolution; @@ -54,6 +56,7 @@ import com.appsmith.server.solutions.EnvironmentPermission; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; import org.junit.jupiter.api.AfterEach; @@ -78,6 +81,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import static com.appsmith.server.acl.AclPermission.READ_PAGES; @@ -86,6 +90,7 @@ import static org.mockito.ArgumentMatchers.any; @ExtendWith(SpringExtension.class) @SpringBootTest +@Slf4j public class ActionExecutionSolutionCETest { @Autowired @@ -163,6 +168,12 @@ public class ActionExecutionSolutionCETest { @Autowired ApplicationPermission applicationPermission; + @Autowired + SessionUserService sessionUserService; + + @Autowired + CacheableRepositoryHelper cacheableRepositoryHelper; + Application testApp = null; PageDTO testPage = null; @@ -181,15 +192,26 @@ public class ActionExecutionSolutionCETest { @BeforeEach public void setup() { - + User currentUser = sessionUserService.getCurrentUser().block(); User apiUser = userService.findByEmail("api_user").block(); Workspace toCreate = new Workspace(); toCreate.setName("ActionServiceCE_Test"); + Set beforeCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User before creating workspace: {}", beforeCreatingWorkspace); Workspace workspace = workspaceService.create(toCreate, apiUser, Boolean.FALSE).block(); workspaceId = workspace.getId(); + Set afterCreatingWorkspace = + cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); + log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); + + log.info("Workspace ID: {}", workspaceId); + log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); + log.info("Policy for created Workspace: {}", workspace.getPolicies()); + log.info("Current User ID: {}", currentUser.getId()); defaultEnvironmentId = workspaceService .getDefaultEnvironmentId(workspaceId, environmentPermission.getExecutePermission()) diff --git a/app/server/appsmith-server/src/test/resources/test_assets/ImportExportServiceTest/partial-export-resource.json b/app/server/appsmith-server/src/test/resources/test_assets/ImportExportServiceTest/partial-export-resource.json new file mode 100644 index 0000000000..dd48c4aad0 --- /dev/null +++ b/app/server/appsmith-server/src/test/resources/test_assets/ImportExportServiceTest/partial-export-resource.json @@ -0,0 +1,766 @@ +{ + "clientSchemaVersion": 1, + "serverSchemaVersion": 7, + "actionList": [ + { + "id": "Send Messages_SendEmail", + "pluginType": "DB", + "pluginId": "smtp-plugin", + "unpublishedAction": { + "name": "SendEmail", + "datasource": { + "userPermissions": [], + "pluginId": "smtp-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "body": "{{EmailText.text}}", + "selfReferencingDataPaths": [], + "formData": { + "command": "SEND", + "send": { + "from": "appsmithtemp@gmail.com", + "to": "kocharrahul8@gmail.com,{{Utils.get()}}", + "subject": "Communication Sent Via Appsmith" + } + } + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "body" + }, + { + "key": "formData.send.to" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "EmailText.text", + "Utils.get()" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "SendEmail", + "configurationPath": "SendEmail.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "body": "{{EmailText.text}}", + "selfReferencingDataPaths": [], + "formData": { + "command": "SEND", + "send": { + "from": "appsmithtemp@gmail.com", + "to": "kocharrahul8@gmail.com,{{Utils.get()}}", + "subject": "Communication Sent Via Appsmith" + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "SendEmail", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "EmailText.text", + "Utils.get()" + ], + "timeoutInMillisecond": 10000 + } + }, + "publishedAction": { + "name": "SendEmail", + "datasource": { + "userPermissions": [], + "pluginId": "smtp-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "body": "{{EmailText.text}}", + "selfReferencingDataPaths": [], + "formData": { + "command": "SEND", + "send": { + "from": "appsmithtemp@gmail.com", + "to": "kocharrahul8@gmail.com,{{Utils.get()}}", + "subject": "Communication Sent Via Appsmith" + } + } + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "body" + }, + { + "key": "formData.send.to" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "EmailText.text", + "Utils.get()" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "SendEmail", + "configurationPath": "SendEmail.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "body": "{{EmailText.text}}", + "selfReferencingDataPaths": [], + "formData": { + "command": "SEND", + "send": { + "from": "appsmithtemp@gmail.com", + "to": "kocharrahul8@gmail.com,{{Utils.get()}}", + "subject": "Communication Sent Via Appsmith" + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "SendEmail", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "EmailText.text", + "Utils.get()" + ], + "timeoutInMillisecond": 10000 + } + }, + "new": false + }, + { + "id": "Send Messages_DiscordAPI", + "pluginType": "API", + "pluginId": "restapi-plugin", + "unpublishedAction": { + "name": "DiscordAPI", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "{{List1.selectedItem.Discord_Webhook}}" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "", + "headers": [ + { + "key": "content-type", + "value": "application/json" + } + ], + "encodeParamsToggle": true, + "queryParameters": [], + "body": "{\n\"content\" : {{DiscordText.text}}\n}", + "httpMethod": "POST", + "selfReferencingDataPaths": [], + "pluginSpecifiedTemplates": [ + { + "value": true + } + ] + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "body" + }, + { + "key": "datasourceUrl" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "DiscordText.text", + "List1.selectedItem.Discord_Webhook" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "DiscordAPI", + "configurationPath": "DiscordAPI.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "", + "headers": [ + { + "key": "content-type", + "value": "application/json" + } + ], + "encodeParamsToggle": true, + "queryParameters": [], + "body": "{\n\"content\" : {{DiscordText.text}}\n}", + "httpMethod": "POST", + "selfReferencingDataPaths": [], + "pluginSpecifiedTemplates": [ + { + "value": true + } + ] + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "DiscordAPI", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "DiscordText.text", + "List1.selectedItem.Discord_Webhook" + ], + "timeoutInMillisecond": 10000 + } + }, + "publishedAction": { + "name": "DiscordAPI", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "{{List1.selectedItem.Discord_Webhook}}" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "", + "headers": [ + { + "key": "content-type", + "value": "application/json" + } + ], + "encodeParamsToggle": true, + "queryParameters": [], + "body": "{\n\"content\" : {{DiscordText.text}}\n}", + "httpMethod": "POST", + "selfReferencingDataPaths": [], + "pluginSpecifiedTemplates": [ + { + "value": true + } + ] + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "body" + }, + { + "key": "datasourceUrl" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "DiscordText.text", + "List1.selectedItem.Discord_Webhook" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "DiscordAPI", + "configurationPath": "DiscordAPI.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "", + "headers": [ + { + "key": "content-type", + "value": "application/json" + } + ], + "encodeParamsToggle": true, + "queryParameters": [], + "body": "{\n\"content\" : {{DiscordText.text}}\n}", + "httpMethod": "POST", + "selfReferencingDataPaths": [], + "pluginSpecifiedTemplates": [ + { + "value": true + } + ] + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "DiscordAPI", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "DiscordText.text", + "List1.selectedItem.Discord_Webhook" + ], + "timeoutInMillisecond": 10000 + } + }, + "new": false + }, + { + "id": "Send Messages_AddNewList", + "pluginType": "DB", + "pluginId": "mongo-plugin", + "unpublishedAction": { + "name": "AddNewList", + "datasource": { + "userPermissions": [], + "pluginId": "mongo-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "insert": { + "documents": { + "data": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}", + "viewType": "component", + "componentData": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}" + } + }, + "collection": { + "data": "MailingList", + "viewType": "component", + "componentData": "MailingList" + }, + "command": { + "data": "INSERT", + "viewType": "component", + "componentData": "INSERT" + }, + "smartSubstitution": { + "data": true, + "viewType": "component", + "componentData": true + } + } + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "formData.insert.documents.data" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "List_Name.text", + "List_ID.text", + "Webhook.text", + "Table2.selectedRows.map(a => a.CustomerID)", + "Icon.text", + "moment().format('YYYY-MM-DDT00:00:00.000Z')" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "AddNewList", + "configurationPath": "AddNewList.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "insert": { + "documents": { + "data": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}", + "viewType": "component", + "componentData": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}" + } + }, + "collection": { + "data": "MailingList", + "viewType": "component", + "componentData": "MailingList" + }, + "command": { + "data": "INSERT", + "viewType": "component", + "componentData": "INSERT" + }, + "smartSubstitution": { + "data": true, + "viewType": "component", + "componentData": true + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "AddNewList", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "List_Name.text", + "List_ID.text", + "Webhook.text", + "Table2.selectedRows.map(a => a.CustomerID)", + "Icon.text", + "moment().format('YYYY-MM-DDT00:00:00.000Z')" + ], + "timeoutInMillisecond": 10000 + } + }, + "publishedAction": { + "name": "AddNewList", + "datasource": { + "userPermissions": [], + "pluginId": "mongo-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "insert": { + "documents": { + "data": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}", + "viewType": "component", + "componentData": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}" + } + }, + "collection": { + "data": "MailingList", + "viewType": "component", + "componentData": "MailingList" + }, + "command": { + "data": "INSERT", + "viewType": "component", + "componentData": "INSERT" + }, + "smartSubstitution": { + "data": true, + "viewType": "component", + "componentData": true + } + } + }, + "executeOnLoad": false, + "dynamicBindingPathList": [ + { + "key": "formData.insert.documents.data" + } + ], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [ + "List_Name.text", + "List_ID.text", + "Webhook.text", + "Table2.selectedRows.map(a => a.CustomerID)", + "Icon.text", + "moment().format('YYYY-MM-DDT00:00:00.000Z')" + ], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "AddNewList", + "configurationPath": "AddNewList.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "insert": { + "documents": { + "data": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}", + "viewType": "component", + "componentData": "\n{\"Discord_Webhook\":{{Webhook.text}}\n\"Mailinglist\":{{List_Name.text}}\n\"MailinglistID\":{{List_ID.text}}\n\"Icon\":{{Icon.text}}\n\"Users\":{{Table2.selectedRows.map(a => a.CustomerID)}}\n\"CreationDate\": ISODate({{moment().format('YYYY-MM-DDT00:00:00.000Z')}})\n}" + } + }, + "collection": { + "data": "MailingList", + "viewType": "component", + "componentData": "MailingList" + }, + "command": { + "data": "INSERT", + "viewType": "component", + "componentData": "INSERT" + }, + "smartSubstitution": { + "data": true, + "viewType": "component", + "componentData": true + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "AddNewList", + "confirmBeforeExecute": false, + "jsonPathKeys": [ + "List_Name.text", + "List_ID.text", + "Webhook.text", + "Table2.selectedRows.map(a => a.CustomerID)", + "Icon.text", + "moment().format('YYYY-MM-DDT00:00:00.000Z')" + ], + "timeoutInMillisecond": 10000 + } + }, + "new": false + }, + { + "id": "Send Messages_doNotDisturbCount", + "pluginType": "DB", + "pluginId": "mongo-plugin", + "unpublishedAction": { + "name": "doNotDisturbCount", + "datasource": { + "userPermissions": [], + "pluginId": "mongo-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "count": { + "query": { + "data": "{\"UsersDoNotDisturb\": 1}", + "viewType": "component", + "componentData": "{\"UsersDoNotDisturb\": 1}" + } + }, + "collection": { + "data": "Customers", + "viewType": "component", + "componentData": "Customers" + }, + "body": { + "data": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n", + "viewType": "component", + "componentData": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n" + }, + "command": { + "data": "COUNT", + "viewType": "component", + "componentData": "COUNT" + } + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "doNotDisturbCount", + "configurationPath": "doNotDisturbCount.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "count": { + "query": { + "data": "{\"UsersDoNotDisturb\": 1}", + "viewType": "component", + "componentData": "{\"UsersDoNotDisturb\": 1}" + } + }, + "collection": { + "data": "Customers", + "viewType": "component", + "componentData": "Customers" + }, + "body": { + "data": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n", + "viewType": "component", + "componentData": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n" + }, + "command": { + "data": "COUNT", + "viewType": "component", + "componentData": "COUNT" + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "doNotDisturbCount", + "confirmBeforeExecute": false, + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + }, + "publishedAction": { + "name": "doNotDisturbCount", + "datasource": { + "userPermissions": [], + "pluginId": "mongo-plugin", + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Send Messages", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "count": { + "query": { + "data": "{\"UsersDoNotDisturb\": 1}", + "viewType": "component", + "componentData": "{\"UsersDoNotDisturb\": 1}" + } + }, + "collection": { + "data": "Customers", + "viewType": "component", + "componentData": "Customers" + }, + "body": { + "data": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n", + "viewType": "component", + "componentData": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n" + }, + "command": { + "data": "COUNT", + "viewType": "component", + "componentData": "COUNT" + } + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "doNotDisturbCount", + "configurationPath": "doNotDisturbCount.actionConfiguration", + "executableConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "selfReferencingDataPaths": [], + "formData": { + "count": { + "query": { + "data": "{\"UsersDoNotDisturb\": 1}", + "viewType": "component", + "componentData": "{\"UsersDoNotDisturb\": 1}" + } + }, + "collection": { + "data": "Customers", + "viewType": "component", + "componentData": "Customers" + }, + "body": { + "data": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n", + "viewType": "component", + "componentData": "{\n \"count\": \"customers\",\n \"filter\": {\n \"UsersDoNotDisturb\": 1\n\t}\n}\n" + }, + "command": { + "data": "COUNT", + "viewType": "component", + "componentData": "COUNT" + } + } + }, + "selfReferencingDataPaths": [], + "dslExecutable": { + "name": "doNotDisturbCount", + "confirmBeforeExecute": false, + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + }, + "new": false + } + ], + "actionCollectionList": [ + { + "id": "Send Messages_Utils", + "unpublishedCollection": { + "name": "Utils", + "pageId": "Send Messages", + "pluginId": "js-plugin", + "pluginType": "JS", + "actions": [], + "archivedActions": [], + "body": "export default {\n\tsendMessages: () => {\n\t\tif(DiscordSwitch.isSwitchedOn){\n\t\t\tDiscordAPI.run();\n\t\t\tconst successMessage1 =\"Discord Message sent to \".concat(List1.selectedItem.Mailinglist).concat(\" server\") \n\t\t\tshowAlert(successMessage1,'success');\n\t\t\t}\n\t\tif(EmailSwitch.isSwitchedOn){\n\t\t\tSendEmail.run();\n\t\t\tconst successMessage2 =\"Email sent to users on \".concat(List1.selectedItem.Mailinglist).concat(\" list\") \n\t\t\tshowAlert(successMessage2,'success');\n\t\t}\n\t\tresetWidget(\"MessageModal\");\n\t\tcloseModal(\"MessageModal\");\n\t},\n\tgetEmails: () => {\n\t\tvar nameArray = Table1.selectedRows.map(function (el) { return el.CustomerEmailID; });\n\t\treturn nameArray.toString();\n\t},\n\taddList: () => {\n\t\tconst a =\"We have created a new list\".concat(List_Name.text);\n\t\tAddNewList.run();\n\t\tshowAlert(a,'success');\n\t\tresetWidget(\"mailingListModal\");\n\t\tGetMarketingList.run();\n\t\tcloseModal(\"mailingListModal\");\n\t}\n}", + "variables": [], + "userPermissions": [] + }, + "publishedCollection": { + "name": "Utils", + "pageId": "Send Messages", + "pluginId": "js-plugin", + "pluginType": "JS", + "actions": [], + "archivedActions": [], + "body": "export default {\n\tsendMessages: () => {\n\t\tif(DiscordSwitch.isSwitchedOn){\n\t\t\tDiscordAPI.run();\n\t\t\tconst successMessage1 =\"Discord Message sent to \".concat(List1.selectedItem.Mailinglist).concat(\" server\") \n\t\t\tshowAlert(successMessage1,'success');\n\t\t\t}\n\t\tif(EmailSwitch.isSwitchedOn){\n\t\t\tSendEmail.run();\n\t\t\tconst successMessage2 =\"Email sent to users on \".concat(List1.selectedItem.Mailinglist).concat(\" list\") \n\t\t\tshowAlert(successMessage2,'success');\n\t\t}\n\t\tresetWidget(\"MessageModal\");\n\t\tcloseModal(\"MessageModal\");\n\t},\n\tgetEmails: () => {\n\t\tvar nameArray = Table1.selectedRows.map(function (el) { return el.CustomerEmailID; });\n\t\treturn nameArray.toString();\n\t},\n\taddList: () => {\n\t\tconst a =\"We have created a new list\".concat(List_Name.text);\n\t\tAddNewList.run();\n\t\tshowAlert(a,'success');\n\t\tresetWidget(\"mailingListModal\");\n\t\tGetMarketingList.run();\n\t\tcloseModal(\"mailingListModal\");\n\t}\n}", + "variables": [], + "userPermissions": [] + }, + "new": false + } + ], + "widgets": "{\"widgets\":[{\"widgetId\":\"n4j5niuf3l\",\"list\":[{\"boxShadow\":\"none\",\"widgetName\":\"Container6CopyCopyCopy\",\"borderColor\":\"transparent\",\"isCanvas\":true,\"displayName\":\"Container\",\"iconSVG\":\"/static/media/icon.1977dca3.svg\",\"topRow\":15,\"bottomRow\":27,\"parentRowSpace\":10,\"type\":\"CONTAINER_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"parentColumnSpace\":18.0625,\"dynamicTriggerPathList\":[],\"leftColumn\":23,\"dynamicBindingPathList\":[],\"children\":[\"fd8c2u1eha\"],\"borderWidth\":\"0\",\"key\":\"p5ueindstb\",\"backgroundColor\":\"#FFFFFF\",\"rightColumn\":36,\"widgetId\":\"n4j5niuf3l\",\"containerStyle\":\"card\",\"isVisible\":true,\"version\":1,\"parentId\":\"0\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"borderRadius\":\"15px\",\"dynamicPropertyPathList\":[{\"key\":\"borderRadius\"}],\"labelTextSize\":\"0.875rem\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Canvas9CopyCopyCopy\",\"rightColumn\":433.5,\"detachFromLayout\":true,\"displayName\":\"Canvas\",\"widgetId\":\"fd8c2u1eha\",\"containerStyle\":\"none\",\"topRow\":0,\"bottomRow\":120,\"parentRowSpace\":1,\"isVisible\":true,\"type\":\"CANVAS_WIDGET\",\"canExtend\":false,\"version\":1,\"hideCard\":true,\"parentId\":\"n4j5niuf3l\",\"minHeight\":400,\"renderMode\":\"CANVAS\",\"isLoading\":false,\"parentColumnSpace\":1,\"leftColumn\":0,\"children\":[\"r03q89gxpz\"],\"key\":\"he8asa5zlk\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\"},{\"widgetName\":\"Statbox1CopyCopyCopy\",\"backgroundColor\":\"#FFFFFF\",\"rightColumn\":64,\"isCanvas\":true,\"displayName\":\"Stats Box\",\"iconSVG\":\"/static/media/icon.382a7c7b.svg\",\"widgetId\":\"r03q89gxpz\",\"topRow\":0,\"bottomRow\":10,\"parentRowSpace\":10,\"isVisible\":true,\"type\":\"STATBOX_WIDGET\",\"hideCard\":false,\"parentId\":\"fd8c2u1eha\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"animateLoading\":true,\"parentColumnSpace\":18.0625,\"dynamicTriggerPathList\":[],\"leftColumn\":0,\"dynamicBindingPathList\":[],\"children\":[\"0tbog0qb4e\"],\"key\":\"e50ggjmboq\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Canvas8CopyCopyCopy\",\"rightColumn\":289,\"detachFromLayout\":true,\"displayName\":\"Canvas\",\"widgetId\":\"0tbog0qb4e\",\"containerStyle\":\"none\",\"topRow\":0,\"bottomRow\":100,\"parentRowSpace\":1,\"isVisible\":true,\"type\":\"CANVAS_WIDGET\",\"canExtend\":false,\"version\":1,\"hideCard\":true,\"parentId\":\"r03q89gxpz\",\"minHeight\":140,\"renderMode\":\"CANVAS\",\"isLoading\":false,\"parentColumnSpace\":1,\"leftColumn\":0,\"children\":[\"p4ieqw603v\",\"yt0hi52if6\",\"8ouo8d7pqy\"],\"key\":\"he8asa5zlk\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\"},{\"widgetName\":\"Text6Copy4CopyCopy\",\"displayName\":\"Text\",\"iconSVG\":\"/static/media/icon.97c59b52.svg\",\"topRow\":0,\"bottomRow\":4,\"type\":\"TEXT_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"overflow\":\"NONE\",\"leftColumn\":0,\"dynamicBindingPathList\":[],\"truncateButtonColor\":\"#FFC13D\",\"text\":\"Reachable Customers\",\"key\":\"deco66gubs\",\"rightColumn\":64,\"textAlign\":\"LEFT\",\"widgetId\":\"p4ieqw603v\",\"isVisible\":true,\"fontStyle\":\"\",\"textColor\":\"#2E3D49\",\"version\":1,\"parentId\":\"0tbog0qb4e\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"fontSize\":\"0.875rem\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\",\"fontFamily\":\"System Default\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Text7Copy3CopyCopy\",\"displayName\":\"Text\",\"iconSVG\":\"/static/media/icon.97c59b52.svg\",\"topRow\":4,\"bottomRow\":8,\"type\":\"TEXT_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"overflow\":\"NONE\",\"leftColumn\":1,\"dynamicBindingPathList\":[{\"key\":\"text\"}],\"truncateButtonColor\":\"#FFC13D\",\"text\":\"{{allCustomerList.data.length}}\",\"key\":\"deco66gubs\",\"rightColumn\":37,\"textAlign\":\"LEFT\",\"widgetId\":\"yt0hi52if6\",\"isVisible\":true,\"fontStyle\":\"BOLD\",\"textColor\":\"#2E3D49\",\"version\":1,\"parentId\":\"0tbog0qb4e\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"fontSize\":\"1.5rem\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"dynamicPropertyPathList\":[{\"key\":\"fontSize\"}],\"labelTextSize\":\"0.875rem\",\"fontFamily\":\"System Default\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"boxShadow\":\"none\",\"widgetName\":\"IconButton2CopyCopy\",\"buttonColor\":\"#64508C\",\"displayName\":\"Icon Button\",\"iconSVG\":\"/static/media/icon.1a0c634a.svg\",\"topRow\":4,\"bottomRow\":8,\"type\":\"ICON_BUTTON_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"leftColumn\":51,\"dynamicBindingPathList\":[],\"isDisabled\":false,\"key\":\"1vdnabqlfq\",\"rightColumn\":64,\"iconName\":\"new-person\",\"widgetId\":\"8ouo8d7pqy\",\"buttonStyle\":\"PRIMARY\",\"isVisible\":true,\"version\":1,\"parentId\":\"0tbog0qb4e\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"borderRadius\":\"0.375rem\",\"buttonVariant\":\"PRIMARY\",\"labelTextSize\":\"0.875rem\"}],\"parentId\":\"0\"},{\"widgetId\":\"6yn34g8ujl\",\"list\":[{\"boxShadow\":\"none\",\"widgetName\":\"Container6CopyCopy\",\"borderColor\":\"transparent\",\"isCanvas\":true,\"displayName\":\"Container\",\"iconSVG\":\"/static/media/icon.1977dca3.svg\",\"topRow\":15,\"bottomRow\":27,\"parentRowSpace\":10,\"type\":\"CONTAINER_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"parentColumnSpace\":18.0625,\"dynamicTriggerPathList\":[],\"leftColumn\":36,\"dynamicBindingPathList\":[],\"children\":[\"ykizpmhpsg\"],\"borderWidth\":\"0\",\"key\":\"p5ueindstb\",\"backgroundColor\":\"#FFFFFF\",\"rightColumn\":49,\"widgetId\":\"6yn34g8ujl\",\"containerStyle\":\"card\",\"isVisible\":true,\"version\":1,\"parentId\":\"0\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"borderRadius\":\"15px\",\"dynamicPropertyPathList\":[{\"key\":\"borderRadius\"}],\"labelTextSize\":\"0.875rem\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Canvas9CopyCopy\",\"rightColumn\":433.5,\"detachFromLayout\":true,\"displayName\":\"Canvas\",\"widgetId\":\"ykizpmhpsg\",\"containerStyle\":\"none\",\"topRow\":0,\"bottomRow\":120,\"parentRowSpace\":1,\"isVisible\":true,\"type\":\"CANVAS_WIDGET\",\"canExtend\":false,\"version\":1,\"hideCard\":true,\"parentId\":\"6yn34g8ujl\",\"minHeight\":400,\"renderMode\":\"CANVAS\",\"isLoading\":false,\"parentColumnSpace\":1,\"leftColumn\":0,\"children\":[\"8vz0i9q06h\"],\"key\":\"he8asa5zlk\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\"},{\"widgetName\":\"Statbox1CopyCopy\",\"backgroundColor\":\"#FFFFFF\",\"rightColumn\":64,\"isCanvas\":true,\"displayName\":\"Stats Box\",\"iconSVG\":\"/static/media/icon.382a7c7b.svg\",\"widgetId\":\"8vz0i9q06h\",\"topRow\":0,\"bottomRow\":10,\"parentRowSpace\":10,\"isVisible\":true,\"type\":\"STATBOX_WIDGET\",\"hideCard\":false,\"parentId\":\"ykizpmhpsg\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"animateLoading\":true,\"parentColumnSpace\":18.0625,\"dynamicTriggerPathList\":[],\"leftColumn\":0,\"dynamicBindingPathList\":[],\"children\":[\"0y0vy0g8hm\"],\"key\":\"e50ggjmboq\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Canvas8CopyCopy\",\"rightColumn\":289,\"detachFromLayout\":true,\"displayName\":\"Canvas\",\"widgetId\":\"0y0vy0g8hm\",\"containerStyle\":\"none\",\"topRow\":0,\"bottomRow\":100,\"parentRowSpace\":1,\"isVisible\":true,\"type\":\"CANVAS_WIDGET\",\"canExtend\":false,\"version\":1,\"hideCard\":true,\"parentId\":\"8vz0i9q06h\",\"minHeight\":140,\"renderMode\":\"CANVAS\",\"isLoading\":false,\"parentColumnSpace\":1,\"leftColumn\":0,\"children\":[\"j3z2ve8ag5\",\"kwtiaoo3ks\",\"fubfdyc3va\"],\"key\":\"he8asa5zlk\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\"},{\"widgetName\":\"Text6Copy4Copy\",\"displayName\":\"Text\",\"iconSVG\":\"/static/media/icon.97c59b52.svg\",\"topRow\":0,\"bottomRow\":4,\"type\":\"TEXT_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"overflow\":\"NONE\",\"leftColumn\":1,\"dynamicBindingPathList\":[],\"truncateButtonColor\":\"#FFC13D\",\"text\":\"Do not Disturb Customers\",\"key\":\"deco66gubs\",\"rightColumn\":64,\"textAlign\":\"LEFT\",\"widgetId\":\"j3z2ve8ag5\",\"isVisible\":true,\"fontStyle\":\"\",\"textColor\":\"#2E3D49\",\"version\":1,\"parentId\":\"0y0vy0g8hm\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"fontSize\":\"0.875rem\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"labelTextSize\":\"0.875rem\",\"fontFamily\":\"System Default\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"widgetName\":\"Text7Copy3Copy\",\"displayName\":\"Text\",\"iconSVG\":\"/static/media/icon.97c59b52.svg\",\"topRow\":4,\"bottomRow\":8,\"type\":\"TEXT_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"overflow\":\"NONE\",\"leftColumn\":1,\"dynamicBindingPathList\":[{\"key\":\"text\"}],\"truncateButtonColor\":\"#FFC13D\",\"text\":\"{{doNotDisturbCount.data.n}}\",\"key\":\"deco66gubs\",\"rightColumn\":37,\"textAlign\":\"LEFT\",\"widgetId\":\"kwtiaoo3ks\",\"isVisible\":true,\"fontStyle\":\"BOLD\",\"textColor\":\"#2E3D49\",\"version\":1,\"parentId\":\"0y0vy0g8hm\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"fontSize\":\"1.5rem\",\"borderRadius\":\"0px\",\"boxShadow\":\"none\",\"dynamicPropertyPathList\":[{\"key\":\"fontSize\"}],\"labelTextSize\":\"0.875rem\",\"fontFamily\":\"System Default\",\"minDynamicHeight\":4,\"maxDynamicHeight\":9000,\"dynamicHeight\":\"FIXED\"},{\"boxShadow\":\"none\",\"widgetName\":\"IconButton2Copy\",\"buttonColor\":\"#64508C\",\"displayName\":\"Icon Button\",\"iconSVG\":\"/static/media/icon.1a0c634a.svg\",\"topRow\":4,\"bottomRow\":8,\"type\":\"ICON_BUTTON_WIDGET\",\"hideCard\":false,\"animateLoading\":true,\"dynamicTriggerPathList\":[],\"leftColumn\":51,\"dynamicBindingPathList\":[],\"isDisabled\":false,\"key\":\"1vdnabqlfq\",\"rightColumn\":64,\"iconName\":\"blocked-person\",\"widgetId\":\"fubfdyc3va\",\"buttonStyle\":\"PRIMARY\",\"isVisible\":true,\"version\":1,\"parentId\":\"0y0vy0g8hm\",\"renderMode\":\"CANVAS\",\"isLoading\":false,\"borderRadius\":\"0.375rem\",\"buttonVariant\":\"PRIMARY\",\"labelTextSize\":\"0.875rem\"}],\"parentId\":\"0\"}],\"flexLayers\":[]}" +} +} diff --git a/deploy/docker/base.dockerfile b/deploy/docker/base.dockerfile index 86ff3555cf..072de958b6 100644 --- a/deploy/docker/base.dockerfile +++ b/deploy/docker/base.dockerfile @@ -36,7 +36,10 @@ RUN curl --silent --show-error --location https://www.mongodb.org/static/pgp/ser RUN set -o xtrace \ && mkdir -p /opt/java \ # Assets from https://github.com/adoptium/temurin17-binaries/releases - && version="$(curl --write-out '%{redirect_url}' 'https://github.com/adoptium/temurin17-binaries/releases/latest' | sed 's,.*jdk-,,')" \ + # TODO: The release jdk-17.0.9+9.1 doesn't include Linux binaries, so this fails. + # Temporarily using hardcoded version in URL until we figure out a more elaborate/smarter solution. + #&& version="$(curl --write-out '%{redirect_url}' 'https://github.com/adoptium/temurin17-binaries/releases/latest' | sed 's,.*jdk-,,')" \ + && version="17.0.9+9" \ && curl --location --output /tmp/java.tar.gz "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-$version/OpenJDK17U-jdk_$(uname -m | sed s/x86_64/x64/)_linux_hotspot_$(echo $version | tr + _).tar.gz" \ && tar -xzf /tmp/java.tar.gz -C /opt/java --strip-components 1 diff --git a/deploy/docker/fs/opt/appsmith/entrypoint.sh b/deploy/docker/fs/opt/appsmith/entrypoint.sh index c76c665d70..5ee6981f7a 100644 --- a/deploy/docker/fs/opt/appsmith/entrypoint.sh +++ b/deploy/docker/fs/opt/appsmith/entrypoint.sh @@ -2,6 +2,8 @@ set -e +echo "Running as: $(id)" + stacks_path=/appsmith-stacks export SUPERVISORD_CONF_TARGET="$TMP/supervisor-conf.d/" # export for use in supervisord.conf @@ -68,7 +70,7 @@ init_env_file() { TEMPLATES_PATH="/opt/appsmith/templates" # Build an env file with current env variables. We single-quote the values, as well as escaping any single-quote characters. - printenv | grep -E '^APPSMITH_|^MONGO_' | sed "s/'/'\\\''/g; s/=/='/; s/$/'/" > "$TEMPLATES_PATH/pre-define.env" + printenv | grep -E '^APPSMITH_|^MONGO_' | sed "s/'/'\\\''/g; s/=/='/; s/$/'/" > "$TMP/pre-define.env" echo "Initialize .env file" if ! [[ -e "$ENV_PATH" ]]; then @@ -99,7 +101,7 @@ init_env_file() { echo "Load environment configuration" set -o allexport . "$ENV_PATH" - . "$TEMPLATES_PATH/pre-define.env" + . "$TMP/pre-define.env" set +o allexport } @@ -316,7 +318,8 @@ setup-custom-ca-certificates() ( # Add the custom CA certificates to the store. find "$stacks_ca_certs_path" -maxdepth 1 -type f -name '*.crt' \ - -exec keytool -import -noprompt -keystore "$store" -file '{}' -storepass changeit ';' + -print \ + -exec keytool -import -alias '{}' -noprompt -keystore "$store" -file '{}' -storepass changeit ';' { echo "-Djavax.net.ssl.trustStore=$store" @@ -345,7 +348,9 @@ configure_supervisord() { mkdir -p "$stacks_path/data/redis" fi if ! [[ -e "/appsmith-stacks/ssl/fullchain.pem" ]] || ! [[ -e "/appsmith-stacks/ssl/privkey.pem" ]]; then - cp "$supervisord_conf_source/cron.conf" "$SUPERVISORD_CONF_TARGET" + if [[ -n "${APPSMITH_CUSTOM_DOMAIN-}" ]]; then + cp "$supervisord_conf_source/cron.conf" "$SUPERVISORD_CONF_TARGET" + fi fi if [[ $runEmbeddedPostgres -eq 1 ]]; then cp "$supervisord_conf_source/postgres.conf" "$SUPERVISORD_CONF_TARGET" diff --git a/deploy/docker/fs/opt/appsmith/renew-certificate.sh b/deploy/docker/fs/opt/appsmith/renew-certificate.sh index 2c8cc25abd..3a4981accb 100644 --- a/deploy/docker/fs/opt/appsmith/renew-certificate.sh +++ b/deploy/docker/fs/opt/appsmith/renew-certificate.sh @@ -3,7 +3,7 @@ set -e ENV_PATH="/appsmith-stacks/configuration/docker.env" -PRE_DEFINED_ENV_PATH="/opt/appsmith/templates/pre-define.env" +PRE_DEFINED_ENV_PATH="$TMP/pre-define.env" if [[ -f /appsmith-stacks/configuration/docker.env ]]; then echo 'Load environment configuration' set -o allexport diff --git a/deploy/docker/fs/opt/appsmith/run-with-env.sh b/deploy/docker/fs/opt/appsmith/run-with-env.sh index 7a892cddc1..93a7bf65fc 100755 --- a/deploy/docker/fs/opt/appsmith/run-with-env.sh +++ b/deploy/docker/fs/opt/appsmith/run-with-env.sh @@ -1,7 +1,7 @@ #!/bin/bash ENV_PATH="/appsmith-stacks/configuration/docker.env" -PRE_DEFINED_ENV_PATH="/opt/appsmith/templates/pre-define.env" +PRE_DEFINED_ENV_PATH="$TMP/pre-define.env" echo 'Load environment configuration' set -o allexport . "$ENV_PATH" diff --git a/deploy/docker/fs/opt/appsmith/templates/supervisord/cron.conf b/deploy/docker/fs/opt/appsmith/templates/supervisord/cron.conf index 16b24e79a3..17c5426d02 100644 --- a/deploy/docker/fs/opt/appsmith/templates/supervisord/cron.conf +++ b/deploy/docker/fs/opt/appsmith/templates/supervisord/cron.conf @@ -1,6 +1,5 @@ [program:cron] command=/usr/sbin/cron -f -user=root priority=35 autostart=true autorestart=true @@ -13,4 +12,4 @@ stderr_logfile_maxbytes=10MB stdout_logfile_backups=5 stderr_logfile_backups=5 stdout_events_enabled=true -stderr_events_enabled=true \ No newline at end of file +stderr_events_enabled=true diff --git a/scripts/deploy_preview.sh b/scripts/deploy_preview.sh index d77fa9451d..7c40002729 100755 --- a/scripts/deploy_preview.sh +++ b/scripts/deploy_preview.sh @@ -102,4 +102,5 @@ helm upgrade -i $CHARTNAME appsmith-ee/$HELMCHART -n $NAMESPACE --create-namespa --set applicationConfig.APPSMITH_SENTRY_ENVIRONMENT="$NAMESPACE" \ --set applicationConfig.APPSMITH_MONGODB_URI="mongodb+srv://$DB_USERNAME:$DB_PASSWORD@$DB_URL/$DBNAME?retryWrites=true&minPoolSize=1&maxPoolSize=10&maxIdleTimeMS=900000&authSource=admin" \ --set applicationConfig.APPSMITH_DISABLE_EMBEDDED_KEYCLOAK=\"1\" \ + --set applicationConfig.APPSMITH_CUSTOMER_PORTAL_URL="https://release-customer.appsmith.com" \ --version $HELMCHART_VERSION