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

Support Multiple Drivers In IFrames #1312

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
IResolvedUrl,
IUrlResolver,
} from "@microsoft/fluid-driver-definitions";
import { configurableUrlResolver } from "@microsoft/fluid-driver-utils";
import {
configurableUrlResolver,
DocumentServiceFactoryProtocolMatcher,
} from "@microsoft/fluid-driver-utils";
import {
ConnectionMode,
IClient,
Expand Down Expand Up @@ -59,7 +62,7 @@ export interface IDocumentServiceFactoryProxy {
export class IFrameDocumentServiceProxyFactory {

public static async create(
documentServiceFactory: IDocumentServiceFactory,
documentServiceFactory: IDocumentServiceFactory | IDocumentServiceFactory[],
frame: HTMLIFrameElement,
options: any,
urlResolver: IUrlResolver | IUrlResolver[],
Expand All @@ -71,7 +74,7 @@ export class IFrameDocumentServiceProxyFactory {
private documentServiceProxy: DocumentServiceFactoryProxy | undefined;

constructor(
private readonly documentServiceFactory: IDocumentServiceFactory,
private readonly documentServiceFactory: IDocumentServiceFactory | IDocumentServiceFactory[],
private readonly frame: HTMLIFrameElement,
private readonly options: any,
private readonly urlResolver: IUrlResolver | IUrlResolver[],
Expand All @@ -80,7 +83,6 @@ export class IFrameDocumentServiceProxyFactory {
}

public async createDocumentService(resolvedUrl: IResolvedUrl): Promise<void> {

// eslint-disable-next-line @typescript-eslint/no-use-before-define
this.documentServiceProxy = new DocumentServiceFactoryProxy(
this.documentServiceFactory,
Expand Down Expand Up @@ -137,22 +139,25 @@ export class DocumentServiceFactoryProxy implements IDocumentServiceFactoryProxy
private readonly tokens: {
[name: string]: string;
};
private readonly driverProtocolMappings: DocumentServiceFactoryProtocolMatcher;

constructor(
private readonly documentServiceFactory: IDocumentServiceFactory,
documentServiceFactory: IDocumentServiceFactory | IDocumentServiceFactory[],
private readonly options: any,
private readonly resolvedUrl: IFluidResolvedUrl,
) {

this.driverProtocolMappings = new DocumentServiceFactoryProtocolMatcher(documentServiceFactory);
this.tokens = this.resolvedUrl.tokens;
this.clients = {};
}

public async createDocumentService(resolvedUrl: IFluidResolvedUrl): Promise<string> {
resolvedUrl.tokens = this.tokens;

const documentServiceFactory = this.driverProtocolMappings.getFactory(resolvedUrl);

resolvedUrl.tokens = this.tokens;
const connectedDocumentService: IDocumentService =
await this.documentServiceFactory.createDocumentService(resolvedUrl);
await documentServiceFactory.createDocumentService(resolvedUrl);

const clientDetails = this.options ? (this.options.client as IClient) : null;
const mode: ConnectionMode = "write";
Expand Down
51 changes: 7 additions & 44 deletions packages/loader/container-loader/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import {
IResolvedUrl,
IUrlResolver,
} from "@microsoft/fluid-driver-definitions";
import { configurableUrlResolver } from "@microsoft/fluid-driver-utils";
import {
configurableUrlResolver,
DocumentServiceFactoryProtocolMatcher,
} from "@microsoft/fluid-driver-utils";
import { ISequencedDocumentMessage } from "@microsoft/fluid-protocol-definitions";
import { Container } from "./container";
import { debug } from "./debug";
Expand Down Expand Up @@ -112,54 +115,14 @@ export class RelativeLoader extends EventEmitter implements ILoader, IExperiment
}
}

/**
* Api that selects a document service factory from the factory map provided according to protocol
* in resolved URL.
* @param resolvedAsFluid - Resolved fluid URL containing driver protocol
* @param protocolToDocumentFactoryMap - Map of protocol name to factories from which one factory
* is selected according to protocol.
*/
export function selectDocumentServiceFactoryForProtocol(
resolvedAsFluid: IFluidResolvedUrl,
protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory>,
): IDocumentServiceFactory {
const urlObj = parse(resolvedAsFluid.url);
if (!urlObj.protocol) {
throw new Error("No protocol provided");
}
const factory: IDocumentServiceFactory | undefined = protocolToDocumentFactoryMap.get(urlObj.protocol);
if (!factory) {
throw new Error("Unknown fluid protocol");
}
return factory;
}

/**
* Api that creates the protocol to factory map.
* @param documentServiceFactories - A single factory or array of document factories.
*/
export function createProtocolToFactoryMapping(
documentServiceFactories: IDocumentServiceFactory | IDocumentServiceFactory[],
): Map<string, IDocumentServiceFactory> {
const protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory> = new Map();
if (Array.isArray(documentServiceFactories)) {
documentServiceFactories.forEach((factory: IDocumentServiceFactory) => {
protocolToDocumentFactoryMap.set(factory.protocolName, factory);
});
} else {
protocolToDocumentFactoryMap.set(documentServiceFactories.protocolName, documentServiceFactories);
}
return protocolToDocumentFactoryMap;
}

/**
* Manages Fluid resource loading
*/
export class Loader extends EventEmitter implements ILoader, IExperimentalLoader {

private readonly containers = new Map<string, Promise<Container>>();
private readonly resolveCache = new Map<string, IResolvedUrl>();
private readonly protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory>;
private readonly protocolToDocumentFactoryMap: DocumentServiceFactoryProtocolMatcher;

public readonly isExperimentalLoader = true;

Expand All @@ -186,7 +149,7 @@ export class Loader extends EventEmitter implements ILoader, IExperimentalLoader
throw new Error("An ICodeLoader must be provided");
}

this.protocolToDocumentFactoryMap = createProtocolToFactoryMapping(documentServiceFactories);
this.protocolToDocumentFactoryMap = new DocumentServiceFactoryProtocolMatcher(documentServiceFactories);
}

public async experimentalCreateDetachedContainer(source: IFluidCodeDetails): Promise<Container> {
Expand Down Expand Up @@ -293,7 +256,7 @@ export class Loader extends EventEmitter implements ILoader, IExperimentalLoader

debug(`${canCache} ${request.headers[LoaderHeader.pause]} ${request.headers[LoaderHeader.version]}`);
const factory: IDocumentServiceFactory =
selectDocumentServiceFactoryForProtocol(resolvedAsFluid, this.protocolToDocumentFactoryMap);
this.protocolToDocumentFactoryMap.getFactory(resolvedAsFluid);

let container: Container;
if (canCache) {
Expand Down
62 changes: 62 additions & 0 deletions packages/loader/driver-utils/src/driverProtocolMapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { parse } from "url";
import { IDocumentServiceFactory, IFluidResolvedUrl } from "@microsoft/fluid-driver-definitions";

/**
* Api that creates the protocol to factory map.
* @param documentServiceFactories - A single factory or array of document factories.
*/
function createProtocolToFactoryMapping(
documentServiceFactories: IDocumentServiceFactory | IDocumentServiceFactory[],
): Map<string, IDocumentServiceFactory> {
const protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory> = new Map();
if (Array.isArray(documentServiceFactories)) {
documentServiceFactories.forEach((factory: IDocumentServiceFactory) => {
protocolToDocumentFactoryMap.set(factory.protocolName, factory);
});
} else {
protocolToDocumentFactoryMap.set(documentServiceFactories.protocolName, documentServiceFactories);
}
return protocolToDocumentFactoryMap;
}

/**
* Api that selects a document service factory from the factory map provided according to protocol
* in resolved URL.
* @param resolvedAsFluid - Resolved fluid URL containing driver protocol
* @param protocolToDocumentFactoryMap - Map of protocol name to factories from which one factory
* is selected according to protocol.
*/
function selectDocumentServiceFactoryForProtocol(
resolvedAsFluid: IFluidResolvedUrl,
protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory>,
): IDocumentServiceFactory {
const urlObj = parse(resolvedAsFluid.url);
if (!urlObj.protocol) {
throw new Error("No protocol provided");
}
const factory: IDocumentServiceFactory | undefined = protocolToDocumentFactoryMap.get(urlObj.protocol);
if (!factory) {
throw new Error("Unknown fluid protocol");
}
return factory;
}


export class DocumentServiceFactoryProtocolMatcher{
private readonly protocolToDocumentFactoryMap: Map<string, IDocumentServiceFactory>;

constructor(documentServiceFactories: IDocumentServiceFactory | IDocumentServiceFactory[]) {
this.protocolToDocumentFactoryMap = createProtocolToFactoryMapping(documentServiceFactories);
}

public getFactory(resolvedAsFluid: IFluidResolvedUrl) {
return selectDocumentServiceFactoryForProtocol(
resolvedAsFluid,
this.protocolToDocumentFactoryMap);
}
}
1 change: 1 addition & 0 deletions packages/loader/driver-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

export * from "./configurableUrlResolver";
export * from "./driverProtocolMapping";
export * from "./documentStorageServiceProxy";
export * from "./network";
export * from "./readAndParse";
Expand Down