This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch release/v1.2
in repository https://gitbox.apache.org/repos/asf/texera.git
commit 41cc7c5196b88ffba323b4b0a2b458d825893720
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sun Aug 23 04:35:35 2026 +0000
fix(frontend, v1.2): sort cache size ascending, drop the broken preset
updater (#7808)
### What changes were proposed in this PR?
Automated backport of #7806 to `release/v1.2`. The cherry-pick
conflicted and was committed with conflict markers; this describes the
hand resolution.
Two of #7806's three defects apply to `release/v1.2`. The third does not
exist on the branch.
**Carried over — `sortBySize` now matches the `NzTableSortFn`
contract.** `user-quota.component.ts` is byte-identical between `main`
and `release/v1.2`, so the fix transfers verbatim: `b - a` → `a - b`.
nz-table uses the comparator's result as-is for `ascend` and negates it
for `descend`, and `nzSortDirections` defaults to
`['ascend','descend',null]`, which this header does not override — so on
`release/v1.2` today the first click on Cache Size **lights the up-caret
while rendering largest-first**.
**Carried over — `PresetService.updatePreset` removed.** Dead (`grep`
for callers across `frontend/src` on this branch: none outside its own
spec) and broken for the reason #7806 gives. The method and its one spec
test are gone.
**Dropped — the hugging-face template fallback.**
`frontend/src/app/workspace/component/hugging-face-image-upload/` does
not exist on `release/v1.2`; the component was never on this branch. The
cherry-pick resolved that conflict by *adding* the whole 51-line
template, which would have landed an orphan file for a nonexistent
component. Removed.
### The `indexOf` import must stay on this branch
#7806 dropped `indexOf` from the lodash import in `preset.service.ts`,
and git applied that hunk **without conflict** because the import line
matched. It would have broken the build: on `release/v1.2`,
`updateOrCreatePreset` still uses `indexOf` at two call sites —
```ts
presets.splice(indexOf(presets, originalPreset), 1);
presets[indexOf(presets, originalPreset)] = replacementPreset;
```
`main` no longer does, because #6691 replaced them with
`findIndex(isEqual)`. That commit was never backported. So `indexOf` is
kept in the import here, and `updateOrCreatePreset` is left exactly as
`release/v1.2` has it — changing it is #6691's business, not this
backport's.
### The spec conflict, and what replaced the 588 lines
`user-quota.component.spec.ts` is 58 lines on `release/v1.2` and 646 on
`main`. #7806's two hunks there are *edits inside test blocks that do
not exist on this branch*, so they have no target. The 588-line gap
comes from four test-only PRs — #6524, #6542, #7437, #7696 — none of
them backported. The cherry-pick's resolution dumped all 588 lines in,
which would have smuggled four unrelated PRs into a fix backport.
Instead the spec is reset to the `release/v1.2` version and given the
**one focused test** that pins the changed line — a pure comparator
assertion, no DOM, no Plotly, no chart machinery:
```ts
expect([...scrambled].sort(component.sortBySize).map(e =>
e.eid)).toEqual([20, 10, 30]);
```
The fixtures are #7806's, whose byte triples are chosen so that ordering
by any single count, or by the sum with any one term dropped, yields a
different sequence than the full sum — and no two of the six orderings
tie. The caret/header-click half of #7806's coverage is not ported: it
depends on the `clickSort`/`caretActive`/`eids` DOM helpers introduced
by #7696.
**Net effect:** the release branch gets the fix *and* a guard for it,
without inheriting test-only history that was never backported.
### Any related issues, documentation, discussions?
Backport of #7806. Originally linked #7805. Source commit:
ff5bd7f8604366feda569ab29917bdb30a460266 · [automation
run](https://github.com/apache/texera/actions/runs/32411750182)
Not backported, and not needed for this change: #6691
(`findIndex(isEqual)` in `updateOrCreatePreset`), #6524 / #6542 / #7437
/ #7696 (the `user-quota` spec suite).
### How was this PR tested?
Dependencies were reinstalled against this branch's `package.json` first
— the stale `node_modules` was missing `monaco-editor-wrapper` and
others, which produced build errors unrelated to the change.
```
npx ng test --watch=false --include="**/user-quota.component.spec.ts"
--include="**/preset.service.spec.ts"
```
```
Test Files 2 passed (2)
Tests 37 passed (37)
```
The new test genuinely guards the fix — with `user-quota.component.ts`
reverted to the `release/v1.2` comparator it fails with exactly the
assertion #7806 reported:
```
AssertionError: expected [ 30, 10, 20 ] to deeply equal [ 20, 10, 30 ]
```
`npx ng build`: **exit 0**, zero errors and zero NG####/TS diagnostics.
Run because `ng test` and `tsc --noEmit` both miss Angular template
errors — and because it is what catches the `indexOf` import break
described above. The 72 warnings are pre-existing and none are in the
touched files.
`yarn format:ci`: exit 0.
No conflict markers remain: `git grep -nE '^(<<<<<<< |>>>>>>> )'` over
the tree is empty.
### Was this PR authored or co-authored using generative AI tooling?
(backported from commit ff5bd7f8604366feda569ab29917bdb30a460266)
Generated-by: Claude Code (Opus 5)
Co-authored-by: Xinyuan Lin <[email protected]>
---
.../user/user-quota/user-quota.component.spec.ts | 32 +++++++++++++++++++++
.../user/user-quota/user-quota.component.ts | 2 +-
.../service/preset/preset.service.spec.ts | 12 --------
.../app/workspace/service/preset/preset.service.ts | 33 ----------------------
4 files changed, 33 insertions(+), 46 deletions(-)
diff --git
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
index 1190a562e9..98f42aa34e 100644
---
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
+++
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
@@ -24,6 +24,26 @@ import { HttpClientTestingModule } from
"@angular/common/http/testing";
import { commonTestProviders } from "../../../../common/testing/test-utils";
import { of } from "rxjs";
import type { Mocked } from "vitest";
+import { ExecutionQuota } from "../../../../common/type/user";
+
+function execution(eid: number, result: number, runtime: number, log: number):
ExecutionQuota {
+ return {
+ eid,
+ workflowId: 1,
+ workflowName: "wf-1",
+ resultBytes: result,
+ runTimeStatsBytes: runtime,
+ logBytes: log,
+ };
+}
+
+// Fixtures for the Cache Size comparator. The three byte triples are picked
so that ordering by
+// any single count — or by the sum with any one of the three terms dropped —
yields a different
+// sequence than the full sum does, and so that no two of the six orderings
share a tie.
+const SIZE_SORT_BIG = execution(30, 1, 450, 452); // 903
+const SIZE_SORT_MIDDLE = execution(10, 800, 1, 1); // 802
+const SIZE_SORT_SMALL = execution(20, 2, 700, 2); // 704
+
describe("UserQuotaComponent", () => {
let component: UserQuotaComponent;
let fixture: ComponentFixture<UserQuotaComponent>;
@@ -55,4 +75,16 @@ describe("UserQuotaComponent", () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
+
+ describe("sortBySize", () => {
+ // Ascending, as NzTableSortFn requires (`a - b`): nz-table takes the
comparator's result as-is
+ // for 'ascend' and negates it for 'descend', so a `b - a` comparator
lights the up caret while
+ // rendering largest-first. nzSortDirections defaults to ['ascend',
'descend', null] and the
+ // Cache Size header does not override it, so the first click is 'ascend'.
+ it("orders executions by total cache size, smallest first", () => {
+ const scrambled = [SIZE_SORT_MIDDLE, SIZE_SORT_SMALL, SIZE_SORT_BIG];
+
+ expect([...scrambled].sort(component.sortBySize).map(e =>
e.eid)).toEqual([20, 10, 30]);
+ });
+ });
});
diff --git
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.ts
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.ts
index ae04467a80..cf2201be6a 100644
---
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.ts
+++
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.ts
@@ -342,5 +342,5 @@ export class UserQuotaComponent implements OnInit {
formatSize = formatSize;
public sortBySize: NzTableSortFn<ExecutionQuota> = (a: ExecutionQuota, b:
ExecutionQuota) =>
- b.resultBytes + b.logBytes + b.runTimeStatsBytes - a.resultBytes -
a.logBytes - a.runTimeStatsBytes;
+ a.resultBytes + a.logBytes + a.runTimeStatsBytes - (b.resultBytes +
b.logBytes + b.runTimeStatsBytes);
}
diff --git a/frontend/src/app/workspace/service/preset/preset.service.spec.ts
b/frontend/src/app/workspace/service/preset/preset.service.spec.ts
index 4095418195..e7c8703223 100644
--- a/frontend/src/app/workspace/service/preset/preset.service.spec.ts
+++ b/frontend/src/app/workspace/service/preset/preset.service.spec.ts
@@ -188,18 +188,6 @@ describe("PresetService", () => {
expect((errors[0] as Error).message).toMatch(/already exists/);
});
- it("updatePreset does not write the preset back when the original preset
is missing", async () => {
- userConfigStub.fetchKey.mockReturnValue(of(JSON.stringify([{
presetProperty: "v1" }])));
-
- const errors = await captureRxjsUnhandled(() =>
- presetService.updatePreset(presetType, presetTarget, { presetProperty:
"missing" }, { presetProperty: "v3" })
- );
-
- expect(userConfigStub.set).not.toHaveBeenCalled();
- expect(errors).toHaveLength(1);
- expect((errors[0] as Error).message).toMatch(/doesn't exist/);
- });
-
it("deletePreset removes the matching preset via savePresets", () => {
const a: Preset = { presetProperty: "v1" };
const b: Preset = { presetProperty: "v2" };
diff --git a/frontend/src/app/workspace/service/preset/preset.service.ts
b/frontend/src/app/workspace/service/preset/preset.service.ts
index 9ba5c2a318..5361ec3484 100644
--- a/frontend/src/app/workspace/service/preset/preset.service.ts
+++ b/frontend/src/app/workspace/service/preset/preset.service.ts
@@ -158,39 +158,6 @@ export class PresetService {
});
}
- /**
- * broadcast savePresets event and also save preset to presetDict, which is
a *view* (in the database sense) of DictionaryService's dictionary that only
stores presets
- * @param type string, usually "operator"
- * @param target string, usualy operatorType
- * @param presets Preset[]
- * @param displayMessage message to display when saving presets
- * @param messageType see AlertMessageType, determines icon used in popup
message
- */
- public updatePreset(
- type: string,
- target: string,
- originalPreset: Preset,
- replacementPreset: Preset,
- displayMessage?: string | null,
- messageType: AlertMessageType = "success"
- ) {
- this.userConfigService
- .fetchKey(`${type}-${target}`)
- .pipe(first())
- .subscribe(presetsString => {
- let presets = JSON.parse(presetsString ?? "[]") as Preset[];
- if (!contains(presets, originalPreset)) {
- throw new Error("attempting to update preset that doesn't exist");
- } else if (contains(presets, replacementPreset)) {
- // implicit deletion by replacing original with existing preset
- presets.splice(indexOf(presets, originalPreset), 1);
- } else {
- presets[indexOf(presets, originalPreset)] = replacementPreset;
- }
- this.savePresets(type, target, presets, displayMessage, messageType);
- });
- }
-
/**
* broadcast savePresets event and also save preset to presetDict, which is
a *view* (in the database sense) of DictionaryService's dictionary that only
stores presets
* @param type string, usually "operator"