Compare commits

..

4 Commits

Author SHA1 Message Date
Tanuj Mittal
f788967ef2 Disable endtoendpuppeteer tests 2020-09-25 14:27:05 -07:00
Tanuj Mittal
823cbc4229 Do not fail when trying to find DE window with cross origin 2020-09-25 13:49:46 -07:00
Steve Faulkner
a04afc48e3 Fix hotfix syntax in workflow 2020-09-21 16:06:40 -05:00
artrejo
b86551c784 Remove AFEC check for Synapse Link and Mongo 2020-09-21 13:58:27 -07:00
12 changed files with 40 additions and 102 deletions

View File

@@ -3,8 +3,8 @@ on:
push: push:
branches: branches:
- master - master
- hotfix/* - hotfix/**
- release/* - release/**
pull_request: pull_request:
branches: branches:
- master - master
@@ -196,26 +196,6 @@ jobs:
shell: bash shell: bash
env: env:
NODE_TLS_REJECT_UNAUTHORIZED: 0 NODE_TLS_REJECT_UNAUTHORIZED: 0
endtoendpuppeteer:
name: "End to end puppeteer tests"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: End to End Puppeteer Tests
run: |
npm ci
npm start &
npm run wait-for-server
npm run test:e2e
shell: bash
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
nuget: nuget:
name: Publish Nuget name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/') if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')

View File

@@ -48,16 +48,15 @@ export function sendCachedDataMessage<TResponseDataModel>(
export function sendMessage(data: any): void { export function sendMessage(data: any): void {
if (canSendMessage()) { if (canSendMessage()) {
const dataExplorerWindow = getDataExplorerWindow(window); // We try to find data explorer window first, then fallback to current window
if (dataExplorerWindow) { const portalChildWindow = getDataExplorerWindow(window) || window;
dataExplorerWindow.parent.postMessage( portalChildWindow.parent.postMessage(
{ {
signature: "pcIframe", signature: "pcIframe",
data: data data: data
}, },
dataExplorerWindow.document.referrer portalChildWindow.document.referrer
); );
}
} }
} }
@@ -66,15 +65,21 @@ export const getDataExplorerWindow = (currentWindow: Window): Window | undefined
// Start with the current window and traverse up the parent hierarchy to find a window // Start with the current window and traverse up the parent hierarchy to find a window
// with `dataExplorerPlatform` property // with `dataExplorerPlatform` property
let dataExplorerWindow: Window | undefined = currentWindow; let dataExplorerWindow: Window | undefined = currentWindow;
// TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet
// eslint-disable-next-line @typescript-eslint/no-explicit-any try {
while (dataExplorerWindow && (dataExplorerWindow as any).dataExplorerPlatform == undefined) { // TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet
// If a window does not have a parent, its parent property is a reference to itself. // eslint-disable-next-line @typescript-eslint/no-explicit-any
if (dataExplorerWindow.parent == dataExplorerWindow) { while (dataExplorerWindow && (dataExplorerWindow as any).dataExplorerPlatform == undefined) {
dataExplorerWindow = undefined; // If a window does not have a parent, its parent property is a reference to itself.
} else { if (dataExplorerWindow.parent == dataExplorerWindow) {
dataExplorerWindow = dataExplorerWindow.parent; dataExplorerWindow = undefined;
} else {
dataExplorerWindow = dataExplorerWindow.parent;
}
} }
} catch (error) {
// This can happen if we come across parent from a different origin
dataExplorerWindow = undefined;
} }
return dataExplorerWindow; return dataExplorerWindow;

View File

@@ -6,7 +6,7 @@ export enum Platform {
interface ConfigContext { interface ConfigContext {
platform: Platform; platform: Platform;
allowedParentFrameOrigins: string[]; allowedParentFrameOrigins: RegExp;
gitSha?: string; gitSha?: string;
proxyPath?: string; proxyPath?: string;
AAD_ENDPOINT: string; AAD_ENDPOINT: string;
@@ -30,12 +30,7 @@ interface ConfigContext {
// Default configuration // Default configuration
let configContext: Readonly<ConfigContext> = { let configContext: Readonly<ConfigContext> = {
platform: Platform.Portal, platform: Platform.Portal,
allowedParentFrameOrigins: [ allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/,
`^https:\\/\\/cosmos.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+.portal.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+.ext.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+microsoftazure.de$`
],
// Webpack injects this at build time // Webpack injects this at build time
gitSha: process.env.GIT_SHA, gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/", hostedExplorerURL: "https://cosmos.azure.com/",
@@ -78,13 +73,8 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
const response = await fetch("./config.json"); const response = await fetch("./config.json");
if (response.status === 200) { if (response.status === 200) {
try { try {
const { allowedParentFrameOrigins, ...externalConfig } = await response.json(); const externalConfig = await response.json();
Object.assign(configContext, externalConfig); Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
});
}
} catch (error) { } catch (error) {
console.error("Unable to parse json in config file"); console.error("Unable to parse json in config file");
console.error(error); console.error(error);

View File

@@ -86,7 +86,6 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => { public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.removeItem(data, event); this.removeItem(data, event);
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
event.stopPropagation(); event.stopPropagation();
return false; return false;
} }
@@ -95,7 +94,7 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
public addItem(): void { public addItem(): void {
this.listItems.push({ value: ko.observable("") }); this.listItems.push({ value: ko.observable("") });
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus(); document.getElementById("uniqueKeyItems").focus();
} }
public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => { public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => {

View File

@@ -115,10 +115,10 @@
<!-- Database provisioned throughput - Start --> <!-- Database provisioned throughput - Start -->
<!-- ko if: canConfigureThroughput --> <!-- ko if: canConfigureThroughput -->
<div class="databaseProvision" aria-label="Provision database throughput" <div class="databaseProvision" aria-label="New database provision support"
data-bind="visible: databaseCreateNew"> data-bind="visible: databaseCreateNew">
<input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput" <input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput"
id="addCollection-databaseSharedThroughput" title="Provision database throughput" id="addCollection-databaseSharedThroughput" title="Provision shared throughput"
data-bind="checked: databaseCreateNewShared" /> data-bind="checked: databaseCreateNewShared" />
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span> <span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
<span class="infoTooltip" role="tooltip" tabindex="0"> <span class="infoTooltip" role="tooltip" tabindex="0">
@@ -522,7 +522,7 @@
<span class="tooltiptext infoTooltipWidth"> <span class="tooltiptext infoTooltipWidth">
Enable analytical store capability to perform near real-time analytics on your operational Enable analytical store capability to perform near real-time analytics on your operational
data, without impacting the performance of transactional workloads. data, without impacting the performance of transactional workloads.
Learn more <a class="errorLink" tabindex="-1" href="https://aka.ms/analytical-store-overview" Learn more <a class="errorLink" href="https://aka.ms/analytical-store-overview"
target="_blank">here</a> target="_blank">here</a>
</span> </span>
</span> </span>
@@ -569,7 +569,7 @@
Azure Synapse Link is required for creating an analytical Azure Synapse Link is required for creating an analytical
store container. Enable Synapse Link for this store container. Enable Synapse Link for this
Cosmos DB account. Cosmos DB account.
<span><a class="errorLink" tabindex="0" href="https://aka.ms/cosmosdb-synapselink" target="_blank">Learn <span><a class="errorLink" href="https://aka.ms/cosmosdb-synapselink" target="_blank">Learn
more</a></span> more</a></span>
</div> </div>

View File

@@ -608,7 +608,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
return true; return true;
} }
if (this.container.isPreferredApiMongoDB() && this.container.hasStorageAnalyticsAfecFeature()) { if (this.container.isPreferredApiMongoDB()) {
return true; return true;
} }

View File

@@ -9,7 +9,6 @@ import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data // TODO: Use specific actions for logging telemetry data
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel { export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
private initalFocusedElement: HTMLElement | undefined;
public id: string; public id: string;
public container: Explorer; public container: Explorer;
public firstFieldHasFocus: ko.Observable<boolean>; public firstFieldHasFocus: ko.Observable<boolean>;
@@ -50,11 +49,9 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
this.visible(false); this.visible(false);
this.isExecuting(false); this.isExecuting(false);
this.resetData(); this.resetData();
this.resetFocus();
} }
public open() { public open() {
this.initalFocusedElement = document.activeElement as HTMLElement;
this.visible(true); this.visible(true);
this.firstFieldHasFocus(true); this.firstFieldHasFocus(true);
this.resizePane(); this.resizePane();
@@ -126,11 +123,4 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
$(paneElement).height(newPaneElementHeight); $(paneElement).height(newPaneElementHeight);
} }
private resetFocus(): void {
if (this.initalFocusedElement) {
this.initalFocusedElement.focus();
this.initalFocusedElement = undefined;
}
}
} }

View File

@@ -67,7 +67,8 @@
name="collectionIdConfirmation" name="collectionIdConfirmation"
required required
class="collid" class="collid"
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus, attr: { 'aria-label': collectionIdConfirmationText }" data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus"
aria-label="Confirm by typing the collection id"
/> />
</p> </p>
</div> </div>

View File

@@ -80,7 +80,7 @@ export default class AddTableEntityPane extends TableEntityPane {
this.updateIsActionEnabled(); this.updateIsActionEnabled();
super.open(); super.open();
} }
const focusElement = document.getElementById("closeAddEntityPane"); const focusElement = document.getElementById("addTableEntityValue");
focusElement && focusElement.focus(); focusElement && focusElement.focus();
} }

View File

@@ -21,7 +21,6 @@
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span role="heading" aria-level="2" data-bind="text: title"></span>
<div <div
id="closeAddEntityPane"
class="closeImg" class="closeImg"
role="button" role="button"
aria-label="Close pane" aria-label="Close pane"

View File

@@ -1,21 +0,0 @@
import { isInvalidParentFrameOrigin } from "./MessageValidation";
test.each`
domain | expected
${"https://cosmos.azure.com"} | ${false}
${"https://cosmos.azure.us"} | ${false}
${"https://cosmos.azure.cn"} | ${false}
${"https://cosmos.microsoftazure.de"} | ${false}
${"https://subdomain.portal.azure.com"} | ${false}
${"https://subdomain.portal.azure.us"} | ${false}
${"https://subdomain.portal.azure.cn"} | ${false}
${"https://subdomain.microsoftazure.de"} | ${false}
${"https://main.documentdb.ext.azure.com"} | ${false}
${"https://main.documentdb.ext.azure.us"} | ${false}
${"https://main.documentdb.ext.azure.cn"} | ${false}
${"https://main.documentdb.ext.microsoftazure.de"} | ${false}
${"https://random.domain"} | ${true}
${"https://malicious.cloudapp.azure.com"} | ${true}
`("returns $expected when called with $domain", ({ domain, expected }) => {
expect(isInvalidParentFrameOrigin({ origin: domain } as MessageEvent)).toBe(expected);
});

View File

@@ -4,18 +4,13 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean {
return !isValidOrigin(configContext.allowedParentFrameOrigins, event); return !isValidOrigin(configContext.allowedParentFrameOrigins, event);
} }
function isValidOrigin(allowedOrigins: string[], event: MessageEvent): boolean { function isValidOrigin(allowedOrigins: RegExp, event: MessageEvent): boolean {
const eventOrigin = (event && event.origin) || ""; const eventOrigin = (event && event.origin) || "";
const windowOrigin = (window && window.origin) || ""; const windowOrigin = (window && window.origin) || "";
if (eventOrigin === windowOrigin) { if (eventOrigin === windowOrigin) {
return true; return true;
} }
for (const origin of allowedOrigins) { const result = allowedOrigins && allowedOrigins.test(eventOrigin);
const result = new RegExp(origin).test(eventOrigin); return result;
if (result) {
return true;
}
}
return false;
} }