Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to PureDataObjectFactory & data store creation flows; introduction of detached data store context #3112

Merged
merged 30 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3a67577
Get rid of createDataStoreWithRealizationFn() completely.
vladsud Jul 31, 2020
2d28d90
Removing scope from initializingFirstTime().
vladsud Jul 31, 2020
08a2f33
unfinished
vladsud Aug 2, 2020
d28d346
Merge branch 'master' of https://github.com/microsoft/FluidFramework …
vladsud Aug 7, 2020
86d01e4
Build breaks after merge & PR feedback
vladsud Aug 7, 2020
2af6695
Back compat:: remove assert for now.
vladsud Aug 7, 2020
e984172
Detached creation flow
vladsud Aug 8, 2020
c4cc9c6
Make IFluidDataStoreFactory.type non-optional
vladsud Aug 8, 2020
940fde7
Remove IFluidDataStoreRegistry from FluidDataStoreRuntime
vladsud Aug 8, 2020
c3370df
Fix UT
vladsud Aug 8, 2020
d398554
Improve createInstance workflow
vladsud Aug 9, 2020
c1973a1
Exploration into using sub-factories instead of names
vladsud Aug 10, 2020
65b62bd
createInstance() can take IContainerRuntimeBase in addition to contex…
vladsud Aug 10, 2020
434c303
Address one of the UTs
vladsud Aug 10, 2020
273290f
Remove AnonymousPureDataObjectFactory as not used
vladsud Aug 10, 2020
5e3f27a
Merge branch 'master' of https://github.com/microsoft/FluidFramework …
vladsud Aug 13, 2020
6dc11a9
Build breaks and incorrect conflict resolution
vladsud Aug 13, 2020
28b0cb5
Move validation to runtime
vladsud Aug 13, 2020
9fc4b0f
Merge branch 'master' of https://github.com/microsoft/FluidFramework …
vladsud Aug 13, 2020
5c359a1
Cleanup
vladsud Aug 13, 2020
dfd5e71
Fix UTs, Break out createInstance API into multiple
vladsud Aug 13, 2020
0cdcf35
Merge branch 'master' of https://github.com/microsoft/FluidFramework …
vladsud Aug 13, 2020
da018d1
Incorrect merges in breaking.md
vladsud Aug 13, 2020
5ab5979
Merge marker
vladsud Aug 13, 2020
e167a86
Comments, cleanup
vladsud Aug 14, 2020
dd0d502
PR feedback
vladsud Aug 14, 2020
b23a9b3
Merge branch 'main' of https://github.com/microsoft/FluidFramework in…
vladsud Aug 29, 2020
8eb33a7
Merge branch 'main' of https://github.com/microsoft/FluidFramework in…
vladsud Aug 29, 2020
8e45fb3
Fix build & tests
vladsud Aug 31, 2020
afb4ad8
self-review
vladsud Aug 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- [IComponentContextLegacy is removed](#IComponentContextLegacy-is-removed)
- [IContainerRuntimeBase._createDataStoreWithProps() is removed](#IContainerRuntimeBase._createDataStoreWithProps-is-removed)
- [_createDataStore() APIs are removed](#_createDataStore-APIs-are-removed)
- [createDataStoreWithRealizationFn() APIs moved](#createDataStoreWithRealizationFn()-APIs-moved)
- [createDataStoreWithRealizationFn() APIs are removed](#createDataStoreWithRealizationFn()-APIs-are-removed)
- [getDataStore() APIs is removed](#getDataStore()-APIs-is-removed)
- [Package Renames](#package-renames)
- [IComponent and IComponent Interfaces Removed](#IComponent-and-IComponent-Interfaces-Removed)
Expand Down Expand Up @@ -40,9 +40,10 @@ Please switch to using one of the following APIs:
1. `IContainerRuntime.createRootDataStore()` - data store created that way is automatically bound to container. It will immediately be visible to remote clients (when/if container is attached). Such data stores are never garbage collected. Note that this API is on `IContainerRuntime` interface, which is not directly accessible to data stores. The intention is that only container owners are creating roots.
2. `IContainerRuntimeBase.createDataStore()` - creates data store that is not bound to container. In order for this store to be bound to container (and thus be observable on remote clients), ensure that handle to it (or any of its objects / DDS) is stored into any other DDS that is already bound to container. In other words, newly created data store has to be reachable (there has to be a path) from some root data store in container. If, in future, such data store becomes unreachable from one of the roots, it will be garbage collected (implementation pending).

### createDataStoreWithRealizationFn() APIs moved
### createDataStoreWithRealizationFn() APIs are removed
Removed from IFluidDataStoreContext & IContainerRuntime.
Temporarily exposed on IContainerRuntimeBase. The intent is to remove it altogether in same release (more info to follow)
Consider using (Pure)DataObject(Factory) for your objects - they support passing initial args.
Otherwise consider implementing similar flow of exposing interface from your fluid object that is used to initialize object after creation.

## getDataStore() APIs is removed
IContainerRuntime.getDataStore() is removed. Only IContainerRuntime.getRootDataStore() is available to retrieve root data stores.
Expand Down
7 changes: 3 additions & 4 deletions examples/apps/spaces/src/fluid-object/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Layout } from "react-grid-layout";
import {
DataObject,
DataObjectFactory,
getFluidObjectFactoryFromInstance,
} from "@fluidframework/aqueduct";
import {
IFluidHandle,
Expand Down Expand Up @@ -126,8 +127,7 @@ export class Spaces extends DataObject implements IFluidHTMLView {
}

protected async initializingFirstTime() {
const storageComponent =
await this.createFluidObject<SpacesStorage<ISpacesItem>>(SpacesStorage.ComponentName);
const storageComponent = await SpacesStorage.getFactory().createChildInstance(this.context);
this.root.set(SpacesStorageKey, storageComponent.handle);
// Set the saved template if there is a template query param
const urlParams = new URLSearchParams(window.location.search);
Expand Down Expand Up @@ -184,8 +184,7 @@ export class Spaces extends DataObject implements IFluidHTMLView {
throw new Error("Unknown item, can't add");
}

// Don't really want to hand out createFluidObject here, see spacesItemMap.ts for more info.
const serializableObject = await itemMapEntry.create(this.createFluidObject.bind(this));
const serializableObject = await itemMapEntry.create(getFluidObjectFactoryFromInstance(this.context));
return this.storageComponent.addItem(
{
serializableObject,
Expand Down
29 changes: 14 additions & 15 deletions examples/apps/spaces/src/fluid-object/spacesItemMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,34 @@
* Licensed under the MIT License.
*/

import { IFluidObject, IFluidHandle, IFluidLoadable } from "@fluidframework/core-interfaces";
import { IFluidHandle, IFluidLoadable } from "@fluidframework/core-interfaces";
import { AsSerializable, Serializable } from "@fluidframework/datastore-definitions";
import { NamedFluidDataStoreRegistryEntries } from "@fluidframework/runtime-definitions";
import { NamedFluidDataStoreRegistryEntries, IFluidDataStoreFactory } from "@fluidframework/runtime-definitions";
import { ReactViewAdapter } from "@fluidframework/view-adapters";
import { fluidExport as cmfe } from "@fluid-example/codemirror/dist/codemirror";
import { CollaborativeText } from "@fluid-example/collaborative-textarea";
import { Coordinate } from "@fluid-example/multiview-coordinate-model";
import { SliderCoordinateView } from "@fluid-example/multiview-slider-coordinate-view";
import { fluidExport as pmfe } from "@fluid-example/prosemirror/dist/prosemirror";
import { ClickerInstantiationFactory } from "@fluid-example/clicker";
import { IFluidDataObjectFactory } from "@fluidframework/aqueduct";

import * as React from "react";
import { Layout } from "react-grid-layout";

export type ICreateAndAttachComponentFunction =
<T extends IFluidObject & IFluidLoadable>(pkg: string, props?: any) => Promise<T>;

interface ISingleHandleItem {
handle: IFluidHandle;
}

const createSingleHandleItem = (type: string) => {
return async (createFluidObject: ICreateAndAttachComponentFunction): Promise<ISingleHandleItem> => {
const component = await createFluidObject(type);
// eslint-disable-next-line @typescript-eslint/promise-function-async, prefer-arrow/prefer-arrow-functions
function createSingleHandleItem(subFactory: IFluidDataStoreFactory) {
return async (dataObjectFactory: IFluidDataObjectFactory): Promise<ISingleHandleItem> => {
const component = await dataObjectFactory.createAnonymousChildInstance<IFluidLoadable>(subFactory);
return {
handle: component.handle,
};
};
};
}

const getAdaptedViewForSingleHandleItem = async (serializableObject: ISingleHandleItem) => {
const handle = serializableObject.handle;
Expand All @@ -51,42 +50,42 @@ const getSliderCoordinateView = async (serializableObject: ISingleHandleItem) =>
export interface ISpacesItemEntry<T extends Serializable = AsSerializable<any>> {
// Would be better if items to bring their own subregistries, and their own ability to create components
// This might be done by integrating these items with the Spaces subcomponent registry?
create: (createFluidObject: ICreateAndAttachComponentFunction) => Promise<T>;
create: (createSubObject: IFluidDataObjectFactory) => Promise<T>;
getView: (serializableObject: T) => Promise<JSX.Element>;
friendlyName: string;
fabricIconName: string;
}

const clickerItemEntry: ISpacesItemEntry<AsSerializable<ISingleHandleItem>> = {
create: createSingleHandleItem(ClickerInstantiationFactory.type),
create: createSingleHandleItem(ClickerInstantiationFactory),
getView: getAdaptedViewForSingleHandleItem,
friendlyName: "Clicker",
fabricIconName: "Touch",
};

const codemirrorItemEntry: ISpacesItemEntry<AsSerializable<ISingleHandleItem>> = {
create: createSingleHandleItem(cmfe.type),
create: createSingleHandleItem(cmfe),
getView: getAdaptedViewForSingleHandleItem,
friendlyName: "Code",
fabricIconName: "Code",
};

const textboxItemEntry: ISpacesItemEntry<AsSerializable<ISingleHandleItem>> = {
create: createSingleHandleItem(CollaborativeText.ComponentName),
create: createSingleHandleItem(CollaborativeText.getFactory()),
getView: getAdaptedViewForSingleHandleItem,
friendlyName: "Text Box",
fabricIconName: "Edit",
};

const prosemirrorItemEntry: ISpacesItemEntry<AsSerializable<ISingleHandleItem>> = {
create: createSingleHandleItem(pmfe.type),
create: createSingleHandleItem(pmfe),
getView: getAdaptedViewForSingleHandleItem,
friendlyName: "Rich Text",
fabricIconName: "FabricTextHighlight",
};

const sliderCoordinateItemEntry: ISpacesItemEntry<AsSerializable<ISingleHandleItem>> = {
create: createSingleHandleItem(Coordinate.getFactory().type),
create: createSingleHandleItem(Coordinate.getFactory()),
getView: getSliderCoordinateView,
friendlyName: "Coordinate",
fabricIconName: "NumberSymbol",
Expand Down
3 changes: 2 additions & 1 deletion examples/data-objects/codemirror/src/codemirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class SmdeFactory implements IFluidDataStoreFactory {

public get IFluidDataStoreFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
const sequenceFactory = SharedString.getFactory();
Expand All @@ -285,6 +285,7 @@ class SmdeFactory implements IFluidDataStoreFactory {
const progressCollection = await progressCollectionP;
return progressCollection.request(request);
});
return runtime;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class ExternalComponentLoader extends DataObject {
* @param componentUrl - the URL of the component to create, adding it to the registry if needed.
*/
public async createComponentFromUrl(componentUrl: string): Promise<IFluidLoadable> {
const urlReg = await this.runtime.IFluidDataStoreRegistry?.get("url");
const urlReg = await this.context.IFluidDataStoreRegistry?.get("url");
if (urlReg?.IFluidDataStoreRegistry === undefined) {
throw new Error("Couldn't get url component registry");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import {
IFluidCodeDetails,
} from "@fluidframework/container-definitions";
import {
IProvideFluidDataStoreRegistry,
FluidDataStoreRegistryEntry,
IFluidDataStoreRegistry,
} from "@fluidframework/runtime-definitions";
import { IFluidObject } from "@fluidframework/core-interfaces";
import { WebCodeLoader, SemVerCdnCodeResolver } from "@fluidframework/web-code-loader";

/**
* A component registry that can load component via their url
*/
export class UrlRegistry implements IFluidDataStoreRegistry {
export class UrlRegistry implements IProvideFluidDataStoreRegistry {
private static readonly WindowKeyPrefix = "FluidExternalComponent";

private readonly urlRegistryMap = new Map<string, Promise<FluidDataStoreRegistryEntry | undefined>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ export class WaterPark extends DataObject implements IFluidHTMLView {
}

protected async initializingFirstTime() {
const storage = await this.createFluidObject(SpacesStorage.ComponentName);
const storage = await SpacesStorage.getFactory().createChildInstance(this.context);
this.root.set(storageKey, storage.handle);
const loader = await this.createFluidObject(ExternalComponentLoader.ComponentName);
const loader = await ExternalComponentLoader.getFactory().createChildInstance(this.context);
this.root.set(loaderKey, loader.handle);
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/key-value-cache/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class KeyValueFactoryComponent implements IRuntimeFactory, IFluidDataStor
public get IRuntimeFactory() { return this; }
public get IFluidDataStoreFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
dataTypes.set(mapFactory.type, mapFactory);
Expand All @@ -131,6 +131,8 @@ export class KeyValueFactoryComponent implements IRuntimeFactory, IFluidDataStor
const keyValue = await keyValueP;
return keyValue.request(request);
});

return runtime;
}

public async instantiateRuntime(context: IContainerContext): Promise<IRuntime> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class Constellation extends DataObject implements IConstellation {

public async addStar(x: number, y: number): Promise<void> {
const starHandles = this.root.get<IFluidHandle<ICoordinate>[]>(starListKey);
const newStar: Coordinate = (await Coordinate.getFactory().createInstance(this.context)) as Coordinate;
const newStar = await Coordinate.getFactory().createChildInstance(this.context);
newStar.x = x;
newStar.y = y;
starHandles.push(newStar.handle);
Expand Down
5 changes: 3 additions & 2 deletions examples/data-objects/pond/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ export class Pond extends DataObject implements IFluidHTMLView {
* Do setup work here
*/
protected async initializingFirstTime() {
const clickerComponent = await Clicker.getFactory().createInstance(this.context);
const clickerComponent = await Clicker.getFactory().createChildInstance(this.context);
this.root.set(Clicker.ComponentName, clickerComponent.handle);

const clickerComponentUsingProvider = await ExampleUsingProviders.getFactory().createInstance(this.context);
const clickerComponentUsingProvider =
await ExampleUsingProviders.getFactory().createChildInstance(this.context);
this.root.set(ExampleUsingProviders.ComponentName, clickerComponentUsingProvider.handle);
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/progress-bars/src/progressBars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class ProgressBarsFactory implements IFluidDataStoreFactory {

public get IFluidDataStoreFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
dataTypes.set(mapFactory.type, mapFactory);
Expand All @@ -245,6 +245,8 @@ class ProgressBarsFactory implements IFluidDataStoreFactory {
const progressCollection = await progressCollectionP;
return progressCollection.request(request);
});

return runtime;
}
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/prosemirror/src/prosemirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class ProseMirrorFactory implements IFluidDataStoreFactory {

public get IFluidDataStoreFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
const sequenceFactory = SharedString.getFactory();
Expand All @@ -193,6 +193,8 @@ class ProseMirrorFactory implements IFluidDataStoreFactory {
const proseMirror = await proseMirrorP;
return proseMirror.request(request);
});

return runtime;
}
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/scribe/src/scribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ class ScribeFactory implements IFluidDataStoreFactory, IRuntimeFactory {
return runtime;
}

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
dataTypes.set(mapFactory.type, mapFactory);
Expand All @@ -497,6 +497,8 @@ class ScribeFactory implements IFluidDataStoreFactory, IRuntimeFactory {
const progressCollection = await progressCollectionP;
return progressCollection.request(request);
});

return runtime;
}
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/shared-text/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class TaskScheduler {
}
}

export function instantiateDataStore(context: IFluidDataStoreContext): void {
export function instantiateDataStore(context: IFluidDataStoreContext) {
const modules = new Map<string, any>();

// Create channel factories
Expand Down Expand Up @@ -335,4 +335,6 @@ export function instantiateDataStore(context: IFluidDataStoreContext): void {
const runner = await runnerP;
return runner.request(request);
});

return runtime;
}
18 changes: 11 additions & 7 deletions examples/data-objects/shared-text/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
// eslint-disable-next-line import/no-unassigned-import
import "./publicpath";

import assert from "assert";
import { IContainerContext, IRuntime, IRuntimeFactory } from "@fluidframework/container-definitions";
import { ContainerRuntime } from "@fluidframework/container-runtime";
import {
IFluidDataStoreContext,
IFluidDataStoreFactory,
IFluidDataStoreRegistry,
IProvideFluidDataStoreFactory,
IProvideFluidDataStoreRegistry,
NamedFluidDataStoreRegistryEntries,
} from "@fluidframework/runtime-definitions";
import {
Expand Down Expand Up @@ -56,15 +57,15 @@ const defaultRegistryEntries: NamedFluidDataStoreRegistryEntries = [
["@fluid-example/image-collection", images.then((m) => m.fluidExport)],
];

class MyRegistry implements IFluidDataStoreRegistry {
class MyRegistry implements IProvideFluidDataStoreRegistry {
constructor(
private readonly context: IContainerContext,
private readonly defaultRegistry: string) {
}

public get IFluidDataStoreRegistry() { return this; }

public async get(name: string): Promise<IFluidDataStoreFactory> {
public async get(name: string): Promise<IProvideFluidDataStoreFactory | IProvideFluidDataStoreRegistry> {
const scope = `${name.split("/")[0]}:cdn`;
const config = {};
config[scope] = this.defaultRegistry;
Expand All @@ -74,18 +75,21 @@ class MyRegistry implements IFluidDataStoreRegistry {
config,
};
const fluidModule = await this.context.codeLoader.load(codeDetails);
return fluidModule.fluidExport.IFluidDataStoreFactory;
const moduleExport = fluidModule.fluidExport;
assert(moduleExport.IFluidDataStoreFactory !== undefined ||
moduleExport.IFluidDataStoreRegistry !== undefined);
return moduleExport as IProvideFluidDataStoreFactory | IProvideFluidDataStoreRegistry;
}
}

class SharedTextFactoryComponent implements IFluidDataStoreFactory, IRuntimeFactory {
class SharedTextFactoryComponent implements IProvideFluidDataStoreFactory, IRuntimeFactory {
public static readonly type = "@fluid-example/shared-text";
public readonly type = SharedTextFactoryComponent.type;

public get IFluidDataStoreFactory() { return this; }
public get IRuntimeFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
return sharedTextComponent.instantiateDataStore(context);
}

Expand Down
2 changes: 1 addition & 1 deletion examples/data-objects/simple-component-embed/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class SimpleComponentEmbed extends DataObject implements IFluidHTMLView {
* but in this scenario we only want it to be created once.
*/
protected async initializingFirstTime() {
const component = await this.createFluidObject(ClickerInstantiationFactory.type);
const component = await ClickerInstantiationFactory.createChildInstance(this.context);
this.root.set("myEmbeddedCounter", component.handle);
}

Expand Down
4 changes: 3 additions & 1 deletion examples/data-objects/smde/src/smde.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class SmdeFactory implements IFluidDataStoreFactory {

public get IFluidDataStoreFactory() { return this; }

public instantiateDataStore(context: IFluidDataStoreContext): void {
public async instantiateDataStore(context: IFluidDataStoreContext) {
const dataTypes = new Map<string, IChannelFactory>();
const mapFactory = SharedMap.getFactory();
const sequenceFactory = SharedString.getFactory();
Expand All @@ -235,6 +235,8 @@ class SmdeFactory implements IFluidDataStoreFactory {
const progressCollection = await progressCollectionP;
return progressCollection.request(request);
});

return runtime;
}
}

Expand Down
Loading