Checkpoint

This commit is contained in:
Steve Faulkner
2021-01-02 17:15:18 -06:00
parent 5652f29d03
commit 2e10b96678
8 changed files with 147 additions and 262 deletions

View File

@@ -1,9 +1,9 @@
import { AccessInputMetadata } from "../../Contracts/DataModels";
import { HostedUtils } from "./HostedUtils";
import { getDatabaseAccountPropertiesFromMetadata } from "./HostedUtils";
describe("getDatabaseAccountPropertiesFromMetadata", () => {
it("should only return an object with the mongoEndpoint key if the apiKind is mongoCompute (5)", () => {
let mongoComputeAccount: AccessInputMetadata = {
const mongoComputeAccount: AccessInputMetadata = {
accountName: "compute-batch2",
apiEndpoint: "compute-batch2.mongo.cosmos.azure.com:10255",
apiKind: 5,
@@ -11,21 +11,21 @@ describe("getDatabaseAccountPropertiesFromMetadata", () => {
expiryTimestamp: "1234",
mongoEndpoint: "https://compute-batch2.mongo.cosmos.azure.com:443/"
};
expect(HostedUtils.getDatabaseAccountPropertiesFromMetadata(mongoComputeAccount)).toEqual({
expect(getDatabaseAccountPropertiesFromMetadata(mongoComputeAccount)).toEqual({
mongoEndpoint: mongoComputeAccount.mongoEndpoint,
documentEndpoint: mongoComputeAccount.documentEndpoint
});
});
it("should not return an object with the mongoEndpoint key if the apiKind is mongo (1)", () => {
let mongoAccount: AccessInputMetadata = {
const mongoAccount: AccessInputMetadata = {
accountName: "compute-batch2",
apiEndpoint: "compute-batch2.mongo.cosmos.azure.com:10255",
apiKind: 1,
documentEndpoint: "https://compute-batch2.documents.azure.com:443/",
expiryTimestamp: "1234"
};
expect(HostedUtils.getDatabaseAccountPropertiesFromMetadata(mongoAccount)).toEqual({
expect(getDatabaseAccountPropertiesFromMetadata(mongoAccount)).toEqual({
documentEndpoint: mongoAccount.documentEndpoint
});
});

View File

@@ -3,33 +3,31 @@ import * as DataModels from "../../Contracts/DataModels";
import { AccessInputMetadata } from "../../Contracts/DataModels";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
export class HostedUtils {
static getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): any {
let properties = { documentEndpoint: metadata.documentEndpoint };
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(metadata.apiKind);
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {
let properties = { documentEndpoint: metadata.documentEndpoint };
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(metadata.apiKind);
if (apiExperience === Constants.DefaultAccountExperience.Cassandra) {
if (apiExperience === Constants.DefaultAccountExperience.Cassandra) {
properties = Object.assign(properties, {
cassandraEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableCassandra }]
});
} else if (apiExperience === Constants.DefaultAccountExperience.Table) {
properties = Object.assign(properties, {
tableEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableTable }]
});
} else if (apiExperience === Constants.DefaultAccountExperience.Graph) {
properties = Object.assign(properties, {
gremlinEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableGremlin }]
});
} else if (apiExperience === Constants.DefaultAccountExperience.MongoDB) {
if (metadata.apiKind === DataModels.ApiKind.MongoDBCompute) {
properties = Object.assign(properties, {
cassandraEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableCassandra }]
mongoEndpoint: metadata.mongoEndpoint
});
} else if (apiExperience === Constants.DefaultAccountExperience.Table) {
properties = Object.assign(properties, {
tableEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableTable }]
});
} else if (apiExperience === Constants.DefaultAccountExperience.Graph) {
properties = Object.assign(properties, {
gremlinEndpoint: metadata.apiEndpoint,
capabilities: [{ name: Constants.CapabilityNames.EnableGremlin }]
});
} else if (apiExperience === Constants.DefaultAccountExperience.MongoDB) {
if (metadata.apiKind === DataModels.ApiKind.MongoDBCompute) {
properties = Object.assign(properties, {
mongoEndpoint: metadata.mongoEndpoint
});
}
}
return properties;
}
return properties;
}

View File

@@ -1,60 +1,17 @@
import * as Constants from "../../Common/Constants";
import AuthHeadersUtil from "./Authorization";
import Q from "q";
import {
AccessInputMetadata,
ApiKind,
DatabaseAccount,
GenerateTokenResponse,
resourceTokenConnectionStringProperties
} from "../../Contracts/DataModels";
import { AuthType } from "../../AuthType";
import { CollectionCreation } from "../../Shared/Constants";
import { DataExplorerInputsFrame } from "../../Contracts/ViewModels";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { HostedUtils } from "./HostedUtils";
import { sendMessage } from "../../Common/MessageHandler";
import { SessionStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { SubscriptionUtilMappings } from "../../Shared/Constants";
import "../../Explorer/Tables/DataTable/DataTableBindingManager";
import Explorer from "../../Explorer/Explorer";
import { updateUserContext } from "../../UserContext";
import * as Constants from "../../Common/Constants";
import { configContext } from "../../ConfigContext";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
import { ApiKind, DatabaseAccount, resourceTokenConnectionStringProperties } from "../../Contracts/DataModels";
import { DataExplorerInputsFrame } from "../../Contracts/ViewModels";
import Explorer from "../../Explorer/Explorer";
import "../../Explorer/Tables/DataTable/DataTableBindingManager";
import { CollectionCreation, SubscriptionUtilMappings } from "../../Shared/Constants";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import AuthHeadersUtil from "./Authorization";
import { extractFeatures } from "./extractFeatures";
import { getDatabaseAccountPropertiesFromMetadata } from "./HostedUtils";
export default class Main {
private static _databaseAccountId: string;
private static _encryptedToken: string;
private static _accessInputMetadata: AccessInputMetadata;
public static initializeExplorer(): Explorer {
const params = new URLSearchParams(window.location.search);
let authType: string = params && params.get("authType");
// Encrypted token flow
if (params && params.has("key")) {
Main._encryptedToken = encodeURIComponent(params.get("key"));
Main._accessInputMetadata = JSON.parse(params.get("metadata"));
authType = AuthType.EncryptedToken;
}
const explorer = new Explorer();
// workaround to resolve cyclic refs with view // TODO. Is this even needed anymore?
explorer.renewExplorerShareAccess = Main.renewExplorerAccess;
window.addEventListener("message", explorer.handleMessage.bind(explorer), false);
if (authType === AuthType.EncryptedToken) {
updateUserContext({
accessToken: Main._encryptedToken
});
Main._initDataExplorerFrameInputs(explorer);
} else if (authType === AuthType.AAD) {
} else {
Main._initDataExplorerFrameInputs(explorer);
}
return explorer;
}
public static parseResourceTokenConnectionString(connectionString: string): resourceTokenConnectionStringProperties {
let accountEndpoint: string;
let collectionId: string;
@@ -87,75 +44,7 @@ export default class Main {
};
}
public static renewExplorerAccess = (explorer: Explorer, connectionString: string): Q.Promise<void> => {
if (!connectionString) {
console.error("Missing or invalid connection string input");
Q.reject("Missing or invalid connection string input");
}
if (Main._isResourceToken(connectionString)) {
return Main._renewExplorerAccessWithResourceToken(explorer, connectionString);
}
const deferred: Q.Deferred<void> = Q.defer<void>();
AuthHeadersUtil.generateUnauthenticatedEncryptedTokenForConnectionString(connectionString).then(
(encryptedToken: GenerateTokenResponse) => {
if (!encryptedToken || !encryptedToken.readWrite) {
deferred.reject("Encrypted token is empty or undefined");
}
Main._encryptedToken = encryptedToken.readWrite;
window.authType = AuthType.EncryptedToken;
updateUserContext({
accessToken: Main._encryptedToken
});
Main._getAccessInputMetadata(Main._encryptedToken).then(
() => {
if (explorer.isConnectExplorerVisible()) {
explorer.notificationConsoleData([]);
explorer.hideConnectExplorerForm();
}
if (Main._accessInputMetadata.apiKind != ApiKind.Graph) {
// do not save encrypted token for graphs because we cannot extract master key in the client
SessionStorageUtility.setEntryString(StorageKey.EncryptedKeyToken, Main._encryptedToken);
window.parent &&
window.parent.history.replaceState(
{ encryptedToken: encryptedToken },
"",
`?key=${Main._encryptedToken}${(window.parent && window.parent.location.hash) || ""}`
); // replace query params if any
} else {
SessionStorageUtility.removeEntry(StorageKey.EncryptedKeyToken);
window.parent &&
window.parent.history.replaceState(
{ encryptedToken: encryptedToken },
"",
`?${(window.parent && window.parent.location.hash) || ""}`
); // replace query params if any
}
const masterKey: string = Main._getMasterKeyFromConnectionString(connectionString);
Main._setExplorerReady(explorer, masterKey);
deferred.resolve();
},
(error: any) => {
console.error(error);
deferred.reject(error);
}
);
},
(error: any) => {
deferred.reject(`Failed to generate encrypted token: ${getErrorMessage(error)}`);
}
);
return deferred.promise.timeout(Constants.ClientDefaults.requestTimeoutMs);
};
private static _initDataExplorerFrameInputs(
public static initDataExplorerFrameInputs(
explorer: Explorer,
masterKey?: string /* master key extracted from connection string if available */,
account?: DatabaseAccount,
@@ -187,7 +76,7 @@ export default class Main {
id: Main._databaseAccountId,
name: Main._accessInputMetadata.accountName,
kind: this._getDatabaseAccountKindFromExperience(apiExperience),
properties: HostedUtils.getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
properties: getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
tags: { defaultExperience: apiExperience }
},
subscriptionId,
@@ -237,7 +126,7 @@ export default class Main {
id: Main._databaseAccountId,
name: Main._accessInputMetadata.accountName,
kind: this._getDatabaseAccountKindFromExperience(apiExperience),
properties: HostedUtils.getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
properties: getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
tags: { defaultExperience: apiExperience }
},
subscriptionId,
@@ -273,11 +162,6 @@ export default class Main {
return Constants.AccountKind.GlobalDocumentDB;
}
private static async _getAccessInputMetadata(accessInput: string): Promise<void> {
const metadata = await AuthHeadersUtil.getAccessInputMetadata(accessInput);
Main._accessInputMetadata = metadata;
}
private static _getMasterKeyFromConnectionString(connectionString: string): string {
if (!connectionString || Main._accessInputMetadata == null || Main._accessInputMetadata.apiKind !== ApiKind.Graph) {
// client only needs master key for Graph API
@@ -291,69 +175,4 @@ export default class Main {
private static _isResourceToken(connectionString: string): boolean {
return connectionString && connectionString.includes("type=resource");
}
private static _renewExplorerAccessWithResourceToken = (
explorer: Explorer,
connectionString: string
): Q.Promise<void> => {
window.authType = AuthType.ResourceToken;
const properties: resourceTokenConnectionStringProperties = Main.parseResourceTokenConnectionString(
connectionString
);
if (
!properties.accountEndpoint ||
!properties.resourceToken ||
!properties.databaseId ||
!properties.collectionId
) {
console.error("Invalid connection string input");
Q.reject("Invalid connection string input");
}
updateUserContext({
resourceToken: properties.resourceToken,
endpoint: properties.accountEndpoint
});
explorer.resourceTokenDatabaseId(properties.databaseId);
explorer.resourceTokenCollectionId(properties.collectionId);
if (properties.partitionKey) {
explorer.resourceTokenPartitionKey(properties.partitionKey);
}
Main._accessInputMetadata = Main._getAccessInputMetadataFromAccountEndpoint(properties.accountEndpoint);
if (explorer.isConnectExplorerVisible()) {
explorer.notificationConsoleData([]);
explorer.hideConnectExplorerForm();
}
Main._setExplorerReady(explorer);
return Q.resolve();
};
private static _getAccessInputMetadataFromAccountEndpoint = (accountEndpoint: string): AccessInputMetadata => {
const documentEndpoint: string = accountEndpoint;
const result: RegExpMatchArray = accountEndpoint.match("https://([^\\.]+)\\..+");
const accountName: string = result && result[1];
const apiEndpoint: string = accountEndpoint.substring(8);
const apiKind: number = ApiKind.SQL;
return {
accountName,
apiEndpoint,
apiKind,
documentEndpoint,
expiryTimestamp: ""
};
};
private static _setExplorerReady(
explorer: Explorer,
masterKey?: string,
account?: DatabaseAccount,
authorizationToken?: string
) {
Main._initDataExplorerFrameInputs(explorer, masterKey, account, authorizationToken);
explorer.isAccountReady.valueHasMutated();
sendMessage("ready");
}
}