diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 6bdadc3945..edda54340f 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -36,8 +36,9 @@ jobs: - name: Install dependencies run: | pip install --upgrade pip - pip install ./submarine-sdk/pysubmarine/. - pip install -q tensorflow==${{ matrix.tf-version }} + pip install --no-cache-dir tensorflow==${{ matrix.tf-version }} + pip install --no-cache-dir torch==1.5.0 + pip install --no-cache-dir ./submarine-sdk/pysubmarine/. pip install -r ./submarine-sdk/pysubmarine/github-actions/test-requirements.txt pip install -r ./submarine-sdk/pysubmarine/github-actions/lint-requirements.txt - name: Check python sdk code style @@ -47,5 +48,3 @@ jobs: - name: Test with pytest run: | pytest --cov=submarine -vs - - diff --git a/submarine-sdk/pysubmarine/setup.py b/submarine-sdk/pysubmarine/setup.py index 35b036e88b..b339533187 100644 --- a/submarine-sdk/pysubmarine/setup.py +++ b/submarine-sdk/pysubmarine/setup.py @@ -39,7 +39,7 @@ 'certifi >= 14.05.14', 'python-dateutil >= 2.5.3', 'pyarrow==0.17.0', - 'torch>=1.4.0', + 'torch>=1.5.0', ], classifiers=[ 'Intended Audience :: Developers', diff --git a/submarine-sdk/pysubmarine/submarine/tracking/utils.py b/submarine-sdk/pysubmarine/submarine/tracking/utils.py index ce19d30451..95a3a285bd 100644 --- a/submarine-sdk/pysubmarine/submarine/tracking/utils.py +++ b/submarine-sdk/pysubmarine/submarine/tracking/utils.py @@ -29,10 +29,11 @@ _TF_CONFIG = "TF_CONFIG" _CLUSTER_SPEC = "CLUSTER_SPEC" -_TASK_INDEX = "TASK_INDEX" _JOB_NAME = "JOB_NAME" +_TYPE = "type" +_TASK = "task" +_INDEX = "index" _RANK = "RANK" -_TASK = "TASK" # Extra environment variables which take precedence for setting the basic/bearer # auth on http requests. @@ -98,14 +99,14 @@ def get_worker_index(): if env.get_env(_TF_CONFIG) is not None: tf_config = json.loads(os.environ.get(_TF_CONFIG)) task_config = tf_config.get(_TASK) - task_type = task_config.get(_JOB_NAME) - task_index = task_config.get(_TASK_INDEX) + task_type = task_config.get(_TYPE) + task_index = task_config.get(_INDEX) worker_index = task_type + '-' + str(task_index) elif env.get_env(_CLUSTER_SPEC) is not None: cluster_spec = json.loads(os.environ.get(_CLUSTER_SPEC)) task_config = cluster_spec.get(_TASK) task_type = task_config.get(_JOB_NAME) - task_index = task_config.get(_TASK_INDEX) + task_index = task_config.get(_INDEX) worker_index = task_type + '-' + str(task_index) # Get PyTorch worker index elif env.get_env(_RANK) is not None: diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java index 346f77c8a1..28950ce856 100644 --- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java +++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java @@ -28,6 +28,7 @@ import javax.ws.rs.core.Response.Status; +import org.apache.submarine.commons.utils.SubmarineConfiguration; import org.apache.submarine.commons.utils.exception.SubmarineRuntimeException; import org.apache.submarine.server.SubmarineServer; import org.apache.submarine.server.SubmitterManager; @@ -36,6 +37,7 @@ import org.apache.submarine.server.api.Submitter; import org.apache.submarine.server.api.experiment.ExperimentLog; import org.apache.submarine.server.api.spec.ExperimentSpec; +import org.apache.submarine.server.rest.RestConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,13 +87,33 @@ private ExperimentManager(Submitter submitter) { */ public Experiment createExperiment(ExperimentSpec spec) throws SubmarineRuntimeException { checkSpec(spec); + + // Submarine sdk will get experimentID and JDBC URL from environment variables in each worker, + // and then log experiment metrics and parameters to submarine server + ExperimentId id = generateExperimentId(); + String url = getSQLAlchemyURL(); + spec.getMeta().getEnvVars().put(RestConstants.JOB_ID, id.toString()); + spec.getMeta().getEnvVars().put(RestConstants.SUBMARINE_TRACKING_URI, url); + Experiment experiment = submitter.createExperiment(spec); - experiment.setExperimentId(generateExperimentId()); + experiment.setExperimentId(id); + + spec.getMeta().getEnvVars().remove(RestConstants.JOB_ID); + spec.getMeta().getEnvVars().remove(RestConstants.SUBMARINE_TRACKING_URI); experiment.setSpec(spec); cachedExperimentMap.putIfAbsent(experiment.getExperimentId().toString(), experiment); return experiment; } + private String getSQLAlchemyURL() { + SubmarineConfiguration conf = SubmarineConfiguration.getInstance(); + String jdbcUrl = conf.getJdbcUrl(); + jdbcUrl = jdbcUrl.substring(jdbcUrl.indexOf("//") + 2, jdbcUrl.indexOf("?")); + String jdbcUserName = conf.getJdbcUserName(); + String jdbcPassword = conf.getJdbcPassword(); + return "mysql+pymysql://" + jdbcUserName + ":" + jdbcPassword + "@" + jdbcUrl; + } + private ExperimentId generateExperimentId() { return ExperimentId.newInstance(SubmarineServer.getServerTimeStamp(), experimentCounter.incrementAndGet()); diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java index 96e67f5dcf..699109cab8 100644 --- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java +++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java @@ -21,22 +21,32 @@ public class RestConstants { public static final String V1 = "v1"; + public static final String EXPERIMENT = "experiment"; + public static final String ID = "id"; + + public static final String JOB_ID = "JOB_ID"; + + public static final String SUBMARINE_TRACKING_URI = "SUBMARINE_TRACKING_URI"; + public static final String PING = "ping"; + public static final String MEDIA_TYPE_YAML = "application/yaml"; + public static final String CHARSET_UTF8 = "charset=utf-8"; public static final String METASTORE = "metastore"; public static final String CLUSTER = "cluster"; + public static final String ADDRESS = "address"; public static final String NODES = "nodes"; + public static final String NODE = "node"; public static final String LOGS = "logs"; - /** * Environment */ diff --git a/submarine-workbench/workbench-web-ng/angular.json b/submarine-workbench/workbench-web-ng/angular.json index a1dab110ee..3560eb4257 100644 --- a/submarine-workbench/workbench-web-ng/angular.json +++ b/submarine-workbench/workbench-web-ng/angular.json @@ -31,9 +31,11 @@ "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/", "output": "/assets/" }, - { "glob": "**/*", - "input": "./node_modules/ngx-monaco-editor/assets/monaco", - "output": "/assets/monaco/" } + { + "glob": "**/*", + "input": "./node_modules/ngx-monaco-editor/assets/monaco", + "output": "/assets/monaco/" + } ], "styles": [ "src/theme.less", @@ -136,5 +138,8 @@ } } }, - "defaultProject": "workbench-web-ng" -} + "defaultProject": "workbench-web-ng", + "cli": { + "analytics": false + } +} \ No newline at end of file diff --git a/submarine-workbench/workbench-web-ng/package.json b/submarine-workbench/workbench-web-ng/package.json index 4c23700950..72b3a6b1a6 100644 --- a/submarine-workbench/workbench-web-ng/package.json +++ b/submarine-workbench/workbench-web-ng/package.json @@ -17,7 +17,7 @@ "@angular/animations": "~8.2.9", "@angular/common": "~8.2.9", "@angular/compiler": "~8.2.9", - "@angular/core": "~8.2.9", + "@angular/core": "^8.2.14", "@angular/forms": "~8.2.9", "@angular/platform-browser": "~8.2.9", "@angular/platform-browser-dynamic": "~8.2.9", @@ -25,7 +25,7 @@ "@swimlane/ngx-charts": "^13.0.1", "date-fns": "^2.6.0", "lint-staged": "^10.2.2", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "md5": "^2.2.1", "ng-zorro-antd": "8.1.2", "ngx-monaco-editor": "^8.0.0", diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html index 83e25e15eb..3f7cac033b 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html @@ -17,22 +17,26 @@ ~ under the License. --> -
- +
+
+ + {{ yAxisLabel }} + +
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss index 4e938821ec..f4950588eb 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss @@ -17,7 +17,8 @@ * under the License. */ - #chartsDiv { - background-color: white; - height: 500px; - } +#chartsTable { + background-color: white; + height: 90vh; + overflow: auto; +} diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts index af6ff40001..6c0828bd9d 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts @@ -17,8 +17,7 @@ * under the License. */ -import { Component, OnInit } from '@angular/core'; -import { test_data } from './data'; +import { Component, Input, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'submarine-charts', @@ -26,28 +25,24 @@ import { test_data } from './data'; styleUrls: ['./charts.component.scss'] }) export class ChartsComponent implements OnInit { - title = 'Metrics'; + @Input() workerIndex; + @Input() metricData; - data: any[]; - view: any[] = [800, 500]; - legend: boolean = true; + title = 'Metrics'; + podMetrics = {}; + view: any[] = [1000, 300]; + legend: boolean = false; showLabels: boolean = true; animations: boolean = true; xAxis: boolean = true; yAxis: boolean = true; showYAxisLabel: boolean = true; showXAxisLabel: boolean = true; - xAxisLabel: string = 'Epoch'; - yAxisLabel: string = 'Percent'; - timeline: boolean = true; - - colorScheme = { - domain: ['#5AA454', '#E44D25', '#CFC0BB', '#7aa3e5'] - }; - - constructor() { - this.data = Object.assign([], test_data); - } + xAxisLabel: string = 'Time (s)'; + yAxisLabels = []; + timeline: boolean = false; + colorScheme = ['cool', 'fire', 'flame', 'air', 'forest', 'neons', 'ocean']; + constructor() {} onSelect(data): void { console.log('Item clicked', JSON.parse(JSON.stringify(data))); @@ -62,4 +57,40 @@ export class ChartsComponent implements OnInit { } ngOnInit() {} + + ngOnChanges(chg: SimpleChanges) { + this.podMetrics = {}; + this.yAxisLabels = []; + this.fetchMetric(); + } + fetchMetric() { + if (this.metricData === undefined) { + return; + } + let key = ''; + let metrics = []; + this.metricData.forEach((data) => { + if (data.workerIndex === undefined) { + return; + } + if (this.workerIndex.indexOf(data.workerIndex) >= 0) { + if (data.key !== key && metrics.length > 0) { + this.yAxisLabels.push(key); + this.podMetrics[key] = []; + this.podMetrics[key].push({ name: key, series: metrics }); + metrics = []; + } + key = data.key; + const d = new Date(0); + d.setUTCMilliseconds(data.timestamp); + const metric = { name: d, value: data.value }; + metrics.push(metric); + } + }); + if (metrics.length > 0) { + this.yAxisLabels.push(key); + this.podMetrics[key] = []; + this.podMetrics[key].push({ name: key, series: metrics }); + } + } } diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts deleted file mode 100644 index 60e4493f4f..0000000000 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export var test_data = [ - { - name: 'loss', - series: [ - { - name: 'Epoch1', - value: 0.204 - }, - { - name: 'Epoch2', - value: 0.1799 - }, - { - name: 'Epoch3', - value: 0.1772 - } - ] - }, - - { - name: 'accuracy', - series: [ - { - name: 'Epoch1', - value: 0.9356 - }, - { - name: 'Epoch2', - value: 0.9406 - }, - { - name: 'Epoch3', - value: 0.944 - } - ] - }, - - { - name: 'val_loss', - series: [ - { - name: 'Epoch1', - value: 0.2655 - }, - { - name: 'Epoch2', - value: 0.1578 - }, - { - name: 'Epoch3', - value: 0.1471 - } - ] - }, - { - name: 'val_accuracy', - series: [ - { - name: 'Epoch1', - value: 0.907 - }, - { - name: 'Epoch2', - value: 0.945 - }, - { - name: 'Epoch3', - value: 0.959 - } - ] - } -]; diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html index 3b16e6a6b7..a076514d1d 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html @@ -103,7 +103,7 @@
- + { - if (data.workerIndex == this.workerIndex) { + if (this.workerIndex.indexOf(data.workerIndex) >= 0) { this.podParam.push(data); } }); diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss index 6ee12d18ec..38aeebab08 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss @@ -17,7 +17,8 @@ * under the License. */ - #metricTable { - background-color: white; - height: 500px; +#metricTable { + background-color: white; + height: 90vh; + overflow:auto; } diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts index 7ba3859e32..70720de704 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Component, OnInit, Input, SimpleChanges } from '@angular/core'; +import { Component, Input, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'submarine-metrics', @@ -36,7 +36,7 @@ export class MetricsComponent implements OnInit { ngOnChanges(chg: SimpleChanges) { this.podMetric.length = 0; this.metricData.forEach((data) => { - if (data.workerIndex == this.workerIndex) { + if (this.workerIndex.indexOf(data.workerIndex) >= 0) { this.podMetric.push(data); } }); diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss index b1f72a4520..2fa14b3ee6 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss @@ -21,6 +21,6 @@ background-color: whitesmoke; color: black; padding: 10px; - height:500px; + height:90vh; overflow:auto; } diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts index 69465e2427..2332200380 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Component, OnInit, Input, SimpleChanges } from '@angular/core'; +import { Component, Input, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'submarine-outputs', @@ -36,7 +36,7 @@ export class OutputsComponent implements OnInit { ngOnChanges(chg: SimpleChanges) { this.podLogArr.forEach((item) => { - if (item.podName == this.podName) { + if (item.podName === this.podName) { this.podLog = item.podLog; } }); diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts index e360ccef32..4e78f7f9e1 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts @@ -21,11 +21,10 @@ import { Component, OnInit } from '@angular/core'; import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; import { ExperimentInfo } from '@submarine/interfaces/experiment-info'; +import { ExperimentSpec, Specs, SpecEnviroment, SpecMeta } from '@submarine/interfaces/experiment-spec'; import { ExperimentService } from '@submarine/services/experiment.service'; import { ExperimentFormService } from '@submarine/services/experiment.validator.service'; import { NzMessageService } from 'ng-zorro-antd'; -import { SpecMeta, Specs, SpecEnviroment, ExperimentSpec } from '@submarine/interfaces/experiment-spec'; -import { stringify } from 'querystring'; @Component({ selector: 'submarine-experiment', @@ -342,16 +341,16 @@ export class ExperimentComponent implements OnInit { fetchExperimentList() { this.experimentService.fetchExperimentList().subscribe((list) => { this.experimentList = list; - var currentTime = new Date(); + const currentTime = new Date(); this.experimentList.forEach((item) => { - if (item.status == 'Succeeded') { - var finTime = new Date(item.finishedTime); - var runTime = new Date(item.runningTime); - var result = (finTime.getTime() - runTime.getTime()) / 1000; + if (item.status === 'Succeeded') { + const finTime = new Date(item.finishedTime); + const runTime = new Date(item.runningTime); + const result = (finTime.getTime() - runTime.getTime()) / 1000; item.duration = this.experimentService.durationHandle(result); } else { - var runTime = new Date(item.runningTime); - var result = (currentTime.getTime() - runTime.getTime()) / 1000; + const runTime = new Date(item.runningTime); + const result = (currentTime.getTime() - runTime.getTime()) / 1000; item.duration = this.experimentService.durationHandle(result); } }); @@ -407,14 +406,14 @@ export class ExperimentComponent implements OnInit { } reloadCheck() { - /* - When reload in info page, ths experimentId will turn into undifined, it will cause breadcrumb miss experimentId. + /* + When reload in info page, ths experimentId will turn into undefined, it will cause breadcrumb miss experimentId. Location.pathname -> /workbench/experiment/info/{experimentID} So slice out experimentId string from location.pathname to reassign experimentId. */ - if (location.pathname != '/workbench/experiment') { - var sliceString = new String('/workbench/experiment/info'); - this.experimentID = location.pathname.slice(sliceString.length); + if (location.pathname !== '/workbench/experiment') { + const sliceString = String('/workbench/experiment/info'); + this.experimentID = location.pathname.slice(sliceString.length + 1); } } diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts index 1df383efe8..966f647235 100644 --- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts +++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts @@ -1,14 +1,13 @@ +import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { NgZorroAntdModule } from 'ng-zorro-antd'; +import { ChartsComponent } from './experiment-info/charts/charts.component'; import { ExperimentInfoComponent } from './experiment-info/experiment-info.component'; -import { MetricsComponent } from './experiment-info/metrics/metrics.component'; import { HyperParamsComponent } from './experiment-info/hyper-params/hyper-params.component'; -import { ChartsComponent } from './experiment-info/charts/charts.component'; +import { MetricsComponent } from './experiment-info/metrics/metrics.component'; import { OutputsComponent } from './experiment-info/outputs/outputs.component'; -import { NgZorroAntdModule } from 'ng-zorro-antd'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { NgxChartsModule } from '@swimlane/ngx-charts'; /* * Licensed to the Apache Software Foundation (ASF) under one diff --git a/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts index 393eee381f..30f5735dc9 100644 --- a/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts +++ b/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts @@ -21,10 +21,10 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Rest } from '@submarine/interfaces'; import { ExperimentInfo } from '@submarine/interfaces/experiment-info'; -import { BaseApiService } from '@submarine/services/base-api.service'; -import { of, Observable, throwError } from 'rxjs'; -import { switchMap, catchError, map } from 'rxjs/operators'; import { ExperimentSpec } from '@submarine/interfaces/experiment-spec'; +import { BaseApiService } from '@submarine/services/base-api.service'; +import { of, throwError, Observable } from 'rxjs'; +import { catchError, map, switchMap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' diff --git a/submarine-workbench/workbench-web-ng/tsconfig.json b/submarine-workbench/workbench-web-ng/tsconfig.json index da63e8bba2..cc513fce36 100644 --- a/submarine-workbench/workbench-web-ng/tsconfig.json +++ b/submarine-workbench/workbench-web-ng/tsconfig.json @@ -28,5 +28,6 @@ "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true - } + }, + "moduleResolution": "node" }