diff --git a/src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts b/src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts
index f17ff3a8d1..260c836bf4 100644
--- a/src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts
+++ b/src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts
@@ -11,13 +11,15 @@
// 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.
-import { Component } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { JOB_LOG_VIEWER_TEMPLATE, JOB_LOG_VIEWER_STYLES } from './job-log-viewer.component.template';
-import { ReplicationService } from '../service/index';
+import { JobLogService } from '../service/index';
import { ErrorHandler } from '../error-handler/index';
import { toPromise } from '../utils';
+const supportSet: string[] = ["replication", "scan"];
+
@Component({
selector: 'job-log-viewer',
template: JOB_LOG_VIEWER_TEMPLATE,
@@ -25,12 +27,32 @@ import { toPromise } from '../utils';
})
export class JobLogViewerComponent {
+ _jobType: string = "replication";
+
opened: boolean = false;
log: string = '';
onGoing: boolean = true;
+ @Input()
+ get jobType(): string {
+ return this._jobType;
+ }
+ set jobType(v: string) {
+ if (supportSet.find((t: string) => t === v)) {
+ this._jobType = v;
+ }
+ }
+
+ get title(): string {
+ if(this.jobType === "scan"){
+ return "VULNERABILITY.JOB_LOG_VIEWER";
+ }
+
+ return "REPLICATION.JOB_LOG_VIEWER";
+ }
+
constructor(
- private replicationService: ReplicationService,
+ private jobLogService: JobLogService,
private errorHandler: ErrorHandler
) { }
@@ -47,7 +69,7 @@ export class JobLogViewerComponent {
load(jobId: number | string): void {
this.onGoing = true;
- toPromise(this.replicationService.getJobLog(jobId))
+ toPromise(this.jobLogService.getJobLog(this.jobType, jobId))
.then((log: string) => {
this.onGoing = false;
this.log = log;
diff --git a/src/ui_ng/lib/src/replication/replication.component.spec.ts b/src/ui_ng/lib/src/replication/replication.component.spec.ts
index 9967f2c8e4..0f9323b60b 100644
--- a/src/ui_ng/lib/src/replication/replication.component.spec.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.spec.ts
@@ -19,6 +19,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
+import { JobLogService, JobLogDefaultService } from '../service/index';
describe('Replication Component (inline template)', ()=>{
@@ -183,7 +184,8 @@ describe('Replication Component (inline template)', ()=>{
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue: config },
{ provide: ReplicationService, useClass: ReplicationDefaultService },
- { provide: EndpointService, useClass: EndpointDefaultService }
+ { provide: EndpointService, useClass: EndpointDefaultService },
+ { provide: JobLogService, useClass: JobLogDefaultService }
]
});
}));
diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts
index 3f3b0224ef..8d16be1712 100644
--- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts
+++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts
@@ -17,6 +17,7 @@ import { SystemInfoService, SystemInfoDefaultService } from '../service/system-i
import { VULNERABILITY_DIRECTIVES } from '../vulnerability-scanning/index';
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from '../push-image/index';
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
+import { JobLogViewerComponent } from '../job-log-viewer/index';
import { click } from '../utils';
@@ -101,7 +102,8 @@ describe('RepositoryComponentStackview (inline template)', () => {
FilterComponent,
VULNERABILITY_DIRECTIVES,
PUSH_IMAGE_BUTTON_DIRECTIVES,
- INLINE_ALERT_DIRECTIVES
+ INLINE_ALERT_DIRECTIVES,
+ JobLogViewerComponent
],
providers: [
ErrorHandler,
diff --git a/src/ui_ng/lib/src/service.config.ts b/src/ui_ng/lib/src/service.config.ts
index 07f3bf0091..268f466486 100644
--- a/src/ui_ng/lib/src/service.config.ts
+++ b/src/ui_ng/lib/src/service.config.ts
@@ -180,4 +180,12 @@ export interface IServiceConfig {
* @memberOf IServiceConfig
*/
configurationEndpoint?: string;
+
+ /**
+ * The base endpoint of scan job service.
+ *
+ * @type {string}
+ * @memberof IServiceConfig
+ */
+ scanJobEndpoint?: string;
}
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/service/index.ts b/src/ui_ng/lib/src/service/index.ts
index 2b10f7ebd5..98bf0cb063 100644
--- a/src/ui_ng/lib/src/service/index.ts
+++ b/src/ui_ng/lib/src/service/index.ts
@@ -7,4 +7,5 @@ export * from './repository.service';
export * from './tag.service';
export * from './RequestQueryParams';
export * from './scanning.service';
-export * from './configuration.service';
\ No newline at end of file
+export * from './configuration.service';
+export * from './job-log.service';
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/service/interface.ts b/src/ui_ng/lib/src/service/interface.ts
index 9482df0b86..17ecdf1bee 100644
--- a/src/ui_ng/lib/src/service/interface.ts
+++ b/src/ui_ng/lib/src/service/interface.ts
@@ -168,6 +168,7 @@ export interface SystemInfo {
has_ca_root?: boolean;
harbor_version?: string;
clair_vulnerability_status?: ClairDBStatus;
+ next_scan_all?: number;
}
/**
diff --git a/src/ui_ng/lib/src/service/job-log.service.spec.ts b/src/ui_ng/lib/src/service/job-log.service.spec.ts
new file mode 100644
index 0000000000..9ee59dbf5d
--- /dev/null
+++ b/src/ui_ng/lib/src/service/job-log.service.spec.ts
@@ -0,0 +1,39 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { JobLogService, JobLogDefaultService } from './job-log.service';
+import { SharedModule } from '../shared/shared.module';
+import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
+
+describe('JobLogService', () => {
+ const mockConfig: IServiceConfig = {
+ replicationJobEndpoint: "/api/jobs/replication/testing",
+ scanJobEndpoint: "/api/jobs/scan/testing"
+ };
+
+ let config: IServiceConfig;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ SharedModule
+ ],
+ providers: [
+ JobLogDefaultService,
+ {
+ provide: JobLogService,
+ useClass: JobLogDefaultService
+ }, {
+ provide: SERVICE_CONFIG,
+ useValue: mockConfig
+ }]
+ });
+
+ config = TestBed.get(SERVICE_CONFIG);
+ });
+
+ it('should be initialized', inject([JobLogDefaultService], (service: JobLogService) => {
+ expect(service).toBeTruthy();
+ expect(config.replicationJobEndpoint).toEqual("/api/jobs/replication/testing");
+ expect(config.scanJobEndpoint).toEqual("/api/jobs/scan/testing");
+ }));
+});
diff --git a/src/ui_ng/lib/src/service/job-log.service.ts b/src/ui_ng/lib/src/service/job-log.service.ts
new file mode 100644
index 0000000000..f35f253cae
--- /dev/null
+++ b/src/ui_ng/lib/src/service/job-log.service.ts
@@ -0,0 +1,84 @@
+import { Observable } from 'rxjs/Observable';
+import { RequestQueryParams } from './RequestQueryParams';
+import { ReplicationJob, ReplicationRule } from './interface';
+import { Injectable, Inject } from "@angular/core";
+import 'rxjs/add/observable/of';
+import { Http, RequestOptions } from '@angular/http';
+import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
+import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from '../utils';
+
+/**
+ * Define the service methods to handle the job log related things.
+ *
+ * @export
+ * @abstract
+ * @class JobLogService
+ */
+export abstract class JobLogService {
+
+ /**
+ * Get the log of the specified job
+ *
+ * @abstract
+ * @param {string} jobType
+ * @param {(number | string)} jobId
+ * @returns {(Observable | Promise | string)}
+ * @memberof JobLogService
+ */
+ abstract getJobLog(jobType: string, jobId: number | string): Observable | Promise | string;
+}
+
+/**
+ * Implement default service for job log service.
+ *
+ * @export
+ * @class JobLogDefaultService
+ * @extends {ReplicationService}
+ */
+@Injectable()
+export class JobLogDefaultService extends JobLogService {
+ _replicationJobBaseUrl: string;
+ _scanningJobBaseUrl: string;
+ _supportedJobTypes: string[];
+
+ constructor(
+ private http: Http,
+ @Inject(SERVICE_CONFIG) config: IServiceConfig
+ ) {
+ super();
+ this._replicationJobBaseUrl = config.replicationJobEndpoint ?
+ config.replicationJobEndpoint : '/api/jobs/replication';
+ this._scanningJobBaseUrl = config.scanJobEndpoint ? config.scanJobEndpoint : "/api/jobs/scan";
+ this._supportedJobTypes = ["replication", "scan"];
+ }
+
+ _getJobLog(logUrl: string): Observable | Promise | string {
+ return this.http.get(logUrl).toPromise()
+ .then(response => response.text())
+ .catch(error => Promise.reject(error));
+ }
+
+ _isSupportedJobType(jobType: string): boolean {
+ if (this._supportedJobTypes.find((t: string) => t === jobType)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public getJobLog(jobType: string, jobId: number | string): Observable | Promise | string {
+ if (!this._isSupportedJobType(jobType)) {
+ return Promise.reject("Unsupport job type: " + jobType);
+ }
+ if (!jobId || jobId <= 0) {
+ return Promise.reject('Bad argument');
+ }
+
+ let logUrl: string = `${this._replicationJobBaseUrl}/${jobId}/log`;
+ if (jobType === "scan") {
+ logUrl = `${this._scanningJobBaseUrl}/${jobId}/log`;
+ }
+
+ return this._getJobLog(logUrl);
+ }
+}
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/tag/tag.component.css.ts b/src/ui_ng/lib/src/tag/tag.component.css.ts
index 32d854d01e..80335b50b0 100644
--- a/src/ui_ng/lib/src/tag/tag.component.css.ts
+++ b/src/ui_ng/lib/src/tag/tag.component.css.ts
@@ -14,7 +14,6 @@ export const TAG_STYLE = `
:host >>> .datagrid {
margin: 0;
- border-bottom: none;
}
:host >>> .datagrid-placeholder {
diff --git a/src/ui_ng/lib/src/tag/tag.component.spec.ts b/src/ui_ng/lib/src/tag/tag.component.spec.ts
index d63279c5a6..2f14b48245 100644
--- a/src/ui_ng/lib/src/tag/tag.component.spec.ts
+++ b/src/ui_ng/lib/src/tag/tag.component.spec.ts
@@ -17,6 +17,8 @@ import { FILTER_DIRECTIVES } from '../filter/index'
import { Observable, Subscription } from 'rxjs/Rx';
import { ChannelService } from '../channel/index';
+import { JobLogViewerComponent } from '../job-log-viewer/index';
+
describe('TagComponent (inline template)', () => {
let comp: TagComponent;
@@ -49,7 +51,8 @@ describe('TagComponent (inline template)', () => {
TagComponent,
ConfirmationDialogComponent,
VULNERABILITY_DIRECTIVES,
- FILTER_DIRECTIVES
+ FILTER_DIRECTIVES,
+ JobLogViewerComponent
],
providers: [
ErrorHandler,
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts
index f796f628ff..7af25b3354 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts
@@ -8,13 +8,16 @@ import {
ScanningResultService,
ScanningResultDefaultService,
TagService,
- TagDefaultService
+ TagDefaultService,
+ JobLogService,
+ JobLogDefaultService
} from '../service/index';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { ErrorHandler } from '../error-handler/index';
import { SharedModule } from '../shared/shared.module';
import { VULNERABILITY_SCAN_STATUS } from '../utils';
import { ChannelService } from '../channel/index';
+import { JobLogViewerComponent } from '../job-log-viewer/index';
describe('ResultBarChartComponent (inline template)', () => {
let component: ResultBarChartComponent;
@@ -52,13 +55,15 @@ describe('ResultBarChartComponent (inline template)', () => {
],
declarations: [
ResultBarChartComponent,
- ResultTipComponent],
+ ResultTipComponent,
+ JobLogViewerComponent],
providers: [
ErrorHandler,
ChannelService,
{ provide: SERVICE_CONFIG, useValue: testConfig },
{ provide: TagService, useValue: TagDefaultService },
- { provide: ScanningResultService, useValue: ScanningResultDefaultService }
+ { provide: ScanningResultService, useValue: ScanningResultDefaultService },
+ { provide: JobLogService, useValue: JobLogDefaultService}
]
});
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts
index 479c42eff1..b51254a311 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts
@@ -4,7 +4,8 @@ import {
OnInit,
OnDestroy,
ChangeDetectionStrategy,
- ChangeDetectorRef
+ ChangeDetectorRef,
+ ViewChild
} from '@angular/core';
import { SCANNING_STYLES } from './scanning.css';
@@ -21,6 +22,7 @@ import { ErrorHandler } from '../error-handler/index';
import { toPromise } from '../utils';
import { Observable, Subscription } from 'rxjs/Rx';
import { ChannelService } from '../channel/index';
+import { JobLogViewerComponent } from '../job-log-viewer/index';
const STATE_CHECK_INTERVAL: number = 2000;//2s
const RETRY_TIMES: number = 3;
@@ -39,6 +41,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
stateCheckTimer: Subscription;
timerHandler: any;
+ @ViewChild("scanningLogViewer")
+ scanningJobLogViewer: JobLogViewerComponent;
+
constructor(
private tagService: TagService,
private scanningService: ScanningResultService,
@@ -188,4 +193,11 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
}
}, duration);
}
+
+ //Check error log
+ viewLog(): void {
+ if (this.summary && this.summary.job_id) {
+ this.scanningJobLogViewer.open(this.summary.job_id);
+ }
+ }
}
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts b/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
index d9a280c227..5b4f9422a7 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
@@ -20,6 +20,7 @@ export const SCANNING_STYLES: string = `
position: relative;
top: 1px;
margin-left: -5px;
+ cursor: pointer;
}
.scanning-button {
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
index 1f607279b8..aace726df6 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
@@ -96,8 +96,10 @@ export const BAR_CHART_COMPONENT_HTML: string = `
{{'VULNERABILITY.STATE.QUEUED' | translate}}
{{'VULNERABILITY.STATE.SCANNING' | translate}}
@@ -110,5 +112,6 @@ export const BAR_CHART_COMPONENT_HTML: string = `
{{'VULNERABILITY.STATE.UNKNOWN' | translate}}
+
`;
\ No newline at end of file
diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json
index 0dab15a1b0..d64f0a6f06 100644
--- a/src/ui_ng/package.json
+++ b/src/ui_ng/package.json
@@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8",
"core-js": "^2.4.1",
- "harbor-ui": "0.3.24",
+ "harbor-ui": "0.3.42",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",
diff --git a/src/ui_ng/src/app/app-config.ts b/src/ui_ng/src/app/app-config.ts
index ce5ac5167f..0702cba128 100644
--- a/src/ui_ng/src/app/app-config.ts
+++ b/src/ui_ng/src/app/app-config.ts
@@ -30,6 +30,7 @@ export class AppConfig {
overall_last_update: 0,
details: []
};
+ this.next_scan_all = 0;
}
with_notary: boolean;
@@ -43,4 +44,5 @@ export class AppConfig {
has_ca_root: boolean;
harbor_version: string;
clair_vulnerability_status?: ClairDBStatus;
+ next_scan_all: number;
}
\ No newline at end of file
diff --git a/src/ui_ng/src/app/config/config.component.html b/src/ui_ng/src/app/config/config.component.html
index 8895e863dc..cdc9f58c41 100644
--- a/src/ui_ng/src/app/config/config.component.html
+++ b/src/ui_ng/src/app/config/config.component.html
@@ -32,7 +32,7 @@