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

Creating worker instance and getting or creating a new sharedworkspace for co-presence #745

Merged
merged 36 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d4739ad
added gulp file and webpack config file to compile copresence worker …
ritikramuka Sep 19, 2023
b28a5f0
dependencies installed
ritikramuka Sep 20, 2023
79e8acc
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
ritikramuka Oct 18, 2023
c316aba
updated gulp file and webpack file to compile webworker script
ritikramuka Oct 18, 2023
1a1641c
passed entity id while loading container & few nit changes
ritikramuka Oct 19, 2023
a2602e3
creating web worker instance get or create sharedworkspace for copres…
ritikramuka Oct 19, 2023
046a9c2
sending message to worker on tab change rather than open active text …
ritikramuka Oct 23, 2023
59e45ae
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Oct 24, 2023
43f883c
moved co-presence on vscode for web related parameters to a single place
ritikramuka Oct 26, 2023
ad6d855
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Oct 26, 2023
a2922d0
disabled co-presence feature for users as under development
ritikramuka Oct 26, 2023
64f0fa3
update in way of joining path
ritikramuka Oct 26, 2023
618928a
added const for worker event messages
ritikramuka Oct 26, 2023
1fcfe2e
renamed a worker event mesages
ritikramuka Oct 26, 2023
1bcf192
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Oct 27, 2023
6df6477
remove redundant event registery of onDidChangeTabs
ritikramuka Oct 27, 2023
0555759
Merge branch 'users/ramukaritik/creating-webworker-instance' of https…
ritikramuka Oct 27, 2023
474ee5d
moved populateSharedWorkSpace to authenticateAndUpdateDataverseProper…
ritikramuka Oct 27, 2023
b4e9319
disabling co-presence feature hard coded
ritikramuka Oct 27, 2023
b2568e6
added worker in webextension context
ritikramuka Oct 27, 2023
5d9d994
used concurrency handler to make fetch call worker script
ritikramuka Oct 27, 2023
eacad21
eslint error fixed
ritikramuka Oct 27, 2023
1e6e2bf
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Oct 30, 2023
231efe4
adding telemetry and moving api path in constant
ritikramuka Oct 30, 2023
aa1f530
removed console log
ritikramuka Oct 30, 2023
ea971cf
updating telemetry log
ritikramuka Oct 30, 2023
5ac8a0a
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Oct 30, 2023
f937bb7
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 2, 2023
9a98b11
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 3, 2023
699aba9
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 7, 2023
65498f7
resolving bug of correct api call
ritikramuka Nov 8, 2023
1a13ba8
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 8, 2023
97a677e
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 8, 2023
0a9860b
Merge branch 'main' into users/ramukaritik/creating-webworker-instance
ritikramuka Nov 16, 2023
8fc16f0
telemetry error logs hard coded
ritikramuka Nov 16, 2023
bbf7d40
Merge branch 'main' of https://github.com/microsoft/powerplatform-vsc…
ritikramuka Nov 19, 2023
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
128 changes: 127 additions & 1 deletion src/web/client/WebExtensionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import {
getWebsiteIdToLcidMap,
getWebsiteLanguageIdToPortalLanguageIdMap,
} from "./utilities/schemaHelperUtil";
import { getCustomRequestURL } from "./utilities/urlBuilderUtil";
import { getCustomRequestURL, getOrCreateSharedWorkspace } from "./utilities/urlBuilderUtil";
import { schemaKey } from "./schema/constants";
import { telemetryEventNames } from "./telemetry/constants";
import { EntityDataMap } from "./context/entityDataMap";
import { FileDataMap } from "./context/fileDataMap";
import { IAttributePath, IEntityInfo } from "./common/interfaces";
import { ConcurrencyHandler } from "./dal/concurrencyHandler";
import { isMultifileEnabled } from "./utilities/commonUtil";
import { UserDataMap } from "./context/userDataMap";

export interface IWebExtensionContext {
// From portalSchema properties
Expand Down Expand Up @@ -102,6 +103,11 @@ class WebExtensionContext implements IWebExtensionContext {
private _userId: string;
private _formsProEligibilityId: string;
private _concurrencyHandler: ConcurrencyHandler
// Co-Presence for Power Pages Vscode for web
private _worker: Worker | undefined;
private _sharedWorkSpaceMap: Map<string, string>;
private _containerId: string;
private _connectedUsers: UserDataMap;

public get schemaDataSourcePropertiesMap() {
return this._schemaDataSourcePropertiesMap;
Expand Down Expand Up @@ -184,6 +190,21 @@ class WebExtensionContext implements IWebExtensionContext {
public get concurrencyHandler() {
return this._concurrencyHandler;
}
public get worker() {
return this._worker;
}
public get sharedWorkSpaceMap() {
return this._sharedWorkSpaceMap;
}
public get connectedUsers() {
return this._connectedUsers;
}
public get containerId() {
return this._containerId;
}
public set containerId(containerId: string) {
this._containerId = containerId;
}

constructor() {
this._schemaDataSourcePropertiesMap = new Map<string, string>();
Expand Down Expand Up @@ -213,6 +234,9 @@ class WebExtensionContext implements IWebExtensionContext {
this._userId = "";
this._formsProEligibilityId = "";
this._concurrencyHandler = new ConcurrencyHandler();
this._sharedWorkSpaceMap = new Map<string, string>();
this._containerId = "";
this._connectedUsers = new UserDataMap();
}

public setWebExtensionContext(
Expand Down Expand Up @@ -301,6 +325,16 @@ class WebExtensionContext implements IWebExtensionContext {
);

await this.setWebsiteLanguageCode();

// Getting website Id to populate shared workspace for Co-Presence
const websiteid = this.urlParametersMap.get(
Constants.queryParameters.WEBSITE_ID
) as string;

const headers = getCommonHeaders(this._dataverseAccessToken);

// Populate shared workspace for Co-Presence
await this.populateSharedworkspace(headers, dataverseOrgUrl, websiteid);
}

public async dataverseAuthentication(firstTimeAuth = false) {
Expand Down Expand Up @@ -627,6 +661,98 @@ class WebExtensionContext implements IWebExtensionContext {
public getVscodeWorkspaceState(key: string): IEntityInfo | undefined {
return this._vscodeWorkspaceState.get(key);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public async getWorkerScript(workerUrl : URL) : Promise<any> {
try {
this.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_FETCH_WORKER_SCRIPT
);

const response = await this.concurrencyHandler.handleRequest(
workerUrl
)

if (!response.ok) {
throw new Error(
`Failed to fetch worker script '${workerUrl.toString()}': ${response.statusText}`
);
}

this.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_FETCH_WORKER_SCRIPT_SUCCESS,
{ workerUrl: workerUrl.toString() }
);

return await response.text();
} catch (error) {
this.telemetry.sendErrorTelemetry(
telemetryEventNames.WEB_EXTENSION_FETCH_WORKER_SCRIPT_FAILED,
this.getWorkerScript.name,
(error as Error)?.message,
tehcrashxor marked this conversation as resolved.
Show resolved Hide resolved
error as Error
);
}
}

public setWorker(worker: Worker) {
this._worker = worker;
}

private async populateSharedworkspace(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tyaginidhi marked this conversation as resolved.
Show resolved Hide resolved
headers: any,
dataverseOrgUrl: string,
websiteid: string
) {
try {
const sharedworkspace = await getOrCreateSharedWorkspace({
headers,
dataverseOrgUrl,
websiteid,
});

const sharedWorkSpaceParamsMap = new Map<string, string>();
for (const key in sharedworkspace) {
sharedWorkSpaceParamsMap.set(
String(key).trim().toLocaleLowerCase(),
String(sharedworkspace[key]).trim()
);
}

this._sharedWorkSpaceMap = sharedWorkSpaceParamsMap;

this.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_POPULATE_SHARED_WORKSPACE_SUCCESS,
{ count: this._sharedWorkSpaceMap.size.toString() }
);
} catch (error) {
this.telemetry.sendErrorTelemetry(
telemetryEventNames.WEB_EXTENSION_POPULATE_SHARED_WORKSPACE_SYSTEM_ERROR,
this.populateSharedworkspace.name,
(error as Error)?.message,
error as Error
);
}
}

public async updateConnectedUsersInContext(
containerId: string,
userName: string,
userId: string,
entityId: string[]
) {
this.connectedUsers.setUserData(
containerId,
userName,
userId,
entityId
);
}

public async removeConnectedUserInContext(userId: string) {
this.connectedUsers.removeUser(userId);
}
}

export default new WebExtensionContext();
15 changes: 15 additions & 0 deletions src/web/client/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ export const MULTI_FILE_FEATURE_SETTING_NAME = "enableMultiFileFeature";
// Co-presence feature constants
export const CO_PRESENCE_FEATURE_SETTING_NAME = "enableCoPresenceFeature";

// Co-presence constants
export const GET_OR_CREATE_SHARED_WORK_SPACE = "/api/data/v9.2/GetOrCreateSharedWorkspace";

export enum sharedWorkspaceParameters {
SHAREWORKSPACE_ID = "sharedworkspaceid",
TENANT_ID = "tenantid",
ACCESS_TOKEN = "accesstoken",
DISCOVERY_ENDPOINT = "discoveryendpoint",
}

export enum workerEventMessages {
UPDATE_CONNECTED_USERS = "client-data",
REMOVE_CONNECTED_USER = "member-removed",
}

export enum initializationEntityName {
WEBSITE = "websites",
WEBSITELANGUAGE = "websitelanguages",
Expand Down
25 changes: 13 additions & 12 deletions src/web/client/common/worker/webworker.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class AzureFluidClient {
}
}

async function loadContainer(config, id, swpId) {
async function loadContainer(config, swpId, entityInfo) {
try {
const { container, audience, map } =
await AzureFluidClient.fetchContainerAndService(config, swpId);
Expand All @@ -101,22 +101,23 @@ async function loadContainer(config, id, swpId) {
if (!initial) {
existingMembers.forEach(async (value, key) => {
const otherUser = value;
const connectionArray = otherUser.connections;

const entityId = [];
const userConnections = otherUser.connections;

const userEntityIdArray = [];

const connectionIdInContainer = await (container.initialObjects.sharedState.get('selection').get());

connectionArray.forEach((connectionId) => {
entityId.push(connectionIdInContainer.get(connectionId));
userConnections.forEach((connection) => {
userEntityIdArray.push(connectionIdInContainer.get(connection.id));
});

self.postMessage({
type: "client-data",
userName: otherUser.userName,
userId: key,
containerId: swpId,
entityId: entityId,
entityId: userEntityIdArray,
});
});
initial = true;
Expand All @@ -127,14 +128,14 @@ async function loadContainer(config, id, swpId) {
return;
} else {
const otherUser = map.get(changed.key);
const connectionArray = otherUser.connections;
const userConnections = otherUser.connections;

const entityId = [];
const userEntityIdArray = [];

const connectionIdInContainer = await (container.initialObjects.sharedState.get('selection').get());

connectionArray.forEach((connectionId) => {
entityId.push(connectionIdInContainer.get(connectionId));
userConnections.forEach((connection) => {
userEntityIdArray.push(connectionIdInContainer.get(connection.id));
});

// eslint-disable-next-line no-undef
Expand All @@ -143,7 +144,7 @@ async function loadContainer(config, id, swpId) {
userId: changed.key,
userName: otherUser.userName,
containerId: swpId,
entityId: entityId,
entityId: userEntityIdArray,
});
}
});
Expand All @@ -160,8 +161,8 @@ function runFluidApp() {

await loadContainer(
message.afrConfig,
message.containerId,
message.afrConfig.swpId,
message.entityInfo
);
});
}
Expand Down
87 changes: 84 additions & 3 deletions src/web/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import * as copilot from "../../common/copilot/PowerPagesCopilot";
import { IOrgInfo } from "../../common/copilot/model";
import { copilotNotificationPanel, disposeNotificationPanel } from "../../common/copilot/welcome-notification/CopilotNotificationPanel";
import { COPILOT_NOTIFICATION_DISABLED } from "../../common/copilot/constants";
import * as Constants from "./common/constants"

export function activate(context: vscode.ExtensionContext): void {
// setup telemetry
Expand Down Expand Up @@ -184,7 +185,7 @@ export function activate(context: vscode.ExtensionContext): void {

processWillSaveDocument(context);

processWillStartCollaboartion();
processWillStartCollaboartion(context);

showWalkthrough(context, WebExtensionContext.telemetry);
}
Expand Down Expand Up @@ -237,6 +238,29 @@ export function processWorkspaceStateChanges(context: vscode.ExtensionContext) {
if (entityInfo.entityId && entityInfo.entityName) {
context.workspaceState.update(document.uri.fsPath, entityInfo);
WebExtensionContext.updateVscodeWorkspaceState(document.uri.fsPath, entityInfo);

// sending message to webworker event listener for Co-Presence feature
if (isCoPresenceEnabled()) {
if (WebExtensionContext.worker !== undefined) {
WebExtensionContext.worker.postMessage({
afrConfig: {
swpId: WebExtensionContext.sharedWorkSpaceMap.get(
Constants.sharedWorkspaceParameters.SHAREWORKSPACE_ID
) as string,
swptenantId: WebExtensionContext.sharedWorkSpaceMap.get(
Constants.sharedWorkspaceParameters.TENANT_ID
) as string,
discoveryendpoint: WebExtensionContext.sharedWorkSpaceMap.get(
Constants.sharedWorkspaceParameters.DISCOVERY_ENDPOINT
) as string,
swpAccessToken: WebExtensionContext.sharedWorkSpaceMap.get(
Constants.sharedWorkspaceParameters.ACCESS_TOKEN
) as string,
},
entityInfo
});
}
}
}
}
});
Expand Down Expand Up @@ -268,9 +292,66 @@ export function processWillSaveDocument(context: vscode.ExtensionContext) {
);
}

export function processWillStartCollaboartion() {
export function processWillStartCollaboartion(context: vscode.ExtensionContext) {
// feature in progress, hence disabling it
if (isCoPresenceEnabled()) {
tyaginidhi marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Add copresence logic
createWebWorkerInstance(context);
}
}

export function createWebWorkerInstance(
context: vscode.ExtensionContext
) {
try {
const webworkerMain = vscode.Uri.joinPath(
ritikramuka marked this conversation as resolved.
Show resolved Hide resolved
context.extensionUri,
"dist",
"web",
"webworker.worker.js"
);

const workerUrl = new URL(webworkerMain.toString());

WebExtensionContext.getWorkerScript(workerUrl)
.then((workerScript) => {
const workerBlob = new Blob([workerScript], {
type: "application/javascript",
});

const urlObj = URL.createObjectURL(workerBlob);

WebExtensionContext.setWorker(new Worker(urlObj));

if (WebExtensionContext.worker !== undefined) {
WebExtensionContext.worker.onmessage = (event) => {
const { data } = event;

WebExtensionContext.containerId = event.data.containerId;

if (data.type === Constants.workerEventMessages.REMOVE_CONNECTED_USER) {
WebExtensionContext.removeConnectedUserInContext(
data.userId
);
}
if (data.type === Constants.workerEventMessages.UPDATE_CONNECTED_USERS) {
WebExtensionContext.updateConnectedUsersInContext(
data.containerId,
data.userName,
data.userId,
data.entityId
);
}
};
}
})

WebExtensionContext.telemetry.sendInfoTelemetry(telemetryEventNames.WEB_EXTENSION_WEB_WORKER_REGISTERED);
} catch(error) {
WebExtensionContext.telemetry.sendErrorTelemetry(
telemetryEventNames.WEB_EXTENSION_WEB_WORKER_REGISTRATION_FAILED,
createWebWorkerInstance.name,
(error as Error)?.message,
error as Error);
}
}

Expand Down
Loading
Loading