2023-05-23 20:02:53 +00:00
|
|
|
import {
|
|
|
|
|
all,
|
|
|
|
|
call,
|
2024-10-09 04:39:13 +00:00
|
|
|
delay,
|
2023-05-23 20:02:53 +00:00
|
|
|
put,
|
|
|
|
|
select,
|
|
|
|
|
take,
|
|
|
|
|
takeLatest,
|
|
|
|
|
} from "redux-saga/effects";
|
2021-08-27 09:25:28 +00:00
|
|
|
import {
|
2023-05-23 12:18:46 +00:00
|
|
|
clearActionResponse,
|
2024-09-05 05:36:43 +00:00
|
|
|
executePageLoadActions,
|
2021-08-27 09:25:28 +00:00
|
|
|
executePluginActionError,
|
|
|
|
|
executePluginActionRequest,
|
|
|
|
|
executePluginActionSuccess,
|
|
|
|
|
runAction,
|
|
|
|
|
updateAction,
|
2023-09-27 22:03:38 +00:00
|
|
|
updateActionData,
|
2021-08-27 09:25:28 +00:00
|
|
|
} from "actions/pluginActionActions";
|
2025-01-03 12:35:09 +00:00
|
|
|
import { handleExecuteJSFunctionSaga } from "sagas/JSPaneSagas";
|
2023-04-14 09:10:03 +00:00
|
|
|
|
2024-08-22 04:19:30 +00:00
|
|
|
import type { ApplicationPayload } from "entities/Application";
|
2025-01-10 04:51:54 +00:00
|
|
|
import type { ReduxAction } from "actions/ReduxActionTypes";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import {
|
2021-08-27 09:25:28 +00:00
|
|
|
ReduxActionErrorTypes,
|
|
|
|
|
ReduxActionTypes,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/constants/ReduxActionConstants";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import type {
|
2021-08-27 09:25:28 +00:00
|
|
|
ActionExecutionResponse,
|
|
|
|
|
ActionResponse,
|
|
|
|
|
ExecuteActionRequest,
|
|
|
|
|
PaginationField,
|
|
|
|
|
} from "api/ActionAPI";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import ActionAPI from "api/ActionAPI";
|
2021-08-27 09:25:28 +00:00
|
|
|
import {
|
|
|
|
|
getAction,
|
2024-02-29 06:23:57 +00:00
|
|
|
getCurrentActions,
|
2021-08-27 09:25:28 +00:00
|
|
|
getCurrentPageNameByActionId,
|
2024-02-29 06:23:57 +00:00
|
|
|
getDatasource,
|
|
|
|
|
getJSCollectionFromAllEntities,
|
2022-04-08 16:32:34 +00:00
|
|
|
getPlugin,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/selectors/entitiesSelector";
|
2021-08-27 09:25:28 +00:00
|
|
|
import {
|
|
|
|
|
getAppMode,
|
|
|
|
|
getCurrentApplication,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/selectors/applicationSelectors";
|
2023-04-27 03:33:32 +00:00
|
|
|
import {
|
|
|
|
|
find,
|
|
|
|
|
flatten,
|
2024-02-29 06:23:57 +00:00
|
|
|
get,
|
|
|
|
|
isArray,
|
2023-04-27 03:33:32 +00:00
|
|
|
isArrayBuffer,
|
|
|
|
|
isEmpty,
|
2024-02-29 06:23:57 +00:00
|
|
|
isNil,
|
|
|
|
|
isString,
|
|
|
|
|
set,
|
2023-04-27 03:33:32 +00:00
|
|
|
unset,
|
2024-12-10 14:43:40 +00:00
|
|
|
zipObject,
|
2023-04-27 03:33:32 +00:00
|
|
|
} from "lodash";
|
2021-08-27 09:25:28 +00:00
|
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
2024-08-06 14:52:22 +00:00
|
|
|
import { ENTITY_TYPE, PLATFORM_ERROR } from "ee/entities/AppsmithConsole/utils";
|
2023-04-03 04:26:18 +00:00
|
|
|
import {
|
|
|
|
|
extractClientDefinedErrorMetadata,
|
|
|
|
|
validateResponse,
|
|
|
|
|
} from "sagas/ErrorSagas";
|
2024-08-06 14:52:22 +00:00
|
|
|
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import type { Action } from "entities/Action";
|
2024-10-08 10:42:27 +00:00
|
|
|
import { ActionExecutionContext } from "entities/Action";
|
2021-08-27 09:25:28 +00:00
|
|
|
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
|
|
|
|
import {
|
2024-02-29 06:23:57 +00:00
|
|
|
ACTION_EXECUTION_CANCELLED,
|
|
|
|
|
ACTION_EXECUTION_FAILED,
|
2021-08-27 09:25:28 +00:00
|
|
|
createMessage,
|
|
|
|
|
ERROR_ACTION_EXECUTE_FAIL,
|
|
|
|
|
ERROR_FAIL_ON_PAGE_LOAD_ACTIONS,
|
|
|
|
|
ERROR_PLUGIN_ACTION_EXECUTE,
|
2023-07-21 05:53:17 +00:00
|
|
|
SWITCH_ENVIRONMENT_SUCCESS,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/constants/messages";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import type {
|
2022-09-24 10:01:52 +00:00
|
|
|
LayoutOnLoadActionErrors,
|
2021-08-27 09:25:28 +00:00
|
|
|
PageAction,
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
} from "constants/AppsmithActionConstants/ActionConstants";
|
2025-02-24 09:18:34 +00:00
|
|
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
2021-08-27 09:25:28 +00:00
|
|
|
import {
|
2023-10-17 16:11:16 +00:00
|
|
|
getCurrentApplicationId,
|
2024-07-31 02:54:51 +00:00
|
|
|
getCurrentBasePageId,
|
2021-08-27 09:25:28 +00:00
|
|
|
getCurrentPageId,
|
2021-12-24 13:59:02 +00:00
|
|
|
getIsSavingEntity,
|
2021-08-27 09:25:28 +00:00
|
|
|
getLayoutOnLoadActions,
|
2022-09-24 10:01:52 +00:00
|
|
|
getLayoutOnLoadIssues,
|
2025-07-02 13:10:44 +00:00
|
|
|
getLayoutOnUnloadActions,
|
2021-08-27 09:25:28 +00:00
|
|
|
} from "selectors/editorSelectors";
|
2024-10-08 10:42:27 +00:00
|
|
|
import log from "loglevel";
|
2023-07-16 18:49:41 +00:00
|
|
|
import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse";
|
2025-05-01 10:23:37 +00:00
|
|
|
import type { DefaultRootState } from "react-redux";
|
2024-08-06 14:52:22 +00:00
|
|
|
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "ee/constants/ApiConstants";
|
2024-12-13 04:14:19 +00:00
|
|
|
import { evaluateActionBindings } from "sagas/EvaluationsSaga";
|
2022-03-02 16:01:50 +00:00
|
|
|
import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils";
|
2021-08-27 09:25:28 +00:00
|
|
|
import { getType, Types } from "utils/TypeHelpers";
|
|
|
|
|
import { matchPath } from "react-router";
|
|
|
|
|
import {
|
2022-03-25 10:43:26 +00:00
|
|
|
API_EDITOR_BASE_PATH,
|
|
|
|
|
API_EDITOR_ID_PATH,
|
|
|
|
|
API_EDITOR_PATH_WITH_SELECTED_PAGE_ID,
|
|
|
|
|
INTEGRATION_EDITOR_PATH,
|
2024-02-29 06:23:57 +00:00
|
|
|
matchQueryBuilderPath,
|
2022-03-25 10:43:26 +00:00
|
|
|
QUERIES_EDITOR_BASE_PATH,
|
|
|
|
|
QUERIES_EDITOR_ID_PATH,
|
2021-08-27 09:25:28 +00:00
|
|
|
} from "constants/routes";
|
2022-03-25 10:43:26 +00:00
|
|
|
import { SAAS_EDITOR_API_ID_PATH } from "pages/Editor/SaaSEditor/constants";
|
2021-08-27 09:25:28 +00:00
|
|
|
import { APP_MODE } from "entities/App";
|
2025-06-17 11:50:23 +00:00
|
|
|
import { FileDataTypes } from "WidgetProvider/types";
|
2021-08-27 09:25:28 +00:00
|
|
|
import { hideDebuggerErrors } from "actions/debuggerActions";
|
2021-09-23 07:21:57 +00:00
|
|
|
import {
|
2021-12-23 14:17:20 +00:00
|
|
|
ActionValidationError,
|
|
|
|
|
getErrorAsString,
|
2021-09-23 07:21:57 +00:00
|
|
|
PluginActionExecutionError,
|
2021-12-23 14:17:20 +00:00
|
|
|
PluginTriggerFailureError,
|
2021-09-23 07:21:57 +00:00
|
|
|
UserCancelledActionExecutionError,
|
|
|
|
|
} from "sagas/ActionExecution/errorUtils";
|
2022-06-21 13:57:34 +00:00
|
|
|
import { shouldBeDefined, trimQueryString } from "utils/helpers";
|
2022-02-18 06:58:36 +00:00
|
|
|
import { requestModalConfirmationSaga } from "sagas/UtilSagas";
|
2022-02-28 17:37:21 +00:00
|
|
|
import { ModalType } from "reducers/uiReducers/modalActionReducer";
|
2024-08-06 14:52:22 +00:00
|
|
|
import { matchBasePath } from "ee/pages/Editor/Explorer/helpers";
|
2022-12-22 06:34:28 +00:00
|
|
|
import {
|
|
|
|
|
findDatatype,
|
2024-02-29 06:23:57 +00:00
|
|
|
isTrueObject,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/workers/Evaluation/evaluationUtils";
|
2025-01-27 16:51:51 +00:00
|
|
|
import { type Plugin, PluginType } from "entities/Plugin";
|
|
|
|
|
import { getIsAnvilEnabledInCurrentApplication } from "../../layoutSystems/anvil/integrations/selectors";
|
2022-06-06 07:57:19 +00:00
|
|
|
import { setDefaultActionDisplayFormat } from "./PluginActionSagaUtils";
|
2022-09-24 10:01:52 +00:00
|
|
|
import { checkAndLogErrorsIfCyclicDependency } from "sagas/helper";
|
2024-08-09 14:20:29 +00:00
|
|
|
import { toast } from "@appsmith/ads";
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
import type { TRunDescription } from "workers/Evaluation/fns/actionFns";
|
2024-10-08 10:42:27 +00:00
|
|
|
import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/constants";
|
2023-04-27 03:33:32 +00:00
|
|
|
import { FILE_SIZE_LIMIT_FOR_BLOBS } from "constants/WidgetConstants";
|
2024-08-06 14:52:22 +00:00
|
|
|
import type { ActionData } from "ee/reducers/entityReducers/actionsReducer";
|
2023-05-23 12:18:46 +00:00
|
|
|
import { handleStoreOperations } from "./StoreActionSaga";
|
2024-07-31 02:54:51 +00:00
|
|
|
import { fetchPageAction } from "actions/pageActions";
|
2023-05-22 12:11:02 +00:00
|
|
|
import type { Datasource } from "entities/Datasource";
|
2023-07-21 05:53:17 +00:00
|
|
|
import { softRefreshDatasourceStructure } from "actions/datasourceActions";
|
2023-09-11 07:09:41 +00:00
|
|
|
import {
|
|
|
|
|
getCurrentEnvironmentDetails,
|
|
|
|
|
getCurrentEnvironmentName,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/selectors/environmentSelectors";
|
|
|
|
|
import { getIsActionCreatedInApp } from "ee/utils/getIsActionCreatedInApp";
|
2023-11-24 07:39:02 +00:00
|
|
|
import {
|
|
|
|
|
endSpan,
|
|
|
|
|
setAttributesToSpan,
|
|
|
|
|
startRootSpan,
|
2024-12-26 05:07:41 +00:00
|
|
|
} from "instrumentation/generateTraces";
|
2023-12-06 19:18:52 +00:00
|
|
|
import {
|
2023-12-19 10:58:02 +00:00
|
|
|
getActionExecutionAnalytics,
|
2024-01-24 16:49:55 +00:00
|
|
|
getActionProperties,
|
2023-12-06 19:18:52 +00:00
|
|
|
getJSActionPathNameToDisplay,
|
|
|
|
|
getPluginActionNameToDisplay,
|
2024-08-06 14:52:22 +00:00
|
|
|
} from "ee/utils/actionExecutionUtils";
|
2023-12-14 14:44:30 +00:00
|
|
|
import type { JSAction, JSCollection } from "entities/JSCollection";
|
2024-01-24 16:49:55 +00:00
|
|
|
import { getAllowedActionAnalyticsKeys } from "constants/AppsmithActionConstants/formConfig/ActionAnalyticsConfig";
|
2024-10-08 10:42:27 +00:00
|
|
|
import {
|
|
|
|
|
changeQuery,
|
|
|
|
|
isActionDirty,
|
|
|
|
|
isActionSaving,
|
|
|
|
|
setPluginActionEditorDebuggerState,
|
|
|
|
|
} from "PluginActionEditor/store";
|
2024-12-10 14:43:40 +00:00
|
|
|
import { objectKeys } from "@appsmith/utils";
|
2024-12-26 05:07:41 +00:00
|
|
|
import type { Span } from "instrumentation/types";
|
2025-01-07 11:30:42 +00:00
|
|
|
import {
|
|
|
|
|
selectGitConnectModalOpen,
|
|
|
|
|
selectGitOpsModalOpen,
|
|
|
|
|
} from "selectors/gitModSelectors";
|
2025-02-24 09:18:34 +00:00
|
|
|
import { createActionExecutionResponse } from "./PluginActionSagaUtils";
|
2025-04-30 05:44:26 +00:00
|
|
|
import { ActionRunBehaviour } from "PluginActionEditor/types/PluginActionTypes";
|
2025-04-29 05:02:36 +00:00
|
|
|
import { appsmithTelemetry } from "instrumentation";
|
2021-08-27 09:25:28 +00:00
|
|
|
|
2023-10-11 07:35:24 +00:00
|
|
|
interface FilePickerInstumentationObject {
|
2023-04-26 16:41:26 +00:00
|
|
|
numberOfFiles: number;
|
|
|
|
|
totalSize: number;
|
|
|
|
|
fileTypes: Array<string>;
|
|
|
|
|
fileSizes: Array<number>;
|
2023-10-11 07:35:24 +00:00
|
|
|
}
|
2023-04-26 16:41:26 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
export const getActionTimeout = (
|
2025-05-01 10:23:37 +00:00
|
|
|
state: DefaultRootState,
|
2021-08-27 09:25:28 +00:00
|
|
|
actionId: string,
|
|
|
|
|
): number | undefined => {
|
2022-09-24 10:01:52 +00:00
|
|
|
const action = find(state.entities.actions, (a) => a.config.id === actionId);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (action) {
|
2022-09-24 10:01:52 +00:00
|
|
|
const timeout = get(
|
2021-08-27 09:25:28 +00:00
|
|
|
action,
|
|
|
|
|
"config.actionConfiguration.timeoutInMillisecond",
|
|
|
|
|
DEFAULT_EXECUTE_ACTION_TIMEOUT_MS,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (timeout) {
|
|
|
|
|
// Extra timeout padding to account for network calls
|
|
|
|
|
return timeout + 5000;
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
return undefined;
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
return undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isErrorResponse = (response: ActionExecutionResponse) => {
|
|
|
|
|
return !response.data.isExecutionSuccess;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param blobUrl string A blob url with type added a query param
|
|
|
|
|
* @returns promise that resolves to file content
|
|
|
|
|
*/
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2021-08-27 09:25:28 +00:00
|
|
|
function* readBlob(blobUrl: string): any {
|
|
|
|
|
const [url, fileType] = parseBlobUrl(blobUrl);
|
2023-10-09 13:54:06 +00:00
|
|
|
const file = yield fetch(url).then(async (r) => r.blob());
|
2021-08-27 09:25:28 +00:00
|
|
|
|
2021-09-03 11:02:28 +00:00
|
|
|
return yield new Promise((resolve) => {
|
2021-08-27 09:25:28 +00:00
|
|
|
const reader = new FileReader();
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (fileType === FileDataTypes.Base64) {
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
} else if (fileType === FileDataTypes.Binary) {
|
2023-04-27 03:33:32 +00:00
|
|
|
if (file.size < FILE_SIZE_LIMIT_FOR_BLOBS) {
|
|
|
|
|
//check size of the file, if less than 5mb, go with binary string method
|
|
|
|
|
// TODO: this method is deprecated, use readAsText instead
|
|
|
|
|
reader.readAsBinaryString(file);
|
|
|
|
|
} else {
|
|
|
|
|
// For files greater than 5 mb, use array buffer method
|
|
|
|
|
// This is to remove the bloat from the file which is added
|
|
|
|
|
// when using read as binary string method
|
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
} else {
|
|
|
|
|
reader.readAsText(file);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
reader.onloadend = () => {
|
|
|
|
|
resolve(reader.result);
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
/**
|
|
|
|
|
* This function resolves :
|
|
|
|
|
* - individual objects containing blob urls
|
|
|
|
|
* - blob urls directly
|
|
|
|
|
* - else returns the value unchanged
|
2022-08-18 19:31:47 +00:00
|
|
|
* - finds datatype of evaluated value
|
|
|
|
|
* - binds dataype to payload
|
2022-07-08 09:29:18 +00:00
|
|
|
*
|
|
|
|
|
* @param value
|
2024-12-10 14:43:40 +00:00
|
|
|
* @param executeActionRequest
|
|
|
|
|
* @param index
|
|
|
|
|
* @param isArray
|
|
|
|
|
* @param arrDatatype
|
2022-07-08 09:29:18 +00:00
|
|
|
*/
|
|
|
|
|
|
2022-08-18 19:31:47 +00:00
|
|
|
function* resolvingBlobUrls(
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2022-08-18 19:31:47 +00:00
|
|
|
value: any,
|
|
|
|
|
executeActionRequest: ExecuteActionRequest,
|
|
|
|
|
index: number,
|
|
|
|
|
isArray?: boolean,
|
|
|
|
|
arrDatatype?: string[],
|
|
|
|
|
) {
|
|
|
|
|
//Get datatypes of evaluated value.
|
|
|
|
|
const dataType: string = findDatatype(value);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-18 19:31:47 +00:00
|
|
|
//If array elements then dont push datatypes to payload.
|
|
|
|
|
isArray
|
|
|
|
|
? arrDatatype?.push(dataType)
|
2023-04-27 03:33:32 +00:00
|
|
|
: (executeActionRequest.paramProperties[`k${index}`] = {
|
|
|
|
|
datatype: dataType,
|
|
|
|
|
});
|
2022-08-18 19:31:47 +00:00
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
if (isTrueObject(value)) {
|
|
|
|
|
const blobUrlPaths: string[] = [];
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2024-12-10 14:43:40 +00:00
|
|
|
objectKeys(value).forEach((propertyName) => {
|
2022-07-08 09:29:18 +00:00
|
|
|
if (isBlobUrl(value[propertyName])) {
|
|
|
|
|
blobUrlPaths.push(propertyName);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const blobUrlPath of blobUrlPaths) {
|
|
|
|
|
const blobUrl = value[blobUrlPath] as string;
|
|
|
|
|
const resolvedBlobValue: unknown = yield call(readBlob, blobUrl);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
set(value, blobUrlPath, resolvedBlobValue);
|
2023-04-27 03:33:32 +00:00
|
|
|
|
|
|
|
|
// We need to store the url path map to be able to update the blob data
|
|
|
|
|
// and send the info to server
|
|
|
|
|
|
|
|
|
|
// Here we fetch the blobUrlPathMap from the action payload and update it
|
|
|
|
|
const blobUrlPathMap = get(value, "blobUrlPaths", {}) as Record<
|
|
|
|
|
string,
|
|
|
|
|
string
|
|
|
|
|
>;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
set(blobUrlPathMap, blobUrlPath, blobUrl);
|
|
|
|
|
set(value, "blobUrlPaths", blobUrlPathMap);
|
2022-07-08 09:29:18 +00:00
|
|
|
}
|
|
|
|
|
} else if (isBlobUrl(value)) {
|
|
|
|
|
// @ts-expect-error: Values can take many types
|
|
|
|
|
value = yield call(readBlob, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
// Function that updates the blob data in the action payload for large file
|
|
|
|
|
// uploads
|
|
|
|
|
function updateBlobDataFromUrls(
|
|
|
|
|
blobUrlPaths: Record<string, string>,
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-04-27 03:33:32 +00:00
|
|
|
newVal: any,
|
|
|
|
|
blobMap: string[],
|
|
|
|
|
blobDataMap: Record<string, Blob>,
|
|
|
|
|
) {
|
|
|
|
|
Object.entries(blobUrlPaths as Record<string, string>).forEach(
|
|
|
|
|
// blobUrl: string eg: blob:1234-1234-1234?type=binary
|
|
|
|
|
([path, blobUrl]) => {
|
|
|
|
|
if (isArrayBuffer(newVal[path])) {
|
|
|
|
|
// remove the ?type=binary from the blob url if present
|
|
|
|
|
const sanitisedBlobURL = blobUrl.split("?")[0];
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
blobMap.push(sanitisedBlobURL);
|
|
|
|
|
set(blobDataMap, sanitisedBlobURL, new Blob([newVal[path]]));
|
|
|
|
|
set(newVal, path, sanitisedBlobURL);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
/**
|
|
|
|
|
* Api1
|
|
|
|
|
* URL: https://example.com/{{Text1.text}}
|
|
|
|
|
* Body: {
|
|
|
|
|
* "name": "{{this.params.name}}",
|
|
|
|
|
* "age": {{this.params.age}},
|
|
|
|
|
* "gender": {{Dropdown1.selectedOptionValue}}
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* If you call
|
|
|
|
|
* Api1.run(undefined, undefined, { name: "Hetu", age: Input1.text });
|
|
|
|
|
*
|
|
|
|
|
* executionParams is { name: "Hetu", age: Input1.text }
|
|
|
|
|
* bindings is [
|
|
|
|
|
* "Text1.text",
|
|
|
|
|
* "Dropdown1.selectedOptionValue",
|
|
|
|
|
* "this.params.name",
|
|
|
|
|
* "this.params.age",
|
|
|
|
|
* ]
|
|
|
|
|
*
|
|
|
|
|
* Return will be [
|
|
|
|
|
* { key: "Text1.text", value: "updateUser" },
|
|
|
|
|
* { key: "Dropdown1.selectedOptionValue", value: "M" },
|
|
|
|
|
* { key: "this.params.name", value: "Hetu" },
|
|
|
|
|
* { key: "this.params.age", value: 26 },
|
|
|
|
|
* ]
|
|
|
|
|
* @param bindings
|
2024-12-10 14:43:40 +00:00
|
|
|
* @param formData
|
|
|
|
|
* @param executeActionRequest
|
|
|
|
|
* @param filePickerInstrumentation
|
2021-08-27 09:25:28 +00:00
|
|
|
* @param executionParams
|
|
|
|
|
*/
|
|
|
|
|
function* evaluateActionParams(
|
|
|
|
|
bindings: string[] | undefined,
|
2022-03-02 16:01:50 +00:00
|
|
|
formData: FormData,
|
2022-08-18 19:31:47 +00:00
|
|
|
executeActionRequest: ExecuteActionRequest,
|
2023-04-26 16:41:26 +00:00
|
|
|
filePickerInstrumentation: FilePickerInstumentationObject,
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2021-08-27 09:25:28 +00:00
|
|
|
executionParams?: Record<string, any> | string,
|
|
|
|
|
) {
|
2022-09-24 10:01:52 +00:00
|
|
|
if (isNil(bindings) || bindings.length === 0) {
|
2022-08-18 19:31:47 +00:00
|
|
|
formData.append("executeActionDTO", JSON.stringify(executeActionRequest));
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-18 19:31:47 +00:00
|
|
|
return [];
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
// Evaluated all bindings of the actions. Pass executionParams if any
|
2022-06-21 13:57:34 +00:00
|
|
|
// @ts-expect-error: Values can take many types
|
|
|
|
|
const values = yield call(evaluateActionBindings, bindings, executionParams);
|
2021-08-27 09:25:28 +00:00
|
|
|
|
2022-08-18 19:31:47 +00:00
|
|
|
const bindingsMap: Record<string, string> = {};
|
|
|
|
|
const bindingBlob = [];
|
2024-12-10 14:43:40 +00:00
|
|
|
const evaluatedParams = zipObject(bindings, values);
|
2022-08-18 19:31:47 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
// Maintain a blob data map to resolve blob urls of large files as array buffer
|
|
|
|
|
const blobDataMap: Record<string, Blob> = {};
|
|
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
// if json bindings have filepicker reference, we need to init the instrumentation object
|
|
|
|
|
// which we will send post execution
|
2024-12-10 14:43:40 +00:00
|
|
|
const recordFilePickerInstrumentation = bindings.some((binding) =>
|
2023-04-26 16:41:26 +00:00
|
|
|
binding.includes(".files"),
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-02 16:01:50 +00:00
|
|
|
// Add keys values to formData for the multipart submission
|
2021-08-27 09:25:28 +00:00
|
|
|
for (let i = 0; i < bindings.length; i++) {
|
|
|
|
|
const key = bindings[i];
|
2023-08-04 11:44:24 +00:00
|
|
|
let value = isArray(values) && values[i];
|
2022-03-16 14:29:52 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
let useBlobMaps = false;
|
|
|
|
|
// Maintain a blob map to resolve blob urls of large files
|
|
|
|
|
const blobMap: Array<string> = [];
|
|
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
if (isArray(value)) {
|
|
|
|
|
const tempArr = [];
|
2023-04-27 03:33:32 +00:00
|
|
|
const arrDatatype: Array<string> = [];
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
// array of objects containing blob urls that is loops and individual object is checked for resolution of blob urls.
|
2024-10-09 04:39:13 +00:00
|
|
|
|
|
|
|
|
const BATCH_CHUNK_SIZE = 100;
|
|
|
|
|
|
|
|
|
|
for (let j = 0; j < value.length; j++) {
|
|
|
|
|
const val = value[j];
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-04-27 03:33:32 +00:00
|
|
|
const newVal: Record<string, any> = yield call(
|
2022-08-18 19:31:47 +00:00
|
|
|
resolvingBlobUrls,
|
|
|
|
|
val,
|
|
|
|
|
executeActionRequest,
|
|
|
|
|
i,
|
|
|
|
|
true,
|
|
|
|
|
arrDatatype,
|
|
|
|
|
);
|
2023-04-27 03:33:32 +00:00
|
|
|
|
|
|
|
|
if (newVal.hasOwnProperty("blobUrlPaths")) {
|
|
|
|
|
updateBlobDataFromUrls(
|
|
|
|
|
newVal.blobUrlPaths,
|
|
|
|
|
newVal,
|
|
|
|
|
blobMap,
|
|
|
|
|
blobDataMap,
|
|
|
|
|
);
|
|
|
|
|
useBlobMaps = true;
|
|
|
|
|
unset(newVal, "blobUrlPaths");
|
2024-12-10 14:43:40 +00:00
|
|
|
evaluatedParams[key] = "blob";
|
2023-04-27 03:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:29:18 +00:00
|
|
|
tempArr.push(newVal);
|
2023-04-26 16:41:26 +00:00
|
|
|
|
|
|
|
|
if (key.includes(".files") && recordFilePickerInstrumentation) {
|
|
|
|
|
filePickerInstrumentation["numberOfFiles"] += 1;
|
|
|
|
|
const { size, type } = newVal;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
filePickerInstrumentation["totalSize"] += size;
|
|
|
|
|
filePickerInstrumentation["fileSizes"].push(size);
|
|
|
|
|
filePickerInstrumentation["fileTypes"].push(type);
|
2024-12-10 14:43:40 +00:00
|
|
|
evaluatedParams[key] = "file";
|
2023-04-26 16:41:26 +00:00
|
|
|
}
|
2024-10-09 04:39:13 +00:00
|
|
|
|
|
|
|
|
if ((j + 1) % BATCH_CHUNK_SIZE === 0) {
|
|
|
|
|
// Yield control back to the event loop and empty the stack trace
|
|
|
|
|
yield delay(0);
|
|
|
|
|
}
|
2022-03-16 14:29:52 +00:00
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-10-18 05:32:37 +00:00
|
|
|
//Adding array datatype along with the datatype of first element of the array
|
|
|
|
|
executeActionRequest.paramProperties[`k${i}`] = {
|
2023-04-27 03:33:32 +00:00
|
|
|
datatype: { array: [arrDatatype[0]] },
|
2022-10-18 05:32:37 +00:00
|
|
|
};
|
2022-07-08 09:29:18 +00:00
|
|
|
value = tempArr;
|
|
|
|
|
} else {
|
|
|
|
|
// @ts-expect-error: Values can take many types
|
2022-08-18 19:31:47 +00:00
|
|
|
value = yield call(resolvingBlobUrls, value, executeActionRequest, i);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
if (key.includes(".files") && recordFilePickerInstrumentation) {
|
|
|
|
|
filePickerInstrumentation["numberOfFiles"] += 1;
|
|
|
|
|
filePickerInstrumentation["totalSize"] += value.size;
|
|
|
|
|
filePickerInstrumentation["fileSizes"].push(value.size);
|
|
|
|
|
filePickerInstrumentation["fileTypes"].push(value.type);
|
2024-12-10 14:43:40 +00:00
|
|
|
evaluatedParams[key] = "file";
|
2023-04-26 16:41:26 +00:00
|
|
|
}
|
2022-03-16 14:29:52 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-30 13:10:05 +00:00
|
|
|
if (typeof value === "object") {
|
2023-04-27 03:33:32 +00:00
|
|
|
// This is used in cases of large files, we store the bloburls with the path they were set in
|
|
|
|
|
// This helps in creating a unique map of blob urls to blob data when passing to the server
|
|
|
|
|
if (!!value && value.hasOwnProperty("blobUrlPaths")) {
|
|
|
|
|
updateBlobDataFromUrls(value.blobUrlPaths, value, blobMap, blobDataMap);
|
|
|
|
|
unset(value, "blobUrlPaths");
|
2024-12-10 14:43:40 +00:00
|
|
|
evaluatedParams[key] = "blob";
|
2023-04-27 03:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-11 14:53:36 +00:00
|
|
|
// Handle null values separately to avoid stringifying them
|
|
|
|
|
if (value === null) {
|
|
|
|
|
value = null;
|
|
|
|
|
evaluatedParams[key] = null;
|
|
|
|
|
} else {
|
|
|
|
|
value = JSON.stringify(value);
|
|
|
|
|
evaluatedParams[key] = value;
|
|
|
|
|
}
|
2022-03-30 13:10:05 +00:00
|
|
|
}
|
2022-07-08 09:29:18 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
// If there are no blob urls in the value, we can directly add it to the formData
|
|
|
|
|
// If there are blob urls, we need to add them to the blobDataMap
|
|
|
|
|
if (!useBlobMaps) {
|
2025-06-11 14:53:36 +00:00
|
|
|
// Handle null values separately to avoid creating a Blob with "null" string
|
|
|
|
|
if (value === null) {
|
|
|
|
|
value = null;
|
|
|
|
|
} else {
|
|
|
|
|
value = new Blob([value], { type: "text/plain" });
|
|
|
|
|
}
|
2023-04-27 03:33:32 +00:00
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-18 19:31:47 +00:00
|
|
|
bindingsMap[key] = `k${i}`;
|
|
|
|
|
bindingBlob.push({ name: `k${i}`, value: value });
|
2023-04-27 03:33:32 +00:00
|
|
|
|
|
|
|
|
// We need to add the blob map to the param properties
|
|
|
|
|
// This will allow the server to handle the scenaio of large files upload using blob data
|
|
|
|
|
const paramProperties = executeActionRequest.paramProperties[`k${i}`];
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-27 03:33:32 +00:00
|
|
|
if (!!paramProperties && typeof paramProperties === "object") {
|
|
|
|
|
paramProperties["blobIdentifiers"] = blobMap;
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
2022-08-18 19:31:47 +00:00
|
|
|
|
|
|
|
|
formData.append("executeActionDTO", JSON.stringify(executeActionRequest));
|
|
|
|
|
formData.append("parameterMap", JSON.stringify(bindingsMap));
|
|
|
|
|
bindingBlob?.forEach((item) => formData.append(item.name, item.value));
|
2023-04-27 03:33:32 +00:00
|
|
|
|
|
|
|
|
// Append blob data map to formData if not empty
|
|
|
|
|
if (!isEmpty(blobDataMap)) {
|
|
|
|
|
// blobDataMap is used to resolve blob urls of large files as array buffer
|
|
|
|
|
// we need to add each blob data to formData as a separate entry
|
|
|
|
|
Object.entries(blobDataMap).forEach(([path, blobData]) =>
|
|
|
|
|
formData.append(path, blobData),
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-12-10 14:43:40 +00:00
|
|
|
|
|
|
|
|
return evaluatedParams;
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function* executePluginActionTriggerSaga(
|
2023-02-11 18:33:20 +00:00
|
|
|
pluginAction: TRunDescription,
|
2021-08-27 09:25:28 +00:00
|
|
|
eventType: EventType,
|
|
|
|
|
) {
|
2023-11-24 07:39:02 +00:00
|
|
|
const span = startRootSpan("executePluginActionTriggerSaga");
|
2023-02-11 18:33:20 +00:00
|
|
|
const { payload: pluginPayload } = pluginAction;
|
|
|
|
|
const { actionId, onError, params } = pluginPayload;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-12-23 14:17:20 +00:00
|
|
|
if (getType(params) !== Types.OBJECT) {
|
|
|
|
|
throw new ActionValidationError(
|
2023-01-06 12:02:08 +00:00
|
|
|
"RUN_PLUGIN_ACTION",
|
2021-12-23 14:17:20 +00:00
|
|
|
"params",
|
|
|
|
|
Types.OBJECT,
|
|
|
|
|
getType(params),
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-07-11 10:07:25 +00:00
|
|
|
|
|
|
|
|
setAttributesToSpan(span, {
|
|
|
|
|
actionId: actionId,
|
|
|
|
|
});
|
|
|
|
|
|
2022-06-21 13:57:34 +00:00
|
|
|
const action = shouldBeDefined<Action>(
|
|
|
|
|
yield select(getAction, actionId),
|
|
|
|
|
`Action not found for id - ${actionId}`,
|
|
|
|
|
);
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-05-22 12:11:02 +00:00
|
|
|
const datasourceId: string = (action?.datasource as any)?.id;
|
|
|
|
|
const plugin: Plugin = yield select(getPlugin, action?.pluginId);
|
2021-08-27 09:25:28 +00:00
|
|
|
const currentApp: ApplicationPayload = yield select(getCurrentApplication);
|
2023-09-11 07:09:41 +00:00
|
|
|
|
|
|
|
|
const currentEnvDetails: { id: string; name: string } = yield select(
|
|
|
|
|
getCurrentEnvironmentDetails,
|
|
|
|
|
);
|
2023-12-19 10:58:02 +00:00
|
|
|
|
2024-01-11 05:06:43 +00:00
|
|
|
const pluginActionNameToDisplay = getPluginActionNameToDisplay(action);
|
|
|
|
|
|
2023-12-19 10:58:02 +00:00
|
|
|
const actionExecutionAnalytics = getActionExecutionAnalytics(
|
|
|
|
|
action,
|
|
|
|
|
plugin,
|
|
|
|
|
params,
|
|
|
|
|
currentApp,
|
|
|
|
|
datasourceId,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-19 10:58:02 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION", actionExecutionAnalytics);
|
2021-08-27 09:25:28 +00:00
|
|
|
const pagination =
|
|
|
|
|
eventType === EventType.ON_NEXT_PAGE
|
|
|
|
|
? "NEXT"
|
|
|
|
|
: eventType === EventType.ON_PREV_PAGE
|
2024-04-22 09:17:28 +00:00
|
|
|
? "PREV"
|
|
|
|
|
: undefined;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-09-23 07:21:57 +00:00
|
|
|
const executePluginActionResponse: ExecutePluginActionResponse = yield call(
|
|
|
|
|
executePluginActionSaga,
|
2023-11-24 06:04:06 +00:00
|
|
|
action,
|
2021-09-23 07:21:57 +00:00
|
|
|
pagination,
|
|
|
|
|
params,
|
2023-11-24 07:39:02 +00:00
|
|
|
undefined,
|
|
|
|
|
span,
|
2021-09-23 07:21:57 +00:00
|
|
|
);
|
|
|
|
|
const { isError, payload } = executePluginActionResponse;
|
2021-08-27 09:25:28 +00:00
|
|
|
|
|
|
|
|
if (isError) {
|
2022-12-07 10:28:29 +00:00
|
|
|
AppsmithConsole.addErrors([
|
|
|
|
|
{
|
|
|
|
|
payload: {
|
|
|
|
|
id: actionId,
|
2023-02-18 12:55:46 +00:00
|
|
|
iconId: action.pluginId,
|
2022-12-07 10:28:29 +00:00
|
|
|
logType: LOG_TYPE.ACTION_EXECUTION_ERROR,
|
2024-12-10 14:43:40 +00:00
|
|
|
text: `Failed execution in ${payload.duration}(ms)`,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentName: currentEnvDetails.name,
|
2022-12-07 10:28:29 +00:00
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: pluginActionNameToDisplay,
|
2022-12-07 10:28:29 +00:00
|
|
|
id: actionId,
|
2024-01-11 05:06:43 +00:00
|
|
|
httpMethod: action?.actionConfiguration?.httpMethod,
|
2023-02-18 12:55:46 +00:00
|
|
|
pluginType: action.pluginType,
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
2024-12-10 14:43:40 +00:00
|
|
|
state: {
|
|
|
|
|
error: !isString(payload.body)
|
|
|
|
|
? JSON.stringify(payload.body)
|
|
|
|
|
: payload.body,
|
|
|
|
|
request: payload.request,
|
|
|
|
|
},
|
2023-02-18 12:55:46 +00:00
|
|
|
pluginErrorDetails: payload.pluginErrorDetails,
|
2021-08-27 09:25:28 +00:00
|
|
|
},
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
|
|
|
|
]);
|
2023-05-22 12:11:02 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION_FAILURE", {
|
2023-12-19 10:58:02 +00:00
|
|
|
...actionExecutionAnalytics,
|
2023-05-22 12:11:02 +00:00
|
|
|
...payload.pluginErrorDetails,
|
|
|
|
|
});
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-12-23 14:17:20 +00:00
|
|
|
if (onError) {
|
2022-07-22 08:27:37 +00:00
|
|
|
throw new PluginTriggerFailureError(
|
2024-02-07 11:06:01 +00:00
|
|
|
createMessage(ERROR_ACTION_EXECUTE_FAIL, pluginActionNameToDisplay),
|
2022-07-22 08:27:37 +00:00
|
|
|
[payload.body, params],
|
|
|
|
|
);
|
2021-12-23 14:17:20 +00:00
|
|
|
} else {
|
|
|
|
|
throw new PluginTriggerFailureError(
|
2024-02-07 11:06:01 +00:00
|
|
|
createMessage(ERROR_PLUGIN_ACTION_EXECUTE, pluginActionNameToDisplay),
|
2023-02-11 18:33:20 +00:00
|
|
|
[],
|
2021-12-23 14:17:20 +00:00
|
|
|
);
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
} else {
|
2023-12-19 10:58:02 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION_SUCCESS", actionExecutionAnalytics);
|
2021-08-27 09:25:28 +00:00
|
|
|
AppsmithConsole.info({
|
|
|
|
|
logType: LOG_TYPE.ACTION_EXECUTION_SUCCESS,
|
2024-12-10 14:43:40 +00:00
|
|
|
text: `Successfully executed in ${payload.duration}(ms)`,
|
2021-08-27 09:25:28 +00:00
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: pluginActionNameToDisplay,
|
2021-08-27 09:25:28 +00:00
|
|
|
id: actionId,
|
|
|
|
|
},
|
|
|
|
|
state: {
|
|
|
|
|
response: payload.body,
|
|
|
|
|
request: payload.request,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2024-01-15 08:44:56 +00:00
|
|
|
return [
|
|
|
|
|
payload.body,
|
|
|
|
|
params,
|
|
|
|
|
{
|
|
|
|
|
isExecutionSuccess: payload.isExecutionSuccess,
|
|
|
|
|
statusCode: payload.statusCode,
|
|
|
|
|
headers: payload.headers,
|
|
|
|
|
},
|
|
|
|
|
];
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function* runActionShortcutSaga() {
|
2022-07-11 04:06:29 +00:00
|
|
|
const pathname = window.location.pathname;
|
|
|
|
|
const baseMatch = matchBasePath(pathname);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-07-11 04:06:29 +00:00
|
|
|
if (!baseMatch) return;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-09-01 20:55:22 +00:00
|
|
|
// get gitSyncModal status
|
2025-01-07 11:30:42 +00:00
|
|
|
const isGitOpsModalOpen: boolean = yield select(selectGitOpsModalOpen);
|
|
|
|
|
const isGitConnectModalOpen: boolean = yield select(
|
|
|
|
|
selectGitConnectModalOpen,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-09-01 20:55:22 +00:00
|
|
|
// if git sync modal is open, prevent action from being executed via shortcut keys.
|
2025-01-07 11:30:42 +00:00
|
|
|
if (isGitOpsModalOpen || isGitConnectModalOpen) return;
|
2022-09-01 20:55:22 +00:00
|
|
|
|
2022-07-11 04:06:29 +00:00
|
|
|
const { path } = baseMatch;
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2022-07-11 04:06:29 +00:00
|
|
|
const match: any = matchPath(pathname, {
|
2021-08-27 09:25:28 +00:00
|
|
|
path: [
|
2022-07-11 04:06:29 +00:00
|
|
|
trimQueryString(`${path}${API_EDITOR_BASE_PATH}`),
|
|
|
|
|
trimQueryString(`${path}${API_EDITOR_ID_PATH}`),
|
|
|
|
|
trimQueryString(`${path}${QUERIES_EDITOR_BASE_PATH}`),
|
|
|
|
|
trimQueryString(`${path}${QUERIES_EDITOR_ID_PATH}`),
|
|
|
|
|
trimQueryString(`${path}${API_EDITOR_PATH_WITH_SELECTED_PAGE_ID}`),
|
|
|
|
|
trimQueryString(`${path}${INTEGRATION_EDITOR_PATH}`),
|
|
|
|
|
trimQueryString(`${path}${SAAS_EDITOR_API_ID_PATH}`),
|
2021-08-27 09:25:28 +00:00
|
|
|
],
|
|
|
|
|
exact: true,
|
|
|
|
|
strict: false,
|
|
|
|
|
});
|
2022-02-25 09:31:06 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (!match || !match.params) return;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2024-08-15 14:10:53 +00:00
|
|
|
const { baseApiId, baseQueryId } = match.params;
|
2024-07-31 02:54:51 +00:00
|
|
|
const actionId = baseApiId || baseQueryId;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-02-25 09:31:06 +00:00
|
|
|
if (actionId) {
|
|
|
|
|
yield put(runAction(actionId));
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-11 07:35:24 +00:00
|
|
|
interface RunActionError {
|
2023-04-03 04:26:18 +00:00
|
|
|
name: string;
|
|
|
|
|
message: string;
|
|
|
|
|
clientDefinedError?: boolean;
|
2023-10-11 07:35:24 +00:00
|
|
|
}
|
2023-04-03 04:26:18 +00:00
|
|
|
|
2023-11-24 06:04:06 +00:00
|
|
|
export function* runActionSaga(
|
2021-08-27 09:25:28 +00:00
|
|
|
reduxAction: ReduxAction<{
|
|
|
|
|
id: string;
|
2023-11-24 06:04:06 +00:00
|
|
|
paginationField?: PaginationField;
|
2023-06-01 17:26:05 +00:00
|
|
|
skipOpeningDebugger: boolean;
|
2023-11-24 06:04:06 +00:00
|
|
|
action?: Action;
|
2024-03-12 10:06:33 +00:00
|
|
|
actionExecutionContext?: ActionExecutionContext;
|
2021-08-27 09:25:28 +00:00
|
|
|
}>,
|
|
|
|
|
) {
|
2023-11-24 07:39:02 +00:00
|
|
|
const span = startRootSpan("runActionSaga");
|
2021-08-27 09:25:28 +00:00
|
|
|
const actionId = reduxAction.payload.id;
|
2022-06-21 13:57:34 +00:00
|
|
|
const isSaving: boolean = yield select(isActionSaving(actionId));
|
|
|
|
|
const isDirty: boolean = yield select(isActionDirty(actionId));
|
|
|
|
|
const isSavingEntity: boolean = yield select(getIsSavingEntity);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-12-24 13:59:02 +00:00
|
|
|
if (isSaving || isDirty || isSavingEntity) {
|
2021-08-27 09:25:28 +00:00
|
|
|
if (isDirty && !isSaving) {
|
|
|
|
|
yield put(updateAction({ id: actionId }));
|
2023-01-13 05:17:45 +00:00
|
|
|
yield take(ReduxActionTypes.UPDATE_ACTION_SUCCESS);
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-11 07:09:41 +00:00
|
|
|
|
|
|
|
|
const currentEnvDetails: { id: string; name: string } = yield select(
|
|
|
|
|
getCurrentEnvironmentDetails,
|
|
|
|
|
);
|
2023-11-24 06:04:06 +00:00
|
|
|
const actionObject =
|
|
|
|
|
reduxAction.payload.action ||
|
|
|
|
|
shouldBeDefined<Action>(
|
|
|
|
|
yield select(getAction, actionId),
|
|
|
|
|
`action not found for id - ${actionId}`,
|
|
|
|
|
);
|
2023-05-22 12:11:02 +00:00
|
|
|
const plugin: Plugin = yield select(getPlugin, actionObject?.pluginId);
|
|
|
|
|
const datasource: Datasource = yield select(
|
|
|
|
|
getDatasource,
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-05-22 12:11:02 +00:00
|
|
|
(actionObject?.datasource as any)?.id,
|
|
|
|
|
);
|
|
|
|
|
const pageName: string = yield select(getCurrentPageNameByActionId, actionId);
|
2022-06-21 13:57:34 +00:00
|
|
|
|
2023-11-24 06:04:06 +00:00
|
|
|
const { paginationField } = reduxAction.payload;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-10 12:59:14 +00:00
|
|
|
// open response tab in debugger on exection of action.
|
2023-06-01 17:26:05 +00:00
|
|
|
if (!reduxAction.payload.skipOpeningDebugger) {
|
2024-10-08 10:42:27 +00:00
|
|
|
yield put(
|
|
|
|
|
setPluginActionEditorDebuggerState({
|
|
|
|
|
open: true,
|
|
|
|
|
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
|
|
|
|
|
}),
|
|
|
|
|
);
|
2023-06-01 17:26:05 +00:00
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
|
|
|
|
|
let payload = EMPTY_RESPONSE;
|
|
|
|
|
let isError = true;
|
2023-04-03 04:26:18 +00:00
|
|
|
let error: RunActionError = {
|
|
|
|
|
name: "",
|
|
|
|
|
message: "",
|
|
|
|
|
};
|
2024-01-11 05:06:43 +00:00
|
|
|
|
|
|
|
|
const pluginActionNameToDisplay = getPluginActionNameToDisplay(actionObject);
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
try {
|
|
|
|
|
const executePluginActionResponse: ExecutePluginActionResponse = yield call(
|
|
|
|
|
executePluginActionSaga,
|
2023-11-24 06:04:06 +00:00
|
|
|
actionObject,
|
2021-08-27 09:25:28 +00:00
|
|
|
paginationField,
|
2023-05-16 13:10:52 +00:00
|
|
|
{},
|
|
|
|
|
true,
|
2023-11-24 07:39:02 +00:00
|
|
|
span,
|
2021-08-27 09:25:28 +00:00
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
payload = executePluginActionResponse.payload;
|
|
|
|
|
isError = executePluginActionResponse.isError;
|
|
|
|
|
} catch (e) {
|
2021-09-23 07:21:57 +00:00
|
|
|
// When running from the pane, we just want to end the saga if the user has
|
|
|
|
|
// cancelled the call. No need to log any errors
|
2021-08-27 09:25:28 +00:00
|
|
|
if (e instanceof UserCancelledActionExecutionError) {
|
2022-08-31 18:08:42 +00:00
|
|
|
// cancel action but do not throw any error.
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionErrorTypes.RUN_ACTION_ERROR,
|
|
|
|
|
payload: {
|
|
|
|
|
error: e.name,
|
|
|
|
|
id: reduxAction.payload.id,
|
|
|
|
|
show: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
2024-01-11 05:06:43 +00:00
|
|
|
toast.show(
|
|
|
|
|
createMessage(ACTION_EXECUTION_CANCELLED, pluginActionNameToDisplay),
|
|
|
|
|
{
|
|
|
|
|
kind: "error",
|
|
|
|
|
},
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
log.error(e);
|
2023-02-18 12:55:46 +00:00
|
|
|
error = { name: (e as Error).name, message: (e as Error).message };
|
2023-04-03 04:26:18 +00:00
|
|
|
|
|
|
|
|
const clientDefinedErrorMetadata = extractClientDefinedErrorMetadata(e);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-03 04:26:18 +00:00
|
|
|
if (clientDefinedErrorMetadata) {
|
|
|
|
|
set(
|
|
|
|
|
payload,
|
|
|
|
|
"statusCode",
|
|
|
|
|
`${clientDefinedErrorMetadata?.statusCode || ""}`,
|
|
|
|
|
);
|
|
|
|
|
set(payload, "request", {});
|
|
|
|
|
set(
|
|
|
|
|
payload,
|
|
|
|
|
"pluginErrorDetails",
|
|
|
|
|
clientDefinedErrorMetadata?.pluginErrorDetails,
|
|
|
|
|
);
|
|
|
|
|
set(error, "clientDefinedError", true);
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
2021-10-14 11:04:43 +00:00
|
|
|
// Error should be readable error if present.
|
2021-12-23 14:17:20 +00:00
|
|
|
// Otherwise, payload's body.
|
2021-10-14 11:04:43 +00:00
|
|
|
// Default to "An unexpected error occurred" if none is available
|
|
|
|
|
|
|
|
|
|
const readableError = payload.readableError
|
2023-02-18 12:55:46 +00:00
|
|
|
? {
|
|
|
|
|
name: "PluginExecutionError",
|
|
|
|
|
message: getErrorAsString(payload.readableError),
|
|
|
|
|
}
|
2021-10-14 11:04:43 +00:00
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
const payloadBodyError = payload.body
|
2023-02-18 12:55:46 +00:00
|
|
|
? {
|
|
|
|
|
name: "PluginExecutionError",
|
|
|
|
|
message: getErrorAsString(payload.body),
|
|
|
|
|
}
|
2021-10-14 11:04:43 +00:00
|
|
|
: undefined;
|
|
|
|
|
|
2023-04-03 04:26:18 +00:00
|
|
|
const clientDefinedError = error.clientDefinedError
|
|
|
|
|
? {
|
|
|
|
|
name: "PluginExecutionError",
|
|
|
|
|
message: error?.message,
|
|
|
|
|
clientDefinedError: true,
|
|
|
|
|
}
|
|
|
|
|
: undefined;
|
|
|
|
|
|
2023-02-18 12:55:46 +00:00
|
|
|
const defaultError = {
|
|
|
|
|
name: "PluginExecutionError",
|
|
|
|
|
message: "An unexpected error occurred",
|
|
|
|
|
};
|
2021-10-14 11:04:43 +00:00
|
|
|
|
2024-01-24 16:49:55 +00:00
|
|
|
const allowedActionAnalyticsKeys = getAllowedActionAnalyticsKeys(
|
2025-06-23 14:30:42 +00:00
|
|
|
plugin?.packageName,
|
2024-01-24 16:49:55 +00:00
|
|
|
);
|
|
|
|
|
const actionAnalyticsPayload = getActionProperties(
|
|
|
|
|
actionObject,
|
|
|
|
|
allowedActionAnalyticsKeys,
|
|
|
|
|
);
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (isError) {
|
2023-04-03 04:26:18 +00:00
|
|
|
error =
|
|
|
|
|
readableError || payloadBodyError || clientDefinedError || defaultError;
|
2021-10-14 11:04:43 +00:00
|
|
|
|
|
|
|
|
// In case of debugger, both the current error message
|
|
|
|
|
// and the readableError needs to be present,
|
|
|
|
|
// since the readableError may be malformed for certain errors.
|
|
|
|
|
|
|
|
|
|
const appsmithConsoleErrorMessageList = [
|
|
|
|
|
{
|
|
|
|
|
message: error,
|
|
|
|
|
type: PLATFORM_ERROR.PLUGIN_EXECUTION,
|
|
|
|
|
subType: payload.errorType,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (error === readableError && !!payloadBodyError) {
|
|
|
|
|
appsmithConsoleErrorMessageList.push({
|
|
|
|
|
message: payloadBodyError,
|
|
|
|
|
type: PLATFORM_ERROR.PLUGIN_EXECUTION,
|
|
|
|
|
subType: payload.errorType,
|
|
|
|
|
});
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
2021-10-14 11:04:43 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
AppsmithConsole.addErrors([
|
|
|
|
|
{
|
|
|
|
|
payload: {
|
|
|
|
|
id: actionId,
|
2023-02-18 12:55:46 +00:00
|
|
|
iconId: actionObject.pluginId,
|
2022-12-07 10:28:29 +00:00
|
|
|
logType: LOG_TYPE.ACTION_EXECUTION_ERROR,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentName: currentEnvDetails.name,
|
2024-12-10 14:43:40 +00:00
|
|
|
text: `Failed execution in ${payload.duration}(ms)`,
|
2022-12-07 10:28:29 +00:00
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: pluginActionNameToDisplay,
|
2022-12-07 10:28:29 +00:00
|
|
|
id: actionId,
|
2024-01-11 05:06:43 +00:00
|
|
|
httpMethod: actionObject?.actionConfiguration?.httpMethod,
|
2023-02-18 12:55:46 +00:00
|
|
|
pluginType: actionObject.pluginType,
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
2024-12-10 14:43:40 +00:00
|
|
|
state: {
|
|
|
|
|
error: error.message,
|
|
|
|
|
request: payload.request,
|
|
|
|
|
},
|
2023-04-03 04:26:18 +00:00
|
|
|
pluginErrorDetails: payload?.pluginErrorDetails,
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
2021-08-27 09:25:28 +00:00
|
|
|
},
|
2022-12-07 10:28:29 +00:00
|
|
|
]);
|
2021-08-27 09:25:28 +00:00
|
|
|
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionErrorTypes.RUN_ACTION_ERROR,
|
2022-03-02 15:52:35 +00:00
|
|
|
payload: {
|
2023-02-18 12:55:46 +00:00
|
|
|
error: appsmithConsoleErrorMessageList[0].message,
|
2022-03-02 15:52:35 +00:00
|
|
|
id: reduxAction.payload.id,
|
2023-04-27 08:50:23 +00:00
|
|
|
show: false,
|
2022-03-02 15:52:35 +00:00
|
|
|
},
|
2021-08-27 09:25:28 +00:00
|
|
|
});
|
2024-03-12 10:06:33 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION_FAILURE", {
|
2023-05-22 12:11:02 +00:00
|
|
|
actionId,
|
2024-01-11 05:06:43 +00:00
|
|
|
actionName: pluginActionNameToDisplay,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentId: currentEnvDetails.id,
|
|
|
|
|
environmentName: currentEnvDetails.name,
|
2023-05-22 12:11:02 +00:00
|
|
|
pageName: pageName,
|
|
|
|
|
apiType: "INTERNAL",
|
|
|
|
|
datasourceId: datasource?.id,
|
|
|
|
|
pluginName: plugin?.name,
|
|
|
|
|
isMock: !!datasource?.isMock,
|
2024-01-24 16:49:55 +00:00
|
|
|
actionConfig: actionAnalyticsPayload,
|
2023-05-22 12:11:02 +00:00
|
|
|
...payload?.pluginErrorDetails,
|
2024-03-12 10:06:33 +00:00
|
|
|
source: reduxAction.payload.actionExecutionContext,
|
2023-05-22 12:11:02 +00:00
|
|
|
});
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 10:06:33 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION", {
|
2021-08-27 09:25:28 +00:00
|
|
|
actionId,
|
2024-01-11 05:06:43 +00:00
|
|
|
actionName: pluginActionNameToDisplay,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentId: currentEnvDetails.id,
|
|
|
|
|
environmentName: currentEnvDetails.name,
|
2021-08-27 09:25:28 +00:00
|
|
|
pageName: pageName,
|
|
|
|
|
responseTime: payload.duration,
|
|
|
|
|
apiType: "INTERNAL",
|
2023-05-22 12:11:02 +00:00
|
|
|
datasourceId: datasource?.id,
|
|
|
|
|
pluginName: plugin?.name,
|
|
|
|
|
isMock: !!datasource?.isMock,
|
2024-01-24 16:49:55 +00:00
|
|
|
actionConfig: actionAnalyticsPayload,
|
2024-03-12 10:06:33 +00:00
|
|
|
source: reduxAction.payload.actionExecutionContext,
|
2025-06-23 14:30:42 +00:00
|
|
|
runBehaviour: actionObject?.runBehaviour,
|
2021-08-27 09:25:28 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.RUN_ACTION_SUCCESS,
|
|
|
|
|
payload: { [actionId]: payload },
|
|
|
|
|
});
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (payload.isExecutionSuccess) {
|
|
|
|
|
AppsmithConsole.info({
|
|
|
|
|
logType: LOG_TYPE.ACTION_EXECUTION_SUCCESS,
|
2024-12-10 14:43:40 +00:00
|
|
|
text: `Successfully executed in ${payload.duration}(ms)`,
|
2021-08-27 09:25:28 +00:00
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: pluginActionNameToDisplay,
|
2021-08-27 09:25:28 +00:00
|
|
|
id: actionId,
|
|
|
|
|
},
|
|
|
|
|
state: {
|
|
|
|
|
response: payload.body,
|
|
|
|
|
request: payload.request,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 07:18:24 +00:00
|
|
|
// This gets called for "onPageLoad" JS actions
|
2022-03-17 12:05:17 +00:00
|
|
|
function* executeOnPageLoadJSAction(pageAction: PageAction) {
|
2023-12-14 14:44:30 +00:00
|
|
|
const collectionId: string = pageAction.collectionId || "";
|
2023-06-19 08:34:29 +00:00
|
|
|
const pageId: string | undefined = yield select(getCurrentPageId);
|
|
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
if (!collectionId) return;
|
2023-06-19 08:34:29 +00:00
|
|
|
|
2023-12-14 14:44:30 +00:00
|
|
|
const collection: JSCollection = yield select(
|
|
|
|
|
getJSCollectionFromAllEntities,
|
|
|
|
|
collectionId,
|
|
|
|
|
);
|
2023-12-06 19:18:52 +00:00
|
|
|
|
|
|
|
|
if (!collection) {
|
2025-04-29 05:02:36 +00:00
|
|
|
appsmithTelemetry.captureException(
|
2023-12-06 19:18:52 +00:00
|
|
|
new Error(
|
|
|
|
|
"Collection present in layoutOnLoadActions but no collection exists ",
|
|
|
|
|
),
|
|
|
|
|
{
|
2025-04-16 06:15:47 +00:00
|
|
|
errorName: "MissingJSCollection",
|
2023-12-06 19:18:52 +00:00
|
|
|
extra: {
|
|
|
|
|
collectionId,
|
|
|
|
|
actionId: pageAction.id,
|
|
|
|
|
pageId,
|
2023-06-19 08:34:29 +00:00
|
|
|
},
|
2023-12-06 19:18:52 +00:00
|
|
|
},
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2023-06-19 08:34:29 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
const jsAction = collection.actions.find(
|
2023-12-14 14:44:30 +00:00
|
|
|
(action: JSAction) => action.id === pageAction.id,
|
2023-12-06 19:18:52 +00:00
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
if (!!jsAction) {
|
|
|
|
|
if (jsAction.confirmBeforeExecute) {
|
|
|
|
|
const jsActionPathNameToDisplay = getJSActionPathNameToDisplay(
|
|
|
|
|
jsAction,
|
|
|
|
|
collection,
|
|
|
|
|
);
|
|
|
|
|
const modalPayload = {
|
|
|
|
|
name: jsActionPathNameToDisplay,
|
|
|
|
|
modalOpen: true,
|
|
|
|
|
modalType: ModalType.RUN_ACTION,
|
|
|
|
|
};
|
2023-06-19 08:34:29 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
const confirmed: boolean = yield call(
|
|
|
|
|
requestModalConfirmationSaga,
|
|
|
|
|
modalPayload,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
if (!confirmed) {
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.RUN_ACTION_CANCELLED,
|
|
|
|
|
payload: { id: pageAction.id },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const jsActionPathNameToDisplay = getJSActionPathNameToDisplay(
|
|
|
|
|
jsAction,
|
|
|
|
|
collection,
|
|
|
|
|
);
|
2022-04-06 07:22:18 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
toast.show(
|
|
|
|
|
createMessage(ACTION_EXECUTION_CANCELLED, jsActionPathNameToDisplay),
|
|
|
|
|
{
|
|
|
|
|
kind: "error",
|
|
|
|
|
},
|
2022-04-06 07:22:18 +00:00
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
// Don't proceed to executing the js function
|
|
|
|
|
return;
|
2022-04-06 07:22:18 +00:00
|
|
|
}
|
2022-03-17 12:05:17 +00:00
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-12-06 19:18:52 +00:00
|
|
|
const data = {
|
|
|
|
|
action: jsAction,
|
|
|
|
|
collection,
|
|
|
|
|
isExecuteJSFunc: true,
|
2024-03-18 07:18:24 +00:00
|
|
|
onPageLoad: true,
|
2023-12-06 19:18:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
yield call(handleExecuteJSFunctionSaga, data);
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
2022-03-17 12:05:17 +00:00
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
|
2024-03-12 10:06:33 +00:00
|
|
|
function* executePageLoadAction(
|
|
|
|
|
pageAction: PageAction,
|
2024-12-26 05:07:41 +00:00
|
|
|
span?: Span,
|
2024-03-12 10:06:33 +00:00
|
|
|
actionExecutionContext?: ActionExecutionContext,
|
|
|
|
|
) {
|
2023-09-11 07:09:41 +00:00
|
|
|
const currentEnvDetails: { id: string; name: string } = yield select(
|
|
|
|
|
getCurrentEnvironmentDetails,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
if (pageAction.hasOwnProperty("collectionId")) {
|
|
|
|
|
yield call(executeOnPageLoadJSAction, pageAction);
|
|
|
|
|
} else {
|
2022-06-21 13:57:34 +00:00
|
|
|
const pageId: string | undefined = yield select(getCurrentPageId);
|
2022-03-17 12:05:17 +00:00
|
|
|
let currentApp: ApplicationPayload = yield select(getCurrentApplication);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
currentApp = currentApp || {};
|
2022-06-21 13:57:34 +00:00
|
|
|
const appMode: APP_MODE | undefined = yield select(getAppMode);
|
2023-05-22 12:11:02 +00:00
|
|
|
|
|
|
|
|
// action is required to fetch the pluginId and pluginType.
|
|
|
|
|
const action = shouldBeDefined<Action>(
|
|
|
|
|
yield select(getAction, pageAction.id),
|
|
|
|
|
`action not found for id - ${pageAction.id}`,
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-05-22 12:11:02 +00:00
|
|
|
const datasourceId: string = (action?.datasource as any)?.id;
|
|
|
|
|
const datasource: Datasource = yield select(getDatasource, datasourceId);
|
|
|
|
|
const plugin: Plugin = yield select(getPlugin, action?.pluginId);
|
2025-01-27 16:51:51 +00:00
|
|
|
const isAnvilEnabled: boolean = yield select(
|
|
|
|
|
getIsAnvilEnabledInCurrentApplication,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION", {
|
|
|
|
|
type: pageAction.pluginType,
|
|
|
|
|
name: pageAction.name,
|
|
|
|
|
pageId: pageId,
|
|
|
|
|
appMode: appMode,
|
|
|
|
|
appId: currentApp.id,
|
|
|
|
|
onPageLoad: true,
|
|
|
|
|
appName: currentApp.name,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentId: currentEnvDetails.id,
|
|
|
|
|
environmentName: currentEnvDetails.name,
|
2022-03-17 12:05:17 +00:00
|
|
|
isExampleApp: currentApp.appIsExample,
|
2023-05-22 12:11:02 +00:00
|
|
|
pluginName: plugin?.name,
|
|
|
|
|
datasourceId: datasourceId,
|
|
|
|
|
isMock: !!datasource?.isMock,
|
|
|
|
|
actionId: pageAction?.id,
|
2023-07-27 10:20:17 +00:00
|
|
|
inputParams: 0,
|
2024-03-12 10:06:33 +00:00
|
|
|
source: !!actionExecutionContext
|
|
|
|
|
? actionExecutionContext
|
|
|
|
|
: ActionExecutionContext.PAGE_LOAD,
|
2025-06-23 14:30:42 +00:00
|
|
|
runBehaviour: action?.runBehaviour,
|
2021-08-27 09:25:28 +00:00
|
|
|
});
|
|
|
|
|
|
2024-01-11 05:06:43 +00:00
|
|
|
const actionName = getPluginActionNameToDisplay(
|
|
|
|
|
pageAction as unknown as Action,
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
let payload = EMPTY_RESPONSE;
|
|
|
|
|
let isError = true;
|
2023-02-18 12:55:46 +00:00
|
|
|
let error = {
|
|
|
|
|
name: "PluginExecutionError",
|
2024-01-11 05:06:43 +00:00
|
|
|
message: createMessage(ACTION_EXECUTION_FAILED, actionName),
|
2023-02-18 12:55:46 +00:00
|
|
|
};
|
2023-04-12 17:08:55 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
try {
|
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
|
|
|
const executePluginActionResponse: ExecutePluginActionResponse =
|
2023-11-24 07:39:02 +00:00
|
|
|
yield call(
|
|
|
|
|
executePluginActionSaga,
|
|
|
|
|
action,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
span,
|
|
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
payload = executePluginActionResponse.payload;
|
|
|
|
|
isError = executePluginActionResponse.isError;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error(e);
|
2022-10-04 12:50:03 +00:00
|
|
|
|
|
|
|
|
if (e instanceof UserCancelledActionExecutionError) {
|
2023-02-18 12:55:46 +00:00
|
|
|
error = {
|
|
|
|
|
name: "PluginExecutionError",
|
2024-01-11 05:06:43 +00:00
|
|
|
message: createMessage(ACTION_EXECUTION_CANCELLED, actionName),
|
2023-02-18 12:55:46 +00:00
|
|
|
};
|
2022-10-04 12:50:03 +00:00
|
|
|
}
|
2022-03-17 12:05:17 +00:00
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-12 17:08:55 +00:00
|
|
|
// open response tab in debugger on exection of action on page load.
|
|
|
|
|
// Only if current page is the page on which the action is executed.
|
2025-01-27 16:51:51 +00:00
|
|
|
if (
|
|
|
|
|
window.location.pathname.includes(pageAction.id) &&
|
|
|
|
|
!(isAnvilEnabled && pageAction.pluginType === PluginType.AI)
|
|
|
|
|
)
|
2024-10-08 10:42:27 +00:00
|
|
|
yield put(
|
|
|
|
|
setPluginActionEditorDebuggerState({
|
|
|
|
|
open: true,
|
|
|
|
|
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
|
|
|
|
|
}),
|
|
|
|
|
);
|
2022-03-17 12:05:17 +00:00
|
|
|
|
|
|
|
|
if (isError) {
|
2022-12-07 10:28:29 +00:00
|
|
|
AppsmithConsole.addErrors([
|
|
|
|
|
{
|
|
|
|
|
payload: {
|
|
|
|
|
id: pageAction.id,
|
2023-02-18 12:55:46 +00:00
|
|
|
iconId: action.pluginId,
|
2022-12-07 10:28:29 +00:00
|
|
|
logType: LOG_TYPE.ACTION_EXECUTION_ERROR,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentName: currentEnvDetails.name,
|
2024-12-10 14:43:40 +00:00
|
|
|
text: `Failed execution in ${payload.duration}(ms)`,
|
2022-12-07 10:28:29 +00:00
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: actionName,
|
2022-12-07 10:28:29 +00:00
|
|
|
id: pageAction.id,
|
2024-01-11 05:06:43 +00:00
|
|
|
httpMethod: action?.actionConfiguration?.httpMethod,
|
2023-02-18 12:55:46 +00:00
|
|
|
pluginType: action.pluginType,
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
2024-12-10 14:43:40 +00:00
|
|
|
state: {
|
|
|
|
|
error:
|
|
|
|
|
payload.pluginErrorDetails?.downstreamErrorMessage ||
|
|
|
|
|
error.message,
|
|
|
|
|
request: payload.request,
|
|
|
|
|
},
|
2023-02-18 12:55:46 +00:00
|
|
|
pluginErrorDetails: payload.pluginErrorDetails,
|
2022-03-17 12:05:17 +00:00
|
|
|
},
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
|
|
|
|
]);
|
2022-03-17 12:05:17 +00:00
|
|
|
|
|
|
|
|
yield put(
|
|
|
|
|
executePluginActionError({
|
|
|
|
|
actionId: pageAction.id,
|
|
|
|
|
isPageLoad: true,
|
2023-02-18 12:55:46 +00:00
|
|
|
error: { message: error.message },
|
2022-03-17 12:05:17 +00:00
|
|
|
data: payload,
|
|
|
|
|
}),
|
|
|
|
|
);
|
2024-04-08 04:27:16 +00:00
|
|
|
|
2023-05-22 12:11:02 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION_FAILURE", {
|
|
|
|
|
type: pageAction.pluginType,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: actionName,
|
2023-05-22 12:11:02 +00:00
|
|
|
pageId: pageId,
|
|
|
|
|
appMode: appMode,
|
|
|
|
|
appId: currentApp.id,
|
|
|
|
|
onPageLoad: true,
|
|
|
|
|
appName: currentApp.name,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentId: currentEnvDetails.id,
|
|
|
|
|
environmentName: currentEnvDetails.name,
|
2023-05-22 12:11:02 +00:00
|
|
|
isExampleApp: currentApp.appIsExample,
|
|
|
|
|
pluginName: plugin?.name,
|
|
|
|
|
datasourceId: datasourceId,
|
|
|
|
|
isMock: !!datasource?.isMock,
|
|
|
|
|
actionId: pageAction?.id,
|
2023-07-27 10:20:17 +00:00
|
|
|
inputParams: 0,
|
2023-05-22 12:11:02 +00:00
|
|
|
...payload.pluginErrorDetails,
|
2024-03-12 10:06:33 +00:00
|
|
|
source: !!actionExecutionContext
|
|
|
|
|
? actionExecutionContext
|
|
|
|
|
: ActionExecutionContext.PAGE_LOAD,
|
2023-05-22 12:11:02 +00:00
|
|
|
});
|
2022-03-17 12:05:17 +00:00
|
|
|
} else {
|
2023-05-22 12:11:02 +00:00
|
|
|
AnalyticsUtil.logEvent("EXECUTE_ACTION_SUCCESS", {
|
|
|
|
|
type: pageAction.pluginType,
|
2024-01-11 05:06:43 +00:00
|
|
|
name: actionName,
|
2023-05-22 12:11:02 +00:00
|
|
|
pageId: pageId,
|
|
|
|
|
appMode: appMode,
|
|
|
|
|
appId: currentApp.id,
|
|
|
|
|
onPageLoad: true,
|
|
|
|
|
appName: currentApp.name,
|
2023-09-11 07:09:41 +00:00
|
|
|
environmentId: currentEnvDetails.id,
|
|
|
|
|
environmentName: currentEnvDetails.name,
|
2023-05-22 12:11:02 +00:00
|
|
|
isExampleApp: currentApp.appIsExample,
|
|
|
|
|
pluginName: plugin?.name,
|
|
|
|
|
datasourceId: datasourceId,
|
|
|
|
|
isMock: !!datasource?.isMock,
|
|
|
|
|
actionId: pageAction?.id,
|
2023-07-27 10:20:17 +00:00
|
|
|
inputParams: 0,
|
2024-03-12 10:06:33 +00:00
|
|
|
source: !!actionExecutionContext
|
|
|
|
|
? actionExecutionContext
|
|
|
|
|
: ActionExecutionContext.PAGE_LOAD,
|
2023-05-22 12:11:02 +00:00
|
|
|
});
|
2024-04-08 04:27:16 +00:00
|
|
|
|
2022-03-17 12:05:17 +00:00
|
|
|
yield take(ReduxActionTypes.SET_EVALUATED_TREE);
|
|
|
|
|
}
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 10:06:33 +00:00
|
|
|
function* executePageLoadActionsSaga(
|
|
|
|
|
actionPayload: ReduxAction<{
|
|
|
|
|
actionExecutionContext?: ActionExecutionContext;
|
|
|
|
|
}>,
|
|
|
|
|
) {
|
2023-11-24 07:39:02 +00:00
|
|
|
const span = startRootSpan("executePageLoadActionsSaga");
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
try {
|
|
|
|
|
const pageActions: PageAction[][] = yield select(getLayoutOnLoadActions);
|
2022-09-24 10:01:52 +00:00
|
|
|
const layoutOnLoadActionErrors: LayoutOnLoadActionErrors[] = yield select(
|
|
|
|
|
getLayoutOnLoadIssues,
|
|
|
|
|
);
|
|
|
|
|
const actionCount = flatten(pageActions).length;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2024-07-11 10:07:25 +00:00
|
|
|
setAttributesToSpan(span, { numActions: actionCount });
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-09-24 10:01:52 +00:00
|
|
|
// when cyclical depedency issue is there,
|
|
|
|
|
// none of the page load actions would be executed
|
2021-08-27 09:25:28 +00:00
|
|
|
for (const actionSet of pageActions) {
|
|
|
|
|
// Load all sets in parallel
|
2022-06-21 13:57:34 +00:00
|
|
|
// @ts-expect-error: no idea how to type this
|
2021-08-27 09:25:28 +00:00
|
|
|
yield* yield all(
|
2023-11-24 07:39:02 +00:00
|
|
|
actionSet.map((apiAction) =>
|
2024-03-12 10:06:33 +00:00
|
|
|
call(
|
|
|
|
|
executePageLoadAction,
|
|
|
|
|
apiAction,
|
|
|
|
|
span,
|
|
|
|
|
actionPayload.payload.actionExecutionContext,
|
|
|
|
|
),
|
2023-11-24 07:39:02 +00:00
|
|
|
),
|
2021-08-27 09:25:28 +00:00
|
|
|
);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2025-06-24 11:21:02 +00:00
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.SET_ONLOAD_ACTION_EXECUTED,
|
|
|
|
|
payload: true,
|
|
|
|
|
});
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
// We show errors in the debugger once onPageLoad actions
|
|
|
|
|
// are executed
|
|
|
|
|
yield put(hideDebuggerErrors(false));
|
2022-09-24 10:01:52 +00:00
|
|
|
checkAndLogErrorsIfCyclicDependency(layoutOnLoadActionErrors);
|
2021-08-27 09:25:28 +00:00
|
|
|
} catch (e) {
|
|
|
|
|
log.error(e);
|
2024-09-05 05:36:43 +00:00
|
|
|
AppsmithConsole.error({
|
|
|
|
|
text: createMessage(ERROR_FAIL_ON_PAGE_LOAD_ACTIONS),
|
2021-08-27 09:25:28 +00:00
|
|
|
});
|
|
|
|
|
}
|
2023-11-24 07:39:02 +00:00
|
|
|
endSpan(span);
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-11 07:35:24 +00:00
|
|
|
interface ExecutePluginActionResponse {
|
2021-08-27 09:25:28 +00:00
|
|
|
payload: ActionResponse;
|
|
|
|
|
isError: boolean;
|
2023-10-11 07:35:24 +00:00
|
|
|
}
|
2024-07-10 08:52:39 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
/*
|
|
|
|
|
* This saga handles the complete plugin action execution flow. It will respond with a
|
|
|
|
|
* payload and isError property which indicates if the response is of an error type.
|
|
|
|
|
* In case of the execution was not completed, it will throw errors of type
|
|
|
|
|
* PluginActionExecutionError which needs to be handled by any saga that calls this.
|
|
|
|
|
* */
|
|
|
|
|
function* executePluginActionSaga(
|
2023-11-24 06:04:06 +00:00
|
|
|
pluginAction: Action,
|
2021-08-27 09:25:28 +00:00
|
|
|
paginationField?: PaginationField,
|
|
|
|
|
params?: Record<string, unknown>,
|
2023-05-16 13:10:52 +00:00
|
|
|
isUserInitiated?: boolean,
|
2024-12-26 05:07:41 +00:00
|
|
|
parentSpan?: Span,
|
2021-08-27 09:25:28 +00:00
|
|
|
) {
|
2023-11-24 06:04:06 +00:00
|
|
|
const actionId = pluginAction.id;
|
2024-07-31 02:54:51 +00:00
|
|
|
const baseActionId = pluginAction.baseId;
|
2023-12-06 19:18:52 +00:00
|
|
|
const pluginActionNameToDisplay = getPluginActionNameToDisplay(pluginAction);
|
2024-07-11 10:07:25 +00:00
|
|
|
|
|
|
|
|
setAttributesToSpan(parentSpan, {
|
|
|
|
|
actionId,
|
|
|
|
|
pluginName: pluginActionNameToDisplay,
|
|
|
|
|
});
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (pluginAction.confirmBeforeExecute) {
|
2022-02-28 17:37:21 +00:00
|
|
|
const modalPayload = {
|
2023-12-06 19:18:52 +00:00
|
|
|
name: pluginActionNameToDisplay,
|
2022-02-28 17:37:21 +00:00
|
|
|
modalOpen: true,
|
|
|
|
|
modalType: ModalType.RUN_ACTION,
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-21 13:57:34 +00:00
|
|
|
const confirmed: unknown = yield call(
|
|
|
|
|
requestModalConfirmationSaga,
|
|
|
|
|
modalPayload,
|
|
|
|
|
);
|
2022-02-28 17:37:21 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
if (!confirmed) {
|
2021-11-01 04:54:06 +00:00
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.RUN_ACTION_CANCELLED,
|
|
|
|
|
payload: { id: actionId },
|
|
|
|
|
});
|
2021-08-27 09:25:28 +00:00
|
|
|
throw new UserCancelledActionExecutionError();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-15 14:10:53 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
yield put(executePluginActionRequest({ id: actionId }));
|
|
|
|
|
|
2022-06-21 13:57:34 +00:00
|
|
|
const appMode: APP_MODE | undefined = yield select(getAppMode);
|
|
|
|
|
const timeout: number | undefined = yield select(getActionTimeout, actionId);
|
2021-08-27 09:25:28 +00:00
|
|
|
|
|
|
|
|
const executeActionRequest: ExecuteActionRequest = {
|
|
|
|
|
actionId: actionId,
|
|
|
|
|
viewMode: appMode === APP_MODE.PUBLISHED,
|
2022-08-18 19:31:47 +00:00
|
|
|
paramProperties: {},
|
2023-05-16 13:10:52 +00:00
|
|
|
analyticsProperties: {
|
|
|
|
|
isUserInitiated: !!isUserInitiated,
|
|
|
|
|
},
|
2021-08-27 09:25:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (paginationField) {
|
|
|
|
|
executeActionRequest.paginationField = paginationField;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 16:01:50 +00:00
|
|
|
const formData = new FormData();
|
2022-08-18 19:31:47 +00:00
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
// Initialising instrumentation object, will only be populated in case
|
|
|
|
|
// files are being uplaoded
|
|
|
|
|
const filePickerInstrumentation: FilePickerInstumentationObject = {
|
|
|
|
|
numberOfFiles: 0,
|
|
|
|
|
totalSize: 0,
|
|
|
|
|
fileTypes: [],
|
|
|
|
|
fileSizes: [],
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-10 14:43:40 +00:00
|
|
|
const evaluatedBindings: Record<string, unknown> = yield call(
|
2022-08-18 19:31:47 +00:00
|
|
|
evaluateActionParams,
|
|
|
|
|
pluginAction.jsonPathKeys,
|
|
|
|
|
formData,
|
|
|
|
|
executeActionRequest,
|
2023-04-26 16:41:26 +00:00
|
|
|
filePickerInstrumentation,
|
2022-08-18 19:31:47 +00:00
|
|
|
params,
|
|
|
|
|
);
|
2022-03-02 16:01:50 +00:00
|
|
|
|
2024-12-10 14:43:40 +00:00
|
|
|
AppsmithConsole.info({
|
|
|
|
|
text: "Began execution",
|
|
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
|
|
|
|
name: pluginAction.name,
|
|
|
|
|
id: actionId,
|
|
|
|
|
},
|
|
|
|
|
state: { requestParams: { ...params, ...evaluatedBindings } },
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-18 12:55:46 +00:00
|
|
|
let payload = EMPTY_RESPONSE;
|
|
|
|
|
let response: ActionExecutionResponse;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
try {
|
2024-10-29 05:55:43 +00:00
|
|
|
response = yield ActionAPI.executeAction(formData, timeout);
|
2023-11-24 07:39:02 +00:00
|
|
|
|
2023-11-16 19:14:01 +00:00
|
|
|
const isError = isErrorResponse(response);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
yield validateResponse(response);
|
2023-02-18 12:55:46 +00:00
|
|
|
payload = createActionExecutionResponse(response);
|
2022-04-08 16:32:34 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
yield put(
|
|
|
|
|
executePluginActionSuccess({
|
|
|
|
|
id: actionId,
|
2024-07-31 02:54:51 +00:00
|
|
|
baseId: baseActionId,
|
2021-08-27 09:25:28 +00:00
|
|
|
response: payload,
|
2023-11-24 06:04:06 +00:00
|
|
|
isActionCreatedInApp: getIsActionCreatedInApp(pluginAction),
|
2021-08-27 09:25:28 +00:00
|
|
|
}),
|
|
|
|
|
);
|
2023-09-27 22:03:38 +00:00
|
|
|
|
|
|
|
|
yield put(
|
2023-11-24 07:39:02 +00:00
|
|
|
updateActionData(
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
entityName: pluginAction.name,
|
|
|
|
|
dataPath: "data",
|
2023-11-29 16:36:38 +00:00
|
|
|
data: payload.body,
|
2023-11-24 07:39:02 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
parentSpan,
|
|
|
|
|
),
|
2023-09-27 22:03:38 +00:00
|
|
|
);
|
2023-02-18 12:55:46 +00:00
|
|
|
// TODO: Plugins are not always fetched before on page load actions are executed.
|
|
|
|
|
try {
|
|
|
|
|
let plugin: Plugin | undefined;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-02-18 12:55:46 +00:00
|
|
|
if (!!pluginAction.pluginId) {
|
|
|
|
|
plugin = shouldBeDefined<Plugin>(
|
|
|
|
|
yield select(getPlugin, pluginAction.pluginId),
|
|
|
|
|
`Plugin not found for id - ${pluginAction.pluginId}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-06-06 07:57:19 +00:00
|
|
|
|
2023-02-18 12:55:46 +00:00
|
|
|
// sets the default display format for action response e.g Raw, Json or Table
|
|
|
|
|
yield setDefaultActionDisplayFormat(actionId, plugin, payload);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error("plugin no found", e);
|
|
|
|
|
}
|
2023-04-26 16:41:26 +00:00
|
|
|
|
|
|
|
|
if (filePickerInstrumentation.numberOfFiles > 0) {
|
|
|
|
|
triggerFileUploadInstrumentation(
|
|
|
|
|
filePickerInstrumentation,
|
|
|
|
|
isError ? "ERROR" : "SUCCESS",
|
|
|
|
|
response.data.statusCode,
|
|
|
|
|
pluginAction.name,
|
|
|
|
|
pluginAction.pluginType,
|
|
|
|
|
response.clientMeta.duration,
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
return {
|
|
|
|
|
payload,
|
2023-04-26 16:41:26 +00:00
|
|
|
isError,
|
2021-08-27 09:25:28 +00:00
|
|
|
};
|
|
|
|
|
} catch (e) {
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-04-03 04:26:18 +00:00
|
|
|
if ("clientDefinedError" in (e as any)) {
|
2023-04-26 16:41:26 +00:00
|
|
|
// Case: error from client side validation
|
|
|
|
|
if (filePickerInstrumentation.numberOfFiles > 0) {
|
|
|
|
|
triggerFileUploadInstrumentation(
|
|
|
|
|
filePickerInstrumentation,
|
|
|
|
|
"ERROR",
|
|
|
|
|
"400",
|
|
|
|
|
pluginAction.name,
|
|
|
|
|
pluginAction.pluginType,
|
|
|
|
|
"NA",
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-03 04:26:18 +00:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
yield put(
|
|
|
|
|
executePluginActionSuccess({
|
|
|
|
|
id: actionId,
|
2024-07-31 02:54:51 +00:00
|
|
|
baseId: baseActionId,
|
2021-08-27 09:25:28 +00:00
|
|
|
response: EMPTY_RESPONSE,
|
2023-11-24 06:04:06 +00:00
|
|
|
isActionCreatedInApp: getIsActionCreatedInApp(pluginAction),
|
2021-08-27 09:25:28 +00:00
|
|
|
}),
|
|
|
|
|
);
|
2023-09-27 22:03:38 +00:00
|
|
|
yield put(
|
2023-11-24 07:39:02 +00:00
|
|
|
updateActionData(
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
entityName: pluginAction.name,
|
|
|
|
|
dataPath: "data",
|
|
|
|
|
data: EMPTY_RESPONSE.body,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
parentSpan,
|
|
|
|
|
),
|
2023-09-27 22:03:38 +00:00
|
|
|
);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-31 18:08:42 +00:00
|
|
|
if (e instanceof UserCancelledActionExecutionError) {
|
2023-04-26 16:41:26 +00:00
|
|
|
// Case: user cancelled the request of file upload
|
|
|
|
|
if (filePickerInstrumentation.numberOfFiles > 0) {
|
|
|
|
|
triggerFileUploadInstrumentation(
|
|
|
|
|
filePickerInstrumentation,
|
|
|
|
|
"CANCELLED",
|
|
|
|
|
"499",
|
|
|
|
|
pluginAction.name,
|
|
|
|
|
pluginAction.pluginType,
|
|
|
|
|
"NA",
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-31 18:08:42 +00:00
|
|
|
throw new UserCancelledActionExecutionError();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
// In case there is no response from server and files are being uploaded
|
|
|
|
|
// we report it as INVALID_RESPONSE. The server didn't send any code or the
|
|
|
|
|
// request was cancelled due to timeout
|
|
|
|
|
if (filePickerInstrumentation.numberOfFiles > 0) {
|
|
|
|
|
triggerFileUploadInstrumentation(
|
|
|
|
|
filePickerInstrumentation,
|
|
|
|
|
"INVALID_RESPONSE",
|
|
|
|
|
"444",
|
|
|
|
|
pluginAction.name,
|
|
|
|
|
pluginAction.pluginType,
|
|
|
|
|
"NA",
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2022-08-31 18:08:42 +00:00
|
|
|
throw new PluginActionExecutionError("Response not valid", false);
|
2021-08-27 09:25:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
// Function to send the file upload event to segment
|
|
|
|
|
function triggerFileUploadInstrumentation(
|
2024-07-31 15:41:28 +00:00
|
|
|
// TODO: Fix this the next time the file is edited
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-04-26 16:41:26 +00:00
|
|
|
filePickerInfo: Record<string, any>,
|
|
|
|
|
status: string,
|
|
|
|
|
statusCode: string,
|
|
|
|
|
pluginName: string,
|
|
|
|
|
pluginType: string,
|
|
|
|
|
timeTaken: string,
|
|
|
|
|
) {
|
|
|
|
|
const { fileSizes, fileTypes, numberOfFiles, totalSize } = filePickerInfo;
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-04-26 16:41:26 +00:00
|
|
|
AnalyticsUtil.logEvent("FILE_UPLOAD_COMPLETE", {
|
|
|
|
|
totalSize,
|
|
|
|
|
fileSizes,
|
|
|
|
|
numberOfFiles,
|
|
|
|
|
fileTypes,
|
|
|
|
|
status,
|
|
|
|
|
statusCode,
|
|
|
|
|
pluginName,
|
|
|
|
|
pluginType,
|
|
|
|
|
timeTaken,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 12:28:26 +00:00
|
|
|
// Function to clear the action responses for the actions which are not runBehaviour: ON_PAGE_LOAD.
|
2023-05-23 12:18:46 +00:00
|
|
|
function* clearTriggerActionResponse() {
|
2023-09-12 11:51:39 +00:00
|
|
|
const currentPageActions: ActionData[] = yield select(getCurrentActions);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-05-23 12:18:46 +00:00
|
|
|
for (const action of currentPageActions) {
|
2025-05-02 12:28:26 +00:00
|
|
|
// Clear the action response if the action has data and is not runBehaviour: ON_PAGE_LOAD.
|
2025-04-24 10:07:11 +00:00
|
|
|
if (
|
|
|
|
|
action.data &&
|
2025-05-02 12:28:26 +00:00
|
|
|
action.config.runBehaviour !== ActionRunBehaviour.ON_PAGE_LOAD
|
2025-04-24 10:07:11 +00:00
|
|
|
) {
|
2023-05-23 12:18:46 +00:00
|
|
|
yield put(clearActionResponse(action.config.id));
|
2023-09-27 22:03:38 +00:00
|
|
|
yield put(
|
2023-11-22 08:34:48 +00:00
|
|
|
updateActionData([
|
|
|
|
|
{
|
|
|
|
|
entityName: action.config.name,
|
|
|
|
|
dataPath: "data",
|
|
|
|
|
data: undefined,
|
|
|
|
|
},
|
|
|
|
|
]),
|
2023-09-27 22:03:38 +00:00
|
|
|
);
|
2023-05-23 12:18:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Function to soft refresh the all the actions on the page.
|
|
|
|
|
function* softRefreshActionsSaga() {
|
|
|
|
|
//get current pageId
|
|
|
|
|
const pageId: string = yield select(getCurrentPageId);
|
2023-10-17 16:11:16 +00:00
|
|
|
const applicationId: string = yield select(getCurrentApplicationId);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-05-23 12:18:46 +00:00
|
|
|
// Fetch the page data before refreshing the actions.
|
2024-07-31 02:54:51 +00:00
|
|
|
yield put(fetchPageAction(pageId));
|
2023-05-23 12:18:46 +00:00
|
|
|
//wait for the page to be fetched.
|
|
|
|
|
yield take([
|
|
|
|
|
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
|
|
|
|
|
ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
|
|
|
|
]);
|
|
|
|
|
// Clear appsmith store
|
|
|
|
|
yield call(handleStoreOperations, [
|
|
|
|
|
{
|
|
|
|
|
payload: null,
|
|
|
|
|
type: "CLEAR_STORE",
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
// Clear all the action responses on the page
|
|
|
|
|
yield call(clearTriggerActionResponse);
|
|
|
|
|
//Rerun all the page load actions on the page
|
2024-03-12 10:06:33 +00:00
|
|
|
yield put(
|
|
|
|
|
executePageLoadActions(
|
|
|
|
|
ActionExecutionContext.REFRESH_ACTIONS_ON_ENV_CHANGE,
|
|
|
|
|
),
|
|
|
|
|
);
|
2023-07-21 05:53:17 +00:00
|
|
|
try {
|
|
|
|
|
// we fork to prevent the call from blocking
|
|
|
|
|
yield put(softRefreshDatasourceStructure());
|
|
|
|
|
} catch (error) {}
|
|
|
|
|
//This will refresh the query editor with the latest datasource structure.
|
2023-10-17 16:11:16 +00:00
|
|
|
// TODO: fix typing of matchQueryBuilderPath, it always returns "any" which can lead to bugs
|
2023-07-21 05:53:17 +00:00
|
|
|
const isQueryPane = matchQueryBuilderPath(window.location.pathname);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-07-21 05:53:17 +00:00
|
|
|
//This is reuired only when the query editor is open.
|
|
|
|
|
if (isQueryPane) {
|
2024-07-31 02:54:51 +00:00
|
|
|
const basePageId: string = yield select(getCurrentBasePageId);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-10-17 16:11:16 +00:00
|
|
|
yield put(
|
|
|
|
|
changeQuery({
|
2024-07-31 02:54:51 +00:00
|
|
|
baseQueryId: isQueryPane.params.baseQueryId,
|
|
|
|
|
basePageId,
|
2023-10-17 16:11:16 +00:00
|
|
|
applicationId,
|
|
|
|
|
}),
|
|
|
|
|
);
|
2023-07-21 05:53:17 +00:00
|
|
|
}
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-09-11 07:09:41 +00:00
|
|
|
const currentEnvName: string = yield select(getCurrentEnvironmentName);
|
2024-09-18 16:35:28 +00:00
|
|
|
|
2023-09-11 07:09:41 +00:00
|
|
|
toast.show(createMessage(SWITCH_ENVIRONMENT_SUCCESS, currentEnvName), {
|
2023-07-21 05:53:17 +00:00
|
|
|
kind: "success",
|
|
|
|
|
});
|
2024-01-19 10:31:22 +00:00
|
|
|
yield put({ type: ReduxActionTypes.SWITCH_ENVIRONMENT_SUCCESS });
|
2023-05-23 12:18:46 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-02 13:10:44 +00:00
|
|
|
// This gets called for "onPageUnload" JS actions
|
|
|
|
|
function* executeOnPageUnloadJSAction(pageAction: Action) {
|
|
|
|
|
const collectionId: string = pageAction.collectionId || "";
|
|
|
|
|
const pageId: string | undefined = yield select(getCurrentPageId);
|
|
|
|
|
|
|
|
|
|
if (!collectionId) return;
|
|
|
|
|
|
|
|
|
|
const collection: JSCollection = yield select(
|
|
|
|
|
getJSCollectionFromAllEntities,
|
|
|
|
|
collectionId,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!collection) {
|
|
|
|
|
appsmithTelemetry.captureException(
|
|
|
|
|
new Error(
|
|
|
|
|
"Collection present in layoutOnUnloadActions but no collection exists ",
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
errorName: "MissingJSCollection",
|
|
|
|
|
extra: {
|
|
|
|
|
collectionId,
|
|
|
|
|
actionId: pageAction.id,
|
|
|
|
|
pageId,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const jsAction = collection.actions.find(
|
|
|
|
|
(action: JSAction) => action.id === pageAction.id,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!!jsAction) {
|
|
|
|
|
yield call(handleExecuteJSFunctionSaga, {
|
|
|
|
|
action: jsAction,
|
|
|
|
|
collection,
|
|
|
|
|
isExecuteJSFunc: true,
|
|
|
|
|
onPageLoad: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function* executePageUnloadActionsSaga() {
|
|
|
|
|
const span = startRootSpan("executePageUnloadActionsSaga");
|
|
|
|
|
|
|
|
|
|
try {
|
feat: implements onPageUnload functionality for the edit app mode page selector (#41074)
## Description
<ins>Problem</ins>
onPageUnload functionality was not consistently triggered during all
types of page navigation in edit mode, leading to potential missed
cleanup or actions when navigating between pages via different UI
elements or programmatic flows.
<ins>Root cause</ins>
Navigation logic was fragmented across multiple components and methods
(button clicks, navigation tabs, page navigator), and direct history
manipulation bypassed centralized handling, preventing reliable
invocation of onPageUnload actions.
<ins>Solution</ins>
This PR handles the integration of onPageUnload functionality with all
page navigation flows in edit mode by centralizing navigation logic
through the navigateToAnotherPage action, enhancing type safety, and
ensuring onPageUnload actions are filtered and executed based on the
current page context.
Fixes #40998
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.JS, @tag.Sanity"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/16042398132>
> Commit: 8ea04e6bb1312d9f468ed3d74ccc080ed6e9bac9
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=16042398132&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS, @tag.Sanity`
> Spec:
> <hr>Thu, 03 Jul 2025 06:44:33 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enhanced page unload actions to only trigger for the current page,
improving accuracy and reliability.
* **Bug Fixes**
* Improved navigation consistency by updating the page switching
mechanism to use a unified action.
* **Tests**
* Added tests to ensure correct filtering of JavaScript actions executed
on page unload.
* **Refactor**
* Streamlined selector logic for better maintainability and performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-03 06:54:53 +00:00
|
|
|
const pageOnUnloadActions: Action[] = yield select(
|
|
|
|
|
getLayoutOnUnloadActions,
|
|
|
|
|
);
|
|
|
|
|
const actionCount = pageOnUnloadActions.length;
|
2025-07-02 13:10:44 +00:00
|
|
|
|
|
|
|
|
setAttributesToSpan(span, { numActions: actionCount });
|
|
|
|
|
|
|
|
|
|
// Execute unload actions in parallel batches
|
|
|
|
|
yield all(
|
feat: implements onPageUnload functionality for the edit app mode page selector (#41074)
## Description
<ins>Problem</ins>
onPageUnload functionality was not consistently triggered during all
types of page navigation in edit mode, leading to potential missed
cleanup or actions when navigating between pages via different UI
elements or programmatic flows.
<ins>Root cause</ins>
Navigation logic was fragmented across multiple components and methods
(button clicks, navigation tabs, page navigator), and direct history
manipulation bypassed centralized handling, preventing reliable
invocation of onPageUnload actions.
<ins>Solution</ins>
This PR handles the integration of onPageUnload functionality with all
page navigation flows in edit mode by centralizing navigation logic
through the navigateToAnotherPage action, enhancing type safety, and
ensuring onPageUnload actions are filtered and executed based on the
current page context.
Fixes #40998
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.JS, @tag.Sanity"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/16042398132>
> Commit: 8ea04e6bb1312d9f468ed3d74ccc080ed6e9bac9
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=16042398132&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS, @tag.Sanity`
> Spec:
> <hr>Thu, 03 Jul 2025 06:44:33 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enhanced page unload actions to only trigger for the current page,
improving accuracy and reliability.
* **Bug Fixes**
* Improved navigation consistency by updating the page switching
mechanism to use a unified action.
* **Tests**
* Added tests to ensure correct filtering of JavaScript actions executed
on page unload.
* **Refactor**
* Streamlined selector logic for better maintainability and performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-03 06:54:53 +00:00
|
|
|
pageOnUnloadActions.map((action) =>
|
|
|
|
|
call(executeOnPageUnloadJSAction, action),
|
|
|
|
|
),
|
2025-07-02 13:10:44 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Publish success event after all actions are executed
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.EXECUTE_PAGE_UNLOAD_ACTIONS_SUCCESS,
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error(e);
|
|
|
|
|
AppsmithConsole.error({
|
|
|
|
|
text: "Failed to execute actions during page unload",
|
|
|
|
|
});
|
|
|
|
|
// Publish error event if something goes wrong
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.EXECUTE_PAGE_UNLOAD_ACTIONS_ERROR,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
endSpan(span);
|
|
|
|
|
}
|
|
|
|
|
// End of Selection
|
|
|
|
|
|
2021-08-27 09:25:28 +00:00
|
|
|
export function* watchPluginActionExecutionSagas() {
|
|
|
|
|
yield all([
|
|
|
|
|
takeLatest(ReduxActionTypes.RUN_ACTION_REQUEST, runActionSaga),
|
|
|
|
|
takeLatest(
|
|
|
|
|
ReduxActionTypes.RUN_ACTION_SHORTCUT_REQUEST,
|
|
|
|
|
runActionShortcutSaga,
|
|
|
|
|
),
|
|
|
|
|
takeLatest(
|
|
|
|
|
ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
|
|
|
|
|
executePageLoadActionsSaga,
|
|
|
|
|
),
|
2023-05-23 12:18:46 +00:00
|
|
|
takeLatest(ReduxActionTypes.PLUGIN_SOFT_REFRESH, softRefreshActionsSaga),
|
2025-07-02 13:10:44 +00:00
|
|
|
takeLatest(
|
|
|
|
|
ReduxActionTypes.EXECUTE_PAGE_UNLOAD_ACTIONS,
|
|
|
|
|
executePageUnloadActionsSaga,
|
|
|
|
|
),
|
2021-08-27 09:25:28 +00:00
|
|
|
]);
|
|
|
|
|
}
|