Skip to content

Commit

Permalink
Remove existing property - Part 4, IRuntimeFactory + RuntimeFactory…
Browse files Browse the repository at this point in the history
…Helper (#6604)

See #3429

optional argument for IRuntimeFactory.instantiateRuntime to replace the existing state
RuntimeFactoryHelper is to be used by current clients of IRuntimeFactory (see RFC - remove use of the existing property across (some) layers #6486 for examples) in a future PR, after the version bump in container definitions. This class now holds the wrapper logic about how the runtime is actually instantiated.

* Support existing in IRuntimeFactory, add abstract helper class for IRuntimeFactory

* Add test infra

* Add small test

* Simplify mocks

* Use partials to mock IContainerContext, add more tests

* Rename tests, make precedence test more explicit

* Remove comment

* Add some param comments

* Update api.md

* remove marker property

* Remove redundant check

* Some PR feedback, make abstract class generic

* Type refactoring

* Fix test

* Fix test typing

* Fix cast
  • Loading branch information
andre4i authored Jul 2, 2021
1 parent fb452ef commit 40e081b
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export const IRuntimeFactory: keyof IProvideRuntimeFactory;

// @public
export interface IRuntimeFactory extends IProvideRuntimeFactory {
instantiateRuntime(context: IContainerContext): Promise<IRuntime>;
instantiateRuntime(context: IContainerContext, existing?: boolean): Promise<IRuntime>;
}

// @public
Expand Down
5 changes: 4 additions & 1 deletion common/lib/container-definitions/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export interface IRuntimeFactory extends IProvideRuntimeFactory {
/**
* Instantiates a new IRuntime for the given IContainerContext to proxy to
* This is the main entry point to the Container's business logic
*
* @param context - container context to be supplied to the runtime
* @param existing - whether to instantiate for the first time or from an existing context
*/
instantiateRuntime(context: IContainerContext): Promise<IRuntime>;
instantiateRuntime(context: IContainerContext, existing?: boolean): Promise<IRuntime>;
}
3 changes: 3 additions & 0 deletions packages/runtime/runtime-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
"dependencies": {
"@fluidframework/common-definitions": "^0.20.1",
"@fluidframework/common-utils": "^0.31.0",
"@fluidframework/container-definitions": "^0.39.0",
"@fluidframework/container-runtime-definitions": "^0.43.0",
"@fluidframework/core-interfaces": "^0.39.0",
"@fluidframework/datastore-definitions": "^0.43.0",
"@fluidframework/garbage-collector": "^0.43.0",
Expand Down Expand Up @@ -89,6 +91,7 @@
"mocha": "^8.4.0",
"nyc": "^15.0.0",
"rimraf": "^2.6.2",
"sinon": "^7.4.2",
"ts-node": "^7.0.1",
"typescript": "~4.1.3",
"typescript-formatter": "7.1.0"
Expand Down
39 changes: 39 additions & 0 deletions packages/runtime/runtime-utils/src/runtimeFactoryHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import {
IContainerContext,
IRuntime,
IRuntimeFactory,
} from "@fluidframework/container-definitions";
import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";

export abstract class RuntimeFactoryHelper<T = IContainerRuntime> implements IRuntimeFactory {
public get IRuntimeFactory() { return this; }

public async instantiateRuntime(
context: IContainerContext,
existing?: boolean,
): Promise<IRuntime> {
const fromExisting = existing === undefined
? context.existing === true
: existing;
const runtime = await this.preInitialize(context, fromExisting);

if (fromExisting) {
await this.instantiateFromExisting(runtime);
} else {
await this.instantiateFirstTime(runtime);
}

await this.hasInitialized(runtime);
return runtime;
}

public abstract preInitialize(context: IContainerContext, existing: boolean): Promise<IRuntime & T>;
public async instantiateFirstTime(_runtime: T): Promise<void> {}
public async instantiateFromExisting(_runtime: T): Promise<void> {}
public async hasInitialized(_runtime: T): Promise<void> {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import { IContainerContext, IRuntime } from "@fluidframework/container-definitions";
import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";
import Sinon from "sinon";
import { RuntimeFactoryHelper } from "../runtimeFactoryHelper";

class TestRuntimeFactoryHelper extends RuntimeFactoryHelper {
constructor(
private readonly runtime: IRuntime & IContainerRuntime,
) {
super();
}

public async preInitialize(
_context: IContainerContext,
_existing: boolean,
): Promise<IRuntime & IContainerRuntime> {
return this.runtime;
}
}

describe("RuntimeFactoryHelper", () => {
const sandbox: Sinon.SinonSandbox = Sinon.createSandbox();
const context: Partial<IContainerContext> = {};
const runtime: Partial<IRuntime & IContainerRuntime> = {};
let helper: TestRuntimeFactoryHelper;
let unit: Sinon.SinonMock;

beforeEach(() => {
helper = new TestRuntimeFactoryHelper(runtime as IRuntime & IContainerRuntime);
unit = sandbox.mock(helper);
unit.expects("preInitialize").once();
unit.expects("hasInitialized").once();
});

afterEach(() => {
sandbox.restore();
});

it("Instantiate when existing flag is `true`", async () => {
unit.expects("instantiateFirstTime").never();
unit.expects("instantiateFromExisting").once();
await helper.instantiateRuntime(context as IContainerContext, /* existing */ true);

unit.verify();
});

it("Instantiate when existing flag is `false`", async () => {
unit.expects("instantiateFirstTime").once();
unit.expects("instantiateFromExisting").never();
await helper.instantiateRuntime(context as IContainerContext, /* existing */ false);

unit.verify();
});

it("Instantiate when existing flag is unset", async () => {
unit.expects("instantiateFirstTime").once();
unit.expects("instantiateFromExisting").never();
await helper.instantiateRuntime(context as IContainerContext);

unit.verify();
});

it("Instantiate when existing flag is unset and context is existing", async () => {
const existingContext: Partial<IContainerContext> = { existing: true };
unit.expects("instantiateFirstTime").never();
unit.expects("instantiateFromExisting").once();
await helper.instantiateRuntime(existingContext as IContainerContext);

unit.verify();
});

it("Instantiate when existing flag takes precedence over context", async () => {
const existingContext: Partial<IContainerContext> = { existing: true };
unit.expects("instantiateFirstTime").once();
unit.expects("instantiateFromExisting").never();
await helper.instantiateRuntime(existingContext as IContainerContext, /* existing */ false);

unit.verify();
});
});

0 comments on commit 40e081b

Please sign in to comment.