Skip to content

Commit 701bbd5

Browse files
Refactor ITree extension interfaces (microsoft#23792)
## Description Refactor ITree extension interfaces to better separate APIs for different consumers of shared tree. This makes it possible to expose APIs like `id` (needed by the dev tools) or `contentSnapshot` (used for unit testing) without exposing the runtime facing APIs like load and summarize. Future refactoring work is planned which will separate the code where these are implemented as part of decoupling most of the tree code from the SharedObject class hierarchy similar to how SharedMap does so with its MapKernel. The change here helps with that planed work.
1 parent 6081e38 commit 701bbd5

File tree

14 files changed

+76
-42
lines changed

14 files changed

+76
-42
lines changed

packages/dds/tree/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export {
8585
ForestTypeReference,
8686
SharedTreeAttributes,
8787
SharedTreeFactoryType,
88+
type IChannelView,
8889
} from "./shared-tree/index.js";
8990

9091
export {
@@ -216,6 +217,7 @@ export {
216217
asTreeViewAlpha,
217218
type NodeSchemaOptions,
218219
type NodeSchemaMetadata,
220+
type ITreeAlpha,
219221
} from "./simple-tree/index.js";
220222
export {
221223
SharedTree,

packages/dds/tree/src/shared-tree/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
export {
77
type ISharedTree,
8+
type ITreePrivate,
89
type SharedTreeOptionsInternal,
910
type SharedTreeOptions,
1011
SharedTree,
@@ -20,6 +21,7 @@ export {
2021
ForestTypeOptimized,
2122
ForestTypeExpensiveDebug,
2223
ForestTypeReference,
24+
type IChannelView,
2325
} from "./sharedTree.js";
2426

2527
export {

packages/dds/tree/src/shared-tree/sharedTree.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
IChannelAttributes,
1010
IFluidDataStoreRuntime,
1111
IChannelStorageService,
12+
IChannel,
1213
} from "@fluidframework/datastore-definitions/internal";
1314
import type { ISharedObject } from "@fluidframework/shared-object-base/internal";
1415
import { UsageError } from "@fluidframework/telemetry-utils/internal";
@@ -72,6 +73,7 @@ import {
7273
FieldKind,
7374
type CustomTreeNode,
7475
type CustomTreeValue,
76+
type ITreeAlpha,
7577
} from "../simple-tree/index.js";
7678

7779
import { SchematizingSimpleTreeView } from "./schematizingTreeView.js";
@@ -84,7 +86,7 @@ import { breakingClass, fail, throwIfBroken } from "../util/index.js";
8486
import type { IIdCompressor } from "@fluidframework/id-compressor";
8587

8688
/**
87-
* Copy of data from an {@link ISharedTree} at some point in time.
89+
* Copy of data from an {@link ITreePrivate} at some point in time.
8890
* @remarks
8991
* This is unrelated to Fluids concept of "snapshots".
9092
*/
@@ -108,30 +110,26 @@ export interface SharedTreeContentSnapshot {
108110
}
109111

110112
/**
111-
* {@link ITree} extended with some non-public APIs.
113+
* Information about a Fluid channel.
114+
* @privateRemarks
115+
* This is distinct from {@link IChannel} as it omits the APIs used by the runtime to manage the channel and instead only has things which are useful (and safe) to expose to users of the channel.
112116
* @internal
113117
*/
114-
export interface ITreeInternal extends ISharedObject, ITree {
115-
/**
116-
* Exports root in the same format as {@link TreeAlpha.(exportVerbose:1)} using stored keys.
117-
* @privateRemarks
118-
* TODO:
119-
* This should probably get promoted to a public API on ITree eventually.
120-
*/
121-
exportVerbose(): VerboseTree | undefined;
118+
export type IChannelView = Pick<IChannel, "id" | "attributes" | "isAttached">;
122119

123-
/**
124-
* Exports the SimpleTreeSchema that is stored in the tree, using stored keys for object fields.
125-
* @remarks
126-
* To get the schema using property keys, use {@link getSimpleSchema} on the view schema.
127-
*/
128-
exportSimpleSchema(): SimpleTreeSchema;
129-
}
120+
/**
121+
* {@link ITree} extended with some non-public APIs.
122+
* @internal
123+
*/
124+
export interface ITreeInternal extends IChannelView, ITreeAlpha {}
130125

131126
/**
132127
* {@link ITreeInternal} extended with some non-exported APIs.
128+
* @remarks
129+
* This allows access to the tree content using the internal data model used at the storage and "flex" layers,
130+
* and should only be needed for testing and debugging this package's internals.
133131
*/
134-
export interface ISharedTree extends ISharedObject, ITreeInternal {
132+
export interface ITreePrivate extends ITreeInternal {
135133
/**
136134
* Provides a copy of the current content of the tree.
137135
* This can be useful for inspecting the tree when no suitable view schema is available.
@@ -142,6 +140,13 @@ export interface ISharedTree extends ISharedObject, ITreeInternal {
142140
contentSnapshot(): SharedTreeContentSnapshot;
143141
}
144142

143+
/**
144+
* {@link ITreePrivate} extended with ISharedObject.
145+
* @remarks
146+
* This is used when integration testing this package with the Fluid runtime as it exposes the APIs the runtime consumes to manipulate the tree.
147+
*/
148+
export interface ISharedTree extends ISharedObject, ITreePrivate {}
149+
145150
/**
146151
* Has an entry for each codec which writes an explicit version into its data.
147152
*

packages/dds/tree/src/simple-tree/api/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export {
1515
type TreeViewAlpha,
1616
type TreeBranch,
1717
type TreeBranchEvents,
18+
type ITreeAlpha,
1819
asTreeViewAlpha,
1920
} from "./tree.js";
2021
export {

packages/dds/tree/src/simple-tree/api/tree.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import { markSchemaMostDerived } from "./schemaFactory.js";
4242
import { fail, getOrCreate } from "../../util/index.js";
4343
import type { MakeNominal } from "../../util/index.js";
4444
import { walkFieldSchema } from "../walkFieldSchema.js";
45+
import type { VerboseTree } from "./verboseTree.js";
46+
import type { SimpleTreeSchema } from "./simpleSchema.js";
4547
/**
4648
* A tree from which a {@link TreeView} can be created.
4749
*
@@ -52,7 +54,6 @@ import { walkFieldSchema } from "../walkFieldSchema.js";
5254
* Maybe rename "exportJsonSchema" to align on "concise" terminology.
5355
* Ensure schema exporting APIs here align and reference APIs for exporting view schema to the same formats (which should include stored vs property key choice).
5456
* Make sure users of independentView can use these export APIs (maybe provide a reference back to the ViewableTree from the TreeView to accomplish that).
55-
* Some of these APIs are on ISharedTree and can get moved here.
5657
* @system @sealed @public
5758
*/
5859
export interface ViewableTree {
@@ -101,6 +102,28 @@ export interface ViewableTree {
101102
*/
102103
export interface ITree extends ViewableTree, IFluidLoadable {}
103104

105+
/**
106+
* {@link ITree} extended with some alpha APIs.
107+
* @privateRemarks
108+
* TODO: Promote this to alpha.
109+
* @internal
110+
*/
111+
export interface ITreeAlpha extends ITree {
112+
/**
113+
* Exports root in the same format as {@link TreeAlpha.(exportVerbose:1)} using stored keys.
114+
* @remarks
115+
* This is `undefined` if and only if the root field is empty (this can only happen if the root field is optional).
116+
*/
117+
exportVerbose(): VerboseTree | undefined;
118+
119+
/**
120+
* Exports the SimpleTreeSchema that is stored in the tree, using stored keys for object fields.
121+
* @remarks
122+
* To get the schema using property keys, use {@link getSimpleSchema} on the view schema.
123+
*/
124+
exportSimpleSchema(): SimpleTreeSchema;
125+
}
126+
104127
/**
105128
* Options when constructing a tree view.
106129
* @public

packages/dds/tree/src/simple-tree/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export {
124124
type CustomTreeNode,
125125
type CustomTreeValue,
126126
tryStoredSchemaAsArray,
127+
type ITreeAlpha,
127128
} from "./api/index.js";
128129
export {
129130
type NodeFromSchema,

packages/dds/tree/src/test/feature-libraries/node-key/nodeKey.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import {
1616
createNodeKeyManager,
1717
MockNodeKeyManager,
1818
} from "../../../feature-libraries/index.js";
19-
import type { ISharedTree } from "../../../shared-tree/index.js";
19+
import type { ITreePrivate } from "../../../shared-tree/index.js";
2020
import { TestTreeProvider } from "../../utils.js";
2121

2222
/**
2323
* Acquire an {@link IIdCompressor} via unsavory means.
2424
* @remarks TODO: Figure out a better way to get an IIDCompressor
2525
*/
26-
async function getIIDCompressor(tree?: ISharedTree): Promise<IIdCompressor> {
26+
async function getIIDCompressor(tree?: ITreePrivate): Promise<IIdCompressor> {
2727
const runtime = (
2828
(tree ?? (await TestTreeProvider.create(1)).trees[0]) as unknown as {
2929
runtime: IFluidDataStoreRuntime;

packages/dds/tree/src/test/shared-tree/fuzz/fuzzEditGenerators.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type {
2424
TreeNodeSchemaIdentifier,
2525
} from "../../../core/index.js";
2626
import { type DownPath, toDownPath } from "../../../feature-libraries/index.js";
27-
import { Tree, type ISharedTree, type SharedTree } from "../../../shared-tree/index.js";
27+
import { Tree, type ITreePrivate, type SharedTree } from "../../../shared-tree/index.js";
2828
import { fail, getOrCreate, makeArray } from "../../../util/index.js";
2929

3030
import {
@@ -109,7 +109,7 @@ export interface FuzzTestState extends DDSFuzzTestState<TreeFactory> {
109109
* Maintaining a separate view here is necessary since async transactions are not supported on the root checkout,
110110
* and the fuzz testing model only simulates async transactions.
111111
*/
112-
transactionViews?: Map<ISharedTree, FuzzTransactionView>;
112+
transactionViews?: Map<ITreePrivate, FuzzTransactionView>;
113113
}
114114

115115
export function viewFromState(

packages/dds/tree/src/test/shared-tree/fuzz/fuzzUtils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
TreeViewConfiguration,
3737
type TreeNodeSchema,
3838
type ValidateRecursiveSchema,
39+
type ViewableTree,
3940
} from "../../../simple-tree/index.js";
4041
import type { IFluidHandle } from "@fluidframework/core-interfaces";
4142

@@ -166,16 +167,16 @@ export class SharedTreeFuzzTestFactory extends SharedTreeTestFactory {
166167
}
167168
}
168169

169-
export const FuzzTestOnCreate = (tree: SharedTree) => {
170+
export const FuzzTestOnCreate = (tree: ViewableTree) => {
170171
const view = tree.viewWith(new TreeViewConfiguration({ schema: initialFuzzSchema }));
171172
view.initialize(populatedInitialState);
172173
view.dispose();
173174
};
174175

175176
export function createOnCreate(
176177
initialState: NodeBuilderData<typeof FuzzNode> | undefined,
177-
): (tree: SharedTree) => void {
178-
return (tree: SharedTree) => {
178+
): (tree: ViewableTree) => void {
179+
return (tree: ViewableTree) => {
179180
const view = tree.viewWith(new TreeViewConfiguration({ schema: initialFuzzSchema }));
180181
view.initialize(initialState);
181182
view.dispose();

packages/dds/tree/src/test/shared-tree/opSize.bench.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
import type { Value } from "../../core/index.js";
2222
import { typeboxValidator } from "../../external-utilities/index.js";
2323
import { TreeCompressionStrategy } from "../../feature-libraries/index.js";
24-
import { Tree, type ISharedTree, type SharedTree } from "../../shared-tree/index.js";
24+
import { Tree, type ITreePrivate } from "../../shared-tree/index.js";
2525
import { type JsonCompatibleReadOnly, getOrAddEmptyToMap } from "../../util/index.js";
2626
import { treeTestFactory } from "../utils.js";
2727
import {
@@ -52,7 +52,7 @@ class Parent extends schemaFactory.array("Test:Opsize-Bench-Root", Child) {}
5252
/**
5353
* Create a default attached tree for op submission
5454
*/
55-
function createConnectedTree(): SharedTree {
55+
function createConnectedTree(): ITreePrivate {
5656
const containerRuntimeFactory = new MockContainerRuntimeFactory();
5757
const dataStoreRuntime = new MockFluidDataStoreRuntime({
5858
idCompressor: createIdCompressor(),
@@ -299,7 +299,7 @@ describe("Op Size", () => {
299299
const currentTestOps: ISequencedDocumentMessage[] = [];
300300

301301
function registerOpListener(
302-
tree: ISharedTree,
302+
tree: ITreePrivate,
303303
resultArray: ISequencedDocumentMessage[],
304304
): void {
305305
// TODO: better way to hook this up. Needs to detect local ops exactly once.
@@ -332,7 +332,7 @@ describe("Op Size", () => {
332332
};
333333
};
334334

335-
const initializeOpDataCollection = (tree: ISharedTree) => {
335+
const initializeOpDataCollection = (tree: ITreePrivate) => {
336336
currentTestOps.length = 0;
337337
registerOpListener(tree, currentTestOps);
338338
};

packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import {
5151
ForestTypeOptimized,
5252
ForestTypeReference,
5353
getBranch,
54-
type ISharedTree,
54+
type ITreePrivate,
5555
type SharedTree,
5656
Tree,
5757
type TreeCheckout,
@@ -1861,7 +1861,7 @@ describe("SharedTree", () => {
18611861
pendingOps,
18621862
);
18631863
const dataStore = (await loadedContainer.getEntryPoint()) as ITestFluidObject;
1864-
const tree = await dataStore.getSharedObject<ISharedTree>("TestSharedTree");
1864+
const tree = await dataStore.getSharedObject<ITreePrivate>("TestSharedTree");
18651865
await waitForContainerConnection(loadedContainer, true);
18661866
await provider.ensureSynchronized();
18671867

packages/dds/tree/src/test/snapshots/opFormat.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from "@fluidframework/test-runtime-utils/internal";
1515
import { takeJsonSnapshot, useSnapshotDirectory } from "./snapshotTools.js";
1616
import { SchemaFactory, TreeViewConfiguration } from "../../simple-tree/index.js";
17-
import { type SharedTree, SharedTreeFormatVersion } from "../../shared-tree/index.js";
17+
import { type ISharedTree, SharedTreeFormatVersion } from "../../shared-tree/index.js";
1818
import type { JsonCompatibleReadOnly } from "../../util/index.js";
1919
import { TreeFactory } from "../../treeFactory.js";
2020

@@ -40,7 +40,7 @@ describe("SharedTree op format snapshots", () => {
4040
}) {}
4141

4242
let containerRuntime: MockContainerRuntime;
43-
let tree: SharedTree;
43+
let tree: ISharedTree;
4444

4545
for (const versionKey of Object.keys(SharedTreeFormatVersion)) {
4646
describe(`using SharedTreeFormatVersion.${versionKey}`, () => {

packages/dds/tree/src/test/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ import { makeSchemaCodec } from "../feature-libraries/schema-index/codec.js";
114114
import {
115115
type CheckoutEvents,
116116
CheckoutFlexTreeView,
117-
type ISharedTree,
117+
type ITreePrivate,
118118
type ITreeCheckout,
119119
SharedTree,
120120
type SharedTreeContentSnapshot,
@@ -282,7 +282,7 @@ export class TestTreeProvider {
282282
}
283283

284284
/**
285-
* Create and initialize a new {@link ISharedTree} that is connected to all other trees from this provider.
285+
* Create and initialize a new {@link ITreePrivate} that is connected to all other trees from this provider.
286286
* @returns the tree that was created. For convenience, the tree can also be accessed via `this[i]` where
287287
* _i_ is the index of the tree in order of creation.
288288
*/
@@ -323,7 +323,7 @@ export class TestTreeProvider {
323323
return summarizeNow(this.summarizer, "TestTreeProvider");
324324
}
325325

326-
public [Symbol.iterator](): IterableIterator<ISharedTree> {
326+
public [Symbol.iterator](): IterableIterator<ITreePrivate> {
327327
return this.trees[Symbol.iterator]();
328328
}
329329

@@ -564,7 +564,7 @@ export function checkRemovedRootsAreSynchronized(trees: readonly ITreeCheckout[]
564564
* This does NOT check that the trees have the same edits, same edit manager state or anything like that.
565565
* This ONLY checks if the content of the forest of the main branch of the trees match.
566566
*/
567-
export function validateTreeConsistency(treeA: ISharedTree, treeB: ISharedTree): void {
567+
export function validateTreeConsistency(treeA: ITreePrivate, treeB: ITreePrivate): void {
568568
// TODO: validate other aspects of these trees are consistent, for example their collaboration window information.
569569
validateSnapshotConsistency(
570570
treeA.contentSnapshot(),

packages/tools/devtools/devtools-core/src/data-visualization/DefaultVisualizers.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@ import {
1515
type ISharedMap,
1616
SharedMap,
1717
type ISharedDirectory,
18-
// eslint-disable-next-line import/no-deprecated
1918
SharedDirectory,
2019
} from "@fluidframework/map/internal";
2120
import { SharedMatrix } from "@fluidframework/matrix/internal";
2221
import { SharedString } from "@fluidframework/sequence/internal";
2322
import type { ISharedObject } from "@fluidframework/shared-object-base/internal";
24-
import type { ITreeInternal } from "@fluidframework/tree/internal";
23+
import type { ITreeInternal, IChannelView } from "@fluidframework/tree/internal";
2524
import { SharedTree } from "@fluidframework/tree/internal";
2625

2726
import { EditType } from "../CommonInterfaces.js";
@@ -250,13 +249,13 @@ export const visualizeSharedString: VisualizeSharedObject = async (
250249
};
251250

252251
/**
253-
* {@link VisualizeSharedObject} for {@link ISharedTree}.
252+
* {@link VisualizeSharedObject} for {@link ITree}.
254253
*/
255254
export const visualizeSharedTree: VisualizeSharedObject = async (
256255
sharedObject: ISharedObject,
257256
visualizeChildData: VisualizeChildData,
258257
): Promise<FluidObjectNode> => {
259-
const sharedTree = sharedObject as ITreeInternal;
258+
const sharedTree = sharedObject as IChannelView as ITreeInternal;
260259

261260
// Root node of the SharedTree's content.
262261
const treeView = sharedTree.exportVerbose();

0 commit comments

Comments
 (0)