Skip to content

Commit

Permalink
Merge branch 'main' into simpleContext
Browse files Browse the repository at this point in the history
  • Loading branch information
CraigMacomber authored Sep 24, 2024
2 parents 64b0cc1 + 28e4f46 commit 5a720d9
Show file tree
Hide file tree
Showing 27 changed files with 585 additions and 357 deletions.
4 changes: 2 additions & 2 deletions packages/framework/presence/src/exposedInternalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ export namespace InternalTypes {
TKey extends string,
TValue extends ValueDirectoryOrState<any>,
TManager,
> = (
> = { instanceBase: new (...args: any[]) => any } & ((
key: TKey,
datastoreHandle: StateDatastoreHandle<TKey, TValue>,
) => {
value?: TValue;
manager: StateValue<TManager>;
};
});

/**
* @system
Expand Down
8 changes: 6 additions & 2 deletions packages/framework/presence/src/latestMapValueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,16 @@ export function LatestMap<
allowableUpdateLatency: 60,
forcedRefreshInterval: 0,
};
return (
const factory = (
key: RegistrationKey,
datastoreHandle: InternalTypes.StateDatastoreHandle<
RegistrationKey,
InternalTypes.MapValueState<T>
>,
) => ({
): {
value: typeof value;
manager: InternalTypes.StateValue<LatestMapValueManager<T, Keys>>;
} => ({
value,
manager: brandIVM<
LatestMapValueManagerImpl<T, RegistrationKey, Keys>,
Expand All @@ -491,4 +494,5 @@ export function LatestMap<
),
),
});
return Object.assign(factory, { instanceBase: LatestMapValueManagerImpl });
}
8 changes: 6 additions & 2 deletions packages/framework/presence/src/latestValueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,16 @@ export function Latest<T extends object, Key extends string = string>(
allowableUpdateLatency: 60,
forcedRefreshInterval: 0,
};
return (
const factory = (
key: Key,
datastoreHandle: InternalTypes.StateDatastoreHandle<
Key,
InternalTypes.ValueRequiredState<T>
>,
) => ({
): {
value: typeof value;
manager: InternalTypes.StateValue<LatestValueManager<T>>;
} => ({
value,
manager: brandIVM<LatestValueManagerImpl<T, Key>, T, InternalTypes.ValueRequiredState<T>>(
new LatestValueManagerImpl(
Expand All @@ -201,4 +204,5 @@ export function Latest<T extends object, Key extends string = string>(
),
),
});
return Object.assign(factory, { instanceBase: LatestValueManagerImpl });
}
7 changes: 5 additions & 2 deletions packages/framework/presence/src/notificationsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,15 @@ export function Notifications<
InternalTypes.ValueRequiredState<InternalTypes.NotificationType>,
NotificationsManager<T>
> {
return (
const factory = (
key: Key,
datastoreHandle: InternalTypes.StateDatastoreHandle<
Key,
InternalTypes.ValueRequiredState<InternalTypes.NotificationType>
>,
) => ({
): {
manager: InternalTypes.StateValue<NotificationsManager<T>>;
} => ({
manager: brandIVM<
NotificationsManagerImpl<T, Key>,
InternalTypes.NotificationType,
Expand All @@ -217,4 +219,5 @@ export function Notifications<
),
),
});
return Object.assign(factory, { instanceBase: NotificationsManagerImpl });
}
9 changes: 8 additions & 1 deletion packages/framework/presence/src/presenceStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,14 @@ class PresenceStatesImpl<TSchema extends PresenceStatesSchema>
content: TSchemaAdditional,
): PresenceStates<TSchema & TSchemaAdditional> {
for (const [key, nodeFactory] of Object.entries(content)) {
this.add(key, nodeFactory);
if (key in this.nodes) {
const node = unbrandIVM(this.nodes[key]);
if (!(node instanceof nodeFactory.instanceBase)) {
throw new TypeError(`State "${key}" previously created by different value manager.`);
}
} else {
this.add(key, nodeFactory);
}
}
return this as PresenceStates<TSchema & TSchemaAdditional>;
}
Expand Down
9 changes: 5 additions & 4 deletions packages/framework/presence/src/test/presenceStates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("LatestValueManager", () => {

declare function createValueManager<T, Key extends string>(
initial: JsonSerializable<T> & JsonDeserialized<T>,
): (
): { instanceBase: new () => unknown } & ((
key: Key,
datastoreHandle: InternalTypes.StateDatastoreHandle<
Key,
Expand All @@ -29,7 +29,7 @@ declare function createValueManager<T, Key extends string>(
) => {
value: InternalTypes.ValueRequiredState<T>;
manager: InternalTypes.StateValue<JsonDeserialized<T>>;
};
});

// ---- test (example) code ----

Expand All @@ -41,11 +41,12 @@ export function checkCompiles(): void {
const presence = {} as IPresence;
const statesWorkspace = presence.getStates("name:testWorkspaceA", {
cursor: createValueManager({ x: 0, y: 0 }),
camera: () => ({
// eslint-disable-next-line prefer-object-spread
camera: Object.assign({ instanceBase: undefined as unknown as new () => unknown }, () => ({
value: { rev: 0, timestamp: Date.now(), value: { x: 0, y: 0, z: 0 } },
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
manager: {} as InternalTypes.StateValue<{ x: number; y: number; z: number }>,
}),
})),
});
// Workaround ts(2775): Assertions require every name in the call target to be declared with an explicit type annotation.
const states: typeof statesWorkspace = statesWorkspace;
Expand Down
10 changes: 6 additions & 4 deletions packages/test/local-server-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"lint:fix": "fluid-build . --task eslint:fix --task format",
"test": "npm run test:mocha",
"test:coverage": "c8 npm test",
"test:mocha": "mocha \"dist/test/**/*.spec.*js\" --exit",
"test:mocha": "mocha \"lib/test/**/*.spec.*js\" --exit",
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha"
},
"c8": {
Expand All @@ -53,8 +53,7 @@
],
"temp-directory": "nyc/.nyc_output"
},
"devDependencies": {
"@biomejs/biome": "~1.8.3",
"dependencies": {
"@fluid-internal/client-utils": "workspace:~",
"@fluid-internal/mocha-test-setup": "workspace:~",
"@fluid-private/test-loader-utils": "workspace:~",
Expand Down Expand Up @@ -91,7 +90,10 @@
"@fluidframework/server-local-server": "^5.0.0",
"@fluidframework/shared-object-base": "workspace:~",
"@fluidframework/telemetry-utils": "workspace:~",
"@fluidframework/test-utils": "workspace:~",
"@fluidframework/test-utils": "workspace:~"
},
"devDependencies": {
"@biomejs/biome": "~1.8.3",
"@types/mocha": "^9.1.1",
"@types/nock": "^9.3.0",
"@types/node": "^18.19.0",
Expand Down
128 changes: 128 additions & 0 deletions packages/test/local-server-tests/src/test/decoupledCreate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import { strict as assert } from "assert";

import type { IRequest } from "@fluidframework/core-interfaces";
import type { FluidObject } from "@fluidframework/core-interfaces/internal";
import {
IDocumentServiceFactory,
type IResolvedUrl,
type ISummaryTree,
} from "@fluidframework/driver-definitions/internal";
import {
LocalDocumentServiceFactory,
LocalResolver,
} from "@fluidframework/local-driver/internal";
import {
LocalDeltaConnectionServer,
type ILocalDeltaConnectionServer,
} from "@fluidframework/server-local-server";
import type { ITestFluidObject } from "@fluidframework/test-utils/internal";

import { createLoader } from "../utils.js";

function createDSFWithOutOfBandCreate({
deltaConnectionServer,
createContainerCallback,
}: {
deltaConnectionServer: ILocalDeltaConnectionServer;
createContainerCallback: (
summary: ISummaryTree | undefined,
resolvedUrl: IResolvedUrl,
) => Promise<IRequest>;
}) {
return new Proxy<IDocumentServiceFactory>(
new LocalDocumentServiceFactory(deltaConnectionServer),
{
get: (t, p: keyof IDocumentServiceFactory, r) => {
if (p === "createContainer") {
return async (summary, resolvedUrl, logger, clientIsSummarizer) => {
const url = await createContainerCallback(summary, resolvedUrl);
// this is more like the load flow, where we resolve the url
// and create the document service, and it works here, as
// the callback actually does the work of creating the container.
const resolver = new LocalResolver();
return t.createDocumentService(
await resolver.resolve(url),
logger,
clientIsSummarizer,
);
};
}

return Reflect.get(t, p, r);
},
},
);
}

async function createContainerOutOfBand(
deltaConnectionServer: ILocalDeltaConnectionServer,
createContainerParams: {
summary: ISummaryTree | undefined;
resolvedUrl: IResolvedUrl;
},
) {
// this actually creates the container
const { summary, resolvedUrl } = createContainerParams;
const documentServiceFactory = new LocalDocumentServiceFactory(deltaConnectionServer);
const documentService = await documentServiceFactory.createContainer(summary, resolvedUrl);
const resolver = new LocalResolver();
return resolver.getAbsoluteUrl(documentService.resolvedUrl, "");
}

describe("Scenario Test", () => {
it("Create container via a decoupled out of band function and validate both attaching container and freshly loaded container both work.", async () => {
const deltaConnectionServer = LocalDeltaConnectionServer.create();

/*
* Setup a document service factory that uses a user specifiable createContainerCallback.
* This callback could make a different server call, and just needs to return the url
* of the newly created container/file.
*/
let request: IRequest | undefined;
const documentServiceFactory = createDSFWithOutOfBandCreate({
deltaConnectionServer,
createContainerCallback: async (summary, resolvedUrl) =>
(request = {
url: await createContainerOutOfBand(deltaConnectionServer, {
summary,
resolvedUrl,
}),
}),
});

const { loader, codeDetails, urlResolver } = createLoader({
deltaConnectionServer,
documentServiceFactory,
});

const container = await loader.createDetachedContainer(codeDetails);

{
// put a bit of data in the detached container so we can validate later
const entryPoint: FluidObject<ITestFluidObject> = await container.getEntryPoint();
entryPoint.ITestFluidObject?.root.set("someKey", "someValue");
}

// kicking off attach will end up calling the create container callback
// which will actually create the container, and eventually finish the attach
await container.attach(urlResolver.createCreateNewRequest("test"));

{
// just reuse the same server, nothing else from the initial create
const { loader: loader2 } = createLoader({ deltaConnectionServer });

// ensure and use the url we got from out of band create to load the container
assert(request !== undefined);
const container2 = await loader2.resolve(request);

// ensure the newly loaded container has the data we expect.
const entryPoint: FluidObject<ITestFluidObject> = await container2.getEntryPoint();
assert.strictEqual(entryPoint.ITestFluidObject?.root.get("someKey"), "someValue");
}
});
});
5 changes: 2 additions & 3 deletions packages/test/local-server-tests/src/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
{
"extends": "../../../../../common/build/build-common/tsconfig.test.node16.json",
"compilerOptions": {
"rootDir": "./",
"outDir": "../../dist/test",
"rootDir": "../",
"outDir": "../../lib",
"types": ["mocha"],
"noUncheckedIndexedAccess": false,
"exactOptionalPropertyTypes": false,
},
"include": ["./**/*"],
}
Loading

0 comments on commit 5a720d9

Please sign in to comment.