Skip to content

Commit 3f62921

Browse files
abstraktorslisson
authored andcommitted
chore(vue-model-api): restore old useReplicatedModel implementation as a fallback for now
1 parent 2845e0c commit 3f62921

File tree

4 files changed

+295
-70
lines changed

4 files changed

+295
-70
lines changed

vue-model-api/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { useModelsFromJson } from "./useModelsFromJson";
22
export { useModelClient } from "./useModelClient";
3-
export { useReplicatedModels, useReplicatedModel } from "./useReplicatedModels";
3+
export { useReplicatedModels } from "./useReplicatedModels";
4+
export { useReplicatedModel } from "./useReplicatedModel";
45
export { useReadonlyVersion } from "./useReadonlyVersion";

vue-model-api/src/useReplicatedModel.test.ts

Lines changed: 163 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { org } from "@modelix/model-client";
22
import { toRoleJS } from "@modelix/ts-model-api";
33
import { watchEffect } from "vue";
44
import { useModelClient } from "./useModelClient";
5-
import { useReplicatedModel } from "./useReplicatedModels";
5+
import { useReplicatedModel } from "./useReplicatedModel";
66
import IdSchemeJS = org.modelix.model.client2.IdSchemeJS;
77

88
type ClientJS = org.modelix.model.client2.ClientJS;
@@ -13,7 +13,33 @@ const { loadModelsFromJson } = org.modelix.model.client2;
1313
import ReplicatedModelParameters = org.modelix.model.client2.ReplicatedModelParameters;
1414

1515
test("test wrapper backwards compatibility", (done) => {
16-
class SuccessfulClientJS {
16+
class SuccessfulClientJS implements ClientJS {
17+
startReplicatedModel(
18+
repositoryId: string,
19+
branchId: string,
20+
idScheme: org.modelix.model.client2.IdSchemeJS,
21+
): Promise<org.modelix.model.client2.ReplicatedModelJS> {
22+
// Mock implementation that returns a dummy object with a branch
23+
const rootNode = loadModelsFromJson([JSON.stringify({ root: {} })]);
24+
rootNode.setPropertyValue(toRoleJS("branchId"), branchId);
25+
26+
const branch = {
27+
rootNode,
28+
getRootNodes: () => [rootNode],
29+
addListener: jest.fn(),
30+
removeListener: jest.fn(),
31+
resolveNode: jest.fn(),
32+
};
33+
34+
const replicatedModel = {
35+
getBranch: () => branch,
36+
dispose: jest.fn(),
37+
getCurrentVersionInformation: jest.fn(),
38+
} as unknown as ReplicatedModelJS;
39+
40+
return Promise.resolve(replicatedModel);
41+
}
42+
1743
startReplicatedModels(
1844
parameters: ReplicatedModelParameters[],
1945
): Promise<ReplicatedModelJS> {
@@ -38,6 +64,141 @@ test("test wrapper backwards compatibility", (done) => {
3864

3965
return Promise.resolve(replicatedModel);
4066
}
67+
68+
readonly __doNotUseOrImplementIt: any;
69+
70+
createBranch(
71+
repositoryId: string,
72+
branchId: string,
73+
versionHash: string,
74+
): Promise<void> {
75+
throw Error("Not implemented");
76+
}
77+
78+
deleteBranch(repositoryId: string, branchId: string): Promise<boolean> {
79+
throw Error("Not implemented");
80+
}
81+
82+
diffAsMutationParameters(
83+
repositoryId: string,
84+
newVersion: string,
85+
oldVersion: string,
86+
): Promise<Array<org.modelix.model.client2.MutationParametersJS>> {
87+
throw Error("Not implemented");
88+
}
89+
90+
dispose(): void {
91+
throw Error("Not implemented");
92+
}
93+
94+
fetchBranches(repositoryId: string): Promise<Array<string>> {
95+
throw Error("Not implemented");
96+
}
97+
98+
fetchBranchesWithHashes(
99+
repositoryId: string,
100+
): Promise<Array<org.modelix.model.server.api.BranchInfo>> {
101+
throw Error("Not implemented");
102+
}
103+
104+
fetchRepositories(): Promise<Array<string>> {
105+
throw Error("Not implemented");
106+
}
107+
108+
getHistoryForFixedIntervals(
109+
repositoryId: string,
110+
headVersion: string,
111+
intervalDurationSeconds: number,
112+
skip: number,
113+
limit: number,
114+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
115+
throw Error("Not implemented");
116+
}
117+
118+
getHistoryForFixedIntervalsForBranch(
119+
repositoryId: string,
120+
branchId: string,
121+
intervalDurationSeconds: number,
122+
skip: number,
123+
limit: number,
124+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
125+
throw Error("Not implemented");
126+
}
127+
128+
getHistoryForProvidedIntervals(
129+
repositoryId: string,
130+
headVersion: string,
131+
splitAt: Array<Date>,
132+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
133+
throw Error("Not implemented");
134+
}
135+
136+
getHistoryForProvidedIntervalsForBranch(
137+
repositoryId: string,
138+
branchId: string,
139+
splitAt: Array<Date>,
140+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
141+
throw Error("Not implemented");
142+
}
143+
144+
getHistoryRange(
145+
repositoryId: string,
146+
headVersion: string,
147+
skip: number,
148+
limit: number,
149+
): Promise<Array<org.modelix.model.client2.VersionInformationJS>> {
150+
throw Error("Not implemented");
151+
}
152+
153+
getHistoryRangeForBranch(
154+
repositoryId: string,
155+
branchId: string,
156+
skip: number,
157+
limit: number,
158+
): Promise<Array<org.modelix.model.client2.VersionInformationJS>> {
159+
throw Error("Not implemented");
160+
}
161+
162+
getHistorySessions(
163+
repositoryId: string,
164+
headVersion: string,
165+
delaySeconds: number,
166+
skip: number,
167+
limit: number,
168+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
169+
throw Error("Not implemented");
170+
}
171+
172+
getHistorySessionsForBranch(
173+
repositoryId: string,
174+
branchId: string,
175+
delaySeconds: number,
176+
skip: number,
177+
limit: number,
178+
): Promise<Array<org.modelix.model.client2.HistoryIntervalJS>> {
179+
throw Error("Not implemented");
180+
}
181+
182+
initRepository(repositoryId: string, useRoleIds?: boolean): Promise<void> {
183+
throw Error("Not implemented");
184+
}
185+
186+
loadReadonlyVersion(
187+
repositoryId: string,
188+
versionHash: string,
189+
): Promise<org.modelix.model.client2.VersionInformationWithModelTree> {
190+
throw Error("Not implemented");
191+
}
192+
193+
revertTo(
194+
repositoryId: string,
195+
branchId: string,
196+
targetVersionHash: string,
197+
): Promise<string> {
198+
throw Error("Not implemented");
199+
}
200+
201+
setClientProvidedUserId(userId: string): void {}
41202
}
42203

43204
const { client } = useModelClient("anURL", () =>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import type { org } from "@modelix/model-client";
2+
import type { INodeJS } from "@modelix/ts-model-api";
3+
import { useLastPromiseEffect } from "./internal/useLastPromiseEffect";
4+
import type { MaybeRefOrGetter, Ref } from "vue";
5+
import { shallowRef, toValue } from "vue";
6+
import type { ReactiveINodeJS } from "./internal/ReactiveINodeJS";
7+
import { toReactiveINodeJS } from "./internal/ReactiveINodeJS";
8+
import { Cache } from "./internal/Cache";
9+
import { handleChange } from "./internal/handleChange";
10+
11+
type ClientJS = org.modelix.model.client2.ClientJS;
12+
type ReplicatedModelJS = org.modelix.model.client2.ReplicatedModelJS;
13+
type ChangeJS = org.modelix.model.client2.ChangeJS;
14+
15+
function isDefined<T>(value: T | null | undefined): value is T {
16+
return value !== null && value !== undefined;
17+
}
18+
19+
/**
20+
* Creates a replicated model for a given repository and branch.
21+
* A replicated model exposes a branch that can be used to read and write model data.
22+
* The written model data is automatically synced to the model server.
23+
* Changed from the model server are automatically synced to the branch in the replicated model
24+
*
25+
* Also creates root node that uses Vues reactivity and can be used in Vue like a reactive object.
26+
* Changes to model data trigger recalculation of computed properties or re-rendering of components using that data.
27+
*
28+
* Calling the returned dispose function stops syncing the root node to the underlying branch on the server.
29+
*
30+
* @param client - Reactive reference of a client to a model server.
31+
* @param repositoryId - Reactive reference of a repositoryId on the model server.
32+
* @param branchId - Reactive reference of a branchId in the repository of the model server.
33+
*
34+
* @returns {Object} values Wrapper around different returned values.
35+
* @returns {Ref<ReplicatedModelJS | null>} values.rootNode Reactive reference to the replicated model for the specified branch.
36+
* @returns {Ref<INodeJS | null>} values.rootNode Reactive reference to the root node with Vue.js reactivity for the specified branch.
37+
* @returns {() => void} values.dispose A function to manually dispose the root node.
38+
* @returns {Ref<unknown>} values.error Reactive reference to a connection error.
39+
*/
40+
export function useReplicatedModel(
41+
client: MaybeRefOrGetter<ClientJS | null | undefined>,
42+
repositoryId: MaybeRefOrGetter<string | null | undefined>,
43+
branchId: MaybeRefOrGetter<string | null | undefined>,
44+
idScheme: MaybeRefOrGetter<
45+
org.modelix.model.client2.IdSchemeJS | null | undefined
46+
>,
47+
): {
48+
replicatedModel: Ref<ReplicatedModelJS | null>;
49+
rootNode: Ref<INodeJS | null>;
50+
dispose: () => void;
51+
error: Ref<unknown>;
52+
} {
53+
// Use `replicatedModel` to access the replicated model without tracking overhead of Vue.js.
54+
let replicatedModel: ReplicatedModelJS | null = null;
55+
const replicatedModelRef: Ref<ReplicatedModelJS | null> = shallowRef(null);
56+
const rootNodeRef: Ref<INodeJS | null> = shallowRef(null);
57+
const errorRef: Ref<unknown> = shallowRef(null);
58+
59+
const dispose = () => {
60+
// Using `replicatedModelRef.value` here would create a circular dependency.
61+
// `toRaw` does not work on `Ref<>`.
62+
if (replicatedModel !== null) {
63+
replicatedModel.dispose();
64+
}
65+
replicatedModelRef.value = null;
66+
rootNodeRef.value = null;
67+
errorRef.value = null;
68+
};
69+
70+
useLastPromiseEffect(
71+
() => {
72+
dispose();
73+
const clientValue = toValue(client);
74+
if (!isDefined(clientValue)) {
75+
return;
76+
}
77+
const repositoryIdValue = toValue(repositoryId);
78+
if (!isDefined(repositoryIdValue)) {
79+
return;
80+
}
81+
const branchIdValue = toValue(branchId);
82+
if (!isDefined(branchIdValue)) {
83+
return;
84+
}
85+
const idSchemeValue = toValue(idScheme);
86+
if (!isDefined(idSchemeValue)) {
87+
return;
88+
}
89+
const cache = new Cache<ReactiveINodeJS>();
90+
return clientValue
91+
.startReplicatedModel(repositoryIdValue, branchIdValue, idSchemeValue)
92+
.then((replicatedModel) => ({ replicatedModel, cache }));
93+
},
94+
(
95+
{ replicatedModel: connectedReplicatedModel, cache },
96+
isResultOfLastStartedPromise,
97+
) => {
98+
if (isResultOfLastStartedPromise) {
99+
replicatedModel = connectedReplicatedModel;
100+
const branch = replicatedModel.getBranch();
101+
branch.addListener((change: ChangeJS) => {
102+
if (cache === null) {
103+
throw Error("The cache is unexpectedly not set up.");
104+
}
105+
handleChange(change, cache);
106+
});
107+
const unreactiveRootNode = branch.rootNode;
108+
const reactiveRootNode = toReactiveINodeJS(unreactiveRootNode, cache);
109+
replicatedModelRef.value = replicatedModel;
110+
rootNodeRef.value = reactiveRootNode;
111+
} else {
112+
connectedReplicatedModel.dispose();
113+
}
114+
},
115+
(reason, isResultOfLastStartedPromise) => {
116+
if (isResultOfLastStartedPromise) {
117+
errorRef.value = reason;
118+
}
119+
},
120+
);
121+
122+
return {
123+
replicatedModel: replicatedModelRef,
124+
rootNode: rootNodeRef,
125+
dispose,
126+
error: errorRef,
127+
};
128+
}

0 commit comments

Comments
 (0)