(el: any) => {
        // Format the data from ServerRespond to the schema
        return {
          stock: el.stock,
          top_ask_price: el.top_ask && el.top_ask.price || 0,
          top_bid_price: el.top_bid && el.top_bid.price || 0,
          timestamp: el.timestamp,
        };
      }


/**
 * React component that renders Perspective based on data
 * parsed from its parent through data property.
 */
class Graph extends Component<IProps, {}> {
  // Perspective table
  table: Table | undefined;

  render() {
    return React.createElement('perspective-viewer');
  }

  componentDidMount() {
    // Get element to attach the table from the DOM.
    const elem = document.getElementsByTagName('perspective-viewer')[0] as unknown as PerspectiveViewerElement;

    const schema = {
      stock: 'string',
      top_ask_price: 'float',
      top_bid_price: 'float',
      timestamp: 'date',
    };

    if (window.perspective && window.perspective.worker()) {
      this.table = window.perspective.worker().table(schema);
    }
    if (this.table) {
      // Load the `table` in the `<perspective-viewer>` DOM reference.

      // Add more Perspective configurations here.
      elem.load(this.table);
      elem.setAttribute('view', 'y_line');
      elem.setAttribute('column-pivots', '["stock"]');
      elem.setAttribute('row-pivots', '["timestamp"]');
      elem.setAttribute('columns','["top_ask_price"]');
      elem.setAttribute('aggregates', `
      {"stock": "discount count",
      "top_ask_price":"avg",
      "top_bid_price": "avg",
      "timestamp":"distinct count"}`)
    }
  }

  componentDidUpdate() {
    // Everytime the data props is updated, insert the data into Perspective table
    if (this.table) {
      // As part of the task, you need to fix the way we update the data props to
      // avoid inserting duplicated entries into Perspective table again.
      this.table.update(this.props.data.map((el: any) => {
        // Format the data from ServerRespond to the schema
        return {
          stock: el.stock,
          top_ask_price: el.top_ask && el.top_ask.price || 0,
          top_bid_price: el.top_bid && el.top_bid.price || 0,
          timestamp: el.timestamp,
        };
      }));
    }
  }
}


/**
 * Props declaration for <Graph />
 */
interface IProps {
  data: ServerRespond[],
}


/**
 * Perspective library adds load to HTMLElement prototype.
 * This interface acts as a wrapper for Typescript compiler.
 */
interface PerspectiveViewerElement extends HTMLElement{
  load: (table: Table) => void,
}


render() {
    return React.createElement('perspective-viewer');
  }


componentDidMount() {
    // Get element to attach the table from the DOM.
    const elem = document.getElementsByTagName('perspective-viewer')[0] as unknown as PerspectiveViewerElement;

    const schema = {
      stock: 'string',
      top_ask_price: 'float',
      top_bid_price: 'float',
      timestamp: 'date',
    };

    if (window.perspective && window.perspective.worker()) {
      this.table = window.perspective.worker().table(schema);
    }
    if (this.table) {
      // Load the `table` in the `<perspective-viewer>` DOM reference.

      // Add more Perspective configurations here.
      elem.load(this.table);
      elem.setAttribute('view', 'y_line');
      elem.setAttribute('column-pivots', '["stock"]');
      elem.setAttribute('row-pivots', '["timestamp"]');
      elem.setAttribute('columns','["top_ask_price"]');
      elem.setAttribute('aggregates', `
      {"stock": "discount count",
      "top_ask_price":"avg",
      "top_bid_price": "avg",
      "timestamp":"distinct count"}`)
    }
  }


componentDidUpdate() {
    // Everytime the data props is updated, insert the data into Perspective table
    if (this.table) {
      // As part of the task, you need to fix the way we update the data props to
      // avoid inserting duplicated entries into Perspective table again.
      this.table.update(this.props.data.map((el: any) => {
        // Format the data from ServerRespond to the schema
        return {
          stock: el.stock,
          top_ask_price: el.top_ask && el.top_ask.price || 0,
          top_bid_price: el.top_bid && el.top_bid.price || 0,
          timestamp: el.timestamp,
        };
      }));
    }
  }


props =>
  createElement('mj-title', null, props.children)


export type ITitleProps = {
  /**
   * Children
   */
  children: string;
};


@Directive({
  selector: '[unicornify-clear]',
})
export class UnicornifyClearDirective implements OnInit {
  constructor(private readonly cornify: UnicornifyService) {}

  ngOnInit(): void {
    this.cornify.remove();
  }

  @HostListener('click', ['$event'])
  clear() {
    this.cornify.remove();
  }
}


ngOnInit(): void {
    this.cornify.remove();
  }


@HostListener('click', ['$event'])
  clear() {
    this.cornify.remove();
  }


@NgModule({
  declarations: [
    AppComponent,
    NewMealComponent,
    EditMealComponent,
    FilterPipe
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


export function inject(config: adal.Config): adal.AuthenticationContext;


interface AuthenticationContext {
        REQUEST_TYPE: {
            LOGIN: string,
            RENEW_TOKEN: string,
            UNKNOWN: string
        };

        callback : any;

        _getItem : any;

        _renewFailed : any;

        CONSTANTS : any;
    }


interface Window {
    AuthenticationContext : any;
    callBackMappedToRenewStates : any;
}


/*
 * cloudbeaver - Cloud Database Manager
 * Copyright (C) 2020 DBeaver Corp and others
 *
 * Licensed under the Apache License, Version 2.0.
 * you may not use this file except in compliance with the License.
 */
export interface IProperty {
  id: string;
  key: string;
  displayName?: string;
  defaultValue?: string;
  description?: string;
  validValues?: string[];
}


(message: string, path: string): void => {
    if (message && message !== '') {
      let gitApi = new Git();
      gitApi.commit(message, path).then(response => {
        this.props.refresh();
      });
    }
  }


response => {
        this.props.refresh();
      }


async (branchName: string) => {
    let gitApi = new Git();
    await gitApi.checkout(
      true,
      true,
      branchName,
      false,
      null,
      this.props.currentFileBrowserPath
    );
    this.toggleNewBranchBox();
    this.props.refresh();
  }


(): void => {
    this.props.refresh();
    if (!this.props.disabled) {
      this.setState({
        showNewBranchBox: !this.state.showNewBranchBox,
        dropdownOpen: false
      });
    } else {
      showErrorMessage('Creating new branch disabled', {
        message:
          'You have staged changes in current branch. Please commit / discard them before creating a new branch.'
      });
    }
  }


(branch: any, branchIndex: number) => {
              return (
                <li
                  className={branchListItemStyle}
                  key={branchIndex}
                  onClick={() => this.switchBranch(branch.name)}


export class BranchHeader extends React.Component<
  IBranchHeaderProps,
  IBranchHeaderState
> {
  interval: any;
  constructor(props: IBranchHeaderProps) {
    super(props);
    this.state = {
      dropdownOpen: false,
      showCommitBox: true,
      showNewBranchBox: false
    };
  }

  /** Commit all staged files */
  commitAllStagedFiles = (message: string, path: string): void => {
    if (message && message !== '') {
      let gitApi = new Git();
      gitApi.commit(message, path).then(response => {
        this.props.refresh();
      });
    }
  };

  /** Update state of commit message input box */
  updateCommitBoxState(disable: boolean, numberOfFiles: number) {
    if (disable) {
      if (numberOfFiles === 0) {
        return classes(
          stagedCommitButtonStyle,
          stagedCommitButtonDisabledStyle
        );
      } else {
        return classes(stagedCommitButtonStyle, stagedCommitButtonReadyStyle);
      }
    } else {
      return stagedCommitButtonStyle;
    }
  }

  /** Switch current working branch */
  async switchBranch(branchName: string) {
    let gitApi = new Git();
    await gitApi.checkout(
      true,
      false,
      branchName,
      false,
      null,
      this.props.currentFileBrowserPath
    );
    this.toggleSelect();
    this.props.refresh();
  }

  createNewBranch = async (branchName: string) => {
    let gitApi = new Git();
    await gitApi.checkout(
      true,
      true,
      branchName,
      false,
      null,
      this.props.currentFileBrowserPath
    );
    this.toggleNewBranchBox();
    this.props.refresh();
  };

  toggleSelect() {
    this.props.refresh();
    if (!this.props.disabled) {
      this.setState({
        showCommitBox: !this.state.showCommitBox,
        dropdownOpen: !this.state.dropdownOpen
      });
    } else {
      showErrorMessage('Switching branch disabled', {
        message:
          'You have staged changes in current branch. Please commit / discard them before switching to another branch.'
      });
    }
  }

  getBranchStyle() {
    if (this.state.dropdownOpen) {
      return classes(branchStyle, expandedBranchStyle);
    } else {
      return this.props.showList
        ? branchStyle
        : classes(branchStyle, smallBranchStyle);
    }
  }

  toggleNewBranchBox = (): void => {
    this.props.refresh();
    if (!this.props.disabled) {
      this.setState({
        showNewBranchBox: !this.state.showNewBranchBox,
        dropdownOpen: false
      });
    } else {
      showErrorMessage('Creating new branch disabled', {
        message:
          'You have staged changes in current branch. Please commit / discard them before creating a new branch.'
      });
    }
  };

  render() {
    return (

        <button
          className={openHistorySideBarButtonStyle}
          onClick={() => this.props.toggleSidebar()}
          title={'Show commit history'}
        >
          History

        </button>

          <h3 className=


export interface IBranchHeaderState {
  dropdownOpen: boolean;
  showCommitBox: boolean;
  showNewBranchBox: boolean;
}


export interface IBranchHeaderProps {
  currentFileBrowserPath: string;
  topRepoPath: string;
  currentBranch: string;
  upstreamBranch: string;
  stagedFiles: any;
  data: any;
  refresh: any;
  disabled: boolean;
  toggleSidebar: Function;
  showList: boolean;
  currentTheme: string;
}


/** Update state of commit message input box */
updateCommitBoxState(disable: boolean, numberOfFiles: number) {
    if (disable) {
      if (numberOfFiles === 0) {
        return classes(
          stagedCommitButtonStyle,
          stagedCommitButtonDisabledStyle
        );
      } else {
        return classes(stagedCommitButtonStyle, stagedCommitButtonReadyStyle);
      }
    } else {
      return stagedCommitButtonStyle;
    }
  }


/** Switch current working branch */
async switchBranch(branchName: string) {
    let gitApi = new Git();
    await gitApi.checkout(
      true,
      false,
      branchName,
      false,
      null,
      this.props.currentFileBrowserPath
    );
    this.toggleSelect();
    this.props.refresh();
  }


toggleSelect() {
    this.props.refresh();
    if (!this.props.disabled) {
      this.setState({
        showCommitBox: !this.state.showCommitBox,
        dropdownOpen: !this.state.dropdownOpen
      });
    } else {
      showErrorMessage('Switching branch disabled', {
        message:
          'You have staged changes in current branch. Please commit / discard them before switching to another branch.'
      });
    }
  }


getBranchStyle() {
    if (this.state.dropdownOpen) {
      return classes(branchStyle, expandedBranchStyle);
    } else {
      return this.props.showList
        ? branchStyle
        : classes(branchStyle, smallBranchStyle);
    }
  }


render() {
    return (
      <div className={this.getBranchStyle()}>
        <button
          className={openHistorySideBarButtonStyle}
          onClick={() => this.props.toggleSidebar()}


export class WelcomeCommand extends ConfigCommand {
  public readonly triggers = ['welcome'];

  public run(message: Message) {
    message.channel.send(localize.t('welcome', { prefix: this.config.prefix }));
  }
}


public run(message: Message) {
    message.channel.send(localize.t('welcome', { prefix: this.config.prefix }));
  }


@ObjectType()
export class AuthenicatedUserType {
  @Field()
  id: string;

  @Field()
  accessToken: string;
}


() => 1.34


() => (
  <div className="sb-container"


constructSenderFromTxConfig(fTxConfig, [fAccount])


@NgModule({
  declarations: [
    // Components
  ],
  imports: [
    CommonModule
    // Modules
  ],
  providers: [
    // Services
  ]
})
export class SomeFeatureModule { }


(params: ParamMap) => this.heroService.getHero(+params.get('id'))


(hero: Hero) => this.hero = hero


() => this.goBack()


@Component({
  selector: 'hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
  @Input() hero: Hero

  constructor(
    private heroService: HeroService,
    private route: ActivatedRoute,
    private location: Location
  ) {}

  ngOnInit(): void {
    this.route.paramMap
      .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
      .subscribe((hero: Hero) => this.hero = hero)
  }

  goBack(): void {
    this.location.back()
  }

  save(): void {
    this.heroService.update(this.hero)
      .then(() => this.goBack())
  }
}


ngOnInit(): void {
    this.route.paramMap
      .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
      .subscribe((hero: Hero) => this.hero = hero)
  }


goBack(): void {
    this.location.back()
  }


save(): void {
    this.heroService.update(this.hero)
      .then(() => this.goBack())
  }


_getDefaultBody(): {
        vendor_id: string;
        vendor_auth_code: string;
    };


/**
     * Get the current list of products
     *
     * @method
     * @returns {Promise}
     * @fulfil {object} - The products list
     *
     * @example
     * const products = await client.getProducts();
     */
getProducts(): Promise<any>;


/**
     * Get the current list of coupons for a product
     *
     * @method
     * @param {number} productID
     * @returns {Promise}
     * @fulfil {object} - The coupons list
     *
     * @example
     * const coupons = await client.getProductCoupons(123);
     */
getProductCoupons(productID: number): Promise<any>;


/**
     * Get the current list of plans for a subscription
     *
     * @method
     * @param {number} productID
     * @returns {Promise}
     * @fulfil {object} - The plans list
     *
     * @example
     * const plans = await client.getProductPlans(123);
     */
getProductPlans(productID: number): Promise<any>;


/**
     * Get the current list of users for a subscription plan
     *
     * @method
     * @param {number} planID
     * @returns {Promise}
     * @fulfil {object} - The users list
     *
     * @example
     * const users = await client.getPlanUsers(123);
     */
getPlanUsers(planID: number): Promise<any>;


/**
     * Get the list of payments for a subscription plan
     *
     * @method
     * @param {number} planID
     * @returns {Promise}
     * @fulfil {object} - The payments list
     *
     * @example
     * const payments = await client.getPlanPayments(123);
     */
getPlanPayments(planID: number): Promise<any>;


/**
     * Get the list of webhooks history
     *
     * @method
     * @returns {Promise}
     * @fulfil {object} - The webhooks history list
     *
     * @example
     * const webhooksHistory = await client.getWebhooksHistory();
     */
getWebhooksHistory(): Promise<any>;


/**
     * Get the list of transations for a user
     *
     * @method
     * @param {number} userID
     * @returns {Promise}
     * @fulfil {object} - The transations list
     *
     * @example
     * const userTransactions = await client.getUserTransactions(123);
     */
getUserTransactions(userID: number): Promise<any>;


/**
     * Get the list of transations for a subscription
     *
     * @method
     * @param {number} subscriptionID
     * @returns {Promise}
     * @fulfil {object} - The transations list
     *
     * @example
     * const subscriptionTransactions = await client.getSubscriptionTransactions(123);
     */
getSubscriptionTransactions(subscriptionID: number): Promise<any>;


/**
     * Get the list of transations for an order
     *
     * @method
     * @param {number} orderID
     * @returns {Promise}
     * @fulfil {object} - The transations list
     *
     * @example
     * const orderTransactions = await client.getOrderTransactions(123);
     */
getOrderTransactions(orderID: number): Promise<any>;


/**
     * Get the list of transations for a checkout
     *
     * @method
     * @param {number} checkoutID
     * @returns {Promise}
     * @fulfil {object} - The transations list
     *
     * @example
     * const checkoutTransactions = await client.getCheckoutTransactions(123);
     */
getCheckoutTransactions(checkoutID: number): Promise<any>;


/**
     * Verify a webhook alert data using signature and a public key to validate that
     * it was indeed sent from Paddle.
     *
     * For more details: https://paddle.com/docs/reference-verifying-webhooks
     *
     * @method
     * @param  {Object} postData The object with all the parameters sent to the webhook
     * @return {boolean}
     *
     * @example
     * const client = new PaddleSDK('your-vendor-id', 'your-unique-api-key', 'your-public-key');
     *
     * // inside an Express handler which uses express.bodyParser middleware
     * const isVerified = client.verifyWebhookData(req.body);
     */
verifyWebhookData(postData: any): boolean;


/**
     * Update (upgrade/downgrade) the plan of a subscription
     *
     * @method
     * @param {number} subscriptionID
     * @param {number} planID
     * @param {boolean} prorate
	 * @param {boolean} billImmediately
     * @returns {Promise}
     * @fulfill {object} - The result of the operation
     *
     * @example
     * const result = await client.updateSubscriptionPlan(123);
     */
updateSubscriptionPlan(subscriptionID: number, planID: number, prorate?: boolean, billImmediately?: boolean): Promise<any>;


/**
     * Update subscription details, quantity, price and or currency
     *
     * @method
     * @param {number} subscriptionID
     * @param {Object} postData { quantity, price, planID, currency }
     * @returns {Promise}
     * @fulfill {object} - The result of the operation
     *
     * @example
     * const result = await client.updateSubscriptionPlan(123, { quantity: 2 });
     */
updateSubscription(subscriptionID: number, postData: any): Promise<any>;


/**
     * Cancels an active subscription
     *
     * @method
     * @param {number} subscriptionID
     * @returns {Promise}
     * @fulfil {object} - The result of the operation
     *
     * @example
     * const result = await client.cancelSubscription(123);
     */
cancelSubscription(subscriptionID: number): Promise<any>;


/**
     * Generate a custom pay link
     *
     * @method
     * @param {object} body
     * @returns {Promise}
     * @fulfil {object} - The new pay link url
     *
     * @example
     * const custom = await client.generatePayLink({
     *  "title" : "my custom checkout",
     *  "custom_message" : "some custom message"
     * 	"prices": [
     *		"USD:19.99",
     *		"EUR:15.99"
     *	 ]
     *	});
     */
generatePayLink(body: any): Promise<any>;


// thanks stackoverflow
function escapeRegex(str: string) {
	return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d');
}


() => {
  let appController: AppController;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [AppController],
    }).compile();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {
    it(`should return "I'm Good!"`, () => {
      expect(appController.getHello()).toBe(`I'm Good!`);
    });
  });
}


async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [AppController],
    }).compile();

    appController = app.get<AppController>(AppController);
  }


() => {
    it(`should return "I'm Good!"`, () => {
      expect(appController.getHello()).toBe(`I'm Good!`);
    });
  }


() => {
      expect(appController.getHello()).toBe(`I'm Good!`);
    }


async (_runner, context) => {
			if (!context.template) {
				return false;
			}
			const name = await this.chooseTemplateName(context.template, type);
			if (context.template.hasExtraConfiguration) {
				await this.customizeTemplateTask(context.template);
			}

			const options: TemplateOptions = {
				name,
				projectName: this.projectName,
				templateInst: context.template as IgniteUIForAngularTemplate
			};
			const schematic = this.context.schematic.collection.createSchematic("single-component");
			await schematic.call(options, of(this.tree), this.context).toPromise();
			return true;
		}


export class SchematicsPromptSession extends BasePromptSession {

	public tree: Tree;
	public context: SchematicContext;
	public projectName: string;
	public userAnswers: Map<string, any>;

	constructor(
		templateManager: BaseTemplateManager) {
		super(templateManager);
		this.config = ProjectConfig.getConfig();
	}

	public setContext(context: SchematicContext, tree: Tree, projectName: string) {
		this.context = context;
		this.tree = tree;
		this.projectName = projectName;
		this.userAnswers = new Map<string, any>();
	}

	public async getUserInput(options: IUserInputOptions, withBackChoice: boolean = false): Promise<string> {
		return super.getUserInput(options, withBackChoice);
	}

	public async getProjectLibrary(framework: Framework): Promise<ProjectLibrary> {
		return super.getProjectLibrary(framework);
	}

	public async getProjectTemplate(projectLibrary: ProjectLibrary): Promise<ProjectTemplate> {
		return super.getProjectTemplate(projectLibrary);
	}

	public async getTheme(projectLibrary: ProjectLibrary): Promise<string> {
		return super.getTheme(projectLibrary);
	}

	public nameIsValid(name: string, checkFolder = true): boolean {
		return super.nameIsValid(name, checkFolder);
	}

	protected completeAndRun(_port?: number) {
		// TODO?
	}

	protected async upgradePackages() {
		this.userAnswers.set("upgradePackages", true);
	}

	protected templateSelectedTask(type: "component" | "view" = "component"): Task<PromptTaskContext> {
		return async (_runner, context) => {
			if (!context.template) {
				return false;
			}
			const name = await this.chooseTemplateName(context.template, type);
			if (context.template.hasExtraConfiguration) {
				await this.customizeTemplateTask(context.template);
			}

			const options: TemplateOptions = {
				name,
				projectName: this.projectName,
				templateInst: context.template as IgniteUIForAngularTemplate
			};
			const schematic = this.context.schematic.collection.createSchematic("single-component");
			await schematic.call(options, of(this.tree), this.context).toPromise();
			return true;
		};
	}

}


public setContext(context: SchematicContext, tree: Tree, projectName: string) {
		this.context = context;
		this.tree = tree;
		this.projectName = projectName;
		this.userAnswers = new Map<string, any>();
	}


public async getUserInput(options: IUserInputOptions, withBackChoice: boolean = false): Promise<string> {
		return super.getUserInput(options, withBackChoice);
	}


public async getProjectLibrary(framework: Framework): Promise<ProjectLibrary> {
		return super.getProjectLibrary(framework);
	}


public async getProjectTemplate(projectLibrary: ProjectLibrary): Promise<ProjectTemplate> {
		return super.getProjectTemplate(projectLibrary);
	}


public async getTheme(projectLibrary: ProjectLibrary): Promise<string> {
		return super.getTheme(projectLibrary);
	}


public nameIsValid(name: string, checkFolder = true): boolean {
		return super.nameIsValid(name, checkFolder);
	}


protected completeAndRun(_port?: number) {
		// TODO?
	}


protected async upgradePackages() {
		this.userAnswers.set("upgradePackages", true);
	}


protected templateSelectedTask(type: "component" | "view" = "component"): Task<PromptTaskContext> {
		return async (_runner, context) => {
			if (!context.template) {
				return false;
			}
			const name = await this.chooseTemplateName(context.template, type);
			if (context.template.hasExtraConfiguration) {
				await this.customizeTemplateTask(context.template);
			}

			const options: TemplateOptions = {
				name,
				projectName: this.projectName,
				templateInst: context.template as IgniteUIForAngularTemplate
			};
			const schematic = this.context.schematic.collection.createSchematic("single-component");
			await schematic.call(options, of(this.tree), this.context).toPromise();
			return true;
		};
	}


@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}


function _validateLeaseDurationAndRenewInterval(duration: number, interval: number): void {
    validateType("leaseDuration", duration, true, "number");
    validateType("leaseRenewInterval", interval, true, "number");

    if (duration <= interval) {
      throw new Error(`Lease duration ${duration} needs to be greater than lease ` + `renew interval ${interval}.`);
    }

    if (duration > maxLeaseDurationInSeconds || duration < minLeaseDurationInSeconds) {
      throw new Error(
        `Lease duration needs to be between ${minLeaseDurationInSeconds} ` +
        `seconds and ${maxLeaseDurationInSeconds} seconds. The given value is: ${duration} seconds.`
      );
    }
  }


function _validatestorageContainerName(name: string): void {
    if (!name || name.match(/^[a-z0-9](([a-z0-9\-[^\-])){1,61}[a-z0-9]$/gi) === null) {
      throw new Error(
        `Azure Storage lease container name "${name}" is invalid. Please check ` +
        `naming conventions at https://msdn.microsoft.com/en-us/library/azure/dd135715.aspx`
      );
    }
  }


function _eitherStorageConnectionStringOrCheckpointLeaseManager(options: EventProcessorHostOptions): void {
    validateType("options", options, true, "object");
    const checkpointManager = options.checkpointManager;
    const leaseManager = options.leaseManager;
    const storageConnectionString = options.storageConnectionString;
    if (storageConnectionString) {
      if (checkpointManager || leaseManager) {
        throw new Error(
          "Either provide ('checkpointManager' and 'leaseManager') or " + "provide 'storageConnectionString'."
        );
      }
    } else if (!(checkpointManager && leaseManager)) {
      throw new Error(
        "Either provide ('checkpointManager' and 'leaseManager') or " + "provide 'storageConnectionString'."
      );
    }
  }


function _eitherLeaseManagerOrleaseDurationAndRenewal(options: EventProcessorHostOptions): void {
    validateType("options", options, true, "object");
    const leaseManager = options.leaseManager;
    const leaseDuration = options.leaseDuration;
    const leaseRenewInterval = options.leaseRenewInterval;
    if (leaseManager) {
      if (leaseDuration || leaseRenewInterval) {
        throw new Error("Either provide ('leaseDuration' and 'leaseRenewInterval') or " + "provide 'leaseManager'.");
      }
    } else if (!(leaseDuration && leaseRenewInterval)) {
      throw new Error("Either provide ('leaseDuration' and 'leaseRenewInterval') or " + "provide 'leaseManager'.");
    }
  }


function _createBase(hostName: string, options: EventProcessorHostOptions): BaseHostContext {
    validateType("hostName", hostName, true, "string");

    const onEphErrorFunc: OnEphError = () => {
      // do nothing
    };
    const config = EventHubConnectionConfig.create(options.eventHubConnectionString!, options.eventHubPath);

    // set defaults
    if (!options.consumerGroup) options.consumerGroup = defaultConsumerGroup;
    if (!options.eventHubPath) options.eventHubPath = config.entityPath;
    if (!options.leaseRenewInterval) options.leaseRenewInterval = defaultLeaseRenewIntervalInSeconds;
    if (!options.leaseDuration) options.leaseDuration = defaultLeaseDurationInSeconds;
    if (!options.onEphError) options.onEphError = onEphErrorFunc;
    if (!options.dataTransformer) options.dataTransformer = new DefaultDataTransformer();
    if (!options.startupScanDelay) options.startupScanDelay = defaultStartupScanDelayInSeconds;
    if (!options.fastScanInterval) options.fastScanInterval = defaultFastScanIntervalInSeconds;
    if (!options.slowScanInterval) options.slowScanInterval = defaultSlowScanIntervalInSeconds;

    validateType("options", options, true, "object");
    validateType("options.eventHubPath", options.eventHubPath, true, "string");
    validateType("options.eventHubConnectionString", options.eventHubConnectionString, true, "string");
    validateType("options.storageConnectionString", options.storageConnectionString, false, "string");
    validateType("options.initialOffset", options.initialOffset, false, "object");
    validateType("options.consumerGroup", options.consumerGroup, false, "string");
    validateType("options.storageContainerName", options.storageContainerName, false, "string");
    validateType("options.storageBlobPrefix", options.storageBlobPrefix, false, "string");
    validateType("options.onEphError", options.onEphError, false, "function");
    validateType("options.leaseRenewInterval", options.leaseRenewInterval, false, "number");
    validateType("options.leaseDuration", options.leaseDuration, false, "number");
    _eitherStorageConnectionStringOrCheckpointLeaseManager(options);
    _eitherLeaseManagerOrleaseDurationAndRenewal(options);

    const context: BaseHostContext = {
      hostName: hostName,
      checkpointLock: new AsyncLock({ maxPending: 100000 }),
      checkpointLockId: `checkpoint-${uuid()}`,
      eventHubConnectionString: options.eventHubConnectionString!,
      connectionConfig: config,
      eventHubPath: options.eventHubPath!,
      tokenProvider: options.tokenProvider,
      blobReferenceByPartition: {},
      partitionIds: [],
      pumps: new Map<string, PartitionPump>(),
      consumerGroup: options.consumerGroup,
      storageContainerName: options.storageContainerName,
      leaseRenewInterval: options.leaseRenewInterval,
      leaseDuration: options.leaseDuration,
      initialOffset: options.initialOffset,
      storageBlobPrefix: options.storageBlobPrefix,
      composedBlobPrefix: options.storageBlobPrefix
        ? `${options.storageBlobPrefix.trim()}${options.consumerGroup}/`
        : `${options.consumerGroup}/`,
      onEphError: options.onEphError,
      startupScanDelay: options.startupScanDelay,
      fastScanInterval: options.fastScanInterval,
      slowScanInterval: options.slowScanInterval,
      userAgent: getUserAgent(options),
      withHost: (msg: string) => {
        return `[${hostName}] ${msg}`;
      },
      withHostAndPartition: (partition: string | PartitionContext | BaseLease, msg: string) => {
        let id: string = "N/A";
        if (typeof partition === "string") {
          id = partition;
        } else if (typeof partition === "object") {
          id = partition.partitionId;
        }
        return `[${hostName}] [${id}] ${msg}`;
      }
    };

    if (options.storageConnectionString) {
      context.storageConnectionString = options.storageConnectionString;
      context.blobService = BlobService.create(hostName, options.storageConnectionString);
    }

    _validateLeaseDurationAndRenewInterval(context.leaseDuration, context.leaseRenewInterval);
    if (context.storageContainerName) _validatestorageContainerName(context.storageContainerName);
    return context;
  }


function _createWithCheckpointLeaseManager(
    hostName: string,
    options: EventProcessorHostOptions
  ): HostContextWithCheckpointLeaseManager {
    const ctxt = _createBase(hostName, options) as HostContextWithCheckpointLeaseManager;
    const checkpointLeaseManager = new AzureStorageCheckpointLeaseManager(ctxt);
    ctxt.leaseManager = options.leaseManager || checkpointLeaseManager;
    ctxt.checkpointManager = options.checkpointManager || checkpointLeaseManager;
    ctxt.getEventHubClient = () => {
      if (ctxt.tokenProvider) {
        return EventHubClient.createFromTokenProvider(
          ctxt.connectionConfig.host,
          ctxt.eventHubPath,
          ctxt.tokenProvider,
          { userAgent: ctxt.userAgent }
        );
      } else {
        return EventHubClient.createFromConnectionString(ctxt.eventHubConnectionString, ctxt.eventHubPath, {
          userAgent: ctxt.userAgent
        });
      }
    };
    ctxt.getHubRuntimeInformation = async () => {
      const client = ctxt.getEventHubClient();
      try {
        return await client.getHubRuntimeInformation();
      } finally {
        client.close().catch(/* do nothing */);
      }
    };
    ctxt.getPartitionInformation = async (id: string | number) => {
      const client = ctxt.getEventHubClient();
      try {
        return await client.getPartitionInformation(id);
      } finally {
        client.close().catch(/* do nothing */);
      }
    };
    ctxt.getPartitionIds = async () => {
      if (!ctxt.partitionIds.length) {
        const client = ctxt.getEventHubClient();
        try {
          ctxt.partitionIds = await client.getPartitionIds();
        } finally {
          client.close().catch(/* do nothing */);
        }
      }
      return ctxt.partitionIds;
    };
    return ctxt;
  }


function _createWithPumpManager(hostName: string, options: EventProcessorHostOptions): HostContextWithPumpManager {
    const context = _createWithCheckpointLeaseManager(hostName, options) as HostContextWithPumpManager;
    context.pumpManager = new PumpManager(context);
    return context;
  }


/**
   * @ignore
   */
export function getUserAgent(options: EventProcessorHostOptions): string {
    const finalUserAgent = options.userAgent ? `${userAgent},${options.userAgent}` : userAgent;
    return finalUserAgent;
  }


/**
   * @ignore
   */
export function create(hostName: string, options: EventProcessorHostOptions): HostContext {
    const context = _createWithPumpManager(hostName, options);
    const hostContext = context as HostContext;
    hostContext.partitionManager = new PartitionManager(context);
    hostContext.partitionScanner = new PartitionScanner(context);
    return hostContext;
  }


() => {
      // do nothing
    }


(msg: string) => {
        return `[${hostName}] ${msg}`;
      }


(partition: string | PartitionContext | BaseLease, msg: string) => {
        let id: string = "N/A";
        if (typeof partition === "string") {
          id = partition;
        } else if (typeof partition === "object") {
          id = partition.partitionId;
        }
        return `[${hostName}] [${id}] ${msg}`;
      }


() => {
      if (ctxt.tokenProvider) {
        return EventHubClient.createFromTokenProvider(
          ctxt.connectionConfig.host,
          ctxt.eventHubPath,
          ctxt.tokenProvider,
          { userAgent: ctxt.userAgent }
        );
      } else {
        return EventHubClient.createFromConnectionString(ctxt.eventHubConnectionString, ctxt.eventHubPath, {
          userAgent: ctxt.userAgent
        });
      }
    }


async () => {
      const client = ctxt.getEventHubClient();
      try {
        return await client.getHubRuntimeInformation();
      } finally {
        client.close().catch(/* do nothing */);
      }
    }


async (id: string | number) => {
      const client = ctxt.getEventHubClient();
      try {
        return await client.getPartitionInformation(id);
      } finally {
        client.close().catch(/* do nothing */);
      }
    }


async () => {
      if (!ctxt.partitionIds.length) {
        const client = ctxt.getEventHubClient();
        try {
          ctxt.partitionIds = await client.getPartitionIds();
        } finally {
          client.close().catch(/* do nothing */);
        }
      }
      return ctxt.partitionIds;
    }


/**
 * @ignore
 */
export interface BaseHostContext {
  hostName: string;
  checkpointLock: AsyncLock;
  checkpointLockId: string;
  consumerGroup: string;
  eventHubPath: string;
  storageContainerName?: string;
  eventHubConnectionString: string;
  connectionConfig: EventHubConnectionConfig;
  onEphError: OnEphError;
  leaseRenewInterval: number;
  leaseDuration: number;
  partitionIds: string[];
  blobReferenceByPartition: Dictionary<AzureBlob>;
  storageConnectionString?: string;
  tokenProvider?: TokenProvider;
  initialOffset?: EventPosition;
  storageBlobPrefix?: string;
  blobService?: BlobService;
  composedBlobPrefix: string;
  onMessage?: OnReceivedMessage;
  onError?: OnReceivedError;
  startupScanDelay?: number;
  fastScanInterval?: number;
  slowScanInterval?: number;
  pumps: Map<string, PartitionPump>;
  userAgent: string;
  withHost(msg: string): string;
  withHostAndPartition(partition: string | { partitionId: string }, msg: string): string;
}


/**
 * @ignore
 */
export interface HostContextWithCheckpointLeaseManager extends BaseHostContext {
  leaseManager: LeaseManager;
  checkpointManager: CheckpointManager;
  getEventHubClient(): EventHubClient;
  getHubRuntimeInformation(): Promise<EventHubRuntimeInformation>;
  getPartitionInformation(partitionId: string | number): Promise<EventHubPartitionRuntimeInformation>;
  getPartitionIds(): Promise<string[]>;
}


export interface HostContextWithPumpManager extends HostContextWithCheckpointLeaseManager {
  pumpManager: PumpManager;
}


export interface HostContext extends HostContextWithPumpManager {
  partitionManager: PartitionManager;
  partitionScanner: PartitionScanner;
}


function withCourseData(WrappedComponent: React.ComponentType<any>, courseId?: number) {
  return class extends React.PureComponent<{ course?: Course | null; session: Session }> {
    static async getInitialProps(context: NextPageContext) {
      try {
        const alias = context.query.course;
        const courses = await new UserService(context).getCourses();
        const course = courses.find(c => (courseId ? c.id === courseId : c.alias === alias)) || null;
        return { course };
      } catch (e) {
        console.error(e.message);
        return { course: undefined };
      }
    }

    render() {
      if (this.props.course || this.props.course === undefined) {
        return <WrappedComponent {...this.props} />;
      }
      return (
        <Row justify="center">
          <Col md={12} xs={18} style={{ marginTop: '60px' }}>
            <Alert
              message="No Access"
              description="Probably you do not participate in the course. Please register or choose another course."
              type="error"
            />
          </Col>
        </Row>
      );
    }


c => (courseId ? c.id === courseId : c.alias === alias)


static async getInitialProps(context: NextPageContext) {
      try {
        const alias = context.query.course;
        const courses = await new UserService(context).getCourses();
        const course = courses.find(c => (courseId ? c.id === courseId : c.alias === alias)) || null;
        return { course };
      } catch (e) {
        console.error(e.message);
        return { course: undefined };
      }
    }


render() {
      if (this.props.course || this.props.course === undefined) {
        return <WrappedComponent {...this.props} />;
      }
      return (
        <Row justify="center">
          <Col md={12} xs={18} style={{ marginTop: '60px' }}


(props: IScrollTableProps): React.ReactElement => {
	const {
		children,
		className,
		style,
		tableWidth,
		hasWordWrap,
		hasBorder,
		...passThroughs
	} = props;

	return (
		<div
			className={cx(
				'&',
				{
					'&-has-border': hasBorder,
				},
				className
			)}
			style={style}
		>
			<Table
				{...omitProps(
					passThroughs,
					undefined,
					_.keys(ScrollTable.propTypes),
					false
				)}
				style={{
					width: tableWidth,
				}}


export interface IScrollTableProps
	extends StandardProps,
		React.DetailedHTMLProps<
			React.HTMLAttributes<HTMLDivElement>,
			HTMLDivElement
		> {
	/** Set the width of the Table inside the scrollable container. */
	tableWidth?: number | string;

	/** Set the Table contents to not allow word wrapping. */
	hasWordWrap: boolean;

	/** render the table with borders on the outer edge. */
	hasBorder: boolean;
}


cx(
				'&',
				{
					'&-has-border': hasBorder,
				},
				className
			)


async (resolve) => {
                const answer = await this.appShell.showInformationMessage(
                    localize.DataScience.pythonExtensionRecommended(),
                    yes,
                    no,
                    doNotShowAgain
                );
                switch (answer) {
                    case yes:
                        await this.installPythonExtension();
                        break;
                    case doNotShowAgain:
                        await surveyPrompt.updateValue(false);
                        break;
                    default:
                        break;
                }
                resolve();
            }


(api) => api.getLanguageServer(resource)


(api) => api.isWindowsStoreInterpreter(pythonPath)


(api) => api.getDebuggerPath()


(api) => api.install(ProductMapping[product], resource, cancel)


(api) => api.getActivatedEnvironmentVariables(resource, interpreter, true)


(api) => api.getSuggestions(resource)


(api) => api.getInterpreters(resource)


(api) => api.getActiveInterpreter(resource)


(api) => api.getInterpreterDetails(pythonPath, resource)


// tslint:disable: max-classes-per-file
@injectable()
export class PythonApiProvider implements IPythonApiProvider {
    private readonly api = createDeferred<PythonApi>();

    private initialized?: boolean;

    constructor(
        @inject(IExtensions) private readonly extensions: IExtensions,
        @inject(IPythonExtensionChecker) private extensionChecker: IPythonExtensionChecker
    ) {}

    public getApi(): Promise<PythonApi> {
        this.init().catch(noop);
        return this.api.promise;
    }

    public setApi(api: PythonApi): void {
        if (this.api.resolved) {
            return;
        }
        this.api.resolve(api);
    }

    private async init() {
        if (this.initialized) {
            return;
        }
        this.initialized = true;
        const pythonExtension = this.extensions.getExtension<{ jupyter: { registerHooks(): void } }>(PythonExtension);
        if (!pythonExtension) {
            await this.extensionChecker.showPythonExtensionInstallRequiredPrompt();
        } else {
            if (!pythonExtension.isActive) {
                await pythonExtension.activate();
            }
            pythonExtension.exports.jupyter.registerHooks();
        }
    }
}


@injectable()
export class PythonExtensionChecker implements IPythonExtensionChecker {
    private extensionChangeHandler: Disposable | undefined;
    private pythonExtensionId = PythonExtension;
    private waitingOnInstallPrompt?: Promise<void>;
    constructor(
        @inject(IExtensions) private readonly extensions: IExtensions,
        @inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory,
        @inject(IApplicationShell) private readonly appShell: IApplicationShell,
        @inject(IApplicationEnvironment) private readonly appEnv: IApplicationEnvironment
    ) {
        // If the python extension is not installed listen to see if anything does install it
        if (!this.isPythonExtensionInstalled) {
            this.extensionChangeHandler = this.extensions.onDidChange(this.extensionsChangeHandler.bind(this));
        }
    }

    public get isPythonExtensionInstalled() {
        return this.extensions.getExtension(this.pythonExtensionId) !== undefined;
    }

    public async showPythonExtensionInstallRequiredPrompt(): Promise<void> {
        if (this.waitingOnInstallPrompt) {
            return this.waitingOnInstallPrompt;
        }
        // Ask user if they want to install and then wait for them to actually install it.
        const yes = localize.Common.bannerLabelYes();
        const no = localize.Common.bannerLabelNo();
        const answer = await this.appShell.showErrorMessage(localize.DataScience.pythonExtensionRequired(), yes, no);
        if (answer === yes) {
            await this.installPythonExtension();
        }
    }

    public async showPythonExtensionInstallRecommendedPrompt() {
        const key = 'ShouldShowPythonExtensionInstallRecommendedPrompt';
        const surveyPrompt = this.persistentStateFactory.createGlobalPersistentState(key, true);
        if (surveyPrompt.value) {
            const yes = localize.Common.bannerLabelYes();
            const no = localize.Common.bannerLabelNo();
            const doNotShowAgain = localize.Common.doNotShowAgain();

            const promise = (this.waitingOnInstallPrompt = new Promise<void>(async (resolve) => {
                const answer = await this.appShell.showInformationMessage(
                    localize.DataScience.pythonExtensionRecommended(),
                    yes,
                    no,
                    doNotShowAgain
                );
                switch (answer) {
                    case yes:
                        await this.installPythonExtension();
                        break;
                    case doNotShowAgain:
                        await surveyPrompt.updateValue(false);
                        break;
                    default:
                        break;
                }
                resolve();
            }));
            await promise;
            this.waitingOnInstallPrompt = undefined;
        }
    }

    private async installPythonExtension() {
        // Have the user install python
        this.appShell.openUrl(`${this.appEnv.uriScheme}:extension/${this.pythonExtensionId}`);
    }

    private async extensionsChangeHandler(): Promise<void> {
        // On extension change see if python was installed, if so unhook our extension change watcher and
        // notify the user that they might need to restart notebooks or interactive windows
        if (this.isPythonExtensionInstalled && this.extensionChangeHandler) {
            this.extensionChangeHandler.dispose();
            this.extensionChangeHandler = undefined;

            this.appShell
                .showInformationMessage(localize.DataScience.pythonExtensionInstalled(), localize.Common.ok())
                .then(noop);
        }
    }
}


@injectable()
export class LanguageServerProvider implements ILanguageServerProvider {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public getLanguageServer(resource?: InterpreterUri): Promise<ILanguageServer | undefined> {
        return this.apiProvider.getApi().then((api) => api.getLanguageServer(resource));
    }
}


@injectable()
export class WindowsStoreInterpreter implements IWindowsStoreInterpreter {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public isWindowsStoreInterpreter(pythonPath: string): Promise<boolean> {
        return this.apiProvider.getApi().then((api) => api.isWindowsStoreInterpreter(pythonPath));
    }
}


@injectable()
export class PythonDebuggerPathProvider implements IPythonDebuggerPathProvider {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public getDebuggerPath(): Promise<string> {
        return this.apiProvider.getApi().then((api) => api.getDebuggerPath());
    }
}


// tslint:disable: max-classes-per-file
@injectable()
export class PythonInstaller implements IPythonInstaller {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public install(
        product: Product,
        resource?: InterpreterUri,
        cancel?: CancellationToken
    ): Promise<InstallerResponse> {
        return this.apiProvider.getApi().then((api) => api.install(ProductMapping[product], resource, cancel));
    }
}


// tslint:disable-next-line: max-classes-per-file
@injectable()
export class EnvironmentActivationService implements IEnvironmentActivationService {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public async getActivatedEnvironmentVariables(
        resource: Resource,
        interpreter?: PythonEnvironment
    ): Promise<NodeJS.ProcessEnv | undefined> {
        return this.apiProvider
            .getApi()
            .then((api) => api.getActivatedEnvironmentVariables(resource, interpreter, true));
    }
}


// tslint:disable-next-line: max-classes-per-file
@injectable()
export class InterpreterSelector implements IInterpreterSelector {
    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public async getSuggestions(resource: Resource): Promise<IInterpreterQuickPickItem[]> {
        return this.apiProvider.getApi().then((api) => api.getSuggestions(resource));
    }
}


// tslint:disable-next-line: max-classes-per-file
@injectable()
export class InterpreterService implements IInterpreterService {
    private readonly didChangeInterpreter = new EventEmitter<void>();

    constructor(@inject(IPythonApiProvider) private readonly apiProvider: IPythonApiProvider) {}

    public get onDidChangeInterpreter(): Event<void> {
        return this.didChangeInterpreter.event;
    }

    public getInterpreters(resource?: Uri): Promise<PythonEnvironment[]> {
        return this.apiProvider.getApi().then((api) => api.getInterpreters(resource));
    }

    public getActiveInterpreter(resource?: Uri): Promise<PythonEnvironment | undefined> {
        return this.apiProvider.getApi().then((api) => api.getActiveInterpreter(resource));
    }

    public getInterpreterDetails(pythonPath: string, resource?: Uri): Promise<undefined | PythonEnvironment> {
        return this.apiProvider.getApi().then((api) => api.getInterpreterDetails(pythonPath, resource));
    }
}


public getApi(): Promise<PythonApi> {
        this.init().catch(noop);
        return this.api.promise;
    }


public setApi(api: PythonApi): void {
        if (this.api.resolved) {
            return;
        }
        this.api.resolve(api);
    }


private async init() {
        if (this.initialized) {
            return;
        }
        this.initialized = true;
        const pythonExtension = this.extensions.getExtension<{ jupyter: { registerHooks(): void } }>(PythonExtension);
        if (!pythonExtension) {
            await this.extensionChecker.showPythonExtensionInstallRequiredPrompt();
        } else {
            if (!pythonExtension.isActive) {
                await pythonExtension.activate();
            }
            pythonExtension.exports.jupyter.registerHooks();
        }
    }


public async showPythonExtensionInstallRequiredPrompt(): Promise<void> {
        if (this.waitingOnInstallPrompt) {
            return this.waitingOnInstallPrompt;
        }
        // Ask user if they want to install and then wait for them to actually install it.
        const yes = localize.Common.bannerLabelYes();
        const no = localize.Common.bannerLabelNo();
        const answer = await this.appShell.showErrorMessage(localize.DataScience.pythonExtensionRequired(), yes, no);
        if (answer === yes) {
            await this.installPythonExtension();
        }
    }


public async showPythonExtensionInstallRecommendedPrompt() {
        const key = 'ShouldShowPythonExtensionInstallRecommendedPrompt';
        const surveyPrompt = this.persistentStateFactory.createGlobalPersistentState(key, true);
        if (surveyPrompt.value) {
            const yes = localize.Common.bannerLabelYes();
            const no = localize.Common.bannerLabelNo();
            const doNotShowAgain = localize.Common.doNotShowAgain();

            const promise = (this.waitingOnInstallPrompt = new Promise<void>(async (resolve) => {
                const answer = await this.appShell.showInformationMessage(
                    localize.DataScience.pythonExtensionRecommended(),
                    yes,
                    no,
                    doNotShowAgain
                );
                switch (answer) {
                    case yes:
                        await this.installPythonExtension();
                        break;
                    case doNotShowAgain:
                        await surveyPrompt.updateValue(false);
                        break;
                    default:
                        break;
                }
                resolve();
            }));
            await promise;
            this.waitingOnInstallPrompt = undefined;
        }
    }


private async installPythonExtension() {
        // Have the user install python
        this.appShell.openUrl(`${this.appEnv.uriScheme}:extension/${this.pythonExtensionId}`);
    }


private async extensionsChangeHandler(): Promise<void> {
        // On extension change see if python was installed, if so unhook our extension change watcher and
        // notify the user that they might need to restart notebooks or interactive windows
        if (this.isPythonExtensionInstalled && this.extensionChangeHandler) {
            this.extensionChangeHandler.dispose();
            this.extensionChangeHandler = undefined;

            this.appShell
                .showInformationMessage(localize.DataScience.pythonExtensionInstalled(), localize.Common.ok())
                .then(noop);
        }
    }


public getLanguageServer(resource?: InterpreterUri): Promise<ILanguageServer | undefined> {
        return this.apiProvider.getApi().then((api) => api.getLanguageServer(resource));
    }


public isWindowsStoreInterpreter(pythonPath: string): Promise<boolean> {
        return this.apiProvider.getApi().then((api) => api.isWindowsStoreInterpreter(pythonPath));
    }


public getDebuggerPath(): Promise<string> {
        return this.apiProvider.getApi().then((api) => api.getDebuggerPath());
    }


public install(
        product: Product,
        resource?: InterpreterUri,
        cancel?: CancellationToken
    ): Promise<InstallerResponse> {
        return this.apiProvider.getApi().then((api) => api.install(ProductMapping[product], resource, cancel));
    }


public async getActivatedEnvironmentVariables(
        resource: Resource,
        interpreter?: PythonEnvironment
    ): Promise<NodeJS.ProcessEnv | undefined> {
        return this.apiProvider
            .getApi()
            .then((api) => api.getActivatedEnvironmentVariables(resource, interpreter, true));
    }


public async getSuggestions(resource: Resource): Promise<IInterpreterQuickPickItem[]> {
        return this.apiProvider.getApi().then((api) => api.getSuggestions(resource));
    }


public getInterpreters(resource?: Uri): Promise<PythonEnvironment[]> {
        return this.apiProvider.getApi().then((api) => api.getInterpreters(resource));
    }


public getActiveInterpreter(resource?: Uri): Promise<PythonEnvironment | undefined> {
        return this.apiProvider.getApi().then((api) => api.getActiveInterpreter(resource));
    }


public getInterpreterDetails(pythonPath: string, resource?: Uri): Promise<undefined | PythonEnvironment> {
        return this.apiProvider.getApi().then((api) => api.getInterpreterDetails(pythonPath, resource));
    }


@Component({
  selector: 'app-availability',
  templateUrl: './availability.component.html',
  styleUrls: ['./availability.component.scss']
})
export class AvailabilityComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}


/**
 * Data Lake Store trusted identity provider information.
 * API Version: 2016-11-01.
 */
export class TrustedIdProvider extends pulumi.CustomResource {
    /**
     * Get an existing TrustedIdProvider resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
    public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): TrustedIdProvider {
        return new TrustedIdProvider(name, undefined as any, { ...opts, id: id });
    }

    /** @internal */
    public static readonly __pulumiType = 'azure-native:datalakestore:TrustedIdProvider';

    /**
     * Returns true if the given object is an instance of TrustedIdProvider.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    public static isInstance(obj: any): obj is TrustedIdProvider {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === TrustedIdProvider.__pulumiType;
    }

    /**
     * The URL of this trusted identity provider.
     */
    public readonly idProvider!: pulumi.Output<string>;
    /**
     * The resource name.
     */
    public /*out*/ readonly name!: pulumi.Output<string>;
    /**
     * The resource type.
     */
    public /*out*/ readonly type!: pulumi.Output<string>;

    /**
     * Create a TrustedIdProvider resource with the given unique name, arguments, and options.
     *
     * @param name The _unique_ name of the resource.
     * @param args The arguments to use to populate this resource's properties.
     * @param opts A bag of options that control this resource's behavior.
     */
    constructor(name: string, args: TrustedIdProviderArgs, opts?: pulumi.CustomResourceOptions) {
        let inputs: pulumi.Inputs = {};
        opts = opts || {};
        if (!opts.id) {
            if ((!args || args.accountName === undefined) && !opts.urn) {
                throw new Error("Missing required property 'accountName'");
            }
            if ((!args || args.idProvider === undefined) && !opts.urn) {
                throw new Error("Missing required property 'idProvider'");
            }
            if ((!args || args.resourceGroupName === undefined) && !opts.urn) {
                throw new Error("Missing required property 'resourceGroupName'");
            }
            inputs["accountName"] = args ? args.accountName : undefined;
            inputs["idProvider"] = args ? args.idProvider : undefined;
            inputs["resourceGroupName"] = args ? args.resourceGroupName : undefined;
            inputs["trustedIdProviderName"] = args ? args.trustedIdProviderName : undefined;
            inputs["name"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
        } else {
            inputs["idProvider"] = undefined /*out*/;
            inputs["name"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
        }
        if (!opts.version) {
            opts = pulumi.mergeOptions(opts, { version: utilities.getVersion()});
        }
        const aliasOpts = { aliases: [{ type: "azure-nextgen:datalakestore:TrustedIdProvider" }, { type: "azure-native:datalakestore/latest:TrustedIdProvider" }, { type: "azure-nextgen:datalakestore/latest:TrustedIdProvider" }, { type: "azure-native:datalakestore/v20161101:TrustedIdProvider" }, { type: "azure-nextgen:datalakestore/v20161101:TrustedIdProvider" }] };
        opts = pulumi.mergeOptions(opts, aliasOpts);
        super(TrustedIdProvider.__pulumiType, name, inputs, opts);
    }
}


/**
 * The set of arguments for constructing a TrustedIdProvider resource.
 */
export interface TrustedIdProviderArgs {
    /**
     * The name of the Data Lake Store account.
     */
    readonly accountName: pulumi.Input<string>;
    /**
     * The URL of this trusted identity provider.
     */
    readonly idProvider: pulumi.Input<string>;
    /**
     * The name of the Azure resource group.
     */
    readonly resourceGroupName: pulumi.Input<string>;
    /**
     * The name of the trusted identity provider. This is used for differentiation of providers in the account.
     */
    readonly trustedIdProviderName?: pulumi.Input<string>;
}


/**
     * Get an existing TrustedIdProvider resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): TrustedIdProvider {
        return new TrustedIdProvider(name, undefined as any, { ...opts, id: id });
    }


/**
     * Returns true if the given object is an instance of TrustedIdProvider.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
public static isInstance(obj: any): obj is TrustedIdProvider {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === TrustedIdProvider.__pulumiType;
    }


export class QuestionCustom extends QuestionCustomModel {
  constructor(public name: string, questionJSON: ComponentQuestionJSON) {
    super(name, questionJSON);
  }
  protected onBaseCreating() {
    super.onBaseCreating();
    new QuestionImplementor(this);
  }
}


export class QuestionComposite extends QuestionCompositeModel {
  constructor(public name: string, questionJSON: ComponentQuestionJSON) {
    super(name, questionJSON);
  }
  protected onBaseCreating() {
    super.onBaseCreating();
    new QuestionImplementor(this);
  }
}


protected onBaseCreating() {
    super.onBaseCreating();
    new QuestionImplementor(this);
  }


({ description, markdownDescription, name, ...props }: Partial<SettingProp>) => {
  return (<Details open>
    <summary>
      <Chevron />
      <span id={`#${name}`}


interface SettingProp {
  prop: any;
  name: string;
  description: string;
  markdownDescription: string;
  type: string | string[];
  items: any;
  default: any;
}


@Directive({
  selector: '[materialIcons]',
  host: {
    'class': 'material-icons'
  }
})
export class MdcMaterialIcons { }


export function newRandomAddress(rng: PRNG): Address {
	return new Address(newRandomUint8Array(rng, 20));
}


/**
 * Md Alert Enter Animation
 */
export default function mdEnterAnimation(Animation: Animation, baseEl: HTMLElement): Promise<Animation> {
  const baseAnimation = new Animation();

  const backdropAnimation = new Animation();
  backdropAnimation.addElement(baseEl.querySelector('ion-backdrop'));

  const wrapperAnimation = new Animation();
  wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper'));

  backdropAnimation.fromTo('opacity', 0.01, 0.5);

  wrapperAnimation.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1);

  return Promise.resolve(baseAnimation
    .addElement(baseEl)
    .easing('ease-in-out')
    .duration(200)
    .add(backdropAnimation)
    .add(wrapperAnimation));
}


pathname => {
  const breadcrumbs = [];

  for (let menu of menus) {
    if (menu.children) {
      let idx = menu.children.findIndex(item => item.path === pathname);
      if (idx > -1) {
        breadcrumbs.push(menu);
        breadcrumbs.push(menu.children[idx]);
        break;
      }
    } else {
      if (menu.path === pathname) {
        breadcrumbs.push(menu);
        break;
      }
    }

    menu.path === "/admin" && breadcrumbs.push(menu);
  }

  return breadcrumbs;
}


item => item.path === pathname


({
  children,
  background = "#fff",
  padding = 24
}) => {
  const setting = useSetting();
  const router = useRouter();
  const [collapsed, setCollapsed] = useState(false);
  const [collapsedWith, setCollapsedWith] = useState(80);
  const { pathname } = router;
  const breadcrumbs = resolveBreadcrumbs(pathname);

  return (
    <Layout className={style.wrapper}>
      <Helmet>
        <title>{"管理后台 - " + setting.systemTitle}</title>
        <meta name="keyword" content={setting.seoKeyword} />
        <meta name="description" content={setting.seoDesc} />
        <link rel="shortcut icon" href={setting.systemFavicon} />
        <link
          href="//fonts.googleapis.com/css?family=Nunito:400,400i,700,700i&display=swap"
          rel="stylesheet"
        ></link>
      </Helmet>
      <Sider
        className={style.sider}
        trigger={null}
        collapsible
        collapsed={collapsed}
        collapsedWidth={collapsedWith}
        breakpoint="xs"
        onBreakpoint={broken


(m: any) => !m.ignore


menu => {
              if (menu.children) {
                return (
                  <SubMenu
                    key={menu.label}
                    title={
                      <>
                        <Icon type={menu.icon} />
                        {menu.label}
                      </>
                    }
                  >
                    {menu.children
                      .filter((m: any) => !m.ignore)
                      .map(subMenu => {
                        return (
                          <Menu.Item
                            key={subMenu.path}
                            onClick={() => {
                              router.push(subMenu.path);
                            }}
                          >
                            {subMenu.label}
                          </Menu.Item>
                        );
                      }


subMenu => {
                        return (
                          <Menu.Item
                            key={subMenu.path}
                            onClick={() => {
                              router.push(subMenu.path);
                            }}


breadcrumb => {
              return (
                <Breadcrumb.Item key={breadcrumb.label}>
                  {breadcrumb.path ? (
                    <Link href={breadcrumb.path}>
                      <a>{breadcrumb.label}</a>
                    </Link>


interface IAdminLayoutProps {
  background?: string;
  padding?: any;
}


function removeFields(container: IContainer) {
    if (container.labels?.['serviceType'] === 'SYSTEM') {
        delete container["network"];
    }
    delete container["ports"];
    delete container["labels"];
    delete container["logs"];
    delete container["containerRules"];
    delete container["containerSimulatedMetrics"];
}


function mapStateToProps(state: ReduxState, props: Props): StateToProps {
    const isLoading = state.entities.containers.isLoadingContainers;
    const error = state.entities.containers.loadContainersError;
    const id = props.match.params.id;
    const newContainerHost = isNew(props.location.search) ? buildNewContainerHost() : undefined;
    const newContainerLocation = isNew(props.location.search) ? buildNewContainerLocation() : undefined;
    const container = !isNew(props.location.search) ? state.entities.containers.data[id] : undefined;
    let formContainer;
    if (container) {
        formContainer = {...container};
        removeFields(formContainer);
    }
    const nodes = state.entities.nodes.data;
    const services = state.entities.services.data;
    const cloudHosts = state.entities.hosts.cloud.data;
    const edgeHosts = state.entities.hosts.edge.data;
    const containers = state.entities.containers.data;
    const workerManagers = state.entities.workerManagers.data;
    return {
        isLoading,
        error,
        newContainerHost,
        newContainerLocation,
        container,
        formContainer,
        nodes,
        services,
        cloudHosts,
        edgeHosts,
        containers,
        workerManagers
    }
}


(): INewContainerHost => ({
    service: undefined,
    externalPort: undefined,
    internalPort: undefined,
    hostAddress: undefined,
})


(): INewContainerLocation => ({
    workerManager: false,
    service: undefined,
    externalPort: undefined,
    internalPort: undefined,
    coordinates: undefined,
})


(dropdown: HTMLButtonElement | null) => {
        if (dropdown) {
            M.Dropdown.init(dropdown,
                {
                    onOpenEnd: this.onOpenDropdown
                });
        }
    }


() =>
        this.scrollbar?.updateScroll()


() => {
        if (!this.isNew()) {
            const containerId = this.props.match.params.id;
            this.props.loadContainers(containerId);
        } else {
            this.props.loadContainers();
        }
    }


() =>
        this.props.container || this.state.container


() =>
        this.props.formContainer || this.state.formContainer


() =>
        isNew(this.props.location.search)


(reply: IReply<IContainer[]>): void => {
        let containers = reply.data;
        if (containers.length === 1) {
            const container = containers[0];
            super.toast(`<span class='green-text'>O contentor ${this.mounted ? `${container.id}` : `${container.id}`} começou a sua execução com sucesso no host ${container.publicIpAddress}</span>`);
            this.saveEntities(container);
            if (this.mounted) {
                this.updateContainer(container);
                this.props.history.replace(container.id.toString());
            }
        } else {
            containers = containers.reverse();
            super.toast(`<span class='green-text'>Foram lançados ${containers.length} contentores novos:<br/><b>${containers.map(container => `${container.id} => Host ${container.publicIpAddress}`).join('<br/>')}</b></span>`);
            if (this.mounted) {
                this.props.history.push("/contentores");
            }
        }
        this.props.addContainers(containers);
    }


container => `${container.id} => Host ${container.publicIpAddress}`


(reason: string, request: INewContainerHost | INewContainerLocation): void => {
        super.toast(`Erro ao iniciar o contentor</b>`, 10000, reason, true);
    }


(container: IContainer): void => {
        super.toast(`<span class='green-text'>O contentor<b>${container.id}</b> foi parado com sucesso</span>`);
        if (this.mounted) {
            this.props.history.push(`/contentores`);
        }
    }


(reason: string, container: IContainer): void =>
        super.toast(`Erro ao parar o contentor ${this.mounted ? `${container.id}` : `${container.id}`}`, 10000, reason, true)


() =>
        <>
            <button
                className


(id: string, onClick: (event: any) => void) => {
        const nodes = Object.values(this.props.nodes)
            .filter(node => node.state === 'ready' && (!id.includes('migrate') || node.publicIpAddress !== this.getContainer()?.publicIpAddress));
        return <ul id={id}
                   className={`dropdown-content ${formStyles.formDropdown}`}


node => node.state === 'ready' && (!id.includes('migrate') || node.publicIpAddress !== this.getContainer()?.publicIpAddress)


(node, index) =>
                    <li key


(event: any) => {
        const container = this.getContainer();
        const hostAddress = decodeHTML((event.target as HTMLLIElement).innerHTML).split(' (');
        const publicIpAddress = hostAddress[0];
        const privateIpAddress = hostAddress[1]?.substr(0, hostAddress[1].length - 1);
        const url = `containers/${container?.id}/replicate`;
        this.setState({loading: {method: 'post', url: url}});
        postData(url, {publicIpAddress: publicIpAddress, privateIpAddress: privateIpAddress},
            (reply: IReply<IContainer>) => this.onReplicateSuccess(reply.data),
            (reason: string) => this.onReplicateFailure(reason, container));
    }


(reply: IReply<IContainer>) => this.onReplicateSuccess(reply.data)


(reason: string) => this.onReplicateFailure(reason, container)


(container: IContainer) => {
        super.toast(`<span class='green-text'>O contentor ${container.image.split('/').splice(1)} foi replicado no contentor </span><a href='/contentores/${container.id}'><b>${container.id}</b></a>`, 15000);
        if (this.mounted) {
            this.setState({loading: undefined});
        }
    }


(reason: string, container?: IContainer) => {
        super.toast(`Erro ao replicar o contentor ${this.mounted ? `${container?.id}` : `${container?.id}`}`, 10000, reason, true);
        if (this.mounted) {
            this.setState({loading: undefined});
        }
    }


(event: any) => {
        const container = this.getContainer();
        const hostAddress = decodeHTML((event.target as HTMLLIElement).innerHTML).split(' (');
        const publicIpAddress = hostAddress[0];
        const privateIpAddress = hostAddress[1]?.substr(0, hostAddress[1].length - 1)
        const url = `containers/${container?.id}/migrate`;
        this.setState({loading: {method: 'post', url: url}});
        postData(url, {publicIpAddress: publicIpAddress, privateIpAddress: privateIpAddress},
            (reply: IReply<IContainer>) => this.onMigrateSuccess(reply.data),
            (reason) => this.onMigrateFailure(reason, container));
    }


(reply: IReply<IContainer>) => this.onMigrateSuccess(reply.data)


(reason) => this.onMigrateFailure(reason, container)


(container: IContainer) => {
        const parentContainer = this.getContainer();
        super.toast(`<span class='green-text'>O contentor ${this.mounted ? parentContainer?.id : `${parentContainer?.id}`} foi migrado para o host ${container.publicIpAddress} com o id </span><a href='/contentores/${container.id}'>${container.id}</a>`, 15000);
        if (this.mounted) {
            this.setState({loading: undefined});
        }
    }


(reason: string, container?: IContainer) => {
        super.toast(`Erro ao migrar o contentor ${this.mounted ? `${container?.id}` : `${container?.id}`}`, 10000, reason, true);
        if (this.mounted) {
            this.setState({loading: undefined});
        }
    }


(rule: string): void => {
        this.setState({
            unsavedRules: this.state.unsavedRules.concat(rule)
        });
    }


(rules: string[]): void => {
        this.setState({
            unsavedRules: this.state.unsavedRules.filter(rule => !rules.includes(rule))
        });
    }


rule => !rules.includes(rule)


(container: IContainer): void => {
        const {unsavedRules} = this.state;
        if (unsavedRules.length) {
            postData(`containers/${container.id}/rules`, unsavedRules,
                () => this.onSaveRulesSuccess(container),
                (reason) => this.onSaveRulesFailure(container, reason));
        }
    }


() => this.onSaveRulesSuccess(container)


(reason) => this.onSaveRulesFailure(container, reason)


(container: IContainer): void => {
        this.props.addContainerRules(container.id.toString(), this.state.unsavedRules);
        if (this.mounted) {
            this.setState({unsavedRules: []});
        }
    }


(container: IContainer, reason: string): void =>
        super.toast(`Não foi possível guardar as regras associadas ao contentor ${this.mounted ? `${container.id}` : `${container.id}`}`, 10000, reason, true)


(simulatedMetric: string): void => {
        this.setState({
            unsavedSimulatedMetrics: this.state.unsavedSimulatedMetrics.concat(simulatedMetric)
        });
    }


(simulatedMetrics: string[]): void => {
        this.setState({
            unsavedSimulatedMetrics: this.state.unsavedSimulatedMetrics.filter(metric => !simulatedMetrics.includes(metric))
        });
    }


metric => !simulatedMetrics.includes(metric)


(container: IContainer): void => {
        const {unsavedSimulatedMetrics} = this.state;
        if (unsavedSimulatedMetrics.length) {
            postData(`containers/${container.id}/simulated-metrics`, unsavedSimulatedMetrics,
                () => this.onSaveSimulatedMetricsSuccess(container),
                (reason) => this.onSaveSimulatedMetricsFailure(container, reason));
        }
    }


() => this.onSaveSimulatedMetricsSuccess(container)


(reason) => this.onSaveSimulatedMetricsFailure(container, reason)


(container: IContainer): void => {
        this.props.addContainerSimulatedMetrics(container.id.toString(), this.state.unsavedSimulatedMetrics);
        if (this.mounted) {
            this.setState({unsavedSimulatedMetrics: []});
        }
    }


(container: IContainer, reason: string): void =>
        super.toast(`Não foi possível guardar as métricas simuladas associadas ao contentor ${this.mounted ? `${container.id}` : `${container.id}`}`, 10000, reason, true)


() =>
        !!this.state.unsavedRules.length
        || !!this.state.unsavedSimulatedMetrics.length


(container: IContainer) => {
        this.saveContainerRules(container);
        this.saveContainerSimulatedMetrics(container);
    }


(container: IContainer) => {
        container = Object.values(normalize(container, Schemas.CONTAINER).entities.containers || {})[0];
        const formContainer = {...container};
        removeFields(formContainer);
        this.setState({container: container, formContainer: formContainer, loading: undefined});
    }


(): IFields => {
        return ['service', 'externalPort', 'internalPort'].map(field => ({
            [field]: {
                id: field,
                label: field,
                validation: {rule: required}
            }
        })).reduce((fields, field) => {
            for (let key in field) {
                fields[key] = field[key];
            }
            return fields;
        }, {});
    }


field => ({
            [field]: {
                id: field,
                label: field,
                validation: {rule: required}
            }
        })


(fields, field) => {
            for (let key in field) {
                fields[key] = field[key];
            }
            return fields;
        }


(container: INewContainerHost | INewContainerLocation | IContainer): IFields => {
        if (this.isNew()) {
            return this.state.currentForm === 'Usando o endereço'
                ? ({
                    ...this.commonFields(),
                    hostAddress: {
                        id: 'hostAddress',
                        label: 'hostAddress',
                        validation: {rule: required}
                    },
                }) : ({
                    ...this.commonFields(),
                    coordinates: {
                        id: 'coordinates',
                        label: 'coordinates',
                        validation: {rule: requireGreaterOrEqualSize, args: 1}
                    },
                })
        } else {
            return Object.entries(container).map(([key, value]) => {
                return {
                    [key]: {
                        id: key,
                        label: key,
                        validation: getTypeFromValue(value) === 'number'
                            ? {rule: requiredAndNumberAndMin, args: 0}
                            : key === 'coordinates'
                                ? {rule: requireGreaterOrEqualSize, args: 1}
                                : {rule: requiredAndTrimmed}
                    }
                };
            }).reduce((fields, field) => {
                for (let key in field) {
                    fields[key] = field[key];
                }
                return fields;
            }, {});
        }
    }


([key, value]) => {
                return {
                    [key]: {
                        id: key,
                        label: key,
                        validation: getTypeFromValue(value) === 'number'
                            ? {rule: requiredAndNumberAndMin, args: 0}
                            : key === 'coordinates'
                                ? {rule: requireGreaterOrEqualSize, args: 1}
                                : {rule: requiredAndTrimmed}
                    }
                };
            }


(fields, field) => {
                for (let key in field) {
                    fields[key] = field[key];
                }
                return fields;
            }


(): Partial<IHostAddress>[] =>
        Object.entries(this.props.nodes)
            .filter(([_, node]) => node.state === 'ready')
            .map(([_, node]) =>
                ({
                    username: node.labels['username'],
                    publicIpAddress: node.publicIpAddress,
                    privateIpAddress: node.labels['privateIpAddress'],
                    coordinates: node.labels['coordinates'] ? JSON.parse(node.labels['coordinates']) : undefined,
                }))


([_, node]) => node.state === 'ready'


([_, node]) =>
                ({
                    username: node.labels['username'],
                    publicIpAddress: node.publicIpAddress,
                    privateIpAddress: node.labels['privateIpAddress'],
                    coordinates: node.labels['coordinates'] ? JSON.parse(node.labels['coordinates']) : undefined,
                })


(hostAddress: Partial<IHostAddress>): string =>
        hostAddress.publicIpAddress + (hostAddress.privateIpAddress ? ("/" + hostAddress.privateIpAddress) : '') + " - " + hostAddress.coordinates?.label


() =>
        Object.values(this.props.services).filter(service => service.serviceType !== 'SYSTEM')
            .map(service => service.serviceName).sort()


service => service.serviceType !== 'SYSTEM'


service => service.serviceName


(serviceName: string) => {
        const service = this.props.services[serviceName];
        this.setState({
            defaultExternalPort: service.defaultExternalPort,
            defaultInternalPort: service.defaultInternalPort
        });
    }


(publicIpAddress: string) => {
        const cloudHost = Object.values(this.props.cloudHosts).filter(c => c.publicIpAddress === publicIpAddress)[0];
        if (cloudHost) {
            return '/hosts/cloud/' + cloudHost.instanceId;
        }
        const edgeHost = Object.values(this.props.edgeHosts).filter(e => e.publicIpAddress === publicIpAddress)[0];
        if (edgeHost) {
            return '/hosts/edge/' + edgeHost.publicIpAddress + "-" + edgeHost.privateIpAddress;
        }
        return null;
    }


c => c.publicIpAddress === publicIpAddress


e => e.publicIpAddress === publicIpAddress


(managerId: string) => {
        if (!!managerId && managerId !== 'master-manager') {
            return `/gestores locais/${managerId}`
        }
        return null;
    }


(region: IRegion) =>
        region.region


(formId: 'Usando o endereço' | 'Usando a localização') =>
        this.setState({currentForm: formId})


(): IMarker[] => {
        const containers: IContainer[] = Object.values(this.props.containers);
        const markers = new Map<String, IMarker>();
        containers
            .forEach(container => {
                const publicIpAddress = container.publicIpAddress;
                const marker = markers.get(publicIpAddress) || {title: '', label: '', latitude: 0, longitude: 0};
                if (marker.title === '') {
                    marker.title += container.coordinates.label + '
';
                }
                marker.title += container.id.toString().substr(0, 5) + ' - ' + container.labels['serviceName'] + '
';
                marker.label = publicIpAddress;
                marker.latitude = container.coordinates.latitude;
                marker.longitude = container.coordinates.longitude;
                marker.color = '#00FF00';
                markers.set(publicIpAddress, marker);
            });
        return Array.from(markers.values());
    }


container => {
                const publicIpAddress = container.publicIpAddress;
                const marker = markers.get(publicIpAddress) || {title: '', label: '', latitude: 0, longitude: 0};
                if (marker.title === '') {
                    marker.title += container.coordinates.label + '
';
                }
                marker.title += container.id.toString().substr(0, 5) + ' - ' + container.labels['serviceName'] + '
';
                marker.label = publicIpAddress;
                marker.latitude = container.coordinates.latitude;
                marker.longitude = container.coordinates.longitude;
                marker.color = '#00FF00';
                markers.set(publicIpAddress, marker);
            }


(formContainer: INewContainerHost | Partial<IContainer>, isNew: boolean): JSX.Element => {
        const {currentForm} = this.state;
        return isNew ?
            currentForm === 'Usando o endereço'
                ?
                <>
                    <Field key={'service'}
                           id={'service'}
                           label={'service'}
                           type={'dropdown'}
                           dropdown={{
                               defaultValue: "Selecionar o serviço",
                               emptyMessage: 'Não há serviços disponíveis',
                               values: this.getSelectableServices(),
                               selectCallback: this.setDefaultPorts,
                           }}


([key, value], index) =>
                    key === 'created'
                        ? <Field key


() => {
        const {isLoading, error} = this.props;
        let {newContainerHost, newContainerLocation} = this.props;
        const {currentForm} = this.state;
        const isNewContainer = this.isNew();
        if (isNewContainer) {
            newContainerHost = {
                ...newContainerHost,
                internalPort: this.state.defaultInternalPort,
                externalPort: this.state.defaultExternalPort
            };
            newContainerLocation = {
                ...newContainerHost,
                internalPort: this.state.defaultInternalPort,
                externalPort: this.state.defaultExternalPort
            };
        }
        const container = isNewContainer ? (currentForm === 'Usando o endereço' ? newContainerHost : newContainerLocation) : this.getContainer();
        const formContainer = this.getFormContainer();
        // @ts-ignore
        const containerKey: (keyof IContainer) = formContainer && Object.keys(formContainer)[0];
        const manager = isNewContainer ? undefined : this.getManagerHost(container as IContainer);
        return (
            <>
                {isLoading && <LoadingSpinner/>}
                {!isLoading && error && <Error message={error}/>}
                {!isLoading && !error && container && (
                    /*@ts-ignore*/
                    <Form id={containerKey}
                          fields={this.getFields(container)}
                          values={container}
                          isNew={isNewContainer}
                          showSaveButton={this.shouldShowSaveButton()}
                          post={{
                              textButton: 'Iniciar',
                              url: `${manager ? `${manager}/api/` : ''}containers`,
                              successCallback: this.onPostSuccess,
                              failureCallback: this.onPostFailure
                          }}
                        // delete button is never present on new nodes, so a type cast is safe
                          delete={{
                              textButton: (container as IContainer).state === 'ready' ? 'Parar' : 'Apagar',
                              url: `${manager ? `${manager}/api/` : ''}containers/${(container as IContainer).id}`,
                              successCallback: this.onDeleteSuccess,
                              failureCallback: this.onDeleteFailure
                          }}
                          customButtons={!isNewContainer && container && (container as IContainer).labels?.['serviceType'] !== 'SYSTEM'
                              && (container as IContainer).state !== 'down'
                              ? [{button: this.replicateButton()}, {button: this.migrateButton()}]
                              : undefined}
                          loading={this.state.loading}
                          saveEntities={this.saveEntities}
                          switchDropdown={isNewContainer ? {
                              options: currentForm === 'Usando o endereço' ? ['Usando a localização'] : ['Usando o endereço'],
                              onSwitch: this.switchForm
                          } : undefined}


(ports: IContainerPort[]) => {
        for (let port of ports) {
            if (port.publicPort) {
                return port.publicPort
            }
        }
    }


(): JSX.Element =>
        <ContainerPortsList isLoadingContainer


(): JSX.Element =>
        <ContainerLabelsList isLoadingContainer


(): JSX.Element =>
        <ContainerLogsList isLoadingContainer


(): JSX.Element =>
        <ContainerRuleList isLoadingContainer


(): JSX.Element =>
        <GenericServiceRuleList/>;


(): JSX.Element =>
        <ContainerSimulatedMetricList isLoadingContainer


(): JSX.Element =>
        <GenericSimulatedServiceMetricList/>;


(): Tab[] => ([
        {
            title: 'Contentor',
            id: 'container',
            content: () => this.container(),
            active: this.props.location.state?.selected === 'container'
        },
        {
            title: 'Portas',
            id: 'ports',
            content: () => this.ports(),
            hidden: this.isNew(),
            active: this.props.location.state?.selected === 'ports'
        },
        {
            title: 'Labels',
            id: 'containerLabels',
            content: () => this.labels(),
            hidden: this.isNew(),
            active: this.props.location.state?.selected === 'containerLabels'
        },
        {
            title: 'Registos',
            id: 'logs',
            content: () => this.logs(),
            hidden: this.isNew(),
            active: this.props.location.state?.selected === 'logs'
        },
        {
            title: 'Regras',
            id: 'rules',
            content: () => this.rules(),
            active: this.props.location.state?.selected === 'rules'
        },
        {
            title: 'Regras genéricas',
            id: 'genericContainerRules',
            content: () => this.genericRules(),
            active: this.props.location.state?.selected === 'genericServiceRules'
        },
        {
            title: 'Métricas simuladas',
            id: 'simulatedMetrics',
            content: () => this.simulatedMetrics(),
            active: this.props.location.state?.selected === 'simulatedMetrics'
        },
        {
            title: 'Métricas simuladas genéricas',
            id: 'genericSimulatedMetrics',
            content: () => this.genericSimulatedMetrics(),
            active: this.props.location.state?.selected === 'genericSimulatedMetrics'
        }
    ])


() => this.container()


() => this.ports()


() => this.labels()


() => this.logs()


() => this.rules()


() => this.genericRules()


() => this.simulatedMetrics()


() => this.genericSimulatedMetrics()


class Container extends BaseComponent<Props, State> {

    state: State = {
        loading: undefined,
        defaultInternalPort: 0,
        defaultExternalPort: 0,
        unsavedRules: [],
        unsavedSimulatedMetrics: [],
        currentForm: 'Usando a localização',
    };
    private mounted = false;
    private scrollbar: (ScrollBar | null) = null;

    public componentDidMount(): void {
        this.loadContainer();
        this.props.loadNodes();
        this.props.loadServices();
        this.props.loadCloudHosts();
        this.props.loadEdgeHosts();
        this.mounted = true;
    };

    public componentWillUnmount(): void {
        this.mounted = false;
    }

    public render() {
        return (
            <MainLayout>
                {this.shouldShowSaveButton() && !isNew(this.props.location.search) && <UnsavedChanged/>}

                    <Tabs {...this.props} tabs={this.tabs()}/>

            </MainLayout>
        );
    }

    private initDropdown = (dropdown: HTMLButtonElement | null) => {
        if (dropdown) {
            M.Dropdown.init(dropdown,
                {
                    onOpenEnd: this.onOpenDropdown
                });
        }
    };

    private onOpenDropdown = () =>
        this.scrollbar?.updateScroll();

    private loadContainer = () => {
        if (!this.isNew()) {
            const containerId = this.props.match.params.id;
            this.props.loadContainers(containerId);
        } else {
            this.props.loadContainers();
        }
    };

    private getContainer = () =>
        this.props.container || this.state.container;

    private getFormContainer = () =>
        this.props.formContainer || this.state.formContainer;

    private isNew = () =>
        isNew(this.props.location.search);

    private onPostSuccess = (reply: IReply<IContainer[]>): void => {
        let containers = reply.data;
        if (containers.length === 1) {
            const container = containers[0];
            super.toast(`<span class='green-text'>O contentor ${this.mounted ? `${container.id}` : `${container.id}`} começou a sua execução com sucesso no host ${container.publicIpAddress}</span>`);
            this.saveEntities(container);
            if (this.mounted) {
                this.updateContainer(container);
                this.props.history.replace(container.id.toString());
            }
        } else {
            containers = containers.reverse();
            super.toast(`<span class='green-text'>Foram lançados ${containers.length} contentores novos:<br/><b>${containers.map(container => `${container.id} => Host ${container.publicIpAddress}`).join('<br/>')}</b></span>`);
            if (this.mounted) {
                this.props.history.push("/contentores");
            }
        }
        this.props.addContainers(containers);
    };

    private onPostFailure = (reason: string, request: INewContainerHost | INewContainerLocation): void => {
        super.toast(`Erro ao iniciar o contentor</b>`, 10000, reason, true);
    }

    private onDeleteSuccess = (container: IContainer): void => {
        super.toast(`<span class='green-text'>O contentor<b>${container.id}</b> foi parado com sucesso</span>`);
        if (this.mounted) {
            this.props.history.push(`/contentores`);
        }
    };

    private onDeleteFailure = (reason: string, container: IContainer): void =>
        super.toast(`Erro ao parar o contentor ${this.mounted ? `${container.id}` : `${container.id}`}`, 10000, reason, true);

    private replicateButton = () =>
        <>
            <button
                className=


export interface IContainer extends IDatabaseData {
    type: ContainerType;
    created: number;
    name: string;
    image: string;
    command: string;
    network: string;
    publicIpAddress: string;
    privateIpAddress: string;
    mounts: string[];
    ports: IContainerPort[];
    labels: IContainerLabel;
    region: IRegion;
    state: string;
    managerId: string;
    coordinates: ICoordinates;
    logs?: string;
    containerRules?: string[];
    containerSimulatedMetrics?: string[];
}


export interface IContainerPort {
    publicPort: number;
    privatePort: number;
    type: string;
    ip: string;
}


export interface IContainerLabel {
    [key: string]: string
}


interface INewContainerHost {
    service?: string,
    externalPort?: number,
    internalPort?: number,
    hostAddress?: IHostAddress,
}


interface INewContainerLocation {
    workerManager?: boolean,
    service?: string,
    externalPort?: number,
    internalPort?: number,
    coordinates?: Point[];
}


interface StateToProps {
    isLoading: boolean;
    error?: string | null;
    newContainerHost?: INewContainerHost;
    newContainerLocation?: INewContainerLocation;
    container?: IContainer;
    formContainer?: Partial<IContainer> | INewContainerHost;
    nodes: { [key: string]: INode };
    services: { [key: string]: IService };
    cloudHosts: { [key: string]: ICloudHost };
    edgeHosts: { [key: string]: IEdgeHost };
    containers: { [key: string]: IContainer };
    workerManagers: { [key: string]: IWorkerManager };
}


interface DispatchToProps {
    loadContainers: (containerId?: string) => void;
    loadNodes: () => void;
    loadServices: () => void;
    loadCloudHosts: () => void;
    loadEdgeHosts: () => void;
    loadWorkerManagers: () => void;
    addContainers: (containers: IContainer[]) => void;
    addContainerRules: (containerId: string, rules: string[]) => void;
    addContainerSimulatedMetrics: (containerId: string, simulatedMetrics: string[]) => void;
}


interface MatchParams {
    id: string;
}


interface LocationState {
    data: IContainer,
    selected: 'container' | 'ports' | 'containerLabels' | 'logs' | 'rules' | 'genericServiceRules'
        | 'simulatedMetrics' | 'genericSimulatedMetrics';
}


interface State {
    container?: IContainer,
    formContainer?: IContainer,
    loading: IFormLoading,
    defaultInternalPort: number,
    defaultExternalPort: number,
    unsavedRules: string[],
    unsavedSimulatedMetrics: string[],
    currentForm: 'Usando o endereço' | 'Usando a localização',
}


export type ContainerType = 'SINGLETON' | 'BY_REQUEST'


type Props = StateToProps & DispatchToProps & RouteComponentProps<MatchParams, {}, LocationState>;


public componentDidMount(): void {
        this.loadContainer();
        this.props.loadNodes();
        this.props.loadServices();
        this.props.loadCloudHosts();
        this.props.loadEdgeHosts();
        this.mounted = true;
    }


public componentWillUnmount(): void {
        this.mounted = false;
    }


public render() {
        return (
            <MainLayout>
                {this.shouldShowSaveButton() && !isNew(this.props.location.search) && <UnsavedChanged/>}
                <div className='container'>
                    <Tabs {...this.props} tabs={this.tabs()}/>
                </div>
            </MainLayout>
        );
    }


export enum ComponentType {
  GLTFShape = 'GLTFShape',
  Transform = 'Transform',
  NFTShape = 'NFTShape',
  Script = 'Script'
}


export type EntityDefinition = {
  id: string
  components: string[] // array of IDs pointing to components
  disableGizmos?: boolean
  name: string
}


export type ComponentData = {
  [ComponentType.GLTFShape]: {
    assetId: string
  }

  [ComponentType.Transform]: {
    position: Vector3
    rotation: Quaternion
    scale: Vector3
  }
  [ComponentType.NFTShape]: {
    url: string
  }
  [ComponentType.Script]: {
    assetId: string
    values: AssetParameterValues
  }
}


export type AnyComponent = ComponentDefinition<ComponentType>


export type ShapeComponent = ComponentDefinition<ComponentType.GLTFShape | ComponentType.NFTShape>


export type ComponentDefinition<T extends ComponentType> = {
  id: string
  type: T
  data: ComponentData[T]
}


export type SceneMetrics = {
  triangles: number
  materials: number
  meshes: number
  bodies: number
  entities: number
  textures: number
}


export type Scene = {
  id: string
  metrics: SceneMetrics
  limits: SceneMetrics
  entities: Record<string, EntityDefinition>
  components: Record<string, AnyComponent>
  assets: Record<string, Asset>
  ground: {
    // id references
    assetId: string
    componentId: string
  } | null
}