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,
|
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";
|
2023-09-06 12:15:04 +00:00
|
|
|
import { FileDataTypes } from "WidgetProvider/constants";
|
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(
|
|
|
|
|
plugin.packageName,
|
|
|
|
|
);
|
|
|
|
|
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,
|
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,
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
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),
|
2021-08-27 09:25:28 +00:00
|
|
|
]);
|
|
|
|
|
}
|