├── developer-portal ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── home │ │ │ ├── home.component.css │ │ │ ├── home.component.ts │ │ │ ├── home.component.spec.ts │ │ │ └── home.component.html │ │ ├── data-load │ │ │ ├── data-load.component.css │ │ │ ├── data-load.component.ts │ │ │ ├── data-load.component.spec.ts │ │ │ └── data-load.component.html │ │ ├── power-bi │ │ │ ├── power-bi.component.css │ │ │ ├── power-bi.component.ts │ │ │ ├── power-bi.component.spec.ts │ │ │ └── power-bi.component.html │ │ ├── authorization │ │ │ ├── components │ │ │ │ └── unauthorized │ │ │ │ │ ├── unauthorized.component.css │ │ │ │ │ ├── unauthorized.component.html │ │ │ │ │ ├── unauthorized.component.ts │ │ │ │ │ └── unauthorized.component.spec.ts │ │ │ ├── services │ │ │ │ ├── authorization.service.spec.ts │ │ │ │ └── authorization.service.ts │ │ │ └── guards │ │ │ │ └── user.guard.ts │ │ ├── models │ │ │ ├── drop-down-option.ts │ │ │ ├── user-profile.ts │ │ │ └── user-group.enum.ts │ │ ├── user-management │ │ │ ├── models │ │ │ │ ├── user-management-status.enum.ts │ │ │ │ ├── data-group.ts │ │ │ │ ├── get-group-members-response.ts │ │ │ │ └── get-groups-response.ts │ │ │ ├── group-assignment │ │ │ │ ├── group-assignment-action.enum.ts │ │ │ │ ├── group-assignment.component.css │ │ │ │ ├── group-assignment.component.spec.ts │ │ │ │ ├── group-assignment.component.html │ │ │ │ └── group-assignment.component.ts │ │ │ ├── user-overview │ │ │ │ ├── user-overview.component.css │ │ │ │ ├── user-overview.component.spec.ts │ │ │ │ ├── user-overview.component.html │ │ │ │ └── user-overview.component.ts │ │ │ ├── services │ │ │ │ ├── assignment-event.service.ts │ │ │ │ └── user-management.service.spec.ts │ │ │ ├── group-overview-row │ │ │ │ ├── group-overview-row.component.css │ │ │ │ ├── group-overview-row.component.spec.ts │ │ │ │ ├── group-overview-row.component.html │ │ │ │ └── group-overview-row.component.ts │ │ │ ├── user-overview-row │ │ │ │ ├── user-overview-row.component.css │ │ │ │ ├── user-overview-row.component.spec.ts │ │ │ │ ├── user-overview-row.component.html │ │ │ │ └── user-overview-row.component.ts │ │ │ ├── user-edit │ │ │ │ ├── user-edit.component.css │ │ │ │ ├── user-edit.component.spec.ts │ │ │ │ ├── user-edit.component.html │ │ │ │ └── user-edit.component.ts │ │ │ ├── user-creation │ │ │ │ ├── user-creation.component.css │ │ │ │ ├── user-creation.component.spec.ts │ │ │ │ ├── user-creation.component.html │ │ │ │ └── user-creation.component.ts │ │ │ └── user-delete │ │ │ │ ├── user-delete.component.css │ │ │ │ ├── user-delete.component.spec.ts │ │ │ │ ├── user-delete.component.html │ │ │ │ └── user-delete.component.ts │ │ ├── swagger │ │ │ ├── swagger.component.css │ │ │ ├── swagger.component.html │ │ │ └── swagger.component.ts │ │ ├── rest │ │ │ ├── rest.component.css │ │ │ ├── rest.component.spec.ts │ │ │ ├── rest.component.ts │ │ │ └── rest.component.html │ │ ├── legal-tag-management │ │ │ ├── legal-tag-form │ │ │ │ ├── legal-tag-form.component.css │ │ │ │ ├── legal-tag-form.component.ts │ │ │ │ ├── legal-tag-form.component.spec.ts │ │ │ │ └── legal-tag-form.component.html │ │ │ ├── models │ │ │ │ ├── legal-tag-properties.ts │ │ │ │ └── legal-tag.ts │ │ │ ├── legal-tag-create │ │ │ │ ├── legal-tag-create.component.css │ │ │ │ ├── legal-tag-create.component.html │ │ │ │ ├── legal-tag-create.component.spec.ts │ │ │ │ └── legal-tag-create.component.ts │ │ │ ├── legal-tag-detail │ │ │ │ ├── legal-tag-detail.component.css │ │ │ │ ├── legal-tag-detail.component.spec.ts │ │ │ │ ├── legal-tag-detail.component.html │ │ │ │ └── legal-tag-detail.component.ts │ │ │ ├── services │ │ │ │ ├── legal-tag-management.service.spec.ts │ │ │ │ └── legal-tag-management.service.ts │ │ │ └── legal-tag-overview │ │ │ │ ├── legal-tag-overview.component.spec.ts │ │ │ │ ├── legal-tag-overview.component.css │ │ │ │ └── legal-tag-overview.component.ts │ │ ├── services │ │ │ ├── osdu-version.service.spec.ts │ │ │ ├── profile.service.spec.ts │ │ │ ├── osdu-version.service.ts │ │ │ └── profile.service.ts │ │ ├── app.component.css │ │ ├── profile │ │ │ ├── profile.component.css │ │ │ ├── profile.component.spec.ts │ │ │ └── profile.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.component.html │ │ └── app-routing.module.ts │ ├── favicon.ico │ ├── main.ts │ ├── styles.css │ ├── index.html │ ├── test.ts │ ├── polyfills.ts │ └── theme.scss ├── .dockerignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── tsconfig.app.json ├── docker-compose.yml ├── tsconfig.spec.json ├── .editorconfig ├── Dockerfile-local ├── tsconfig.json ├── .browserslistrc ├── Dockerfile ├── .gitignore ├── nginx.conf ├── .eslintrc.json ├── karma.conf.js ├── package.json └── angular.json ├── control-plane ├── .gitignore ├── containerRun.sh ├── Dockerfile ├── main.bicep ├── modules │ ├── lab_dataload.bicep │ └── lab_storage.bicep └── README.md ├── .github └── workflows │ ├── pull-request.yml │ ├── ci.yml │ ├── generate-arm.yml │ ├── release.yml │ ├── validate.yml │ └── codeql-analysis.yml ├── CODE_OF_CONDUCT.md ├── rest-scripts ├── README.md ├── sample.csv ├── auth.http └── services │ ├── entitlement.http │ ├── partition.http │ ├── search.http │ ├── legal.http │ └── schema.http ├── SUPPORT.md ├── data-load ├── README.md └── open-test-data │ └── downloadTnoData.sh ├── LICENSE ├── .gitignore ├── SECURITY.md └── README.md /developer-portal/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /developer-portal/src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /developer-portal/src/app/data-load/data-load.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /developer-portal/src/app/power-bi/power-bi.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /control-plane/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .envrc 3 | __pycache__ 4 | build 5 | dist 6 | datasets 7 | -------------------------------------------------------------------------------- /control-plane/containerRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft Corporation. 3 | # Licensed under the MIT License. 4 | curl $SETUP_SCRIPT | bash 5 | -------------------------------------------------------------------------------- /developer-portal/.dockerignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | node_modules 5 | .git 6 | .gitignore 7 | -------------------------------------------------------------------------------- /developer-portal/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-data-manager-for-energy-experience-lab/HEAD/developer-portal/src/favicon.ico -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/components/unauthorized/unauthorized.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .error-message { 5 | color: #f44336; 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: [ "main" ] 6 | 7 | jobs: 8 | validate: 9 | name: Validation 10 | uses: ./.github/workflows/validate.yml 11 | -------------------------------------------------------------------------------- /developer-portal/src/app/models/drop-down-option.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export class DropDownOption { 5 | public value: string; 6 | public displayValue: string; 7 | } 8 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/models/user-management-status.enum.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export enum ProcessStatus { 5 | InProgress, 6 | Succeeded, 7 | Failed 8 | } 9 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/components/unauthorized/unauthorized.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

You are not authorized to view this page

5 | -------------------------------------------------------------------------------- /developer-portal/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": [ 4 | "angular.ng-template", 5 | "davidanson.vscode-markdownlint", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /developer-portal/src/app/swagger/swagger.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .alert { 5 | padding: 20px; 6 | background-color: #ffe895; 7 | color: black; 8 | margin-top: 15px; 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | paths-ignore: 7 | - 'artifacts/**' 8 | 9 | workflow_dispatch: 10 | 11 | jobs: 12 | validate: 13 | name: Validation 14 | uses: ./.github/workflows/validate.yml 15 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-assignment/group-assignment-action.enum.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export enum GroupAssignmentAction { 5 | Addition, 6 | Removal // This isn't used, but was added as an example 7 | } 8 | -------------------------------------------------------------------------------- /developer-portal/src/app/swagger/swagger.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 | Info: Copy access token from profile page, select 'Authorize' and paste token 6 |
7 |
8 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/models/data-group.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Member } from './get-group-members-response'; 5 | 6 | export interface DataGroup { 7 | name: string; 8 | email: string; 9 | members: Member[]; 10 | } 11 | -------------------------------------------------------------------------------- /developer-portal/src/app/models/user-profile.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { UserGroup } from "./user-group.enum"; 5 | 6 | export class UserProfile { 7 | public name: string; 8 | public id: string; 9 | public email: string; 10 | public groups: UserGroup[] 11 | } 12 | -------------------------------------------------------------------------------- /developer-portal/src/main.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | 6 | import { AppModule } from './app/app.module'; 7 | 8 | platformBrowserDynamic().bootstrapModule(AppModule) 9 | .catch(err => console.error(err)); 10 | -------------------------------------------------------------------------------- /developer-portal/src/app/rest/rest.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | pre { 5 | overflow-x: auto; 6 | white-space: pre-wrap; 7 | word-wrap: break-word; 8 | background-color: rgba(0,0,0,.06); 9 | padding: 18px; 10 | width: 75%; 11 | margin-top: 0px; 12 | } 13 | -------------------------------------------------------------------------------- /developer-portal/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component } from '@angular/core'; 5 | 6 | @Component({ 7 | selector: 'app-home', 8 | templateUrl: './home.component.html' 9 | }) 10 | export class HomeComponent { 11 | 12 | constructor() { } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /developer-portal/src/app/models/user-group.enum.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export enum UserGroup { 5 | Users = "users", 6 | Viewer = "users.datalake.viewers", 7 | Editor = "users.datalake.editors", 8 | Admin = "users.datalake.admins", 9 | Ops = "users.datalake.ops" 10 | } 11 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/models/get-group-members-response.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export interface GetGroupMembersResponse { 5 | members: Member[]; 6 | } 7 | 8 | export interface Member { 9 | dataPartitionId: string; 10 | email: string; 11 | memberType: string; 12 | role: string; 13 | } 14 | -------------------------------------------------------------------------------- /developer-portal/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /developer-portal/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | version: "3.8" 5 | services: 6 | web: 7 | build: 8 | context: . 9 | dockerfile: Dockerfile-local 10 | image: developer-portal-local-development 11 | ports: 12 | - "80:80" 13 | volumes: 14 | - .:/app 15 | - /app/node_modules 16 | -------------------------------------------------------------------------------- /developer-portal/src/styles.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; 5 | html, body { height: 100%; } 6 | body { 7 | font-family: "Segoe UI Web (West European)","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif !important; 8 | margin: 0; 9 | } 10 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-form/legal-tag-form.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .form-container { 5 | display: flex; 6 | flex-direction: column; 7 | padding-bottom: 25px; 8 | padding-left: 10px; 9 | } 10 | 11 | .properties-container { 12 | display: flex; 13 | flex-direction: column; 14 | } 15 | -------------------------------------------------------------------------------- /control-plane/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | FROM mcr.microsoft.com/azure-cli:2.36.0 as release 5 | 6 | ENV SETUP_SCRIPT https://gist.githubusercontent.com/danielscholl/12e7107115b01ecc756a38226a6ce9df/raw/c3053de26f33ff247245836eb3707e7ffcad0731/hello.sh 7 | ADD containerRun.sh /root/ 8 | 9 | CMD ["/bin/bash", "/root/containerRun.sh"] 10 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/models/get-groups-response.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export interface GetGroupsResponse { 5 | desId: string; 6 | memberEmail: string; 7 | groups: EntitlementGroup[]; 8 | } 9 | 10 | export interface EntitlementGroup { 11 | name: string; 12 | description: string; 13 | email: string; 14 | } 15 | -------------------------------------------------------------------------------- /developer-portal/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /developer-portal/.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Editor configuration, see https://editorconfig.org 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.ts] 15 | quote_type = single 16 | 17 | [*.md] 18 | max_line_length = off 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/models/legal-tag-properties.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | export interface LegalTagProperties { 5 | countryOfOrigin: string[]; 6 | expirationDate: string; 7 | contractId: string; 8 | originator: string; 9 | dataType: string; 10 | securityClassification: string; 11 | personalData: string; 12 | exportClassification: string; 13 | } 14 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/components/unauthorized/unauthorized.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component } from '@angular/core'; 5 | 6 | @Component({ 7 | selector: 'app-unauthorized', 8 | templateUrl: './unauthorized.component.html', 9 | styleUrls: ['./unauthorized.component.css'] 10 | }) 11 | export class UnauthorizedComponent { 12 | 13 | constructor() { } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /developer-portal/Dockerfile-local: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | FROM mcr.microsoft.com/mirror/docker/library/node:18 5 | 6 | WORKDIR /app 7 | 8 | # Install dependencies 9 | COPY package.json /app/package.json 10 | RUN npm install 11 | RUN npm install -g @angular/cli 12 | 13 | # Copy the site to the working directory 14 | COPY . /app 15 | 16 | # Run the site 17 | CMD ng serve --host 0.0.0.0 --port 80 --disableHostCheck=true --poll 100 18 | -------------------------------------------------------------------------------- /developer-portal/src/app/data-load/data-load.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component } from '@angular/core'; 5 | import { environment } from 'src/environments/environment'; 6 | 7 | @Component({ 8 | selector: 'app-data-load', 9 | templateUrl: './data-load.component.html' 10 | }) 11 | export class DataLoadComponent { 12 | public tnoTemplateSpecUrl: string = environment.tnoTemplateSpecUrl; 13 | constructor() { } 14 | } 15 | -------------------------------------------------------------------------------- /developer-portal/src/app/power-bi/power-bi.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component } from '@angular/core'; 5 | import { environment } from 'src/environments/environment'; 6 | 7 | @Component({ 8 | selector: 'app-power-bi', 9 | templateUrl: './power-bi.component.html' 10 | }) 11 | export class PowerBiComponent { 12 | public powerBiConnectorFileName = environment.powerBiConnectorFileName; 13 | constructor() { } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/models/legal-tag.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { LegalTagProperties } from "./legal-tag-properties"; 5 | 6 | export interface LegalTag { 7 | name: string; 8 | description: string; 9 | properties: LegalTagProperties; 10 | } 11 | 12 | export interface LegalTagUpdateRequest { 13 | name: string; 14 | expirationDate: string; 15 | contractId: string; 16 | description: string; 17 | } 18 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-form/legal-tag-form.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, Input } from '@angular/core'; 5 | import { FormGroup } from '@angular/forms'; 6 | 7 | @Component({ 8 | selector: 'app-legal-tag-form', 9 | templateUrl: './legal-tag-form.component.html', 10 | styleUrls: ['./legal-tag-form.component.css'] 11 | }) 12 | export class LegalTagFormComponent { 13 | 14 | @Input() form: FormGroup; 15 | 16 | constructor() { } 17 | } 18 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-create/legal-tag-create.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .action-container { 5 | display: flex; 6 | flex-direction: row; 7 | align-items: center; 8 | margin-bottom: 20px; 9 | } 10 | 11 | .error-message { 12 | color: #f44336; 13 | margin-right: auto; 14 | max-width: 350px; 15 | } 16 | 17 | .processing-spinner { 18 | margin-left: auto; 19 | margin-right: 20px; 20 | } 21 | 22 | .creation-container { 23 | max-width: 450px; 24 | } 25 | -------------------------------------------------------------------------------- /developer-portal/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "pwa-chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:80/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /developer-portal/src/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | Microsoft Experience Lab 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /rest-scripts/README.md: -------------------------------------------------------------------------------- 1 | # HTTP REST Scripts 2 | 3 | ## About 4 | 5 | These HTTP REST Scripts can be used to execute several Microsoft Azure Data Manager for Energy APIs and conduct basic smoke testing (CSV ingestion, manifest ingestion, retrieve legal tags, upload schema, verify data load, etc). These scripts are compatible with the VS Code Extension [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client). 6 | 7 | [REST Client Settings](https://github.com/Huachao/vscode-restclient#environment-variables) can be set to create environments and saved in [VS Code Settings](https://vscode.readthedocs.io/en/latest/getstarted/settings/). 8 | -------------------------------------------------------------------------------- /developer-portal/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2017", 14 | "module": "es2020", 15 | "lib": [ 16 | "es2020", 17 | "dom" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "enableI18nLegacyMessageIdFormat": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please open an [issue](https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/issues/new) against the Github repository. We actively triage these and will work on this as best effort. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 14 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview/user-overview.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .create-user-container, 5 | .overview-container { 6 | max-width: 900px; 7 | } 8 | 9 | .spinner { 10 | width:50%; 11 | margin:0 auto; 12 | margin-top: 12px; 13 | margin-bottom: 12px; 14 | } 15 | 16 | .button-container { 17 | display: flex; 18 | justify-content: flex-end; 19 | margin-bottom: 14px; 20 | margin-right: 6px; 21 | } 22 | 23 | .overlay-container { 24 | min-width: 381px; 25 | } 26 | 27 | .create-button-icon { 28 | margin: 0 6px 0 -6px; 29 | } 30 | 31 | .error-message { 32 | color: #f44336; 33 | } 34 | -------------------------------------------------------------------------------- /developer-portal/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not ios_saf 15.2-15.3 18 | not safari 15.2-15.3 -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/services/assignment-event.service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Injectable } from "@angular/core"; 5 | import { BehaviorSubject } from "rxjs"; 6 | import { GroupAssignmentAction } from "../group-assignment/group-assignment-action.enum"; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class GroupAssignmentEventService { 12 | private groupAssignmentEvent = new BehaviorSubject(null); 13 | 14 | emitEvent(action: GroupAssignmentAction) { 15 | this.groupAssignmentEvent.next(action); 16 | } 17 | 18 | eventListener() { 19 | return this.groupAssignmentEvent.asObservable(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/generate-arm.yml: -------------------------------------------------------------------------------- 1 | name: Generate ARM Template 2 | 3 | on: 4 | pull_request: 5 | branches: [ "main" ] 6 | paths: 7 | - '**.bicep' 8 | 9 | jobs: 10 | generateArmTemplate: 11 | name: Generate ARM Template 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | - name: Bicep build ARM template 17 | uses: Azure/bicep-build-action@v1.0.0 18 | with: 19 | bicepFilePath: ./control-plane/main.bicep 20 | outputFilePath: azuredeploy.json 21 | - name: Commit Generated ARM Template 22 | uses: stefanzweifel/git-auto-commit-action@v4 23 | with: 24 | commit_message: Commit generated ARM template -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-overview-row/group-overview-row.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .row-button { 5 | align-self: center; 6 | } 7 | 8 | .button-container { 9 | display: flex; 10 | align-items: center; 11 | gap: 10px; 12 | } 13 | 14 | .button-icon { 15 | padding-bottom: 5px; 16 | padding-right: 10px; 17 | } 18 | 19 | .member-row { 20 | display: flex; 21 | flex-direction: row; 22 | justify-content: space-between; 23 | line-height: 4em; 24 | } 25 | 26 | .overlay-container { 27 | min-width: 381px; 28 | } 29 | 30 | .row-overview-container { 31 | display: flex; 32 | flex-direction: row; 33 | justify-content: space-between; 34 | } 35 | -------------------------------------------------------------------------------- /developer-portal/src/app/services/osdu-version.service.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { OsduVersionService } from './osdu-version.service'; 8 | 9 | describe('OsduVersionService', () => { 10 | let service: OsduVersionService; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [HttpClientTestingModule], 15 | providers: [OsduVersionService] 16 | }); 17 | service = TestBed.inject(OsduVersionService); 18 | }); 19 | 20 | it('should be created', () => { 21 | expect(service).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-detail/legal-tag-detail.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .main-container { 5 | padding-bottom: 15px; 6 | max-width: 450px; 7 | } 8 | 9 | .loading-container { 10 | display: flex; 11 | flex-direction: row; 12 | align-items: center; 13 | } 14 | 15 | .loading-spinner { 16 | margin-left: 20px; 17 | } 18 | 19 | .error-message { 20 | color: #f44336; 21 | } 22 | 23 | .action-container { 24 | display: flex; 25 | flex-direction: row; 26 | } 27 | 28 | .action-container > .action { 29 | margin-right: 10px; 30 | } 31 | 32 | .footer-container { 33 | display: flex; 34 | flex-direction: row; 35 | align-items: center; 36 | min-height: 43px; 37 | } 38 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/services/user-management.service.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { UserManagementService } from './user-management.service'; 8 | 9 | describe('UserManagementService', () => { 10 | let service: UserManagementService; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [ 15 | HttpClientTestingModule 16 | ] 17 | }); 18 | service = TestBed.inject(UserManagementService); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(service).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/services/authorization.service.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { AuthorizationService } from './authorization.service'; 8 | 9 | describe('AuthorizationService', () => { 10 | let service: AuthorizationService; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [HttpClientTestingModule], 15 | providers: [AuthorizationService] 16 | }); 17 | service = TestBed.inject(AuthorizationService); 18 | }); 19 | 20 | it('should be created', () => { 21 | expect(service).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /developer-portal/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | FROM mcr.microsoft.com/mirror/docker/library/node:18 as build 5 | 6 | WORKDIR /app 7 | 8 | # Install dependencies 9 | COPY package.json /app/package.json 10 | RUN npm install 11 | RUN npm install -g @angular/cli 12 | 13 | # Copy the site to the working directory 14 | COPY . /app 15 | 16 | # Build the site 17 | RUN npm run build 18 | 19 | FROM mcr.microsoft.com/mirror/docker/library/nginx:1.21 20 | 21 | COPY nginx.conf /etc/nginx/nginx.conf 22 | 23 | ## Remove default nginx index page 24 | RUN rm -rf /usr/share/nginx/html/* 25 | 26 | # Copy the build output to replace the default nginx contents. 27 | COPY --from=build /app/dist/developer-portal /usr/share/nginx/html 28 | 29 | EXPOSE 80 30 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/services/legal-tag-management.service.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { LegalTagManagementService } from './legal-tag-management.service'; 7 | 8 | describe('LegalTagManagementService', () => { 9 | let service: LegalTagManagementService; 10 | 11 | beforeEach(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [ 14 | HttpClientTestingModule 15 | ] 16 | }); 17 | service = TestBed.inject(LegalTagManagementService); 18 | }); 19 | 20 | it('should be created', () => { 21 | expect(service).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview-row/user-overview-row.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .row-container { 5 | display: flex; 6 | justify-content: space-between; 7 | line-height: 4em; 8 | } 9 | 10 | .id-container, 11 | .button-container { 12 | display: inline-block; 13 | } 14 | 15 | .id-container { 16 | margin-left: 6px; 17 | margin-right: 6px; 18 | } 19 | 20 | .button-container { 21 | margin-right: 6px; 22 | } 23 | 24 | .edit-button { 25 | margin-right: 8px; 26 | } 27 | 28 | .edit-user-overlay-container { 29 | min-width: 256px; 30 | } 31 | 32 | .delete-user-overlay-container { 33 | min-width: 350px; 34 | } 35 | 36 | .delete-button-icon { 37 | margin: 0 6px 0 -6px; 38 | } 39 | -------------------------------------------------------------------------------- /developer-portal/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | 45 | /src/environments/environment.ts 46 | /src/assets/rest-scripts.zip 47 | -------------------------------------------------------------------------------- /developer-portal/src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | 6 | import { HomeComponent } from './home.component'; 7 | 8 | describe('HomeComponent', () => { 9 | let component: HomeComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ HomeComponent ] 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(HomeComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /data-load/README.md: -------------------------------------------------------------------------------- 1 | # Data Load 2 | 3 | ## About 4 | 5 | Experience Lab provides simple integration for performing basic data loads into a Microsoft Azure Data Manager for Energy instance. 6 | 7 | ## TNO Data Set 8 | 9 | Experience Lab uses the [osdu-data-load-tno](https://github.com/Azure/osdu-data-load-tno) library for loading the TNO data set. The data set is downloaded from the [OSDU community site](https://community.opengroup.org/osdu/platform/data-flow/data-loading/open-test-data) to the control plane's storage account during control plane setup by executing [downloadTnoData.sh](./open-test-data/downloadTnoData.sh). 10 | 11 | Initiate a data load with the ```open-test-data``` template spec in the Experience Lab instance's resource group. This takes approximately 1.5 hours to complete. Progress can be monitored in the created container instance's logs. 12 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-edit/user-edit.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .form-container { 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .submit-and-status-container { 10 | display: flex; 11 | flex-direction: row; 12 | height: 36px; 13 | } 14 | 15 | .submit-button { 16 | width: 117px; 17 | } 18 | 19 | .status-container { 20 | position: relative; 21 | margin-top: 9px; 22 | margin-left: 10px; 23 | } 24 | 25 | .error-message { 26 | color: #f44336; 27 | } 28 | 29 | .edit-container { 30 | background-color: white; 31 | padding: 10px; 32 | } 33 | 34 | .close-button, 35 | .close-button-icon { 36 | width: 24px; 37 | height: 24px; 38 | position: absolute; 39 | top: 0; 40 | right: 0; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-assignment/group-assignment.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .overlay-container { 5 | background-color: white; 6 | padding: 10px; 7 | } 8 | 9 | .form-container { 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | .header { 15 | margin-bottom: 10px; 16 | } 17 | 18 | .submit-and-status-container { 19 | display: flex; 20 | flex-direction: row; 21 | height: 36px; 22 | margin-top: 10px; 23 | } 24 | 25 | .status-container { 26 | position: relative; 27 | margin-top: 9px; 28 | margin-left: 10px; 29 | } 30 | 31 | .error-message { 32 | color: #f44336; 33 | } 34 | 35 | .close-button, 36 | .close-button-icon { 37 | width: 24px; 38 | height: 24px; 39 | position: absolute; 40 | top: 0; 41 | right: 0; 42 | } 43 | -------------------------------------------------------------------------------- /developer-portal/src/app/swagger/swagger.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, OnInit } from '@angular/core'; 5 | 6 | declare const SwaggerUIBundle: any; 7 | 8 | @Component({ 9 | selector: 'app-swagger', 10 | templateUrl: './swagger.component.html', 11 | styleUrls: ['./swagger.component.css'] 12 | }) 13 | export class SwaggerComponent implements OnInit { 14 | 15 | constructor() { } 16 | 17 | ngOnInit(): void { 18 | const ui = SwaggerUIBundle({ 19 | dom_id: '#swagger-ui', 20 | layout: 'BaseLayout', 21 | presets: [ 22 | SwaggerUIBundle.presets.apis, 23 | SwaggerUIBundle.SwaggerUIStandalonePreset 24 | ], 25 | url: '/assets/swagger.yaml', 26 | docExpansion: 'none', 27 | operationsSorter: 'alpha' 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /developer-portal/src/app/power-bi/power-bi.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | 6 | import { PowerBiComponent } from './power-bi.component'; 7 | 8 | describe('PowerBiComponent', () => { 9 | let component: PowerBiComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ PowerBiComponent ] 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(PowerBiComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /developer-portal/src/app/data-load/data-load.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | 6 | import { DataLoadComponent } from './data-load.component'; 7 | 8 | describe('DataLoadComponent', () => { 9 | let component: DataLoadComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ DataLoadComponent ] 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(DataLoadComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /developer-portal/nginx.conf: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | user nginx; 5 | worker_processes auto; 6 | 7 | error_log /var/log/nginx/error.log notice; 8 | pid /var/run/nginx.pid; 9 | 10 | events { 11 | worker_connections 1024; 12 | } 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | 18 | log_format compression '$remote_addr - $remote_user [$time_local] ' 19 | '"$request" $status $upstream_addr ' 20 | '"$http_referer" "$http_user_agent"'; 21 | 22 | server { 23 | listen 80; 24 | access_log /var/log/nginx/access.log compression; 25 | 26 | root /usr/share/nginx/html; 27 | index index.html; 28 | 29 | location / { 30 | try_files $uri $uri/ $uri.html /index.html; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-creation/user-creation.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .form-container { 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .submit-and-status-container { 10 | display: flex; 11 | flex-direction: row; 12 | height: 36px; 13 | } 14 | 15 | .submit-button { 16 | width: 117px; 17 | } 18 | 19 | .status-container { 20 | position: relative; 21 | margin-top: 9px; 22 | margin-left: 10px; 23 | } 24 | 25 | .group-entry { 26 | margin-top: 10px 27 | } 28 | 29 | .error-message { 30 | color: #f44336; 31 | } 32 | 33 | .creation-container { 34 | background-color: white; 35 | padding: 10px; 36 | } 37 | 38 | .close-button, 39 | .close-button-icon { 40 | width: 24px; 41 | height: 24px; 42 | position: absolute; 43 | top: 0; 44 | right: 0; 45 | } 46 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-delete/user-delete.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .form-container { 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | .submit-and-status-container { 10 | display: flex; 11 | flex-direction: row; 12 | height: 36px; 13 | } 14 | 15 | .delete-button { 16 | width: 117px; 17 | } 18 | 19 | .status-container { 20 | position: relative; 21 | margin-top: 9px; 22 | margin-left: 10px; 23 | } 24 | 25 | .error-message { 26 | color: #f44336; 27 | } 28 | 29 | .delete-container { 30 | background-color: white; 31 | padding: 10px; 32 | } 33 | 34 | .close-button, 35 | .close-button-icon { 36 | width: 24px; 37 | height: 24px; 38 | position: absolute; 39 | top: 0; 40 | right: 0; 41 | } 42 | 43 | .delete-button-icon { 44 | margin: 0 6px 0 -6px; 45 | } 46 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/components/unauthorized/unauthorized.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | 6 | import { UnauthorizedComponent } from './unauthorized.component'; 7 | 8 | describe('UnauthorizedComponent', () => { 9 | let component: UnauthorizedComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ UnauthorizedComponent ] 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(UnauthorizedComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-form/legal-tag-form.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | 6 | import { LegalTagFormComponent } from './legal-tag-form.component'; 7 | 8 | describe('LegalTagFormComponent', () => { 9 | let component: LegalTagFormComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ LegalTagFormComponent ] 15 | }) 16 | .compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(LegalTagFormComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /rest-scripts/sample.csv: -------------------------------------------------------------------------------- 1 | UWI,PARENT_ID,WB_NAME,WB_NUMBER,SPUD_DATE,MD,TVD,OPERATOR,LONGUITUDE,LATITUDE,ELEVATION,ELEVATION_REF,LEASE,FIELD,BASIN,FORMATION_AT_TD,COUNTRY,STATE,COUNTY,CLASS,STATUS,STATUS_DATE,PERMIT_NUMBER,PERMIT_DATE,INITIAL_COMPLETION_DATE,WELLBORE_SHAPE,ORIGINAL_OPERATOR,EPSG_CODE,CRS,UNIT_SYSTEM,API 2 | MS1010,MS105,SMP G09995 001S0B1,MS101,23-Mar-97,20960,20711,SCHLUMBERGER,-89.66605738,27.85011793,84,KB,SMP G09995,ATWATER VLLY B 8,AT,MIOCENE LOWER,USA,NORTHERN GULF OF MEXICO,ATWATER VALLEY,NEW FIELD WILDCAT,DRY & ABANDONED,12-Jan-16,SMP-09995,3-Mar-97,30-Nov-00,DIRECTIONAL,SCHLUMBERGER,4267,GCS_North_American_1927,English,API 3 | MS1011,MS106,SMP G21826 SS001S0B0,MS102,7-Nov-01,21268,20999,SCHLUMBERGER,-88.25303147,27.85065713,79,KB,SMP G21826,ATWATER VLLY B 37,AT,MIOCENE LOWER,USA,NORTHERN GULF OF MEXICO,ATWATER VALLEY,NEW FIELD WILDCAT,DRY & ABANDONED,6-Aug-15,SMP21826,5-Nov-01,5-Jan-02,DIRECTIONAL,SCHLUMBERGER,4267,GCS_North_American_1927,English,API -------------------------------------------------------------------------------- /developer-portal/src/test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 5 | 6 | import 'zone.js/testing'; 7 | import { getTestBed } from '@angular/core/testing'; 8 | import { 9 | BrowserDynamicTestingModule, 10 | platformBrowserDynamicTesting 11 | } from '@angular/platform-browser-dynamic/testing'; 12 | 13 | declare const require: { 14 | context(path: string, deep?: boolean, filter?: RegExp): { 15 | (id: string): T; 16 | keys(): string[]; 17 | }; 18 | }; 19 | 20 | // First, initialize the Angular testing environment. 21 | getTestBed().initTestEnvironment( 22 | BrowserDynamicTestingModule, 23 | platformBrowserDynamicTesting(), 24 | ); 25 | 26 | // Then we find all the tests. 27 | const context = require.context('./', true, /\.spec\.ts$/); 28 | // And load the modules. 29 | context.keys().map(context); 30 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-overview/legal-tag-overview.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { LegalTagOverviewComponent } from './legal-tag-overview.component'; 7 | 8 | describe('LegalTagOverviewComponent', () => { 9 | let component: LegalTagOverviewComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | declarations: [ LegalTagOverviewComponent ], 15 | imports: [ HttpClientTestingModule ] 16 | }) 17 | .compileComponents(); 18 | fixture = TestBed.createComponent(LegalTagOverviewComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-creation/user-creation.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { UserCreationComponent } from './user-creation.component'; 8 | 9 | describe('UserCreationComponent', () => { 10 | let component: UserCreationComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [ UserCreationComponent ], 16 | imports: [HttpClientTestingModule] 17 | }) 18 | .compileComponents(); 19 | }); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(UserCreationComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | }); 26 | 27 | it('should create', () => { 28 | expect(component).toBeTruthy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-create/legal-tag-create.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

Create Legal Tag

5 |
6 |
7 | 8 |
9 | 12 | There was an error creating the legal tag. {{this.errorMessage}} 13 | 14 | 19 | 24 |
25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /developer-portal/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-delete/user-delete.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { UserDeleteComponent } from './user-delete.component'; 8 | 9 | describe('UserDeleteComponent', () => { 10 | let component: UserDeleteComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [ UserDeleteComponent ], 16 | imports: [ HttpClientTestingModule ] 17 | }) 18 | .compileComponents(); 19 | fixture = TestBed.createComponent(UserDeleteComponent); 20 | component = fixture.componentInstance; 21 | component.userProfile = { 22 | id: "id", 23 | name: "name", 24 | email: "email", 25 | groups: [] 26 | }; 27 | fixture.detectChanges(); 28 | }); 29 | 30 | 31 | it('should create', () => { 32 | expect(component).toBeTruthy(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-edit/user-edit.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { UserEditComponent } from './user-edit.component'; 8 | 9 | describe('UserEditComponent', () => { 10 | let component: UserEditComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [ UserEditComponent ], 16 | imports: [ HttpClientTestingModule ] 17 | }) 18 | .compileComponents(); 19 | }); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(UserEditComponent); 23 | component = fixture.componentInstance; 24 | component.userProfile = { 25 | id: "id", 26 | name: "name", 27 | email: "email", 28 | groups: [] 29 | }; 30 | fixture.detectChanges(); 31 | }); 32 | 33 | it('should create', () => { 34 | expect(component).toBeTruthy(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /developer-portal/src/app/rest/rest.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { ProfileService } from '../services/profile.service'; 6 | 7 | import { RestComponent } from './rest.component'; 8 | 9 | describe('RestComponent', () => { 10 | let component: RestComponent; 11 | let fixture: ComponentFixture; 12 | 13 | const profileServiceStub: Partial = { 14 | getRefreshToken(): string { 15 | return "token"; 16 | } 17 | }; 18 | 19 | beforeEach(async () => { 20 | await TestBed.configureTestingModule({ 21 | declarations: [ RestComponent ], 22 | providers: [ 23 | { provide: ProfileService, useValue: profileServiceStub } 24 | ] 25 | }) 26 | .compileComponents(); 27 | }); 28 | 29 | beforeEach(() => { 30 | fixture = TestBed.createComponent(RestComponent); 31 | component = fixture.componentInstance; 32 | fixture.detectChanges(); 33 | }); 34 | 35 | it('should create', () => { 36 | expect(component).toBeTruthy(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-assignment/group-assignment.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | 7 | import { GroupAssignmentComponent } from './group-assignment.component'; 8 | 9 | describe('GroupAssignmentComponent', () => { 10 | let component: GroupAssignmentComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [ GroupAssignmentComponent ], 16 | imports: [HttpClientTestingModule] 17 | }) 18 | .compileComponents(); 19 | fixture = TestBed.createComponent(GroupAssignmentComponent); 20 | component = fixture.componentInstance; 21 | component.group = { 22 | description: "description", 23 | name: "name", 24 | email: "email" 25 | }; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should create', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview-row/user-overview-row.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { OverlayModule } from "@angular/cdk/overlay"; 6 | 7 | import { UserOverviewRowComponent } from './user-overview-row.component'; 8 | 9 | describe('UserOverviewRowComponent', () => { 10 | let component: UserOverviewRowComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [ UserOverviewRowComponent ], 16 | imports: [ OverlayModule ] 17 | }) 18 | .compileComponents(); 19 | }); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(UserOverviewRowComponent); 23 | component = fixture.componentInstance; 24 | component.userProfile = { 25 | id: "id", 26 | name: "name", 27 | email: "email", 28 | groups: [] 29 | }; 30 | fixture.detectChanges(); 31 | }); 32 | 33 | it('should create', () => { 34 | expect(component).toBeTruthy(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /developer-portal/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "**/tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates" 20 | ], 21 | "rules": { 22 | "@angular-eslint/directive-selector": [ 23 | "error", 24 | { 25 | "type": "attribute", 26 | "prefix": "app", 27 | "style": "camelCase" 28 | } 29 | ], 30 | "@angular-eslint/component-selector": [ 31 | "error", 32 | { 33 | "type": "element", 34 | "prefix": "app", 35 | "style": "kebab-case" 36 | } 37 | ] 38 | } 39 | }, 40 | { 41 | "files": [ 42 | "*.html" 43 | ], 44 | "extends": [ 45 | "plugin:@angular-eslint/template/recommended" 46 | ], 47 | "rules": {} 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-create/legal-tag-create.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { Router } from '@angular/router'; 7 | 8 | import { LegalTagCreateComponent } from './legal-tag-create.component'; 9 | 10 | describe('LegalTagCreateComponent', () => { 11 | let component: LegalTagCreateComponent; 12 | let fixture: ComponentFixture; 13 | const routerSpy = jasmine.createSpyObj('Router', ['navigate']); 14 | 15 | beforeEach(async () => { 16 | await TestBed.configureTestingModule({ 17 | declarations: [ LegalTagCreateComponent ], 18 | imports: [ HttpClientTestingModule ], 19 | providers: [ 20 | { provide: Router, useValue: routerSpy } 21 | ] 22 | }) 23 | .compileComponents(); 24 | fixture = TestBed.createComponent(LegalTagCreateComponent); 25 | component = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should create', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /developer-portal/src/app/data-load/data-load.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

TNO Data Load

5 |

6 | Test features in Microsoft Azure Data Manager for Energy after loading the TNO data set. 7 |

8 |

9 | Note: Manifests are automatically assigned a data.tno.viewers and data.tno.owners Access Control List (ACL). 10 | The first user created during Experience Lab's deployment will be assigned these ACLs. 11 | Additional users will need to be a member of these ACLs to query and manipulate TNO data. 12 | Users can be added to these ACLs from the User Management page. 13 |

14 | 15 |
16 |

Overview

17 |
    18 |
  1. Go to the open-test-data Template Spec in your Experience Lab Resource Group
  2. 19 |
  3. Click Deploy
  4. 20 |
  5. Click Review + Create and start the deployment
  6. 21 |
  7. The data load will run from a container instance in the same resource group. You can monitor progress in its logs.
  8. 22 |
  9. Wait approx. 1 hour for data load to complete.
  10. 23 |
24 |
25 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-detail/legal-tag-detail.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 5 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 6 | import { FormGroup } from '@angular/forms'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | 9 | import { LegalTagDetailComponent } from './legal-tag-detail.component'; 10 | 11 | describe('LegalTagDetailComponent', () => { 12 | let component: LegalTagDetailComponent; 13 | let fixture: ComponentFixture; 14 | 15 | beforeEach(async () => { 16 | await TestBed.configureTestingModule({ 17 | declarations: [ LegalTagDetailComponent ], 18 | imports: [ 19 | RouterTestingModule, 20 | HttpClientTestingModule 21 | ] 22 | }) 23 | .compileComponents(); 24 | }); 25 | 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(LegalTagDetailComponent); 28 | component = fixture.componentInstance; 29 | component.editForm = new FormGroup({}); 30 | fixture.detectChanges(); 31 | }); 32 | 33 | it('should create', () => { 34 | expect(component).toBeTruthy(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /developer-portal/src/app/services/profile.service.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { MsalService, MsalBroadcastService } from '@azure/msal-angular'; 6 | import { InteractionStatus } from '@azure/msal-browser'; 7 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 8 | import { Observable, of } from 'rxjs'; 9 | 10 | import { ProfileService } from './profile.service'; 11 | 12 | describe('ProfileService', () => { 13 | let service: ProfileService; 14 | 15 | const msalServiceStub: Partial = { 16 | logoutRedirect(request?: any): Observable { 17 | return of(null); 18 | } 19 | }; 20 | 21 | class msalBroadcastServiceStub { 22 | inProgress$: Observable 23 | }; 24 | 25 | beforeEach(() => { 26 | TestBed.configureTestingModule({ 27 | imports: [ 28 | HttpClientTestingModule 29 | ], 30 | providers: [ 31 | { provide: MsalService, useValue: msalServiceStub }, 32 | { provide: MsalBroadcastService, useValue: msalBroadcastServiceStub } 33 | ] 34 | }); 35 | service = TestBed.inject(ProfileService); 36 | }); 37 | 38 | it('should be created', () => { 39 | expect(service).toBeTruthy(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview/user-overview.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { HarnessLoader } from '@angular/cdk/testing'; 7 | import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 8 | import { OverlayModule } from "@angular/cdk/overlay"; 9 | 10 | import { UserOverviewComponent } from './user-overview.component'; 11 | 12 | describe('UserOverviewComponent', () => { 13 | let component: UserOverviewComponent; 14 | let fixture: ComponentFixture; 15 | let loader: HarnessLoader; 16 | 17 | beforeEach(async () => { 18 | await TestBed.configureTestingModule({ 19 | declarations: [ UserOverviewComponent ], 20 | imports: [ 21 | HttpClientTestingModule, 22 | OverlayModule 23 | ] 24 | }) 25 | .compileComponents().then(() => { 26 | fixture = TestBed.createComponent(UserOverviewComponent); 27 | loader = TestbedHarnessEnvironment.loader(fixture); 28 | component = fixture.componentInstance; 29 | fixture.detectChanges(); 30 | }); 31 | }); 32 | 33 | it('should create', () => { 34 | expect(component).toBeTruthy(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-overview-row/group-overview-row.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { OverlayModule } from "@angular/cdk/overlay"; 7 | import { MatSelectModule } from '@angular/material/select'; 8 | import { MatSelectionList } from '@angular/material/list'; 9 | 10 | import { GroupOverviewRowComponent } from './group-overview-row.component'; 11 | 12 | describe('GroupOverviewRowComponent', () => { 13 | let component: GroupOverviewRowComponent; 14 | let fixture: ComponentFixture; 15 | 16 | beforeEach(async () => { 17 | await TestBed.configureTestingModule({ 18 | imports: [ 19 | HttpClientTestingModule, 20 | MatSelectModule, 21 | OverlayModule 22 | ], 23 | declarations: [ 24 | GroupOverviewRowComponent, 25 | MatSelectionList 26 | ] 27 | }) 28 | .compileComponents(); 29 | fixture = TestBed.createComponent(GroupOverviewRowComponent); 30 | component = fixture.componentInstance; 31 | component.dataGroup = { 32 | name: "test", 33 | email: "test", 34 | members: [] 35 | }; 36 | fixture.detectChanges(); 37 | }); 38 | 39 | it('should create', () => { 40 | expect(component).toBeTruthy(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /rest-scripts/auth.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | # 7 | # Purpose: OAuth2.0 Authorization Code 8 | 9 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 10 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 11 | @scopes = {{CLIENT_ID}}/.default openid profile offline_access 12 | @AUTH_CODE = 13 | 14 | 15 | # ----------------------- 16 | # OAUTH authorization_code 17 | # ----------------------- 18 | ### 19 | # @name authorize 20 | POST https://{{oauth_token_host}} HTTP/1.1 21 | Content-Type: application/x-www-form-urlencoded 22 | 23 | grant_type=authorization_code 24 | &redirect_uri=https://experiencelab10030.z21.web.core.windows.net/ 25 | &client_id={{CLIENT_ID}} 26 | &client_secret={{CLIENT_SECRET}} 27 | &scope={{scopes}} 28 | &code={{AUTH_CODE}} 29 | 30 | 31 | # ----------------------- 32 | # OAUTH refresh_token 33 | # ----------------------- 34 | ### 35 | # @name refresh 36 | POST https://{{oauth_token_host}} HTTP/1.1 37 | Content-Type: application/x-www-form-urlencoded 38 | 39 | grant_type=refresh_token 40 | &client_id={{CLIENT_ID}} 41 | &client_secret={{CLIENT_SECRET}} 42 | &refresh_token={{authorize.response.body.refresh_token}} 43 | &scope={{scopes}} 44 | 45 | 46 | # ----------------------- 47 | # API (Variables) 48 | # ----------------------- 49 | ### 50 | @access_token = {{refresh.response.body.access_token}} -------------------------------------------------------------------------------- /developer-portal/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .user-overview-container { 5 | text-align: right; 6 | display: grid; 7 | } 8 | 9 | .toolbar-spacer { 10 | flex: 1 1 auto; 11 | } 12 | 13 | .display-name { 14 | max-height: 20px; 15 | font-size: 18px; 16 | } 17 | 18 | .email { 19 | font-size: 15px; 20 | } 21 | 22 | .menu-option-label { 23 | margin-left: 4px; 24 | } 25 | 26 | .toolbar { 27 | background: #0078D4; 28 | color: white; 29 | max-height: 48px; 30 | } 31 | 32 | 33 | .menu-navigation { 34 | margin: 16px; 35 | width: 250px; 36 | border-right: none; 37 | background: #0078D4; 38 | color: white; 39 | padding: 16px; 40 | text-align: center; 41 | } 42 | 43 | .content { 44 | margin: 16px; 45 | margin-left: 32px; 46 | word-break: break-word; 47 | padding-left: 12px; 48 | padding-right: 12px; 49 | padding-top: 1px; 50 | padding-bottom: 1px; 51 | } 52 | 53 | .menu-navigation-container { 54 | height: calc(100vh - 50px); 55 | } 56 | 57 | .menu-button { 58 | width: 100%; 59 | display: flex; 60 | align-items: center; 61 | justify-content: flex-start; 62 | font-size: 1rem; 63 | } 64 | 65 | .divider { 66 | margin-top: 16px; 67 | margin-bottom: 16px; 68 | background-color: ghostwhite; 69 | } 70 | 71 | .version-container { 72 | position: absolute; 73 | display: grid; 74 | bottom: 0; 75 | left: 0; 76 | margin-left: 65px; 77 | font-size: 13px; 78 | margin-bottom: 10px; 79 | } 80 | -------------------------------------------------------------------------------- /developer-portal/src/app/rest/rest.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, OnInit } from '@angular/core'; 5 | import { environment } from 'src/environments/environment'; 6 | import { ProfileService } from '../services/profile.service'; 7 | 8 | @Component({ 9 | selector: 'app-rest', 10 | templateUrl: './rest.component.html', 11 | styleUrls: ['./rest.component.css'] 12 | }) 13 | export class RestComponent implements OnInit { 14 | public clientId = environment.clientId; 15 | public tenantId = environment.tenantId; 16 | public host = environment.apiHost; 17 | public dataPartition = environment.dataPartition; 18 | public instanceName = environment.instanceName; 19 | public developerPortalUrl = environment.redirectUrl; 20 | 21 | public showRestClientSettings; 22 | public restClientSettingsLabel; 23 | 24 | public get refreshToken() { 25 | return this.profileService.getRefreshToken(); 26 | } 27 | 28 | constructor(private profileService: ProfileService) { } 29 | 30 | ngOnInit(): void { 31 | this.showRestClientSettings = false; 32 | this.restClientSettingsLabel = "Show Client Config" 33 | } 34 | 35 | public toggleRestClientSettings() { 36 | this.showRestClientSettings = !this.showRestClientSettings; 37 | if (this.showRestClientSettings) { 38 | this.restClientSettingsLabel = "Hide Client Config" 39 | } 40 | else { 41 | this.restClientSettingsLabel = "Show Client Config" 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/guards/user.guard.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Injectable } from '@angular/core'; 5 | import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; 6 | import { Observable, map, switchMap } from 'rxjs'; 7 | import { UserGroup } from 'src/app/models/user-group.enum'; 8 | import { ProfileService } from 'src/app/services/profile.service'; 9 | import { AuthorizationService } from '../services/authorization.service'; 10 | 11 | @Injectable({ 12 | providedIn: 'root' 13 | }) 14 | export class UserGuard implements CanActivate { 15 | 16 | constructor(private router: Router, 17 | private profileService: ProfileService, 18 | private authorizationService: AuthorizationService) { 19 | } 20 | 21 | /** 22 | * Verifies the user exists 23 | * @returns True if the user is in the users group, false/redirects to unauthorized if not 24 | */ 25 | public canActivate( 26 | route: ActivatedRouteSnapshot, 27 | state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { 28 | return this.profileService.getUserProfile().pipe( 29 | switchMap(_ => this.authorizationService.IsUserInGroup(UserGroup.Users).pipe(map(result => { 30 | if (result) { 31 | return true; 32 | } 33 | else { 34 | return this.router.parseUrl('/unauthorized'); 35 | } 36 | }))) 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /control-plane/main.bicep: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | targetScope = 'subscription' 5 | 6 | @description('Control Plane Name') 7 | param controlPlaneName string 8 | 9 | @description('Control Plane Location') 10 | param controlPlaneLocation string = 'eastus' 11 | 12 | // Create Resource Group 13 | resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-01-01' = { 14 | name: 'experiencelab-controlplane-${controlPlaneName}' 15 | location: controlPlaneLocation 16 | } 17 | 18 | // Module Storage Account with Copy 19 | module blob 'modules/lab_storage.bicep' = { 20 | name: 'labStorage' 21 | scope: resourceGroup 22 | params: { 23 | location: controlPlaneLocation 24 | } 25 | } 26 | 27 | module acr 'modules/lab_acr.bicep' = { 28 | name: 'labContainerRegistry' 29 | scope: resourceGroup 30 | params: { 31 | location: controlPlaneLocation 32 | } 33 | } 34 | 35 | module dataLoad 'modules/lab_dataload.bicep' = { 36 | name: 'dataLoad' 37 | scope: resourceGroup 38 | params: { 39 | storageAccountId: blob.outputs.storageAccountId 40 | storageAccountName: blob.outputs.storageAccountName 41 | imagePath: acr.outputs.containerImagePath 42 | location: controlPlaneLocation 43 | managedIdentityId: acr.outputs.managedIdentityId 44 | } 45 | } 46 | 47 | module deploy 'modules/lab_deploy.bicep' = { 48 | name: 'labDeploy' 49 | scope: resourceGroup 50 | params: { 51 | imagePath: acr.outputs.containerImagePath 52 | location: controlPlaneLocation 53 | storageAccountName: blob.outputs.storageAccountName 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-edit/user-edit.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |
6 |
7 | 8 | 9 | {{group.displayValue}} 10 | 11 | 12 | 15 |
16 | 17 |
18 | {{this.editUserResultMessage}} 19 | {{this.editUserResultMessage}} 20 |
21 | 22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | validate: 9 | name: Validation 10 | uses: ./.github/workflows/validate.yml 11 | create_assets_for_release: 12 | name: Create Assets for Release 13 | needs: validate 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v3 18 | 19 | - name: Tokenize portal version 20 | uses: cschleiden/replace-tokens@v1 21 | with: 22 | files: '["**/control-plane/experiencelab.sh"]' 23 | env: 24 | Build.BuildNumber: ${{ github.ref_name }} 25 | 26 | - name: Compress portal 27 | run: tar -czvf developer-portal.tar.gz developer-portal 28 | 29 | - name: Compress REST scripts 30 | run: zip -r rest-scripts.zip rest-scripts 31 | 32 | - name: Bicep build ARM template 33 | uses: Azure/bicep-build-action@v1.0.0 34 | with: 35 | bicepFilePath: ./control-plane/main.bicep 36 | outputFilePath: ./azuredeploy.json 37 | 38 | - name: Upload artifacts to release 39 | uses: softprops/action-gh-release@v1 40 | with: 41 | tag_name: ${{ github.ref_name }} 42 | files: | 43 | developer-portal.tar.gz 44 | rest-scripts.zip 45 | azuredeploy.json 46 | data-load/open-test-data/downloadTnoData.sh 47 | data-load/open-test-data/template.json 48 | control-plane/Dockerfile 49 | control-plane/containerRun.sh 50 | control-plane/experiencelab.sh 51 | -------------------------------------------------------------------------------- /developer-portal/src/app/profile/profile.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .spacing { 5 | margin-bottom: 10px; 6 | } 7 | 8 | .token { 9 | margin-top: 0px; 10 | overflow-x: auto; 11 | white-space: pre-wrap; 12 | word-wrap: break-word; 13 | background-color: rgba(0,0,0,.06); 14 | padding: 18px; 15 | max-width: 50%; 16 | min-width: 75%; 17 | } 18 | 19 | .container { 20 | position: relative; 21 | min-height: 200px; 22 | max-width: 50%; 23 | min-width: 75%; 24 | margin-bottom: 10px 25 | } 26 | 27 | .table-container { 28 | position: relative; 29 | max-height: calc(100vh - 474px); 30 | overflow: auto; 31 | } 32 | 33 | .filter-field { 34 | width: 180px; 35 | } 36 | 37 | table { 38 | width: 100%; 39 | } 40 | 41 | .loading-shade { 42 | position: absolute; 43 | min-height: 200px; 44 | top: 0; 45 | left: 0; 46 | bottom: 56px; 47 | right: 0; 48 | background: rgba(0, 0, 0, 0.15); 49 | z-index: 1; 50 | display: flex; 51 | align-items: center; 52 | justify-content: center; 53 | } 54 | 55 | .user-groups-error-message { 56 | color: #f44336; 57 | max-width: 360px; 58 | text-align: center; 59 | } 60 | 61 | /* Column Widths */ 62 | .mat-column-number, 63 | .mat-column-state { 64 | max-width: 64px; 65 | } 66 | 67 | .mat-column-created { 68 | max-width: 124px; 69 | } 70 | 71 | .mat-icon-button { 72 | width: 15px; 73 | height: 15px; 74 | line-height: 15px; 75 | margin-left: 6px; 76 | } 77 | 78 | .mat-icon { 79 | height: 15px; 80 | width: 15px; 81 | font-size: 15px; 82 | line-height: 15px; 83 | } 84 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-delete/user-delete.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |
6 |
7 | 8 | Object ID 9 | 10 | person_outline 11 | 12 | 15 |
16 | 17 |
18 | {{this.deleteUserResultMessage}} 19 | {{this.deleteUserResultMessage}} 20 |
21 | 22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /developer-portal/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | // Karma configuration file, see link for more information 5 | // https://karma-runner.github.io/1.0/config/configuration-file.html 6 | 7 | module.exports = function (config) { 8 | config.set({ 9 | basePath: '', 10 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 11 | plugins: [ 12 | require('karma-jasmine'), 13 | require('karma-chrome-launcher'), 14 | require('karma-jasmine-html-reporter'), 15 | require('karma-coverage'), 16 | require('@angular-devkit/build-angular/plugins/karma') 17 | ], 18 | client: { 19 | jasmine: { 20 | // you can add configuration options for Jasmine here 21 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 22 | // for example, you can disable the random execution with `random: false` 23 | // or set a specific seed with `seed: 4321` 24 | }, 25 | clearContext: false // leave Jasmine Spec Runner output visible in browser 26 | }, 27 | jasmineHtmlReporter: { 28 | suppressAll: true // removes the duplicated traces 29 | }, 30 | coverageReporter: { 31 | dir: require('path').join(__dirname, './coverage/developer-portal'), 32 | subdir: '.', 33 | reporters: [ 34 | { type: 'html' }, 35 | { type: 'text-summary' } 36 | ] 37 | }, 38 | reporters: ['progress', 'kjhtml'], 39 | port: 9876, 40 | colors: true, 41 | logLevel: config.LOG_INFO, 42 | autoWatch: true, 43 | browsers: ['Chrome'], 44 | singleRun: false, 45 | restartOnFileChange: true 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview-row/user-overview-row.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 |
6 |
7 | {{this.userProfile.id}} - {{this.getGroup()}} 8 |
9 |
10 | 11 | 12 |
13 |
14 | 19 | Edit user component 22 | 23 | 28 | Edit user component 31 | 32 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview-row/user-overview-row.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 5 | import { UserProfile } from 'src/app/models/user-profile'; 6 | import { UserGroup } from 'src/app/models/user-group.enum'; 7 | 8 | @Component({ 9 | selector: 'app-user-overview-row', 10 | templateUrl: './user-overview-row.component.html', 11 | styleUrls: ['./user-overview-row.component.css'] 12 | }) 13 | export class UserOverviewRowComponent { 14 | @Input() public userProfile: UserProfile; 15 | @Output() public deletedUser: EventEmitter = new EventEmitter(); 16 | public showEditUser = false; 17 | public showDeleteUser = false; 18 | 19 | constructor() { } 20 | 21 | public toggleEditUser() { 22 | this.showEditUser = !this.showEditUser; 23 | } 24 | 25 | public toggleDeleteUser() { 26 | this.showDeleteUser = !this.showDeleteUser; 27 | } 28 | 29 | public closeEditUser() { 30 | this.showEditUser = false; 31 | } 32 | 33 | public closeDeleteUser() { 34 | this.showDeleteUser = false; 35 | } 36 | 37 | public handleDeletedUser(user: UserProfile) { 38 | this.deletedUser.emit(user); 39 | } 40 | 41 | public getGroup() { 42 | if (!this.userProfile.groups) { 43 | return "Assign Group" 44 | } 45 | 46 | switch (this.userProfile.groups[0]) { 47 | case UserGroup.Admin: 48 | return "Admin"; 49 | case UserGroup.Editor: 50 | return "Contributor"; 51 | case UserGroup.Ops: 52 | return "Owner"; 53 | case UserGroup.Viewer: 54 | return "Reader"; 55 | default: 56 | return "Assign Group" 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /developer-portal/src/app/services/osdu-version.service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpClient } from '@angular/common/http'; 5 | import { Injectable } from '@angular/core'; 6 | import { map, Observable } from 'rxjs'; 7 | import { environment } from 'src/environments/environment'; 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class OsduVersionService { 13 | 14 | constructor(private http: HttpClient) { } 15 | 16 | public getOsduVersion(): Observable { 17 | return this.http.get(`https://${environment.apiHost}/api/entitlements/v2/info`) 18 | .pipe(map((versionResponse: any) => { 19 | const endVersionIndex = 4; 20 | const apiVersion = versionResponse.version.substring(0,endVersionIndex); 21 | let osduVersion = ""; 22 | switch(apiVersion) { 23 | case "0.09": 24 | osduVersion = "M6"; 25 | break; 26 | case "0.10": 27 | osduVersion = "M7"; 28 | break; 29 | case "0.11": 30 | osduVersion = "M8"; 31 | break; 32 | case "0.12": 33 | osduVersion = "M9"; 34 | break; 35 | case "0.13": 36 | osduVersion = "M10"; 37 | break; 38 | case "0.14": 39 | osduVersion = "M11"; 40 | break; 41 | case "0.15": 42 | osduVersion = "M12"; 43 | break; 44 | case "0.16": 45 | osduVersion = "M13"; 46 | break; 47 | case "0.17": 48 | osduVersion = "M14"; 49 | break; 50 | default: 51 | osduVersion = "Unknown"; 52 | } 53 | 54 | return osduVersion; 55 | })); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rest-scripts/services/entitlement.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | # 7 | # Purpose: Sample requests for Entitlement Service 8 | # Refer to manage-users.http for other samples 9 | 10 | # ----------------------- 11 | # OAUTH (Variables) 12 | # ----------------------- 13 | ### 14 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 15 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 16 | @scopes = {{CLIENT_ID}}/.default openid profile offline_access 17 | 18 | 19 | # ----------------------- 20 | # OAUTH refresh_token 21 | # ----------------------- 22 | ### 23 | # @name refresh 24 | POST https://{{oauth_token_host}} HTTP/1.1 25 | Content-Type: application/x-www-form-urlencoded 26 | Origin: {{DEVELOPER_PORTAL}} 27 | 28 | grant_type=refresh_token 29 | &client_id={{CLIENT_ID}} 30 | &refresh_token={{REFRESH_TOKEN}} 31 | &scope={{scopes}} 32 | 33 | 34 | # ----------------------- 35 | # API (Variables) 36 | # ----------------------- 37 | ### 38 | @access_token = {{refresh.response.body.access_token}} 39 | @ENDPOINT = https://{{HOST}} 40 | @ENTITLEMENTS_HOST = {{ENDPOINT}}/api/entitlements/v2 41 | 42 | 43 | # ----------------------- 44 | # API: Version 45 | # ----------------------- 46 | ### 47 | # @name info 48 | GET {{ENTITLEMENTS_HOST}}/info 49 | Authorization: Bearer {{access_token}} 50 | Accept: application/json 51 | 52 | 53 | # ----------------------- 54 | # API: entitlements 55 | # ----------------------- 56 | ### 57 | # Get groups for the user encoded in the token 58 | # @name myPermissions 59 | GET {{ENTITLEMENTS_HOST}}/groups 60 | Authorization: Bearer {{access_token}} 61 | Accept: application/json 62 | data-partition-id: {{DATA_PARTITION}} -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview/user-overview.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

Manage Users ({{this.userProfiles.length}})

5 |
6 |
7 | 8 |
9 | 10 | Unable to load users 11 | 13 |
14 | 19 | 20 | 21 | 22 |

Manage Data Groups/ACLs ({{this.dataGroups.length}})

23 |
24 | 25 | Unable to load data groups 26 | 27 |
28 | -------------------------------------------------------------------------------- /developer-portal/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { TestBed } from '@angular/core/testing'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { MsalService } from '@azure/msal-angular'; 7 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 8 | import { Observable, of } from 'rxjs'; 9 | import { MatSidenavModule } from '@angular/material/sidenav'; 10 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 11 | import { AppComponent } from './app.component'; 12 | import { UserProfile } from './models/user-profile'; 13 | import { ProfileService } from './services/profile.service'; 14 | 15 | describe('AppComponent', () => { 16 | const msalServiceStub: Partial = { 17 | logoutRedirect(request?: any): Observable { 18 | return of(null); 19 | } 20 | }; 21 | 22 | const profileServiceStub: Partial = { 23 | getUserProfile(request?: any):Observable { 24 | return of(null); 25 | } 26 | }; 27 | 28 | beforeEach(async () => { 29 | await TestBed.configureTestingModule({ 30 | imports: [ 31 | RouterTestingModule, 32 | HttpClientTestingModule, 33 | MatSidenavModule, 34 | NoopAnimationsModule 35 | ], 36 | declarations: [ 37 | AppComponent 38 | ], 39 | providers: [ 40 | { provide: MsalService, useValue: msalServiceStub }, 41 | { provide: ProfileService, useValue: profileServiceStub } 42 | ] 43 | }).compileComponents(); 44 | }); 45 | 46 | it('should create the app', () => { 47 | const fixture = TestBed.createComponent(AppComponent); 48 | const app = fixture.componentInstance; 49 | expect(app).toBeTruthy(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-overview-row/group-overview-row.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 |
6 |

{{this.dataGroup.name}} - Members ({{this.dataGroup.members.length}})

7 |
8 | 17 | 27 |
28 |
29 | 30 | {{member.email}} 31 | 32 | 37 | 43 | 44 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-overview/legal-tag-overview.component.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. 2 | Licensed under the MIT License. */ 3 | 4 | .action-container { 5 | display: flex; 6 | align-items: center; 7 | } 8 | 9 | .header-container { 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: space-between; 13 | } 14 | 15 | .overview-container { 16 | margin-bottom: 15px;; 17 | } 18 | 19 | .spinner { 20 | width:50%; 21 | margin:0 auto; 22 | margin-top: 12px; 23 | margin-bottom: 12px; 24 | } 25 | 26 | .error-message { 27 | color: #f44336; 28 | } 29 | 30 | .legal-tag-name { 31 | min-width: 405px; 32 | max-width: 405px; 33 | } 34 | 35 | .contents-container { 36 | display: flex; 37 | flex-direction: column; 38 | margin-right: 25px; 39 | } 40 | 41 | .legal-tag-manage-button { 42 | margin-right: 4px; 43 | margin-top: 8px; 44 | min-width: 73px; 45 | } 46 | 47 | .contents-parent-container { 48 | display: flex; 49 | flex-direction: row; 50 | justify-content: flex-start; 51 | margin-bottom: 15px; 52 | } 53 | 54 | @media screen and (max-width: 900px) { 55 | .contents-parent-container { 56 | flex-direction: column; 57 | } 58 | } 59 | 60 | table { 61 | width: 100%; 62 | } 63 | 64 | tr.example-detail-row { 65 | height: 0; 66 | } 67 | 68 | tr.legal-tag-row:not(.legal-tag-expanded-row):hover { 69 | background: whitesmoke; 70 | } 71 | 72 | tr.legal-tag-row:not(.legal-tag-expanded-row):active { 73 | background: #efefef; 74 | } 75 | 76 | .legal-tag-row td { 77 | border-bottom-width: 0; 78 | } 79 | 80 | .legal-tag-detail { 81 | overflow: hidden; 82 | display: flex; 83 | } 84 | 85 | .header-action-container { 86 | display: flex; 87 | flex-direction: row; 88 | align-items: center; 89 | } 90 | 91 | .header-action-container > * { 92 | margin-right: 15px; 93 | } 94 | -------------------------------------------------------------------------------- /developer-portal/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

Overview

5 |

6 | Experience Lab is an automated, end-to-end deployment accelerator with a friendly web-based UI that provides easy, fast deployments of Microsoft Azure Data Manager for Energy with sample dataset(s) for learning, testing, demo, and training purposes. 7 | This enables business audience and domain practitioners who are not familiar with OSDUTM to be able to create fully loaded and configured Microsoft Azure Data Manager for Energy deployments. 8 |

9 |

10 | It is not intended to be a guide to Microsoft Azure Data Manager for Energy or intended to deploy in production environments. 11 |

12 |
13 |

14 | Experience Lab provides several integration capabilities for Microsoft Azure Data Manager for Energy: 15 |

16 | 25 |

26 | *OSDU is a trademark of The Open Group. 27 |

28 | -------------------------------------------------------------------------------- /developer-portal/src/app/authorization/services/authorization.service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 5 | import { Injectable } from '@angular/core'; 6 | import { catchError, map, Observable, of } from 'rxjs'; 7 | import { UserGroup } from 'src/app/models/user-group.enum'; 8 | import { environment } from 'src/environments/environment'; 9 | 10 | @Injectable({ 11 | providedIn: 'root' 12 | }) 13 | export class AuthorizationService { 14 | 15 | /** 16 | * Caches the user's groups to prevent unnecessary HTTP calls 17 | */ 18 | private cachedUserGroups: any[] = null; 19 | 20 | constructor(private http: HttpClient) { } 21 | 22 | /** 23 | * Checks if the authenticated user is in the specified group 24 | * @param group The group to verify the user belongs to 25 | * @returns True if the user in the group, false otherwise 26 | */ 27 | public IsUserInGroup(group: UserGroup): Observable { 28 | if (this.cachedUserGroups !== null) { 29 | return of(this.ContainsGroup(this.cachedUserGroups, group)); 30 | } 31 | 32 | let headers = new HttpHeaders(); 33 | headers = headers.set('data-partition-id', environment.dataPartition); 34 | return this.http.get(`https://${environment.apiHost}/api/entitlements/v2/groups`, {headers: headers}) 35 | .pipe(map((userGroupsResponse: any) => { 36 | this.cachedUserGroups = userGroupsResponse.groups; 37 | return this.ContainsGroup(this.cachedUserGroups, group); 38 | }), catchError(() => { 39 | return of(false); 40 | })); 41 | } 42 | 43 | private ContainsGroup(usersGroups: any[], group: UserGroup): boolean { 44 | return Array.isArray(usersGroups) && 45 | !!usersGroups.length && 46 | usersGroups.some(usersGroup => usersGroup.name === group); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rest-scripts/services/partition.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | # 7 | # Purpose: Sample requests for Partition Service 8 | # Requires CLIENT_SECRET available in KV 9 | # Create, Update and Delete should never be used 10 | 11 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 12 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 13 | 14 | # ----------------------- 15 | # OAUTH CLIENT 16 | # ----------------------- 17 | ### 18 | # @name login 19 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 20 | @oauth_token_host = {{login_base}}/oauth2/token 21 | POST https://{{oauth_token_host}} HTTP/1.1 22 | Content-Type: application/x-www-form-urlencoded 23 | 24 | grant_type=client_credentials 25 | &client_id={{CLIENT_ID}} 26 | &client_secret={{CLIENT_SECRET}} 27 | &resource={{CLIENT_ID}} 28 | 29 | 30 | # ----------------------- 31 | # API (Variables) 32 | # ----------------------- 33 | ### 34 | @access_token = {{login.response.body.access_token}} 35 | @ENDPOINT = https://{{HOST}} 36 | @PARTITION_HOST = {{ENDPOINT}}/api/partition/v1 37 | 38 | 39 | # ----------------------- 40 | # API: Version 41 | # ----------------------- 42 | 43 | ### 44 | # @name info 45 | GET {{PARTITION_HOST}}/info 46 | Authorization: Bearer {{access_token}} 47 | Accept: application/json 48 | 49 | 50 | # ----------------------- 51 | # API: Partition 52 | # ----------------------- 53 | 54 | ### 55 | # @name getPartitions 56 | GET {{PARTITION_HOST}}/partitions/ 57 | Authorization: Bearer {{access_token}} 58 | Accept: application/json 59 | 60 | @partition = {{getPartitions.response.body.$[0]}} 61 | 62 | 63 | ### 64 | # @name getPartition 65 | GET {{PARTITION_HOST}}/partitions/{{partition}} 66 | Authorization: Bearer {{access_token}} 67 | Content-Type: application/json 68 | -------------------------------------------------------------------------------- /developer-portal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "developer-portal", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "test:prod": "ng test --browsers=ChromeHeadless --watch=false --code-coverage", 11 | "lint": "ng lint" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "~13.3.2", 16 | "@angular/cdk": "^13.3.2", 17 | "@angular/common": "~13.3.2", 18 | "@angular/compiler": "~13.3.2", 19 | "@angular/core": "~13.3.2", 20 | "@angular/forms": "~13.3.2", 21 | "@angular/material": "^13.3.2", 22 | "@angular/platform-browser": "~13.3.2", 23 | "@angular/platform-browser-dynamic": "~13.3.2", 24 | "@angular/router": "~13.3.2", 25 | "@azure/msal-angular": "^2.2.0", 26 | "@azure/msal-browser": "^2.23.0", 27 | "rxjs": "~7.5.5", 28 | "swagger-ui-dist": "4.10.3", 29 | "tslib": "^2.3.1", 30 | "zone.js": "~0.11.5" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "~13.3.9", 34 | "@angular-eslint/builder": "13.2.1", 35 | "@angular-eslint/eslint-plugin": "13.2.1", 36 | "@angular-eslint/eslint-plugin-template": "13.2.1", 37 | "@angular-eslint/schematics": "13.2.1", 38 | "@angular-eslint/template-parser": "13.2.1", 39 | "@angular/cli": "~13.3.9", 40 | "@angular/compiler-cli": "~13.3.2", 41 | "@types/eslint": "8.4.3", 42 | "@types/jasmine": "~4.0.2", 43 | "@types/node": "^17.0.23", 44 | "@typescript-eslint/eslint-plugin": "5.17.0", 45 | "@typescript-eslint/parser": "5.17.0", 46 | "eslint": "^8.12.0", 47 | "jasmine-core": "~4.1.0", 48 | "karma": "~6.3.18", 49 | "karma-chrome-launcher": "~3.1.1", 50 | "karma-coverage": "~2.2.0", 51 | "karma-jasmine": "~5.0.0", 52 | "karma-jasmine-html-reporter": "~1.7.0", 53 | "typescript": "~4.6.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-overview-row/group-overview-row.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, Input, ViewChild } from '@angular/core'; 5 | import { MatSelectionList } from '@angular/material/list'; 6 | import { GroupAssignmentAction } from '../group-assignment/group-assignment-action.enum'; 7 | import { DataGroup } from '../models/data-group'; 8 | import { GroupAssignmentEventService } from '../services/assignment-event.service'; 9 | import { UserManagementService } from '../services/user-management.service'; 10 | 11 | @Component({ 12 | selector: 'app-group-overview-row', 13 | templateUrl: './group-overview-row.component.html', 14 | styleUrls: ['./group-overview-row.component.css'] 15 | }) 16 | export class GroupOverviewRowComponent { 17 | @Input() public dataGroup: DataGroup; 18 | 19 | @ViewChild('groups') selectedGroupsList: MatSelectionList 20 | 21 | public showGroupAssignment: boolean = false; 22 | public groupAssignmentAction: GroupAssignmentAction; 23 | public eGroupAssignmentAction = GroupAssignmentAction; 24 | 25 | constructor(private userManagementService: UserManagementService, private eventService: GroupAssignmentEventService) { } 26 | 27 | public toggleGroupAssignment(groupAssignmentAction: GroupAssignmentAction) { 28 | this.groupAssignmentAction = groupAssignmentAction; 29 | this.showGroupAssignment = !this.showGroupAssignment; 30 | } 31 | 32 | public closeGroupAssignment() { 33 | this.showGroupAssignment = false; 34 | } 35 | 36 | public removeSelectedUsers() { 37 | var membersToDelete: string[] = this.selectedGroupsList.selectedOptions.selected.map(s => s.value); 38 | membersToDelete.forEach(memberEmail => { 39 | this.userManagementService.removeUserFromGroup(memberEmail, this.dataGroup.name).subscribe({ 40 | error: (_) => {}, 41 | complete: () => {} 42 | }); 43 | }) 44 | 45 | this.eventService.emitEvent(this.groupAssignmentAction); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-detail/legal-tag-detail.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

Legal Tag: {{this.selectedLegalTagName}}

5 |
6 |
9 | Loading... 10 | 15 |
16 |
18 | 19 | There was an error while loading the legal tag. 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 | 60 |
61 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-creation/user-creation.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |
6 |
7 | 10 | 11 | User Object ID 12 | 13 | person_outline 14 | Found in the Azure AD Portal 15 | 16 | 17 | 18 | {{group.displayValue}} 19 | 20 | 21 |
22 | 23 |
24 | {{this.creationResultMessage}} 25 | {{this.creationResultMessage}} 26 |
27 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-delete/user-delete.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 5 | import { FormGroup, FormControl, Validators } from '@angular/forms'; 6 | import { UserProfile } from 'src/app/models/user-profile'; 7 | import { ProcessStatus } from '../models/user-management-status.enum'; 8 | import { UserManagementService } from '../services/user-management.service'; 9 | 10 | @Component({ 11 | selector: 'app-user-delete', 12 | templateUrl: './user-delete.component.html', 13 | styleUrls: ['./user-delete.component.css'] 14 | }) 15 | export class UserDeleteComponent implements OnInit { 16 | @Input() public userProfile: UserProfile; 17 | @Output() public deletedUser: EventEmitter = new EventEmitter(); 18 | @Output() public closedDeleteUser: EventEmitter = new EventEmitter(); 19 | 20 | public deleteUserForm = new FormGroup({ 21 | objectId: new FormControl({value: '', disabled: true}, [Validators.required]) 22 | }); 23 | public eUserManagementStatus = ProcessStatus; 24 | public deleteStatus: ProcessStatus; 25 | public deleteUserResultMessage: String; 26 | 27 | constructor(private userManagementService: UserManagementService) { 28 | } 29 | 30 | ngOnInit(): void { 31 | this.deleteUserForm.controls.objectId.setValue(this.userProfile.id); 32 | } 33 | 34 | public closeDeleteUser() { 35 | this.closedDeleteUser.emit(); 36 | } 37 | 38 | /** 39 | * Deletes a user 40 | */ 41 | public deleteUser() { 42 | this.deleteStatus = ProcessStatus.InProgress; 43 | this.userManagementService.deleteUser(this.userProfile.id).subscribe({ 44 | error: (_) => { 45 | this.deleteStatus = ProcessStatus.Failed; 46 | this.deleteUserResultMessage = "Failed to delete user"; 47 | }, 48 | complete: () => { 49 | this.deleteStatus = ProcessStatus.Succeeded 50 | this.deleteUserResultMessage = "Deleted user" 51 | this.deletedUser.emit(this.userProfile); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | validate_installation: 8 | name: Validate Installation 9 | runs-on: ubuntu-latest 10 | defaults: 11 | run: 12 | working-directory: ./control-plane 13 | shell: bash 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Validate main script 19 | run: bash -n experiencelab.sh 20 | 21 | - name: Validate container script 22 | run: bash -n containerRun.sh 23 | 24 | - name: Validate Dockerfile 25 | run: docker build . 26 | 27 | validate_portal: 28 | name: Validate Developer Portal 29 | runs-on: ubuntu-latest 30 | defaults: 31 | run: 32 | working-directory: ./developer-portal 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | - uses: actions/setup-node@v3 37 | with: 38 | node-version: '14' 39 | 40 | - run: npm install 41 | - name: Lint files 42 | run: npm run-script lint 43 | - name: Create temporary environment file 44 | run: | 45 | mkdir -p src/environments 46 | echo " 47 | export const environment = { 48 | tenantId: \"\", 49 | clientId: \"\", 50 | apiHost: \"\", 51 | dataPartition: \"\", 52 | scopes: \"\", 53 | redirectUrl: \"\", 54 | instanceName: \"\", 55 | domain: \"\", 56 | buildNumber: \"\", 57 | powerBiConnectorFileName: \"\", 58 | tnoTemplateSpecUrl: \"\" 59 | }; 60 | " > src/environments/environment.ts 61 | - name: Unit tests 62 | run: npm run-script test:prod 63 | - name: Docker build 64 | run: docker build . 65 | 66 | validate_data_load: 67 | name: Validate Data Load 68 | runs-on: ubuntu-latest 69 | defaults: 70 | run: 71 | working-directory: ./data-load 72 | shell: bash 73 | 74 | steps: 75 | - uses: actions/checkout@v3 76 | 77 | - name: Validate TNO download script 78 | run: bash -n open-test-data/downloadTnoData.sh 79 | -------------------------------------------------------------------------------- /developer-portal/src/app/profile/profile.component.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { MsalService } from '@azure/msal-angular'; 7 | import { AuthenticationResult, SilentRequest } from '@azure/msal-browser'; 8 | import { MatTooltipModule } from '@angular/material/tooltip'; 9 | import { Observable, of } from 'rxjs'; 10 | 11 | import { ProfileComponent } from './profile.component'; 12 | import { ProfileService } from '../services/profile.service'; 13 | import { UserProfile } from '../models/user-profile'; 14 | 15 | describe('ProfileComponent', () => { 16 | let component: ProfileComponent; 17 | let fixture: ComponentFixture; 18 | const instanceSpy = jasmine.createSpyObj('IPublicClientApplication', ['getActiveAccount']); 19 | 20 | const msalServiceStub: Partial = { 21 | acquireTokenSilent(silentRequest: SilentRequest): Observable { 22 | return of(); 23 | }, 24 | instance: instanceSpy 25 | }; 26 | 27 | const profileServiceStub: Partial = { 28 | getAssignedGroups(): Observable { 29 | return of(); 30 | }, 31 | getUserProfile(): Observable { 32 | return of(); 33 | }, 34 | getRefreshToken(): string { 35 | return "token"; 36 | } 37 | }; 38 | 39 | beforeEach(async () => { 40 | await TestBed.configureTestingModule({ 41 | declarations: [ ProfileComponent ], 42 | imports: [ 43 | HttpClientTestingModule, 44 | MatTooltipModule 45 | ], 46 | providers: [ 47 | { provide: MsalService, useValue: msalServiceStub }, 48 | { provide: ProfileService, useValue: profileServiceStub } 49 | ] 50 | }) 51 | .compileComponents().then(() => { 52 | fixture = TestBed.createComponent(ProfileComponent); 53 | component = fixture.componentInstance; 54 | fixture.detectChanges(); 55 | }); 56 | }); 57 | 58 | it('should create', () => { 59 | expect(component).toBeTruthy(); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-assignment/group-assignment.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |
6 |
7 |
8 | Group: {{this.group.name}} 9 |
10 | 11 | User Object ID 12 | 13 | person_outline 14 | Found in the Azure AD Portal 15 | 16 | 19 |
20 | 27 | 34 |
35 | {{this.groupAssignmentResultMessage}} 36 | {{this.groupAssignmentResultMessage}} 37 |
38 | 39 |
40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /rest-scripts/services/search.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | 7 | ## This script provides a few samples for calling search. 8 | 9 | 10 | # ----------------------- 11 | # OAUTH (Variables) 12 | # ----------------------- 13 | ### 14 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 15 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 16 | @scopes = {{CLIENT_ID}}/.default openid profile offline_access 17 | 18 | 19 | # ----------------------- 20 | # OAUTH refresh_token 21 | # ----------------------- 22 | ### 23 | # @name refresh 24 | POST https://{{oauth_token_host}} HTTP/1.1 25 | Content-Type: application/x-www-form-urlencoded 26 | Origin: {{DEVELOPER_PORTAL}} 27 | 28 | grant_type=refresh_token 29 | &client_id={{CLIENT_ID}} 30 | &refresh_token={{REFRESH_TOKEN}} 31 | &scope={{scopes}} 32 | 33 | 34 | # ----------------------- 35 | # API (Variables) 36 | # ----------------------- 37 | ### 38 | @access_token = {{refresh.response.body.access_token}} 39 | @ENDPOINT = https://{{HOST}} 40 | @SEARCH_HOST = {{ENDPOINT}}/api/search/v2/query 41 | 42 | 43 | ### 44 | # @name fulltext_search 45 | POST {{SEARCH_HOST}} 46 | Authorization: Bearer {{access_token}} 47 | Content-Type: application/json 48 | data-partition-id: {{DATA_PARTITION}} 49 | 50 | { 51 | "kind": "osdu:wks:*:1.0.0", 52 | "query": "BIR*" 53 | } 54 | 55 | ### 56 | # @name fulltext_search_wildcard 57 | POST {{SEARCH_HOST}} 58 | Authorization: Bearer {{access_token}} 59 | Content-Type: application/json 60 | data-partition-id: {{DATA_PARTITION}} 61 | 62 | { 63 | "kind": "osdu:wks:*:1.0.0", 64 | "query": "(BIR AND 0?)" 65 | } 66 | 67 | ### 68 | # @name fulltext_search 69 | POST {{SEARCH_HOST}} 70 | Authorization: Bearer {{access_token}} 71 | Content-Type: application/json 72 | data-partition-id: {{DATA_PARTITION}} 73 | 74 | { 75 | "kind": "osdu:wks:*:1.0.0", 76 | "query": "BIR*" 77 | } 78 | 79 | ### 80 | # @name fulltext_search_wildcard 81 | POST {{SEARCH_HOST}} 82 | Authorization: Bearer {{access_token}} 83 | Content-Type: application/json 84 | data-partition-id: {{DATA_PARTITION}} 85 | 86 | { 87 | "kind": "osdu:wks:*:1.0.0", 88 | "query": "BIR 0?" 89 | } 90 | 91 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-create/legal-tag-create.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component } from '@angular/core'; 5 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 6 | import { Router } from '@angular/router'; 7 | import { ProcessStatus } from 'src/app/user-management/models/user-management-status.enum'; 8 | import { LegalTagManagementService } from '../services/legal-tag-management.service'; 9 | 10 | @Component({ 11 | selector: 'app-legal-tag-create', 12 | templateUrl: './legal-tag-create.component.html', 13 | styleUrls: ['./legal-tag-create.component.css'] 14 | }) 15 | export class LegalTagCreateComponent { 16 | 17 | public creationForm: FormGroup = new FormGroup({ 18 | name: new FormControl('', [Validators.required]), 19 | description: new FormControl('', [Validators.required]), 20 | properties: new FormGroup({ 21 | countryOfOrigin: new FormControl('', [Validators.required]), 22 | contractId: new FormControl('', [Validators.required]), 23 | dataType: new FormControl('', [Validators.required]), 24 | expirationDate: new FormControl('', [Validators.required]), 25 | exportClassification: new FormControl('', [Validators.required]), 26 | originator: new FormControl('', [Validators.required]), 27 | personalData: new FormControl('', [Validators.required]), 28 | securityClassification: new FormControl('', [Validators.required]), 29 | }) 30 | }); 31 | public tagCreationStatus: ProcessStatus; 32 | public eProcessStatus = ProcessStatus; 33 | public errorMessage: string; 34 | 35 | constructor(private legalTagService: LegalTagManagementService, private router: Router) { } 36 | 37 | public createLegalTag() { 38 | this.tagCreationStatus = ProcessStatus.InProgress; 39 | var legalTag = this.creationForm.value; 40 | legalTag.properties.countryOfOrigin = [legalTag.properties.countryOfOrigin]; 41 | 42 | this.legalTagService.addLegalTag(legalTag).subscribe({ 43 | error: (errorResponse) => { 44 | this.tagCreationStatus = ProcessStatus.Failed; 45 | }, 46 | complete: () => { 47 | this.tagCreationStatus = ProcessStatus.Succeeded; 48 | this.router.navigate(['/legal']); 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /control-plane/modules/lab_dataload.bicep: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | @description('Storage Account ID') 5 | param storageAccountId string 6 | @description('Storage Account name') 7 | param storageAccountName string 8 | @description('Image for data load containers') 9 | param imagePath string 10 | @description('Azure region where resources should be deployed') 11 | param location string = resourceGroup().location 12 | @description('Resource ID for managed identity') 13 | param managedIdentityId string 14 | @description('Time for calculating token expiry') 15 | param baseTime string = utcNow('O') 16 | 17 | var tnoLoadContainerName = 'tnoDataLoad' 18 | var cpuCores = 4 19 | var memoryInGb = 16 20 | var expiryTime = dateTimeAdd(baseTime, 'P6M') 21 | 22 | var accountSasProperties = { 23 | signedServices: 'fb' 24 | signedPermission: 'rwdlacup' 25 | signedExpiry: expiryTime 26 | signedResourceTypes: 'co' 27 | } 28 | var sasToken = listAccountSas(storageAccountId, '2018-07-01', accountSasProperties).accountSasToken 29 | var storageAccountKey = listKeys(storageAccountId, '2018-07-01').keys[0].value 30 | var tnoScriptPath = format('https://{0}.blob.{1}/files/downloadTnoData.sh?{2}', storageAccountName, environment().suffixes.storage, sasToken) 31 | 32 | resource tnoLoadContainer 'Microsoft.ContainerInstance/containerGroups@2021-09-01' = { 33 | name: tnoLoadContainerName 34 | location: location 35 | identity: { 36 | type: 'UserAssigned' 37 | userAssignedIdentities: { 38 | '${managedIdentityId}': {} 39 | } 40 | } 41 | properties: { 42 | containers: [ 43 | { 44 | name: 'load-data' 45 | properties: { 46 | image: imagePath 47 | environmentVariables: [ 48 | { 49 | name: 'SETUP_SCRIPT' 50 | value: tnoScriptPath 51 | } 52 | { 53 | name: 'CONTROL_PLANE_STORAGE' 54 | value: storageAccountName 55 | } 56 | { 57 | name: 'STORAGE_ACCOUNT_KEY' 58 | secureValue: storageAccountKey 59 | } 60 | ] 61 | resources: { 62 | requests: { 63 | cpu: cpuCores 64 | memoryInGB: memoryInGb 65 | } 66 | } 67 | } 68 | } 69 | ] 70 | osType: 'Linux' 71 | restartPolicy: 'Never' 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /developer-portal/src/app/power-bi/power-bi.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |

Power BI Visualization

6 |

7 | Included in Experience Lab is a pre-configured Power BI connector for visualizing TNO data. 8 | The connector is open source, and can be found on Kadri Umay's GitHub. 9 | Currently, the Power BI connector only supports client-side Power BI reports - it does not support reports published to Power BI Online. 10 |

11 |
12 |
13 |

Prerequisites

14 |
    15 |
  • You must be using a Windows PC
  • 16 |
  • You have ingested Open Test Data (TNO) into your Microsoft Azure Data Manager for Energy instance
  • 17 |
18 |
19 |
20 |

Instructions

21 |

The following steps explain how to use Power BI with Azure Data Manager for Energy.

22 |
    23 |
  1. Download and install Power BI Desktop
  2. 24 |
  3. Enable unsigned connectors
  4. 25 |
      26 |
    1. Open Power BI Desktop
    2. 27 |
    3. Navigate to File > Options and Settings > Options > Security
    4. 28 |
    5. Under "Data Extensions", select "Allow any extension to load without validation or warning" 29 | 30 | (Read more about custom connectors) 31 |
    6. 32 |
    7. Click OK
    8. 33 |
    34 |
  5. Download Power BI Connector, 35 | and save it to C:\Users\<profile>\Documents\Power BI Desktop\Custom Connectors. Create this folder if it doesn't exist.
  6. 36 |
  7. Download the sample Power BI Template Report from GitHub
  8. 37 |
  9. Open the report in Power BI Desktop
  10. 38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /developer-portal/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * This file includes polyfills needed by Angular and is loaded before the app. 6 | * You can add your own extra polyfills to this file. 7 | * 8 | * This file is divided into 2 sections: 9 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 10 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 11 | * file. 12 | * 13 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 14 | * automatically update themselves. This includes recent versions of Safari, Chrome (including 15 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 16 | * 17 | * Learn more in https://angular.io/guide/browser-support 18 | */ 19 | 20 | /*************************************************************************************************** 21 | * BROWSER POLYFILLS 22 | */ 23 | 24 | /** 25 | * By default, zone.js will patch all possible macroTask and DomEvents 26 | * user can disable parts of macroTask/DomEvents patch by setting following flags 27 | * because those flags need to be set before `zone.js` being loaded, and webpack 28 | * will put import in the top of bundle, so user need to create a separate file 29 | * in this directory (for example: zone-flags.ts), and put the following flags 30 | * into that file, and then add the following code before importing zone.js. 31 | * import './zone-flags'; 32 | * 33 | * The flags allowed in zone-flags.ts are listed here. 34 | * 35 | * The following flags will work for all browsers. 36 | * 37 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 38 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 39 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 40 | * 41 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 42 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 43 | * 44 | * (window as any).__Zone_enable_cross_context_check = true; 45 | * 46 | */ 47 | 48 | /*************************************************************************************************** 49 | * Zone JS is required by default for Angular itself. 50 | */ 51 | import 'zone.js'; // Included with Angular CLI. 52 | 53 | 54 | /*************************************************************************************************** 55 | * APPLICATION IMPORTS 56 | */ 57 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-overview/user-overview.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, OnInit } from '@angular/core'; 5 | import { UserProfile } from 'src/app/models/user-profile'; 6 | import { DataGroup } from '../models/data-group' 7 | import { GroupAssignmentEventService } from '../services/assignment-event.service'; 8 | import { UserManagementService } from '../services/user-management.service'; 9 | 10 | @Component({ 11 | selector: 'app-user-overview', 12 | templateUrl: './user-overview.component.html', 13 | styleUrls: ['./user-overview.component.css'] 14 | }) 15 | export class UserOverviewComponent implements OnInit { 16 | 17 | constructor(private userManagementService: UserManagementService, private eventService: GroupAssignmentEventService) { } 18 | public userProfiles: UserProfile[] = []; 19 | public dataGroups: DataGroup[] = []; 20 | public showCreateUser = false; 21 | public failedToLoadUsers = false; 22 | public failedToLoadDataGroups = false; 23 | 24 | ngOnInit(): void { 25 | this.getUsers(); 26 | this.getDataGroups(); 27 | 28 | this.eventService.eventListener().subscribe(() => { 29 | this.getDataGroups(); 30 | }) 31 | } 32 | 33 | public isLoadingUsers(): Boolean { 34 | const areUsersLoaded = Array.isArray(this.userProfiles) && !this.userProfiles.length; 35 | return !this.failedToLoadUsers && areUsersLoaded; 36 | } 37 | 38 | public isLoadingDataGroups(): Boolean { 39 | const areDataGroupsLoaded = Array.isArray(this.dataGroups) && !this.dataGroups.length; 40 | return !this.failedToLoadDataGroups && areDataGroupsLoaded; 41 | } 42 | 43 | public removeUserFromView(userProfile: UserProfile) { 44 | this.userProfiles = this.userProfiles.filter(element => element !== userProfile); 45 | } 46 | 47 | public toggleCreateUser() { 48 | this.showCreateUser = !this.showCreateUser; 49 | } 50 | 51 | public closeCreateUser() { 52 | this.showCreateUser = false; 53 | } 54 | 55 | public addUserToView(newUser: UserProfile) { 56 | this.userProfiles.unshift(newUser); 57 | } 58 | 59 | private getUsers() { 60 | this.userManagementService.getUsers().subscribe({ 61 | error: (_) => { 62 | this.failedToLoadUsers = true; 63 | this.userProfiles = [] 64 | }, 65 | next: (users) => { 66 | this.userProfiles = users; 67 | } 68 | }); 69 | } 70 | 71 | private getDataGroups() { 72 | this.userManagementService.getDataGroups().subscribe({ 73 | error: (_) => { 74 | this.failedToLoadDataGroups = true; 75 | this.dataGroups = [] 76 | }, 77 | next: (dataGroups) => { 78 | this.dataGroups = dataGroups; 79 | } 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | .envrc 116 | .env.ps1 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ 141 | 142 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /developer-portal/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; 5 | import { MsalService } from '@azure/msal-angular'; 6 | import { environment } from 'src/environments/environment'; 7 | import { MatSidenav } from '@angular/material/sidenav'; 8 | import { BreakpointObserver } from '@angular/cdk/layout'; 9 | import { ProfileService } from './services/profile.service'; 10 | import { UserProfile } from './models/user-profile'; 11 | import { AuthorizationService } from './authorization/services/authorization.service'; 12 | import { UserGroup } from './models/user-group.enum'; 13 | import { OsduVersionService } from './services/osdu-version.service'; 14 | 15 | @Component({ 16 | selector: 'app-root', 17 | templateUrl: './app.component.html', 18 | styleUrls: ['./app.component.css'] 19 | }) 20 | export class AppComponent implements OnInit, AfterViewInit { 21 | public profile: UserProfile; 22 | public isUserAuthorized: boolean; 23 | public osduVersion: string; 24 | 25 | @ViewChild(MatSidenav) 26 | sidenav!: MatSidenav; 27 | 28 | constructor(private authService: MsalService, 29 | private observer: BreakpointObserver, 30 | private profileService: ProfileService, 31 | private authorizationService: AuthorizationService, 32 | private osduVersionService: OsduVersionService) { } 33 | 34 | public ngOnInit() { 35 | this.setUserProfile(); 36 | this.getOsduVersion(); 37 | this.authorizationService.IsUserInGroup(UserGroup.Users) 38 | .subscribe({ 39 | next: (result) => { 40 | this.isUserAuthorized = result; 41 | }, 42 | error: () => { 43 | this.isUserAuthorized = false; 44 | } 45 | }) 46 | } 47 | 48 | public ngAfterViewInit() { 49 | setTimeout(() => { 50 | this.observer.observe(['(max-width: 800px)']).subscribe((state) => { 51 | if (state.matches) { 52 | this.sidenav.mode = 'over'; 53 | this.sidenav.close(); 54 | } else { 55 | this.sidenav.mode = 'side'; 56 | this.sidenav.open(); 57 | } 58 | }); 59 | }, 1); 60 | } 61 | 62 | public logout() { 63 | this.authService.logoutRedirect({ 64 | postLogoutRedirectUri: environment.redirectUrl 65 | }); 66 | } 67 | 68 | public getBuildNumber(): string { 69 | return environment.buildNumber; 70 | } 71 | 72 | private getOsduVersion() { 73 | this.osduVersionService.getOsduVersion().subscribe({ 74 | error: (_) => { 75 | this.osduVersion = "Unknown"; 76 | }, 77 | next: (version) => { 78 | this.osduVersion = version; 79 | } 80 | }); 81 | } 82 | 83 | /** 84 | * Sets the user's profile 85 | */ 86 | private setUserProfile() { 87 | this.profileService.getUserProfile().subscribe({ 88 | next: (response) => { 89 | this.profile = response; 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | # Runs at midnight 22 | - cron: '0 0 * * *' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'javascript' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 38 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | 53 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 54 | # queries: security-extended,security-and-quality 55 | 56 | 57 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 58 | # If this step fails, then you should remove it and run the build manually (see below) 59 | - name: Autobuild 60 | uses: github/codeql-action/autobuild@v2 61 | 62 | # ℹ️ Command-line programs to run using the OS shell. 63 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 64 | 65 | # If the Autobuild fails above, remove it and uncomment the following three lines. 66 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 67 | 68 | # - run: | 69 | # echo "Run, Build Application using script" 70 | # ./location_of_script_within_repo/buildscript.sh 71 | 72 | - name: Perform CodeQL Analysis 73 | uses: github/codeql-action/analyze@v2 74 | -------------------------------------------------------------------------------- /developer-portal/src/app/services/profile.service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 5 | import { Injectable } from '@angular/core'; 6 | import { MsalBroadcastService, MsalService } from '@azure/msal-angular'; 7 | import { AccountInfo, InteractionStatus } from '@azure/msal-browser'; 8 | import { filter, map, Observable, of } from 'rxjs'; 9 | import { environment } from 'src/environments/environment'; 10 | import { UserProfile } from '../models/user-profile'; 11 | 12 | @Injectable({ 13 | providedIn: 'root' 14 | }) 15 | export class ProfileService { 16 | 17 | constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService, private http: HttpClient) { } 18 | 19 | /** 20 | * Gets the user's profile 21 | * @returns The users profile 22 | */ 23 | public getUserProfile(): Observable { 24 | var activeUser = this.authService.instance.getActiveAccount(); 25 | if (activeUser) { 26 | return of(this.convertProfileFromMsal(activeUser)); 27 | } 28 | return this.msalBroadcastService.inProgress$.pipe( 29 | filter((status: InteractionStatus) => status === InteractionStatus.None && this.authService.instance.getAllAccounts().length > 0), 30 | map(() => { 31 | this.setActiveAccount(); 32 | var activeUser = this.authService.instance.getActiveAccount(); 33 | return this.convertProfileFromMsal(activeUser); 34 | }) 35 | ); 36 | } 37 | 38 | public getAssignedGroups(): Observable { 39 | let headers = new HttpHeaders(); 40 | headers = headers.set('data-partition-id', environment.dataPartition); 41 | return this.http.get(`https://${environment.apiHost}/api/entitlements/v2/groups`, {headers: headers}) 42 | .pipe(map((userGroupsResponse: any) => { 43 | return userGroupsResponse.groups; 44 | })); 45 | } 46 | 47 | /** 48 | * Gets the user's refresh token 49 | * @returns The user's refresh token 50 | */ 51 | public getRefreshToken(): string { 52 | var refreshTokenKey = Object.keys(window.localStorage).find(key => key.includes('refreshtoken')); 53 | return refreshTokenKey ? JSON.parse(window.localStorage.getItem(refreshTokenKey)).secret : "Error: Unable to locate refresh token"; 54 | } 55 | 56 | /** 57 | * Converts the MSAL account to a UserProfile 58 | * @param msalAccount The msal account 59 | * @returns A converted user profile 60 | */ 61 | private convertProfileFromMsal(msalAccount: AccountInfo): UserProfile { 62 | const userProfile = new UserProfile(); 63 | userProfile.email = msalAccount.username; 64 | userProfile.name = msalAccount.name; 65 | userProfile.id = msalAccount.localAccountId; 66 | return userProfile; 67 | } 68 | 69 | /** 70 | * Sets MSAL's active account 71 | */ 72 | private setActiveAccount() { 73 | let accounts = this.authService.instance.getAllAccounts(); 74 | this.authService.instance.setActiveAccount(accounts[0]); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /developer-portal/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 13 | Microsoft Experience Lab 14 |
15 |
16 | {{this.profile?.name}} 17 | 18 |
19 |
20 | 21 | 22 | 23 | 59 | 63 |
64 | Version: {{ this.osduVersion }} 65 | {{ this.getBuildNumber() }} 66 |
67 |
68 | 69 |
70 | 71 |
72 |
73 |
74 | -------------------------------------------------------------------------------- /developer-portal/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { NgModule } from '@angular/core'; 5 | import { Routes, RouterModule } from '@angular/router'; 6 | import { HomeComponent } from './home/home.component'; 7 | import { ProfileComponent } from './profile/profile.component'; 8 | import { MsalGuard } from '@azure/msal-angular'; 9 | import { RestComponent } from './rest/rest.component'; 10 | import { SwaggerComponent } from './swagger/swagger.component'; 11 | import { UserOverviewComponent } from './user-management/user-overview/user-overview.component'; 12 | import { LegalTagOverviewComponent } from './legal-tag-management/legal-tag-overview/legal-tag-overview.component'; 13 | import { UserGuard } from './authorization/guards/user.guard'; 14 | import { UnauthorizedComponent } from './authorization/components/unauthorized/unauthorized.component'; 15 | import { PowerBiComponent } from './power-bi/power-bi.component'; 16 | import { DataLoadComponent } from './data-load/data-load.component'; 17 | import { LegalTagDetailComponent } from './legal-tag-management/legal-tag-detail/legal-tag-detail.component'; 18 | import { LegalTagCreateComponent } from './legal-tag-management/legal-tag-create/legal-tag-create.component'; 19 | 20 | const routes: Routes = [ 21 | 22 | { 23 | path: '', 24 | component: HomeComponent, 25 | canActivate: [MsalGuard, UserGuard] 26 | }, 27 | { 28 | path: 'profile', 29 | component: ProfileComponent, 30 | canActivate: [MsalGuard, UserGuard] 31 | }, 32 | { 33 | path: 'rest', 34 | component: RestComponent, 35 | canActivate: [MsalGuard, UserGuard] 36 | }, 37 | { 38 | path: 'swagger', 39 | component: SwaggerComponent, 40 | canActivate: [MsalGuard, UserGuard] 41 | }, 42 | { 43 | path: 'users', 44 | component: UserOverviewComponent, 45 | canActivate: [MsalGuard, UserGuard] 46 | }, 47 | { 48 | path: 'legal', 49 | component: LegalTagOverviewComponent, 50 | canActivate: [MsalGuard, UserGuard] 51 | }, 52 | { 53 | path: 'legal/new', 54 | component: LegalTagCreateComponent, 55 | canActivate: [MsalGuard, UserGuard] 56 | }, 57 | { 58 | path: 'legal/:id', 59 | component: LegalTagDetailComponent, 60 | canActivate: [MsalGuard, UserGuard] 61 | }, 62 | { 63 | path: 'powerbi', 64 | component: PowerBiComponent, 65 | canActivate: [MsalGuard, UserGuard] 66 | }, 67 | { 68 | path: 'dataload', 69 | component: DataLoadComponent, 70 | canActivate: [MsalGuard, UserGuard] 71 | }, 72 | { 73 | path: 'unauthorized', 74 | component: UnauthorizedComponent 75 | }, 76 | { 77 | path: '**', 78 | redirectTo: '', 79 | canActivate: [MsalGuard, UserGuard] 80 | }, 81 | ]; 82 | 83 | const isIframe = window !== window.parent && !window.opener; 84 | 85 | @NgModule({ 86 | imports: [RouterModule.forRoot(routes, { 87 | initialNavigation: !isIframe ? 'enabled' : 'disabled' // Don't perform initial navigation in iframes 88 | })], 89 | exports: [RouterModule] 90 | }) 91 | export class AppRoutingModule { } 92 | -------------------------------------------------------------------------------- /developer-portal/src/theme.scss: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | @import '~@angular/material/theming'; 5 | $custom-typography: mat-typography-config( 6 | $font-family: '"Segoe UI Web (West European)","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif', 7 | ); 8 | @include mat-core($custom-typography); 9 | 10 | $portal-primary-color-palette: ( 11 | 50: #E0EFFA, 12 | 100: #80BCEA , 13 | 200: #80BCEA, 14 | 300: #4DA1E1, 15 | 400: #268CDA, 16 | 500: #0078D4, 17 | 600: #0070CF, 18 | 700: #0065C9, 19 | 800: #005BC3, 20 | 900: #0048B9, 21 | A100: #E2EBFF, 22 | A200: #AFC9FF, 23 | A400: #7CA6FF, 24 | A700: #6394FF, 25 | contrast: ( 26 | 50: #000000, 27 | 100: #000000, 28 | 200: #000000, 29 | 300: #000000, 30 | 400: #ffffff, 31 | 500: #ffffff, 32 | 600: #ffffff, 33 | 700: #ffffff, 34 | 800: #ffffff, 35 | 900: #ffffff, 36 | A100: #000000, 37 | A200: #000000, 38 | A400: #000000, 39 | A700: #000000, 40 | ) 41 | ); 42 | 43 | $portal-accent-color-palette: ( 44 | 50: #e0e4ea, 45 | 100: #b3bccb, 46 | 200: #8090a8, 47 | 300: #4d6385, 48 | 400: #26416a, 49 | 500: #002050, 50 | 600: #001c49, 51 | 700: #001840, 52 | 800: #001337, 53 | 900: #000b27, 54 | A100: #627bff, 55 | A200: #2f50ff, 56 | A400: #0028fb, 57 | A700: #0024e1, 58 | contrast: ( 59 | 50: #000000, 60 | 100: #000000, 61 | 200: #000000, 62 | 300: #ffffff, 63 | 400: #ffffff, 64 | 500: #ffffff, 65 | 600: #ffffff, 66 | 700: #ffffff, 67 | 800: #ffffff, 68 | 900: #ffffff, 69 | A100: #000000, 70 | A200: #ffffff, 71 | A400: #ffffff, 72 | A700: #ffffff, 73 | ) 74 | ); 75 | 76 | 77 | $portal-warn-color-palette: ( 78 | 50: #fce2e5, 79 | 100: #f8b8bd, 80 | 200: #f48891, 81 | 300: #ef5865, 82 | 400: #eb3544, 83 | 500: #e81123, 84 | 600: #e50f1f, 85 | 700: #e20c1a, 86 | 800: #de0a15, 87 | 900: #d8050c, 88 | A100: #ffffff, 89 | A200: #ffcece, 90 | A400: #ff9b9c, 91 | A700: #ff8183, 92 | contrast: ( 93 | 50: #000000, 94 | 100: #000000, 95 | 200: #000000, 96 | 300: #000000, 97 | 400: #ffffff, 98 | 500: #ffffff, 99 | 600: #ffffff, 100 | 700: #ffffff, 101 | 800: #ffffff, 102 | 900: #ffffff, 103 | A100: #000000, 104 | A200: #000000, 105 | A400: #000000, 106 | A700: #000000, 107 | ) 108 | ); 109 | 110 | 111 | 112 | $primary-color: mat-palette($portal-primary-color-palette); 113 | $accent-color: mat-palette($portal-accent-color-palette); 114 | $warn-color: mat-palette($portal-warn-color-palette); 115 | $portal-theme: mat-light-theme($primary-color, $accent-color, $warn-color); 116 | @include angular-material-theme($portal-theme); 117 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-form/legal-tag-form.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 | 6 | Name 7 | 8 | 9 | 10 | Description 11 | 12 | 13 |
14 | 15 | Country of Origin 16 | 17 | 18 | 19 | Contract ID 20 | 21 | 22 | 23 | Data Type 24 | 25 | Public Domain Data 26 | Second Party Data 27 | First Party Data 28 | Third Party Data 29 | Transferred Data 30 | 31 | 32 | 33 | Expiration Date 34 | 35 | YYYY-MM-DD 36 | 37 | 38 | Export Classification 39 | 40 | No License Required 41 | 0A998 42 | Not - Technical Data 43 | EAR99 44 | 45 | 46 | 47 | Originator 48 | 49 | 50 | 51 | Personal Data 52 | 53 | Personally Identifiable 54 | No Personal Data 55 | 56 | 57 | 58 | Security Classifications 59 | 60 | Private 61 | Public 62 | Confidential 63 | 64 | 65 |
66 |
67 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-creation/user-creation.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpErrorResponse } from '@angular/common/http'; 5 | import { Component, EventEmitter, OnInit, Output } from '@angular/core'; 6 | import { FormGroup, FormControl, Validators } from '@angular/forms'; 7 | import { DropDownOption } from 'src/app/models/drop-down-option'; 8 | import { UserGroup } from 'src/app/models/user-group.enum'; 9 | import { UserManagementService } from '../services/user-management.service' 10 | import { ProcessStatus } from '../models/user-management-status.enum'; 11 | import { UserProfile } from 'src/app/models/user-profile'; 12 | 13 | @Component({ 14 | selector: 'app-user-creation', 15 | templateUrl: './user-creation.component.html', 16 | styleUrls: ['./user-creation.component.css'] 17 | }) 18 | export class UserCreationComponent implements OnInit { 19 | @Output() public closedCreateUser: EventEmitter = new EventEmitter(); 20 | @Output() public createdUser: EventEmitter = new EventEmitter(); 21 | public creationForm = new FormGroup({ 22 | objectId: new FormControl('', [Validators.required]), 23 | group: new FormControl('', [Validators.required]) 24 | }); 25 | public eUserCreationStatus = ProcessStatus; 26 | public creationStatus: ProcessStatus; 27 | public creationResultMessage: String; 28 | public groups: DropDownOption[]; 29 | 30 | constructor(private userManagementService: UserManagementService) { } 31 | 32 | ngOnInit(): void { 33 | this.groups = [ 34 | { value: UserGroup.Viewer, displayValue: "Reader" }, 35 | { value: UserGroup.Editor, displayValue: "Contributor" }, 36 | { value: UserGroup.Admin, displayValue: "Admin" }, 37 | { value: UserGroup.Ops, displayValue: "Owner" } 38 | ]; 39 | } 40 | 41 | public closeCreateUser() { 42 | this.closedCreateUser.emit(); 43 | } 44 | 45 | public createUser() { 46 | this.creationStatus = ProcessStatus.InProgress 47 | this.userManagementService.createUser(this.creationForm.value.objectId).subscribe({ 48 | next: (_) => { 49 | var newUser = new UserProfile(); 50 | newUser.id = this.creationForm.value.objectId; 51 | this.userManagementService.assignUserToGroup(this.creationForm.value.objectId, this.creationForm.value.group).subscribe({ 52 | error: (_) => { 53 | this.creationStatus = ProcessStatus.Failed; 54 | this.creationResultMessage = "Created user but failed to assign group"; 55 | }, 56 | complete: () => { 57 | newUser.groups = [this.creationForm.value.group as UserGroup]; 58 | this.creationStatus = ProcessStatus.Succeeded; 59 | this.creationResultMessage = "Created user and assigned group" 60 | this.createdUser.emit(newUser) 61 | } 62 | }) 63 | }, 64 | error: (creationErrorResponse) => { 65 | this.creationStatus = ProcessStatus.Failed 66 | this.creationResultMessage = "Failed to create user"; 67 | if (creationErrorResponse instanceof HttpErrorResponse && creationErrorResponse.status === 409) { 68 | this.creationResultMessage = "User already exists"; 69 | } 70 | } 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-overview/legal-tag-overview.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, OnInit } from '@angular/core'; 5 | import { LegalTag } from '../models/legal-tag'; 6 | import { LegalTagManagementService } from '../services/legal-tag-management.service'; 7 | import { animate, state, style, transition, trigger } from '@angular/animations'; 8 | import { MatTableDataSource } from '@angular/material/table'; 9 | import { ProcessStatus } from 'src/app/user-management/models/user-management-status.enum'; 10 | 11 | @Component({ 12 | selector: 'app-legal-tag-overview', 13 | templateUrl: './legal-tag-overview.component.html', 14 | styleUrls: ['./legal-tag-overview.component.css'], 15 | animations: [ 16 | trigger('detailExpand', [ 17 | state('collapsed', style({height: '0px', minHeight: '0'})), 18 | state('expanded', style({height: '*'})), 19 | transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), 20 | ]), 21 | ], 22 | }) 23 | export class LegalTagOverviewComponent implements OnInit { 24 | public legalTags: LegalTag[] = []; 25 | public tagLoadingStatus: ProcessStatus; 26 | public tagDeletingStatus: ProcessStatus; 27 | public eProcessStatus = ProcessStatus; 28 | public legalTagsDataSource: MatTableDataSource; 29 | columnsToDisplay = ['name', 'description']; 30 | columnsToDisplayWithExpand = [...this.columnsToDisplay, 'expand']; 31 | expandedElement: LegalTag | null; 32 | 33 | constructor(private legalTagManagementService: LegalTagManagementService) { } 34 | 35 | ngOnInit(): void { 36 | this.legalTagsDataSource = new MatTableDataSource(); 37 | this.loadLegalTags(); 38 | } 39 | 40 | public isLoadingLegalTags(): Boolean { 41 | const areLegalTagsSet = Array.isArray(this.legalTags) && !this.legalTags.length; 42 | return this.tagLoadingStatus == ProcessStatus.InProgress && areLegalTagsSet; 43 | } 44 | 45 | public applyFilter(filterValue: string) { 46 | this.legalTagsDataSource.filter = filterValue.trim().toLowerCase(); 47 | if (this.legalTagsDataSource.paginator) { 48 | this.legalTagsDataSource.paginator.firstPage(); 49 | } 50 | } 51 | 52 | public deleteLegalTag(legalTagName: string) { 53 | this.tagDeletingStatus = ProcessStatus.InProgress; 54 | this.legalTagManagementService.deleteLegalTag(legalTagName).subscribe({ 55 | error: (_) => { 56 | this.tagDeletingStatus = ProcessStatus.Failed; 57 | }, 58 | next: (_) => { 59 | this.legalTags = this.legalTags.filter(tag => tag.name !== legalTagName); 60 | this.legalTagsDataSource.data = this.legalTags; 61 | this.tagDeletingStatus = ProcessStatus.Succeeded; 62 | } 63 | }); 64 | } 65 | 66 | private loadLegalTags() { 67 | this.tagLoadingStatus = ProcessStatus.InProgress; 68 | this.legalTagManagementService.getLegalTags().subscribe({ 69 | error: (_) => { 70 | this.tagLoadingStatus = ProcessStatus.Failed; 71 | this.legalTags = [] 72 | }, 73 | next: (legalTags) => { 74 | this.legalTagsDataSource.data = legalTags; 75 | this.legalTags = legalTags; 76 | this.tagLoadingStatus = ProcessStatus.Succeeded; 77 | } 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /control-plane/modules/lab_storage.bicep: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | @description('Azure region where resources should be deployed') 5 | param location string = resourceGroup().location 6 | 7 | @description('Desired name of the storage account') 8 | param storageAccountName string = uniqueString(resourceGroup().id, deployment().name) 9 | 10 | @description('Name of the blob container') 11 | param containerName string = 'files' 12 | 13 | var azCliVersion = '2.26.1' 14 | 15 | resource storage 'Microsoft.Storage/storageAccounts@2021-04-01' = { 16 | name: storageAccountName 17 | location: location 18 | sku: { 19 | name: 'Standard_LRS' 20 | } 21 | kind: 'StorageV2' 22 | 23 | resource blobService 'blobServices' = { 24 | name: 'default' 25 | 26 | resource container 'containers' = { 27 | name: containerName 28 | } 29 | } 30 | } 31 | 32 | resource blobDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { 33 | name: 'load-blob-${deployment().name}' 34 | location: location 35 | kind: 'AzureCLI' 36 | properties: { 37 | azCliVersion: azCliVersion 38 | timeout: 'PT10M' 39 | retentionInterval: 'PT1H' 40 | environmentVariables: [ 41 | { 42 | name: 'AZURE_STORAGE_ACCOUNT' 43 | value: storage.name 44 | } 45 | { 46 | name: 'AZURE_STORAGE_KEY' 47 | secureValue: storage.listKeys().keys[0].value 48 | } 49 | { 50 | name: 'AZURE_STORAGE_CONTAINER' 51 | value: containerName 52 | } 53 | ] 54 | scriptContent: ''' 55 | #!/bin/bash 56 | set -e 57 | 58 | echo \"Downloading Container Script: File- experiencelab.sh\" 59 | wget -O experiencelab.sh https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/releases/latest/download/experiencelab.sh 60 | az storage blob upload -f experiencelab.sh -c $AZURE_STORAGE_CONTAINER -n experiencelab.sh 61 | 62 | echo \"Downloading Container Script: File- template-open-test-data.json\" 63 | wget -O template-open-test-data.json https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/releases/latest/download/template.json 64 | az storage blob upload -f template-open-test-data.json -c $AZURE_STORAGE_CONTAINER -n template-open-test-data.json 65 | 66 | echo \"Downloading Container Script: File- downloadTnoData.sh\" 67 | wget -O downloadTnoData.sh https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/releases/latest/download/downloadTnoData.sh 68 | az storage blob upload -f downloadTnoData.sh -c $AZURE_STORAGE_CONTAINER -n downloadTnoData.sh 69 | 70 | echo \"Downloading Container Script: File- developer-portal.tar.gz\" 71 | wget -O developer-portal.tar.gz https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/releases/latest/download/developer-portal.tar.gz 72 | az storage blob upload -f developer-portal.tar.gz -c $AZURE_STORAGE_CONTAINER -n developer-portal.tar.gz 73 | 74 | echo \"Downloading Container Script: File- rest-scripts.zip\" 75 | wget -O rest-scripts.zip https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/releases/latest/download/rest-scripts.zip 76 | az storage blob upload -f rest-scripts.zip -c $AZURE_STORAGE_CONTAINER -n rest-scripts.zip 77 | ''' 78 | } 79 | } 80 | 81 | output storageAccountId string = storage.id 82 | output storageAccountName string = storageAccountName 83 | -------------------------------------------------------------------------------- /rest-scripts/services/legal.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | # 7 | # Purpose: Sample requests for Legal Service 8 | 9 | # ----------------------- 10 | # OAUTH (Variables) 11 | # ----------------------- 12 | ### 13 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 14 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 15 | @scopes = {{CLIENT_ID}}/.default openid profile offline_access 16 | 17 | 18 | # ----------------------- 19 | # OAUTH refresh_token 20 | # ----------------------- 21 | ### 22 | # @name refresh 23 | POST https://{{oauth_token_host}} HTTP/1.1 24 | Content-Type: application/x-www-form-urlencoded 25 | Origin: {{DEVELOPER_PORTAL}} 26 | 27 | grant_type=refresh_token 28 | &client_id={{CLIENT_ID}} 29 | &refresh_token={{REFRESH_TOKEN}} 30 | &scope={{scopes}} 31 | 32 | 33 | # ----------------------- 34 | # API (Variables) 35 | # ----------------------- 36 | ### 37 | @access_token = {{refresh.response.body.access_token}} 38 | @ENDPOINT = https://{{HOST}} 39 | @LEGAL_HOST = {{ENDPOINT}}/api/legal/v1 40 | @tag = rest-testing-tag 41 | 42 | # ----------------------- 43 | # API: Version 44 | # ----------------------- 45 | ### 46 | # @name info 47 | GET {{LEGAL_HOST}}/info 48 | Authorization: Bearer {{access_token}} 49 | Accept: application/json 50 | 51 | 52 | # ----------------------- 53 | # API: legal 54 | # ----------------------- 55 | ### 56 | # @name getTagProperties 57 | GET {{LEGAL_HOST}}/legaltags:properties 58 | Authorization: Bearer {{access_token}} 59 | Accept: application/json 60 | data-partition-id: {{DATA_PARTITION}} 61 | 62 | 63 | ### 64 | # @name getAllTag 65 | GET {{LEGAL_HOST}}/legaltags 66 | Authorization: Bearer {{access_token}} 67 | Accept: application/json 68 | data-partition-id: {{DATA_PARTITION}} 69 | 70 | 71 | ### 72 | # @name create_tag 73 | POST {{LEGAL_HOST}}/legaltags 74 | Authorization: Bearer {{access_token}} 75 | Content-Type: application/json 76 | data-partition-id: {{DATA_PARTITION}} 77 | 78 | { 79 | "name": "{{tag}}", 80 | "description": "This is a test tag from Rest Scripts", 81 | "properties": { 82 | "countryOfOrigin": [ 83 | "US" 84 | ], 85 | "contractId": "A1234", 86 | "expirationDate": "2099-01-25", 87 | "originator": "MyCompany", 88 | "dataType": "Transferred Data", 89 | "securityClassification": "Public", 90 | "personalData": "No Personal Data", 91 | "exportClassification": "EAR99" 92 | } 93 | } 94 | 95 | 96 | ### 97 | # @name getTag 98 | GET {{LEGAL_HOST}}/legaltags/{{DATA_PARTITION}}-{{tag}} 99 | Authorization: Bearer {{access_token}} 100 | Accept: application/json 101 | data-partition-id: {{DATA_PARTITION}} 102 | 103 | 104 | ### 105 | # @name updateTag 106 | PUT {{LEGAL_HOST}}/legaltags 107 | Authorization: Bearer {{access_token}} 108 | Content-Type: application/json 109 | data-partition-id: {{DATA_PARTITION}} 110 | 111 | { 112 | "name": "{{DATA_PARTITION}}-{{tag}}", 113 | "contractId": "A1234", 114 | "description": "Updated: This is a test tag from Rest Scripts", 115 | "expirationDate": "2088-12-25" 116 | } 117 | 118 | 119 | ### 120 | # @name deleteTag 121 | DELETE {{LEGAL_HOST}}/legaltags/{{DATA_PARTITION}}-{{tag}} 122 | Authorization: Bearer {{access_token}} 123 | Accept: application/json 124 | data-partition-id: {{DATA_PARTITION}} 125 | -------------------------------------------------------------------------------- /control-plane/README.md: -------------------------------------------------------------------------------- 1 | # Control Plane 2 | 3 | ## About 4 | 5 | The control plane contains all the scripts and resources necessary to create instances of Experience lab (Microsoft Azure Data Manager for Energy instance, portal site, TNO data set, etc.). It can be created in an Azure subscription via ARM template. 6 | 7 | ## Installing the Control Plane 8 | 9 | ### Requirements 10 | 11 | - You must have the ability to create resources in your subscription. 12 | 13 | ### Deploy Via Hosted ARM Template 14 | 15 | [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fazure-data-manager-for-energy-experience-lab%2Fmain%2Fazuredeploy.json) 16 | 17 | Azure Data Manager for Energy is currently only available in the following Azure regions. We recommend you also use one of these for the control plane. 18 | 19 | - East US 20 | - South Central US 21 | - North Europe 22 | - West Europe 23 | 24 | ### Deploy Via Local Machine 25 | 26 | Install the following items 27 | 28 | - Azure CLI ([install](https://docs.microsoft.com/cli/azure/install-azure-cli)) 29 | - Azure Bicep CLI (install with `az bicep install`) 30 | 31 | You can use the main.bicep file locally to run the deployment from your machine. 32 | 33 | ```bash 34 | az login 35 | az account set --subscription "" 36 | 37 | $deploymentName="" 38 | $location="" # e.g. eastus 39 | $name="" 40 | 41 | az deployment sub create \ 42 | --name $deploymentName \ 43 | --location $location \ 44 | --parameters controlPlaneName=$name controlPlaneLocation=$location \ 45 | --template-file ./main.bicep 46 | ``` 47 | 48 | ## Creating Experience Lab Instances 49 | 50 | ### Requirements 51 | 52 | Creating an Experience Lab instance involves the creation of an Azure AD Application with a Service Principal Identity. You must have sufficient permissions to register an application with your Azure AD tenant, the ability to create a resource group with resources, and assign roles to a managed identity. 53 | 54 | ### Via Control Plane 55 | 56 | Inside each control plane is a template spec named ```CreateLab```. Use this to deploy Experience Lab instances. You must provide a name and the default user's object ID (this can be retrieved from AAD in Azure). 57 | 58 | Deploying it will create a container instance that executes [experiencelab.sh](./experiencelab.sh). 59 | 60 | **IMPORTANT:** After the template spec completes you must go to the created container instance and complete a device login. This will allow the script to execute. 61 | 62 | #### Example 63 | 64 | ``` 65 | To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ABCDE1234 to authenticate. 66 | ``` 67 | 68 | This script will create each resource included in Experience Lab. This will take approximately 1.5 hours. Deploying Azure Data Manager for Energy takes the longest portion of time. Once this is complete you can go to the created app service and browse to your Experience Lab site. 69 | 70 | **IMPORTANT:** There will be another set of commands to run at the end to enable a redirect URI to your Experience Lab site. 71 | 72 | Example: 73 | 74 | ``` 75 | az account set --subscription 6bea78f7-e27e-ccd1-bab8-bec000bc145f 76 | az rest --method PATCH --uri 'https://graph.microsoft.com/v1.0/applications/fbf37b91-80f9-4b2e-a941-f0bb171add68' --headers 'Content-Type=application/json' --body '{"spa":{"redirectUris":["https://experiencelab10880.azurewebsites.net/"]}}' 77 | ``` 78 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/user-edit/user-edit.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 5 | import { FormGroup, FormControl, Validators } from '@angular/forms'; 6 | import { DropDownOption } from 'src/app/models/drop-down-option'; 7 | import { UserGroup } from 'src/app/models/user-group.enum'; 8 | import { UserManagementService } from '../services/user-management.service' 9 | import { ProcessStatus } from '../models/user-management-status.enum'; 10 | import { UserProfile } from 'src/app/models/user-profile'; 11 | 12 | @Component({ 13 | selector: 'app-user-edit', 14 | templateUrl: './user-edit.component.html', 15 | styleUrls: ['./user-edit.component.css'] 16 | }) 17 | export class UserEditComponent implements OnInit { 18 | @Input() public userProfile: UserProfile; 19 | @Output() public closedEditUser: EventEmitter = new EventEmitter(); 20 | 21 | public editUserForm = new FormGroup({ 22 | objectId: new FormControl({value: '', disabled: true}, [Validators.required]), 23 | group: new FormControl([Validators.required]) 24 | }); 25 | public eUserEditStatus = ProcessStatus; 26 | public editStatus: ProcessStatus; 27 | public editUserResultMessage: String; 28 | public groups: DropDownOption[]; 29 | 30 | constructor(private userManagementService: UserManagementService) { } 31 | 32 | ngOnInit(): void { 33 | this.editUserForm.controls.objectId.setValue(this.userProfile.id); 34 | this.editUserForm.controls.group.setValue(this.userProfile.groups[0]); 35 | this.groups = [ 36 | { value: UserGroup.Viewer, displayValue: "Reader" }, 37 | { value: UserGroup.Editor, displayValue: "Contributor" }, 38 | { value: UserGroup.Admin, displayValue: "Admin" }, 39 | { value: UserGroup.Ops, displayValue: "Owner" } 40 | ]; 41 | } 42 | 43 | /** 44 | * Handler for the close edit user button 45 | */ 46 | public closeEditUser() { 47 | this.closedEditUser.emit(); 48 | } 49 | 50 | /** 51 | * Removes all groups from the user, regardless if they have the group, and assigns the desired one. 52 | * To avoid a race condition we wait for the removal of the group that the user is trying to assign before assigning it. 53 | */ 54 | public editUser() { 55 | this.editStatus = ProcessStatus.InProgress 56 | const groupsToRemove = new Set(Object.values(UserGroup)); 57 | groupsToRemove.delete(this.editUserForm.value.group); 58 | groupsToRemove.delete(UserGroup.Users); 59 | 60 | this.userManagementService.removeGroup(this.editUserForm.getRawValue().objectId, this.editUserForm.value.group).subscribe({ 61 | error: (_) =>{ 62 | this.assignGroup(this.editUserForm.getRawValue().objectId, this.editUserForm.value.group); 63 | }, 64 | complete: () => { 65 | this.assignGroup(this.editUserForm.getRawValue().objectId, this.editUserForm.value.group); 66 | 67 | } 68 | }) 69 | 70 | groupsToRemove.forEach(group => { 71 | this.userManagementService.removeGroup(this.editUserForm.getRawValue().objectId, group).subscribe(); 72 | }); 73 | } 74 | 75 | /** 76 | * Assigns a group to the user 77 | */ 78 | private assignGroup(oid: String, group: String) { 79 | this.userManagementService.assignUserToGroup(oid, group).subscribe({ 80 | error: (_) => { 81 | this.editStatus = ProcessStatus.Failed; 82 | this.editUserResultMessage = "Failed to update group"; 83 | }, 84 | complete: () => { 85 | this.editStatus = ProcessStatus.Succeeded 86 | this.editUserResultMessage = "Updated group" 87 | this.userProfile.groups = [group as UserGroup]; 88 | } 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /developer-portal/src/app/profile/profile.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |

Name: {{this.profile?.name}}

6 |

Email: {{this.profile?.email}}

7 |

Id: {{this.profile?.id}}

8 |
9 | Access Token 10 | 18 | 19 |

{{this.tokens?.access}}

20 |
21 |
22 | Refresh Token 23 | 31 | 32 |

{{this.tokens?.refresh}}

33 |
34 |

Assigned Groups

35 |
36 | 37 | 38 | 39 |
40 |
41 | 42 |
43 | Unable to get user's groups 44 |
45 |
46 |
47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
Group {{group.name}} Description {{group.description}}
No data matching the filter "{{input.value}}"
63 |
64 | 65 |
66 |
67 | -------------------------------------------------------------------------------- /developer-portal/src/app/user-management/group-assignment/group-assignment.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpErrorResponse } from '@angular/common/http'; 5 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 6 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 7 | import { EntitlementGroup } from '../models/get-groups-response'; 8 | import { ProcessStatus } from '../models/user-management-status.enum'; 9 | import { GroupAssignmentEventService } from '../services/assignment-event.service'; 10 | import { UserManagementService } from '../services/user-management.service'; 11 | import { GroupAssignmentAction } from './group-assignment-action.enum'; 12 | 13 | @Component({ 14 | selector: 'app-group-assignment', 15 | templateUrl: './group-assignment.component.html', 16 | styleUrls: ['./group-assignment.component.css'] 17 | }) 18 | export class GroupAssignmentComponent { 19 | @Output() public closedGroupAssignment: EventEmitter = new EventEmitter(); 20 | @Output() public groupAssignmentCompleted: EventEmitter = new EventEmitter(); 21 | 22 | @Input() public groupAssignmentAction: GroupAssignmentAction; 23 | @Input() public group: EntitlementGroup; 24 | 25 | public groupAssignmentStatus: ProcessStatus; 26 | public eProcessStatus = ProcessStatus; 27 | public groupAssignmentForm = new FormGroup({ 28 | objectId: new FormControl('', [Validators.required]) 29 | }); 30 | public groupAssignmentResultMessage: string; 31 | 32 | constructor(private userManagementService: UserManagementService, private eventService: GroupAssignmentEventService) { } 33 | 34 | public get isGroupAddition(): boolean{ 35 | return this.groupAssignmentAction === GroupAssignmentAction.Addition; 36 | } 37 | 38 | public get isGroupRemoval(): boolean{ 39 | return this.groupAssignmentAction === GroupAssignmentAction.Removal; 40 | } 41 | 42 | public closeGroupAssignment() { 43 | this.closedGroupAssignment.emit(); 44 | } 45 | 46 | public enactGroupAssignment() { 47 | this.groupAssignmentStatus = ProcessStatus.InProgress; 48 | 49 | if (this.groupAssignmentAction === GroupAssignmentAction.Addition) { 50 | this.userManagementService.assignUserToGroup(this.groupAssignmentForm.value.objectId, this.group.name).subscribe({ 51 | error: (assignmentErrorResponse) => { 52 | this.groupAssignmentStatus = ProcessStatus.Failed; 53 | this.groupAssignmentResultMessage = "Failed to assign user" 54 | if (assignmentErrorResponse instanceof HttpErrorResponse && assignmentErrorResponse.status === 409) { 55 | this.groupAssignmentResultMessage = "User already assigned to group"; 56 | } 57 | }, 58 | complete: () => { 59 | this.groupAssignmentStatus = ProcessStatus.Succeeded; 60 | this.groupAssignmentResultMessage = "Assigned user to group" 61 | this.groupAssignmentCompleted.emit(); 62 | this.eventService.emitEvent(this.groupAssignmentAction); 63 | } 64 | }); 65 | } else if (this.groupAssignmentAction === GroupAssignmentAction.Removal) { 66 | this.userManagementService.removeUserFromGroup(this.groupAssignmentForm.value.objectId, this.group.name).subscribe({ 67 | error: (_) => { 68 | this.groupAssignmentStatus = ProcessStatus.Failed; 69 | this.groupAssignmentResultMessage = "Failed to remove user" 70 | }, 71 | complete: () => { 72 | this.groupAssignmentStatus = ProcessStatus.Succeeded; 73 | this.groupAssignmentResultMessage = "Removed user from group" 74 | this.groupAssignmentCompleted.emit(); 75 | this.eventService.emitEvent(this.groupAssignmentAction); 76 | } 77 | }); 78 | } else { 79 | this.groupAssignmentStatus = ProcessStatus.Failed; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /data-load/open-test-data/downloadTnoData.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) Microsoft Corporation. 3 | # Licensed under the MIT License. 4 | 5 | # Check if az is installed, if not exit the script out. 6 | var='az' 7 | if ! which $var &>/dev/null; then 8 | echo "This script will not run until Azure CLI is installed and you have been are logged in." 9 | exit 1 10 | fi 11 | 12 | az=$(which az) 13 | 14 | OPEN_TEST_DATA_SHARE_NAME="open-test-data" 15 | 16 | function GetOpenTestDataSetAndUploadToFileShare() { 17 | echo " Removing Existing Share" 18 | az storage share delete --name $OPEN_TEST_DATA_SHARE_NAME --account-name $CONTROL_PLANE_STORAGE --account-key $STORAGE_ACCOUNT_KEY --only-show-errors --output none 19 | 20 | OPEN_TEST_DATA_ARCHIVE_FILE_NAME=open-test-data.gz 21 | TEMP_DATA_DIR="osduTnoData" 22 | 23 | echo " Downloading M12 Open Test Data (TNO) from OSDU" 24 | wget --quiet -O $OPEN_TEST_DATA_ARCHIVE_FILE_NAME https://community.opengroup.org/osdu/platform/data-flow/data-loading/open-test-data/-/archive/v0.17.0/open-test-data-v0.17.0.tar.gz 25 | 26 | echo " Creating Open Test Data (TNO) directory structure" 27 | mkdir -p $TEMP_DATA_DIR/datasets/documents 28 | mkdir -p $TEMP_DATA_DIR/datasets/markers 29 | mkdir -p $TEMP_DATA_DIR/datasets/trajectories 30 | mkdir -p $TEMP_DATA_DIR/datasets/well-logs 31 | mkdir -p $TEMP_DATA_DIR/schema 32 | mkdir -p $TEMP_DATA_DIR/templates 33 | mkdir -p $TEMP_DATA_DIR/TNO/contrib 34 | mkdir -p $TEMP_DATA_DIR/TNO/provided 35 | 36 | echo " Extracting Dataset Documents" 37 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/datasets/documents --strip-components=5 open-test-data-v0.17.0/rc--1.0.0/1-data/3-provided/USGS_docs 38 | 39 | echo " Extracting Dataset Markers" 40 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/datasets/markers --strip-components=5 open-test-data-v0.17.0/rc--1.0.0/1-data/3-provided/markers 41 | 42 | echo " Extracting Dataset Trajectories" 43 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/datasets/trajectories --strip-components=5 open-test-data-v0.17.0/rc--1.0.0/1-data/3-provided/trajectories 44 | 45 | echo " Extracting Dataset Well Logs" 46 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/datasets/well-logs --strip-components=5 open-test-data-v0.17.0/rc--1.0.0/1-data/3-provided/well-logs 47 | 48 | echo " Extracting Schemas" 49 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/schema --strip-components=3 open-test-data-v0.17.0/rc--3.0.0/3-schema 50 | 51 | echo " Extracting Templates" 52 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/templates --strip-components=3 open-test-data-v0.17.0/rc--3.0.0/5-templates 53 | 54 | echo " Extracting TNO Contrib" 55 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/TNO/contrib --strip-components=5 open-test-data-v0.17.0/rc--3.0.0/1-data/3-provided/TNO 56 | 57 | echo " Extracting TNO Provided" 58 | tar -xzf $OPEN_TEST_DATA_ARCHIVE_FILE_NAME -C $TEMP_DATA_DIR/TNO/provided --strip-components=3 open-test-data-v0.17.0/rc--3.0.0/4-instances/TNO 59 | 60 | echo " Creating Share" 61 | az storage share create --name $OPEN_TEST_DATA_SHARE_NAME --account-name $CONTROL_PLANE_STORAGE --account-key $STORAGE_ACCOUNT_KEY --only-show-errors --output none 62 | 63 | echo " Uploading Open Test Data (TNO) to Azure Storage File Share" 64 | $az storage file upload-batch --account-name $CONTROL_PLANE_STORAGE --account-key $STORAGE_ACCOUNT_KEY --destination $OPEN_TEST_DATA_SHARE_NAME --source $TEMP_DATA_DIR --only-show-errors --output none --no-progress 65 | echo " Finished Uploading Open Test Data (TNO) to Azure Storage File Share" 66 | 67 | rm -rf $TEMP_DATA_DIR 68 | rm -f $OPEN_TEST_DATA_ARCHIVE_FILE_NAME 69 | } 70 | 71 | echo "Downloading Community Open Test Data (TNO) Data Set and Uploading to Azure Storage File Share" 72 | GetOpenTestDataSetAndUploadToFileShare -------------------------------------------------------------------------------- /developer-portal/src/app/rest/rest.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |

Overview

5 |

6 | This component assists in testing Microsoft Azure Data Manager for Energy REST APIs to execute underlying service API calls or 7 | perform functional flows. 8 |

9 | 10 |
11 |

Getting Started

12 |

There are many different ways that REST APIs can be called from the use of simple curl commands to complex Postman 13 | projects. For this lab, a Rest Client will be used 15 | for a simple way to send a request. This extension allows for simple http scripts to be executed and leverages 16 | Visual Studio Settings to be able to easily point to different environments.

17 |
    18 |
  1. Install Visual Studio Code
  2. 19 |
  3. Download and install REST Client for Visual 21 | Studio Code
  4. 22 |
  5. Download, extract, and open REST 23 | Scripts in Visual Studio Code
  6. 24 |
  7. In Visual Studio Code please open the Preferences/Settings and directly edit the settings.json file. This file 25 | can also be opened using the VSCode Command Palette and searching for: `Preferences: Open Settings (JSON)`. 26 |
  8. 27 |
  9. Paste the following JSON into the settings.json file and save it which allows for the use of the REST Client 28 | Environments.
  10. 29 | 30 |
    31 |
    
    32 |         "rest-client.environmentVariables": {
    33 |             "{{this.instanceName}}": {
    34 |                 "TENANT_ID": "{{this.tenantId}}",
    35 |                 "CLIENT_ID": "{{this.clientId}}",
    36 |                 "HOST": "{{this.host}}",
    37 |                 "DATA_PARTITION": "{{this.dataPartition}}",
    38 |                 "DEVELOPER_PORTAL": "{{this.developerPortalUrl}}",
    39 |                 "REFRESH_TOKEN": "{{this.refreshToken}}"
    40 |             }
    41 |         }
    42 |       
    43 |
    44 |
  11. Open the VSCode Command Pallette and choose `Rest Client: Switch Environment`, and select 45 | {{this.instanceName}}.
  12. 46 |
47 |
48 | 49 |
50 |

Perform a Core Functionality Validation Test.

51 |

Basic core functionality for includes: the creation of a legal tag, the submission of a storage record, 52 | and then the search and retrieval for that record.

53 | 54 |

Execute Send Requests

55 |

Send Requests from rest/check-core.http

56 |
57 | 58 |
59 |

Perform a Manifest Ingestion

60 |

Basic manifest ingestion functionality includes: the creation of a legal tag, triggering a workflow, 61 | retrieving the status, waiting for completion, and then querying for the data uploaded.

62 |

Execute Send Requests

63 |

Send Requests from rest/check-csv.http

64 |
65 | 66 |
67 |

Perform a CSV Ingestion

68 |

Basic csv data ingestion functionality includes: the creation of a legal tag, creation of a schema, 69 | retrieval of an Upload URL, uploading a CSV file to that URL, triggering the workflow, retrieving the 70 | status, waiting for completion, and then querying the data uploaded.

71 |

Execute Send Requests

72 |

Send Requests from rest/check-csv.http

73 |
74 | -------------------------------------------------------------------------------- /developer-portal/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "developer-portal": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/developer-portal", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css", 27 | "src/theme.scss", 28 | "node_modules/swagger-ui-dist/swagger-ui.css" 29 | ], 30 | "scripts": [ 31 | "node_modules/swagger-ui-dist/swagger-ui-bundle.js", 32 | "node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js" 33 | ] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "budgets": [ 38 | { 39 | "type": "initial", 40 | "maximumWarning": "2mb", 41 | "maximumError": "5mb" 42 | }, 43 | { 44 | "type": "anyComponentStyle", 45 | "maximumWarning": "6kb", 46 | "maximumError": "10kb" 47 | } 48 | ], 49 | "outputHashing": "all" 50 | }, 51 | "development": { 52 | "buildOptimizer": false, 53 | "optimization": false, 54 | "vendorChunk": true, 55 | "extractLicenses": false, 56 | "sourceMap": true, 57 | "namedChunks": true 58 | } 59 | }, 60 | "defaultConfiguration": "production" 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "configurations": { 65 | "production": { 66 | "browserTarget": "developer-portal:build:production" 67 | }, 68 | "development": { 69 | "browserTarget": "developer-portal:build:development" 70 | } 71 | }, 72 | "defaultConfiguration": "development" 73 | }, 74 | "extract-i18n": { 75 | "builder": "@angular-devkit/build-angular:extract-i18n", 76 | "options": { 77 | "browserTarget": "developer-portal:build" 78 | } 79 | }, 80 | "test": { 81 | "builder": "@angular-devkit/build-angular:karma", 82 | "options": { 83 | "main": "src/test.ts", 84 | "polyfills": "src/polyfills.ts", 85 | "tsConfig": "tsconfig.spec.json", 86 | "karmaConfig": "karma.conf.js", 87 | "assets": [ 88 | "src/favicon.ico", 89 | "src/assets" 90 | ], 91 | "styles": [ 92 | "src/styles.css", 93 | "src/theme.scss" 94 | ], 95 | "scripts": [] 96 | } 97 | }, 98 | "lint": { 99 | "builder": "@angular-eslint/builder:lint", 100 | "options": { 101 | "lintFilePatterns": [ 102 | "src/**/*.ts", 103 | "src/**/*.html" 104 | ] 105 | } 106 | } 107 | } 108 | } 109 | }, 110 | "defaultProject": "developer-portal", 111 | "cli": { 112 | "defaultCollection": "@angular-eslint/schematics" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /rest-scripts/services/schema.http: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # -------HTTP REST CLIENT ------- 5 | # https://marketplace.visualstudio.com/items?itemName=humao.rest-client 6 | # 7 | # Purpose: Sample requests for Schema Service 8 | # WARNING: Schemas can not be deleted 9 | 10 | # ----------------------- 11 | # OAUTH (Variables) 12 | # ----------------------- 13 | ### 14 | @login_base = login.microsoftonline.com/{{TENANT_ID}} 15 | @oauth_token_host = {{login_base}}/oauth2/v2.0/token 16 | @scopes = {{CLIENT_ID}}/.default openid profile offline_access 17 | 18 | 19 | # ----------------------- 20 | # OAUTH refresh_token 21 | # ----------------------- 22 | ### 23 | # @name refresh 24 | POST https://{{oauth_token_host}} HTTP/1.1 25 | Content-Type: application/x-www-form-urlencoded 26 | Origin: {{DEVELOPER_PORTAL}} 27 | 28 | grant_type=refresh_token 29 | &client_id={{CLIENT_ID}} 30 | &refresh_token={{REFRESH_TOKEN}} 31 | &scope={{scopes}} 32 | 33 | 34 | # ----------------------- 35 | # API (Variables) 36 | # ----------------------- 37 | ### 38 | @access_token = {{refresh.response.body.access_token}} 39 | @ENDPOINT = https://{{HOST}} 40 | @SCHEMA_HOST = {{ENDPOINT}}/api/schema-service/v1 41 | 42 | 43 | # ----------------------- 44 | # API: Version 45 | # ----------------------- 46 | 47 | ### 48 | # @name info 49 | GET {{SCHEMA_HOST}}/info 50 | Authorization: Bearer {{access_token}} 51 | Accept: application/json 52 | 53 | 54 | # ----------------------- 55 | # API: schema 56 | # ----------------------- 57 | 58 | ### 59 | # @name getSchemaInfoList 60 | GET {{SCHEMA_HOST}}/schema 61 | Authorization: Bearer {{access_token}} 62 | Accept: application/json 63 | data-partition-id: {{DATA_PARTITION}} 64 | 65 | 66 | ### 67 | # @name createSchema 68 | POST {{SCHEMA_HOST}}/schema 69 | Authorization: Bearer {{access_token}} 70 | Content-Type: application/json 71 | data-partition-id: {{DATA_PARTITION}} 72 | 73 | { 74 | "schemaInfo": { 75 | "schemaIdentity": { 76 | "authority": "lab", 77 | "source": "test", 78 | "entityType": "testSchema", 79 | "schemaVersionMajor": 0, 80 | "schemaVersionMinor": 0, 81 | "schemaVersionPatch": 1, 82 | "id": "lab:test:testSchema:0.0.1" 83 | }, 84 | "status": "DEVELOPMENT", 85 | "scope": "INTERNAL", 86 | "createdBy": "John Smith", 87 | "dateCreated": "2022-03-31T11:16:03Z" 88 | }, 89 | "schema": { 90 | "ValidationString": "Initial Creation", 91 | "newfield": "something", 92 | "definitions": {} 93 | } 94 | } 95 | 96 | 97 | ### 98 | # @name getSchema 99 | GET {{SCHEMA_HOST}}/schema/lab:test:testSchema:0.0.1 100 | Authorization: Bearer {{access_token}} 101 | Accept: application/json 102 | data-partition-id: {{DATA_PARTITION}} 103 | 104 | 105 | ### 106 | # @name updateSchema 107 | PUT {{SCHEMA_HOST}}/schema 108 | Authorization: Bearer {{access_token}} 109 | Content-Type: application/json 110 | data-partition-id: {{DATA_PARTITION}} 111 | 112 | { 113 | "schemaInfo": { 114 | "schemaIdentity": { 115 | "authority": "lab", 116 | "source": "test", 117 | "entityType": "testSchema", 118 | "schemaVersionMajor": 0, 119 | "schemaVersionMinor": 1, 120 | "schemaVersionPatch": 0, 121 | "id": "lab:test:testSchema:0.1.0" 122 | }, 123 | "status": "DEVELOPMENT", 124 | "scope": "INTERNAL", 125 | "createdBy": "John Smith", 126 | "dateCreated": "2022-03-31T11:16:03Z" 127 | }, 128 | "schema": { 129 | "ValidationString": "Updated Schema", 130 | "newfield": "something", 131 | "anotherfield": "else", 132 | "definitions": {} 133 | } 134 | } 135 | 136 | 137 | ### 138 | # @name getUpdatedSchema 139 | GET {{SCHEMA_HOST}}/schema/lab:test:testSchema:0.1.0 140 | Authorization: Bearer {{access_token}} 141 | Accept: application/json 142 | data-partition-id: {{DATA_PARTITION}} -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/services/legal-tag-management.service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 5 | import { Injectable } from '@angular/core'; 6 | import { map, Observable } from 'rxjs'; 7 | import { environment } from 'src/environments/environment'; 8 | import { LegalTag, LegalTagUpdateRequest } from '../models/legal-tag'; 9 | 10 | @Injectable({ 11 | providedIn: 'root' 12 | }) 13 | export class LegalTagManagementService { 14 | 15 | constructor(private http: HttpClient) { } 16 | 17 | /** 18 | * Gets a legal tag 19 | * @param name The legal tag name 20 | * @returns The legal tag 21 | */ 22 | public getLegalTag(name: string): Observable { 23 | let headers = this.getHeaders(); 24 | 25 | return this.http.get(`https://${environment.apiHost}/api/legal/v1/legaltags/${name}`, {headers: headers}) 26 | .pipe(map((response: LegalTag) => { 27 | return response; 28 | })); 29 | } 30 | 31 | /** 32 | * Adds a legal tag 33 | * @param legalTag The legal tag to add 34 | * @returns The legal tag added 35 | */ 36 | public addLegalTag(legalTag: LegalTag): Observable { 37 | let headers = this.getHeaders(); 38 | return this.http.post(`https://${environment.apiHost}/api/legal/v1/legaltags`, legalTag, {headers: headers}); 39 | } 40 | 41 | /** 42 | * Deletes a legal tag by name 43 | * @param tagNameToDelete The name of the tag to delete 44 | * @returns Nothing 45 | */ 46 | public deleteLegalTag(tagNameToDelete: string): Observable { 47 | let headers = this.getHeaders(); 48 | return this.http.delete(`https://${environment.apiHost}/api/legal/v1/legaltags/${tagNameToDelete}`, {headers: headers}); 49 | } 50 | 51 | /** 52 | * Gets all legal tags 53 | * @returns The legal tags 54 | */ 55 | public getLegalTags(): Observable { 56 | let headers = this.getHeaders(); 57 | 58 | return this.http.get(`https://${environment.apiHost}/api/legal/v1/legaltags`, {headers: headers}) 59 | .pipe(map((response: any) => { 60 | var legalTags: LegalTag[] = [] 61 | response.legalTags.forEach(legalTagResponse => { 62 | legalTags.push(this.mapLegalTagFromDto(legalTagResponse)); 63 | }); 64 | return legalTags; 65 | })); 66 | } 67 | 68 | /** 69 | * Updates a legal tag by name 70 | * @param legalTagUpdates The updates to make to a legal tag 71 | * @returns The updated legal tag 72 | */ 73 | public saveLegalTag(legalTagUpdates: LegalTagUpdateRequest): Observable { 74 | let headers = this.getHeaders(); 75 | return this.http.put(`https://${environment.apiHost}/api/legal/v1/legaltags`, legalTagUpdates, {headers: headers}); 76 | } 77 | 78 | private getHeaders(): HttpHeaders { 79 | let headers = new HttpHeaders(); 80 | headers = headers.set('data-partition-id', environment.dataPartition); 81 | 82 | return headers; 83 | } 84 | 85 | private mapLegalTagFromDto(legalTagResponse: any): LegalTag { 86 | var legalTag: LegalTag = { 87 | name: legalTagResponse.name, 88 | description: legalTagResponse.description, 89 | properties: { 90 | contractId: legalTagResponse.properties.contractId, 91 | originator: legalTagResponse.properties.originator, 92 | dataType: legalTagResponse.properties.dataType, 93 | securityClassification: legalTagResponse.properties.securityClassification, 94 | personalData: legalTagResponse.properties.personalData, 95 | exportClassification: legalTagResponse.properties.exportClassification, 96 | countryOfOrigin: legalTagResponse.properties.countryOfOrigin, 97 | expirationDate: legalTagResponse.properties.expirationDate 98 | } 99 | }; 100 | 101 | return legalTag; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /developer-portal/src/app/legal-tag-management/legal-tag-detail/legal-tag-detail.component.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Component, OnInit } from '@angular/core'; 5 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 6 | import { ActivatedRoute, Router } from '@angular/router'; 7 | import { ProcessStatus } from 'src/app/user-management/models/user-management-status.enum'; 8 | import { LegalTag } from '../models/legal-tag'; 9 | import { LegalTagManagementService } from '../services/legal-tag-management.service'; 10 | 11 | @Component({ 12 | selector: 'app-legal-tag-detail', 13 | templateUrl: './legal-tag-detail.component.html', 14 | styleUrls: ['./legal-tag-detail.component.css'] 15 | }) 16 | export class LegalTagDetailComponent implements OnInit { 17 | public selectedLegalTagName: string; 18 | public legalTag: LegalTag; 19 | public tagLoadingStatus: ProcessStatus; 20 | public tagSavingStatus: ProcessStatus; 21 | public eProcessStatus = ProcessStatus; 22 | public editForm: FormGroup; 23 | 24 | constructor( 25 | private route: ActivatedRoute, 26 | private legalTagService: LegalTagManagementService, 27 | private router: Router 28 | ) {} 29 | 30 | ngOnInit() { 31 | this.selectedLegalTagName = this.route.snapshot.paramMap.get('id'); 32 | this.tagLoadingStatus = ProcessStatus.InProgress; 33 | this.legalTagService.getLegalTag(this.selectedLegalTagName).subscribe({ 34 | error: (_) => { 35 | this.tagLoadingStatus = ProcessStatus.Failed; 36 | }, 37 | next: (legalTag) => { 38 | this.legalTag = legalTag; 39 | this.editForm = new FormGroup({ 40 | name: new FormControl({ value: this.legalTag.name, disabled: true }, [Validators.required]), 41 | description: new FormControl(this.legalTag.description, [Validators.required]), 42 | properties: new FormGroup({ 43 | countryOfOrigin: new FormControl({ value: this.legalTag.properties.countryOfOrigin.toString(), disabled: true }, [Validators.required]), 44 | contractId: new FormControl(this.legalTag.properties.contractId, [Validators.required]), 45 | dataType: new FormControl({ value: this.legalTag.properties.dataType, disabled: true }, [Validators.required]), 46 | expirationDate: new FormControl(this.legalTag.properties.expirationDate, [Validators.required]), 47 | exportClassification: new FormControl({ value: this.legalTag.properties.exportClassification, disabled: true }, [Validators.required]), 48 | originator: new FormControl({ value: this.legalTag.properties.originator, disabled: true }, [Validators.required]), 49 | personalData: new FormControl({ value: this.legalTag.properties.personalData, disabled: true }, [Validators.required]), 50 | securityClassification: new FormControl({ value: this.legalTag.properties.securityClassification, disabled: true }, [Validators.required]) 51 | }) 52 | }); 53 | this.tagLoadingStatus = ProcessStatus.Succeeded; 54 | } 55 | }); 56 | } 57 | 58 | public getCountriesOfOrigin(): string { 59 | return this.legalTag.properties.countryOfOrigin.toString(); 60 | } 61 | 62 | public userCanEditLegalTags(): boolean { 63 | return true; 64 | } 65 | 66 | public saveLegalTag(): void { 67 | this.tagSavingStatus = ProcessStatus.InProgress; 68 | var formInput = this.editForm.value; 69 | var legalTagUpdates = { 70 | name: this.legalTag.name, 71 | contractId: formInput.properties.contractId, 72 | description: formInput.description, 73 | expirationDate: formInput.properties.expirationDate 74 | }; 75 | 76 | this.legalTagService.saveLegalTag(legalTagUpdates).subscribe({ 77 | error: (errorResponse) => { 78 | this.tagSavingStatus = ProcessStatus.Failed; 79 | }, 80 | next: (legalTagResponse: LegalTag) => { 81 | this.tagSavingStatus = ProcessStatus.Succeeded; 82 | this.router.navigate(['/legal']); 83 | } 84 | }); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experience Lab - Microsoft Azure Data Manager for Energy 2 | 3 | ## Build Status 4 | 5 | [![CI](https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/azure-data-manager-for-energy-experience-lab/actions/workflows/ci.yml) 6 | 7 | ## About 8 | 9 | Experience Lab is an automated, end-to-end deployment accelerator for Microsoft Azure Data Manager for Energy that provides easy, fast deployments with sample dataset(s) for learning, testing, demo and training purposes for customers and partners. 10 | 11 | Experience Lab makes it easy to create a Developer Tier instance of Azure Data Manager for Energy. It includes a simple web UI to perform basic management tasks like the creation of users and legal tags, and performing data load of standard, sample data sets. It also includes integration with Power BI Desktop to perform data validation and visualization of loaded data. 12 | 13 | It therefore allows even the audience that is not deeply technical to be able to create fully configured, data-loaded instances of Azure Data Manager for Energy quickly, with ease. 14 | 15 | Experience Lab is only recommended for non-production use cases. It is open source, so that our customers and partners can freely use it and extend it to their bespoke use cases, including automation of deploying their own applications with Azure Data Manager for Energy. 16 | 17 | ### Components 18 | 19 | - [Control plane](./control-plane) 20 | - [Portal](./developer-portal) 21 | - [REST Scripts](./rest-scripts) 22 | - [Swagger](./developer-portal/src/assets/swagger.yaml) 23 | - [Data load](./data-load) 24 | 25 | ## Installing and running Experience Lab 26 | 27 | ### Requirements 28 | 29 | - Experience Lab must be deployed in a region currently supported by Azure Data Manager for Energy. 30 | - The default installation script for the Experience Lab control plane requires the following privileges: 31 | - Owner, Service Administrator, Co-Administrator, or Contributor + User Access Administrator at the subscription level. 32 | - Permission to register an application with your Azure AD tenant. 33 | 34 | ### Create Control Plane Via ARM Template 35 | 36 | Use the button below to deploy the Experience Lab Control Plane to your Azure Subscription. Further instructions are in [`/control-plane`](/control-plane) 37 | 38 | [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fazure-data-manager-for-energy-experience-lab%2Fmain%2Fazuredeploy.json) 39 | 40 | ## Contributing 41 | 42 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 43 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 44 | the rights to use your contribution. For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com). 45 | 46 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 47 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 48 | provided by the bot. You will only need to do this once across all repos using our CLA. 49 | 50 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 51 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 52 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 53 | 54 | ## Trademarks 55 | 56 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 57 | trademarks or logos is subject to and must follow 58 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 59 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 60 | Any use of third-party trademarks or logos are subject to those third-party's policies. 61 | --------------------------------------------------------------------------------