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"

Reply via email to