You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/09/27 22:07:09 UTC
(texera) branch gh-readonly-queue/main/pr-8695-facdc2186d03f7efa87f02431e8a3b749830f94c created (now fcaf27a561)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-8695-facdc2186d03f7efa87f02431e8a3b749830f94c
in repository https://gitbox.apache.org/repos/asf/texera.git
at fcaf27a561 refactor(frontend): remove the unused ObservableContextManager (#8695)
This branch includes the following new commits:
new fcaf27a561 refactor(frontend): remove the unused ObservableContextManager (#8695)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(texera) 01/01: refactor(frontend): remove the unused ObservableContextManager (#8695)
Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-8695-facdc2186d03f7efa87f02431e8a3b749830f94c
in repository https://gitbox.apache.org/repos/asf/texera.git
commit fcaf27a561bc0d29bad052b191062019f75eafa1
Author: Xinyuan Lin <xi...@uci.edu>
AuthorDate: Sun Sep 27 22:06:55 2026 +0000
refactor(frontend): remove the unused ObservableContextManager (#8695)
### What changes were proposed in this PR?
Deletes `ObservableContextManager` and re-parents its only subclass,
`JointGraphContext`, onto `ContextManager`. **+8/−223 lines.** There is
no behaviour change: the removed layer only emitted enter/exit events on
Subjects nothing subscribes to, and `JointGraphContext`'s own `enter` /
`exit` overrides still call `super`.
### History
| | |
| --- | --- |
| **Introduced by** | #1433 (2022-02-07), "Bugfix for Albert load
workflow opt". `JointGraphContext` used the change stream to buffer
JointJS events in async mode |
| **Usage removed by** | #1810 (2023-01-27), "Fix buffering events in
JointJS async context". It deleted both `getChangeContextStream()`
subscribers |
It has been dead for over three and a half years. Every paper-context
enter/exit since then has emitted into streams with no reader.
> Reviewer note: `context.spec.ts` goes from 23 cases to 12. The 11
stream-only cases are deleted. 4 others move into
`describe("ContextManager")` with their stream assertions dropped,
because they cover `ContextManager` paths the original 8 cases did not:
object-identity contexts, nested unwinding when a callable throws, falsy
return values, and re-entering the same value.
### Any related issues, documentation, discussions?
Closes #8694
### How was this PR tested?
No new tests. The kept `ContextManager` cases cover the surviving code.
From `frontend/`:
- `npx ng test --watch=false --include='**/context.spec.ts'
--include='**/joint-graph-wrapper.spec.ts'
--include='**/workflow-editor.component.spec.ts'`: 3 files, 199 tests,
all pass.
- `yarn format:ci`: clean.
- `npx ng build`: success.
To re-check:
```
git grep -n "ObservableContextManager\|getChangeContextStream\|getEnterStream\|getExitStream" -- frontend # no hits
```
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 5.5)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
frontend/src/app/common/util/context.spec.ts | 186 +--------------------
frontend/src/app/common/util/context.ts | 41 -----
.../workflow-graph/model/joint-graph-wrapper.ts | 4 +-
3 files changed, 8 insertions(+), 223 deletions(-)
diff --git a/frontend/src/app/common/util/context.spec.ts b/frontend/src/app/common/util/context.spec.ts
index 7fd6a815ae..37511b77fd 100644
--- a/frontend/src/app/common/util/context.spec.ts
+++ b/frontend/src/app/common/util/context.spec.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ContextManager, ObservableContextManager } from "./context";
+import { ContextManager } from "./context";
describe("ContextManager", () => {
it("should return the default context initially", () => {
@@ -106,128 +106,15 @@ describe("ContextManager", () => {
expect(managerB.getContext()).toBe("defaultB");
});
});
-});
-
-describe("ObservableContextManager", () => {
- it("should emit [exiting, entering] on the enter stream when a context is entered", () => {
- const manager = ObservableContextManager<string>("default");
- const enterEvents: [string, string][] = [];
- manager.getEnterStream().subscribe(event => enterEvents.push(event));
-
- manager.withContext("inner", () => {});
-
- expect(enterEvents).toEqual([["default", "inner"]]);
- });
-
- it("should emit [exiting, entering] on the exit stream when a context is exited", () => {
- const manager = ObservableContextManager<string>("default");
- const exitEvents: [string, string][] = [];
- manager.getExitStream().subscribe(event => exitEvents.push(event));
-
- manager.withContext("inner", () => {});
-
- expect(exitEvents).toEqual([["inner", "default"]]);
- });
-
- it("should emit the enter event after the context stack is updated", () => {
- const manager = ObservableContextManager<string>("default");
- const contextsAtEmission: string[] = [];
- manager.getEnterStream().subscribe(() => contextsAtEmission.push(manager.getContext()));
-
- manager.withContext("inner", () => {});
-
- // the new context is already on the stack when the enter event fires
- expect(contextsAtEmission).toEqual(["inner"]);
- });
-
- it("should emit the exit event after the context stack is popped", () => {
- const manager = ObservableContextManager<string>("default");
- const contextsAtEmission: string[] = [];
- manager.getExitStream().subscribe(() => contextsAtEmission.push(manager.getContext()));
-
- manager.withContext("inner", () => {});
-
- // the exited context is already off the stack when the exit event fires
- expect(contextsAtEmission).toEqual(["default"]);
- });
-
- it("should deliver both enter and exit events on the change context stream", () => {
- const manager = ObservableContextManager<string>("default");
- const changeEvents: [string, string][] = [];
- manager.getChangeContextStream().subscribe(event => changeEvents.push(event));
-
- manager.withContext("inner", () => {});
-
- expect(changeEvents).toEqual([
- ["default", "inner"],
- ["inner", "default"],
- ]);
- });
-
- it("should emit events in the correct order across nested withContext calls", () => {
- const manager = ObservableContextManager<string>("default");
- const events: { type: string; event: [string, string] }[] = [];
- manager.getEnterStream().subscribe(event => events.push({ type: "enter", event }));
- manager.getExitStream().subscribe(event => events.push({ type: "exit", event }));
-
- manager.withContext("outer", () => {
- manager.withContext("inner", () => {});
- });
-
- expect(events).toEqual([
- { type: "enter", event: ["default", "outer"] },
- { type: "enter", event: ["outer", "inner"] },
- { type: "exit", event: ["inner", "outer"] },
- { type: "exit", event: ["outer", "default"] },
- ]);
- });
-
- it("should emit enter and exit events when the callable throws", () => {
- const manager = ObservableContextManager<string>("default");
- const changeEvents: [string, string][] = [];
- manager.getChangeContextStream().subscribe(event => changeEvents.push(event));
-
- expect(() =>
- manager.withContext("inner", () => {
- throw new Error("callable failure");
- })
- ).toThrowError("callable failure");
-
- // the exit event still fires because withContext exits in a finally block
- expect(changeEvents).toEqual([
- ["default", "inner"],
- ["inner", "default"],
- ]);
-
- expect(manager.getContext()).toBe("default");
- });
-
- it("should still provide the basic ContextManager behavior", () => {
- const manager = ObservableContextManager<string>("default");
- const result = manager.withContext("inner", () => {
- expect(manager.getContext()).toBe("inner");
- expect(manager.prevContext()).toBe("default");
- return "value";
- });
-
- expect(result).toBe("value");
- expect(manager.getContext()).toBe("default");
- });
-
- it("should preserve object references through the stack and in emitted tuples", () => {
+ it("should preserve object references through the stack", () => {
// mirrors the real JointGraphContextType usage where the context is an object
interface ObjectContext {
readonly name: string;
}
const defaultContext: ObjectContext = { name: "default" };
const innerContext: ObjectContext = { name: "inner" };
- const manager = ObservableContextManager<ObjectContext>(defaultContext);
-
- const enterEvents: [ObjectContext, ObjectContext][] = [];
- const exitEvents: [ObjectContext, ObjectContext][] = [];
- manager.getEnterStream().subscribe(event => enterEvents.push(event));
- manager.getExitStream().subscribe(event => exitEvents.push(event));
+ const manager = ContextManager<ObjectContext>(defaultContext);
// the same reference is returned for the default context
expect(manager.getContext()).toBe(defaultContext);
@@ -240,18 +127,10 @@ describe("ObservableContextManager", () => {
// the default reference is restored after exit
expect(manager.getContext()).toBe(defaultContext);
-
- // emitted tuples carry the original object references
- expect(enterEvents[0][0]).toBe(defaultContext);
- expect(enterEvents[0][1]).toBe(innerContext);
- expect(exitEvents[0][0]).toBe(innerContext);
- expect(exitEvents[0][1]).toBe(defaultContext);
});
it("should unwind both levels via finally blocks when a nested callable throws", () => {
- const manager = ObservableContextManager<string>("default");
- const changeEvents: [string, string][] = [];
- manager.getChangeContextStream().subscribe(event => changeEvents.push(event));
+ const manager = ContextManager<string>("default");
expect(() =>
manager.withContext("outer", () =>
@@ -263,73 +142,20 @@ describe("ObservableContextManager", () => {
// the error propagated all the way out and the stack is fully restored
expect(manager.getContext()).toBe("default");
-
- // both levels' exit events fire during unwinding via their finally blocks
- expect(changeEvents).toEqual([
- ["default", "outer"],
- ["outer", "inner"],
- ["inner", "outer"],
- ["outer", "default"],
- ]);
- });
-
- it("should not replay earlier events to a late subscriber", () => {
- const manager = ObservableContextManager<string>("default");
-
- // trigger a full enter/exit cycle before anyone subscribes
- manager.withContext("inner", () => {});
-
- const enterEvents: [string, string][] = [];
- manager.getEnterStream().subscribe(event => enterEvents.push(event));
-
- // the streams are plain Subjects (no replay), so the late subscriber sees nothing
- expect(enterEvents).toEqual([]);
- });
-
- it("should allow prevContext() to be called inside the enter subscriber", () => {
- const manager = ObservableContextManager<string>("default");
- const prevContexts: string[] = [];
-
- // at enter-emission time the new context is already pushed, so prevContext is valid
- manager.getEnterStream().subscribe(() => prevContexts.push(manager.prevContext()));
-
- manager.withContext("inner", () => {});
-
- expect(prevContexts).toEqual(["default"]);
- });
-
- it("should emit correctly for sequential sibling withContext calls", () => {
- const manager = ObservableContextManager<string>("default");
- const changeEvents: [string, string][] = [];
- manager.getChangeContextStream().subscribe(event => changeEvents.push(event));
-
- manager.withContext("A", () => {});
- manager.withContext("B", () => {});
-
- expect(changeEvents).toEqual([
- ["default", "A"],
- ["A", "default"],
- ["default", "B"],
- ["B", "default"],
- ]);
});
it("should handle entering the same context value as the current one", () => {
- const manager = ObservableContextManager<string>("default");
- const enterEvents: [string, string][] = [];
- manager.getEnterStream().subscribe(event => enterEvents.push(event));
+ const manager = ContextManager<string>("default");
manager.withContext("default", () => {
// dedup is positional (stack depth), not value-based
expect(manager.getContext()).toBe("default");
expect(manager.prevContext()).toBe("default");
});
-
- expect(enterEvents).toEqual([["default", "default"]]);
});
it("should return falsy values produced by the callable unchanged", () => {
- const manager = ObservableContextManager<string>("default");
+ const manager = ContextManager<string>("default");
// guards against an `|| fallback` regression in the return path
expect(manager.withContext("inner", () => undefined)).toBeUndefined();
diff --git a/frontend/src/app/common/util/context.ts b/frontend/src/app/common/util/context.ts
index b6f7a28b12..a122eb84ea 100644
--- a/frontend/src/app/common/util/context.ts
+++ b/frontend/src/app/common/util/context.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-import { merge, Subject } from "rxjs";
-
export function ContextManager<Context>(defaultContext: Context) {
abstract class ContextManager {
private static contextStack: Context[] = [defaultContext];
@@ -54,42 +52,3 @@ export function ContextManager<Context>(defaultContext: Context) {
return ContextManager;
}
-
-export function ObservableContextManager<Context>(defaultContext: Context) {
- abstract class ObservableContextManager extends ContextManager(defaultContext) {
- private static enterStream = new Subject<[exiting: Context, entering: Context]>();
- private static exitStream = new Subject<[exiting: Context, entering: Context]>();
- private static changeContextStream = ObservableContextManager.createChangeContextStream();
-
- public static getEnterStream() {
- return this.enterStream.asObservable();
- }
-
- public static getExitStream() {
- return this.exitStream.asObservable();
- }
-
- public static getChangeContextStream() {
- return this.changeContextStream;
- }
-
- private static createChangeContextStream() {
- return merge(this.getEnterStream(), this.getExitStream());
- }
-
- protected static enter(context: Context): void {
- const oldContext = this.getContext();
- const newContext = context;
- super.enter(context);
- this.enterStream.next([oldContext, newContext]);
- }
-
- protected static exit(): void {
- const oldContext = this.getContext();
- super.exit();
- const newContext = this.getContext();
- this.exitStream.next([oldContext, newContext]);
- }
- }
- return ObservableContextManager;
-}
diff --git a/frontend/src/app/workspace/service/workflow-graph/model/joint-graph-wrapper.ts b/frontend/src/app/workspace/service/workflow-graph/model/joint-graph-wrapper.ts
index ee1fd6a6fd..acb31c4917 100644
--- a/frontend/src/app/workspace/service/workflow-graph/model/joint-graph-wrapper.ts
+++ b/frontend/src/app/workspace/service/workflow-graph/model/joint-graph-wrapper.ts
@@ -23,7 +23,7 @@ import { LogicalPort, Point } from "../../../types/workflow-common.interface";
import * as joint from "jointjs";
import * as dagre from "dagre";
import * as graphlib from "graphlib";
-import { ObservableContextManager } from "src/app/common/util/context";
+import { ContextManager } from "src/app/common/util/context";
import { Coeditor, User } from "../../../../common/type/user";
import { operatorCoeditorChangedPropertyClass, operatorCoeditorEditingClass } from "../../joint-ui/joint-ui.service";
import { HeatmapView } from "../../heatmap/heatmap-scoring";
@@ -817,7 +817,7 @@ export class JointGraphWrapper {
}
public static jointGraphContextFactory() {
- class JointGraphContext extends ObservableContextManager<JointGraphContextType>(DefaultContext) {
+ class JointGraphContext extends ContextManager<JointGraphContextType>(DefaultContext) {
private static jointPaper: joint.dia.Paper | undefined;
public static async() {