diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 08fd57d0a..4c41206b0 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -546,38 +546,9 @@ paths: operationId: listAuditLogs parameters: - $ref: '#/parameters/requestId' + - $ref: '#/parameters/query' - $ref: '#/parameters/page' - $ref: '#/parameters/pageSize' - - name: username - in: query - type: string - required: false - description: Username of the operator. - - name: resource - in: query - type: string - required: false - description: The identity of resource - - name: resource_type - in: query - type: string - required: false - description: The type of resource, artifact/tag/repository - - name: operation - in: query - type: string - required: false - description: The operation, create/delete - - name: begin_timestamp - in: query - type: string - required: false - description: The begin timestamp - - name: end_timestamp - in: query - type: string - required: false - description: The end timestamp responses: '200': description: Success diff --git a/src/api/event/metadata/artifact.go b/src/api/event/metadata/artifact.go index 8f116d246..593fd1f28 100644 --- a/src/api/event/metadata/artifact.go +++ b/src/api/event/metadata/artifact.go @@ -32,14 +32,17 @@ type PushArtifactEventMetadata struct { // Resolve to the event from the metadata func (p *PushArtifactEventMetadata) Resolve(event *event.Event) error { + ae := &event2.ArtifactEvent{ + EventType: event2.TopicPushArtifact, + Repository: p.Artifact.RepositoryName, + Artifact: p.Artifact, + OccurAt: time.Now(), + } + if p.Tag != "" { + ae.Tags = []string{p.Tag} + } data := &event2.PushArtifactEvent{ - ArtifactEvent: &event2.ArtifactEvent{ - EventType: event2.TopicPushArtifact, - Repository: p.Artifact.RepositoryName, - Artifact: p.Artifact, - Tags: []string{p.Tag}, - OccurAt: time.Now(), - }, + ArtifactEvent: ae, } ctx, exist := security.FromContext(p.Ctx) if exist { @@ -59,14 +62,17 @@ type PullArtifactEventMetadata struct { // Resolve to the event from the metadata func (p *PullArtifactEventMetadata) Resolve(event *event.Event) error { + ae := &event2.ArtifactEvent{ + EventType: event2.TopicPullArtifact, + Repository: p.Artifact.RepositoryName, + Artifact: p.Artifact, + OccurAt: time.Now(), + } + if p.Tag != "" { + ae.Tags = []string{p.Tag} + } data := &event2.PullArtifactEvent{ - ArtifactEvent: &event2.ArtifactEvent{ - EventType: event2.TopicPullArtifact, - Repository: p.Artifact.RepositoryName, - Artifact: p.Artifact, - Tags: []string{p.Tag}, - OccurAt: time.Now(), - }, + ArtifactEvent: ae, } ctx, exist := security.FromContext(p.Ctx) if exist { diff --git a/src/portal/src/app/log/audit-log.component.html b/src/portal/src/app/log/audit-log.component.html index c3c176153..fe557208a 100644 --- a/src/portal/src/app/log/audit-log.component.html +++ b/src/portal/src/app/log/audit-log.component.html @@ -30,16 +30,16 @@
- + {{'AUDIT_LOG.USERNAME' | translate}} - {{'AUDIT_LOG.REPOSITORY_NAME' | translate}} - {{'AUDIT_LOG.TAGS' | translate}} + {{'AUDIT_LOG.RESOURCE' | translate}} + {{'AUDIT_LOG.RESOURCE_TYPE' | translate}} {{'AUDIT_LOG.OPERATION' | translate}} {{'AUDIT_LOG.TIMESTAMP' | translate}} {{l.username}} - {{l.repo_name}} - {{l.repo_tag}} + {{l.resource}} + {{l.resource_type}} {{l.operation}} {{l.op_time | date: 'short'}} diff --git a/src/portal/src/app/log/audit-log.component.spec.ts b/src/portal/src/app/log/audit-log.component.spec.ts index b9b8afa68..e343b894a 100644 --- a/src/portal/src/app/log/audit-log.component.spec.ts +++ b/src/portal/src/app/log/audit-log.component.spec.ts @@ -2,16 +2,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { AuditLogComponent } from './audit-log.component'; import { ClarityModule } from '@clr/angular'; -import { FormsModule } from '@angular/forms'; -import { AuditLogService } from './audit-log.service'; import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; import { ActivatedRoute, Router } from '@angular/router'; import { of } from 'rxjs'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { delay } from 'rxjs/operators'; import { HarborLibraryModule } from "../../lib/harbor-library.module"; +import { AuditLog } from "../../../ng-swagger-gen/models/audit-log"; +import { HttpHeaders, HttpResponse } from "@angular/common/http"; +import { ProjectService } from "../../../ng-swagger-gen/services/project.service"; +import { click } from "../../lib/utils/utils"; +import { FormsModule } from "@angular/forms"; describe('AuditLogComponent', () => { let component: AuditLogComponent; @@ -19,27 +22,59 @@ describe('AuditLogComponent', () => { const mockMessageHandlerService = { handleError: () => {} }; - const mockAuditLogService = { - listAuditLogs: () => { - return of({ - headers: new Map().set('x-total-count', 0), - body: [] - }).pipe(delay(0)); - }, - }; const mockActivatedRoute = { + parent: { + snapshot: { + data: null + } + }, + snapshot: { + data: null + }, data: of({ auditLogResolver: "" - }).pipe(delay(0)), - snapshot: { - parent: { - params: { - id: 1 + }).pipe(delay(0)) + }; + const mockRouter = null; + const mockedAuditLogs: AuditLog [] = []; + for (let i = 0; i < 18; i++) { + let item: AuditLog = { + id: 234 + i, + resource: "myProject/Demo" + i, + resource_type: "N/A", + operation: "create", + op_time: "2017-04-11T10:26:22Z", + username: "user91" + i + }; + mockedAuditLogs.push(item); + } + const fakedAuditlogService = { + getLogsResponse(params: ProjectService.GetLogsParams) { + if (params.q && params.q.indexOf('Demo0') !== -1) { + return of(new HttpResponse({ + body: mockedAuditLogs.slice(0, 1), + headers: new HttpHeaders({ + "x-total-count": "18" + }) + })).pipe(delay(0)); } + if (params.page <= 1) { + return of(new HttpResponse({ + body: mockedAuditLogs.slice(0, 15), + headers: new HttpHeaders({ + "x-total-count": "18" + }) + })).pipe(delay(0)); + } else { + return of(new HttpResponse({ + body: mockedAuditLogs.slice(15), + headers: new HttpHeaders({ + "x-total-count": "18" + }) + })).pipe(delay(0)); } } }; - const mockRouter = null; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -60,7 +95,7 @@ describe('AuditLogComponent', () => { TranslateService, { provide: ActivatedRoute, useValue: mockActivatedRoute }, { provide: Router, useValue: mockRouter }, - { provide: AuditLogService, useValue: mockAuditLogService }, + { provide: ProjectService, useValue: fakedAuditlogService }, { provide: MessageHandlerService, useValue: mockMessageHandlerService }, ] @@ -76,4 +111,57 @@ describe('AuditLogComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + it('should get data from AccessLogService', async(() => { + fixture.detectChanges(); + fixture.whenStable().then(() => { // wait for async getRecentLogs + fixture.detectChanges(); + expect(component.auditLogs).toBeTruthy(); + expect(component.auditLogs.length).toEqual(15); + }); + })); + + it('should render data to view', async(() => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let de: DebugElement = fixture.debugElement.query(del => del.classes['datagrid-cell']); + expect(de).toBeTruthy(); + let el: HTMLElement = de.nativeElement; + expect(el).toBeTruthy(); + expect(el.textContent.trim()).toEqual('user910'); + }); + })); + it('should support pagination', async () => { + fixture.autoDetectChanges(true); + await fixture.whenStable(); + let el: HTMLButtonElement = fixture.nativeElement.querySelector('.pagination-next'); + expect(el).toBeTruthy(); + el.click(); + fixture.detectChanges(); + await fixture.whenStable(); + expect(component.currentPage).toEqual(2); + expect(component.auditLogs.length).toEqual(3); + }); + + it('should support filtering list by keywords', async(() => { + fixture.detectChanges(); + let el: HTMLElement = fixture.nativeElement.querySelector('.search-btn'); + expect(el).toBeTruthy("Not found search icon"); + click(el); + fixture.detectChanges(); + let el2: HTMLInputElement = fixture.nativeElement.querySelector('input'); + expect(el2).toBeTruthy("Not found input"); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + component.doSearchAuditLogs("Demo0"); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.auditLogs).toBeTruthy(); + expect(component.auditLogs.length).toEqual(1); + }); + }); + })); }); diff --git a/src/portal/src/app/log/audit-log.component.ts b/src/portal/src/app/log/audit-log.component.ts index d4bb810a2..7c375bd39 100644 --- a/src/portal/src/app/log/audit-log.component.ts +++ b/src/portal/src/app/log/audit-log.component.ts @@ -11,17 +11,14 @@ // 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, OnInit, ViewChild } from '@angular/core'; -import { NgModel } from '@angular/forms'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; - -import { AuditLog } from './audit-log'; import { SessionUser } from '../shared/session-user'; - -import { AuditLogService } from './audit-log.service'; import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; - -import { State } from '../../lib/services/interface'; +import { ProjectService } from "../../../ng-swagger-gen/services/project.service"; +import { AuditLog } from "../../../ng-swagger-gen/models/audit-log"; +import { Project } from "../project/project"; +import { finalize } from "rxjs/operators"; const optionalSearch: {} = { 0: 'AUDIT_LOG.ADVANCED', 1: 'AUDIT_LOG.SIMPLE' }; @@ -55,8 +52,13 @@ export class AuditLogComponent implements OnInit { search: SearchOption = new SearchOption(); currentUser: SessionUser; projectId: number; - queryParam: AuditLog = new AuditLog(); + projectName: string; + queryUsername: string; + queryStartTime: string; + queryEndTime: string; + queryOperation: string[] = []; auditLogs: AuditLog[]; + loading: boolean = true; toggleName = optionalSearch; currentOption: number = 0; @@ -82,25 +84,60 @@ export class AuditLogComponent implements OnInit { constructor( private route: ActivatedRoute, private router: Router, - private auditLogService: AuditLogService, + private auditLogService: ProjectService, private messageHandlerService: MessageHandlerService) { // Get current user from registered resolver. this.route.data.subscribe(data => this.currentUser = data['auditLogResolver']); } ngOnInit(): void { - this.projectId = +this.route.snapshot.parent.params['id']; - this.queryParam.project_id = this.projectId; - this.queryParam.page_size = this.pageSize; - + const resolverData = this.route.parent.snapshot.data; + if (resolverData) { + const pro: Project = resolverData['projectResolver']; + this.projectName = pro.name; + } } - private retrieve(): void { + retrieve() { + const arr: string[] = []; + if (this.queryUsername) { + arr.push(`username=~${this.queryUsername}`); + } + if (this.queryStartTime && this.queryEndTime) { + arr.push(`op_time=[${this.queryStartTime}~${this.queryEndTime}]`); + } else { + if (this.queryStartTime) { + arr.push(`op_time=[${this.queryStartTime}~]`); + } + if (this.queryEndTime) { + arr.push(`op_time=[~${this.queryEndTime}]`); + } + } + if (this.queryOperation && this.queryOperation.length > 0) { + arr.push(`operation={${this.queryOperation.join(' ')}}`); + } + + const param: ProjectService.GetLogsParams = { + projectName: this.projectName, + pageSize: this.pageSize, + page: this.currentPage, + }; + if (arr && arr.length > 0) { + param.q = encodeURIComponent(arr.join(',')); + } + this.loading = true; this.auditLogService - .listAuditLogs(this.queryParam) + .getLogsResponse(param) + .pipe(finalize(() => this.loading = false)) .subscribe( response => { - this.totalRecordCount = Number.parseInt(response.headers.get('x-total-count')); + // Get total count + if (response.headers) { + let xHeader: string = response.headers.get("x-total-count"); + if (xHeader) { + this.totalRecordCount = Number.parseInt(xHeader); + } + } this.auditLogs = response.body; }, error => { @@ -108,24 +145,18 @@ export class AuditLogComponent implements OnInit { } ); } - - retrievePage() { - this.queryParam.page = this.currentPage; - this.retrieve(); - } - doSearchAuditLogs(searchUsername: string): void { - this.queryParam.username = searchUsername; + this.queryUsername = searchUsername; this.retrieve(); } doSearchByStartTime(fromTimestamp: string): void { - this.queryParam.begin_timestamp = fromTimestamp; + this.queryStartTime = fromTimestamp; this.retrieve(); } doSearchByEndTime(toTimestamp: string): void { - this.queryParam.end_timestamp = toTimestamp; + this.queryEndTime = toTimestamp; this.retrieve(); } @@ -134,7 +165,7 @@ export class AuditLogComponent implements OnInit { let operationFilter: string[] = []; for (let filterOption of this.filterOptions) { if (filterOption.checked) { - operationFilter.push('operation=' + filterOption.key); + operationFilter.push(filterOption.key); } else { selectAll = false; } @@ -142,7 +173,7 @@ export class AuditLogComponent implements OnInit { if (selectAll) { operationFilter = []; } - this.queryParam.keywords = operationFilter.join('&'); + this.queryOperation = operationFilter; this.retrieve(); } diff --git a/src/portal/src/app/log/audit-log.service.spec.ts b/src/portal/src/app/log/audit-log.service.spec.ts deleted file mode 100644 index a4ed2a5d6..000000000 --- a/src/portal/src/app/log/audit-log.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { AuditLogService } from './audit-log.service'; - -describe('AuditLogService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule - ], - providers: [AuditLogService] - }); - }); - - it('should be created', inject([AuditLogService], (service: AuditLogService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/src/portal/src/app/log/audit-log.service.ts b/src/portal/src/app/log/audit-log.service.ts deleted file mode 100644 index 7bcea24ff..000000000 --- a/src/portal/src/app/log/audit-log.service.ts +++ /dev/null @@ -1,65 +0,0 @@ - -import {throwError as observableThrowError, Observable } from "rxjs"; - -import {map, catchError} from 'rxjs/operators'; -// Copyright (c) 2017 VMware, Inc. All Rights Reserved. -// -// Licensed 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. -import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; -import { AuditLog } from './audit-log'; -import { - buildHttpRequestOptions, - buildHttpRequestOptionsWithObserveResponse, - CURRENT_BASE_HREF -} from "../../lib/utils/utils"; -import { RequestQueryParams } from "../../lib/services"; - -export const logEndpoint = CURRENT_BASE_HREF + '/logs'; - -@Injectable() -export class AuditLogService { - - constructor(private http: HttpClient) {} - - listAuditLogs(queryParam: AuditLog): Observable { - let params: HttpParams = new HttpParams({fromString: queryParam.keywords}); - if (queryParam.begin_timestamp) { - params = params.set('begin_timestamp', queryParam.begin_timestamp); - } - if (queryParam.end_timestamp) { - params = params.set('end_timestamp', queryParam.end_timestamp); - } - if (queryParam.username) { - params = params.set('username', queryParam.username); - } - if (queryParam.page) { - params = params.set('page', queryParam.page); - } - if (queryParam.page_size) { - params = params.set('page_size', queryParam.page_size); - } - return this.http - .get>(`${ CURRENT_BASE_HREF }/projects/${queryParam.project_id}/logs` - , buildHttpRequestOptionsWithObserveResponse(params)).pipe( - catchError(error => observableThrowError(error)), ); - } - - getRecentLogs(lines: number): Observable { - let params: RequestQueryParams = new RequestQueryParams(); - params = params.set('page_size', '' + lines); - return this.http.get(logEndpoint, buildHttpRequestOptions(params)).pipe( - map(response => response as AuditLog[]), - catchError(error => observableThrowError(error)), ); - } -} diff --git a/src/portal/src/app/log/audit-log.ts b/src/portal/src/app/log/audit-log.ts deleted file mode 100644 index 6218fd50b..000000000 --- a/src/portal/src/app/log/audit-log.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Project Harbor Authors -// -// Licensed 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. -/* - { - "log_id": 3, - "user_id": 0, - "project_id": 0, - "repo_name": "library/mysql", - "repo_tag": "5.6", - "guid": "", - "operation": "push", - "op_time": "2017-02-14T09:22:58Z", - "username": "admin", - "keywords": "", - "BeginTime": "0001-01-01T00:00:00Z", - "begin_timestamp": 0, - "EndTime": "0001-01-01T00:00:00Z", - "end_timestamp": 0 - } -*/ -export class AuditLog { - log_id: number | string; - project_id: number | string; - username: string; - repo_name: string; - repo_tag: string; - operation: string; - op_time: Date; - begin_timestamp: number | string; - end_timestamp: number | string; - keywords: string; - page: number | string; - page_size: number | string; - fromTime: string; - toTime: string; -} diff --git a/src/portal/src/app/log/log.module.ts b/src/portal/src/app/log/log.module.ts index eb5a54e8e..3712d7e45 100644 --- a/src/portal/src/app/log/log.module.ts +++ b/src/portal/src/app/log/log.module.ts @@ -14,7 +14,6 @@ import { NgModule } from '@angular/core'; import { AuditLogComponent } from './audit-log.component'; import { SharedModule } from '../shared/shared.module'; -import { AuditLogService } from './audit-log.service'; import { LogPageComponent } from './log-page.component'; @NgModule({ @@ -23,7 +22,6 @@ import { LogPageComponent } from './log-page.component'; AuditLogComponent, LogPageComponent ], - providers: [AuditLogService], exports: [ AuditLogComponent, LogPageComponent] diff --git a/src/portal/src/lib/components/datetime-picker/datetime-picker.component.ts b/src/portal/src/lib/components/datetime-picker/datetime-picker.component.ts index f68a0dfd2..859e432f5 100644 --- a/src/portal/src/lib/components/datetime-picker/datetime-picker.component.ts +++ b/src/portal/src/lib/components/datetime-picker/datetime-picker.component.ts @@ -33,29 +33,10 @@ export class DatePickerComponent implements OnChanges { false ); } - - convertDate(strDate: string): string { - if ( - /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test( - strDate - ) - ) { - let parts = strDate.split(/[-\/]/); - strDate = - parts[2] /*Year*/ + "-" + parts[0] /*Month*/+ "-" + parts[1] /*Date*/; - } - return strDate; - } - doSearch() { let searchTerm: string = ""; if (this.searchTime.valid && this.dateInput) { - let timestamp: number = - new Date(this.convertDate(this.searchTime.value)).getTime() / 1000; - if (this.oneDayOffset) { - timestamp += 3600 * 24; - } - searchTerm = timestamp.toString(); + searchTerm = this.searchTime.value; } this.search.emit(searchTerm); } diff --git a/src/portal/src/lib/components/log/recent-log.component.html b/src/portal/src/lib/components/log/recent-log.component.html index 6381c095d..01f42d84d 100644 --- a/src/portal/src/lib/components/log/recent-log.component.html +++ b/src/portal/src/lib/components/log/recent-log.component.html @@ -7,7 +7,7 @@
diff --git a/src/portal/src/lib/components/log/recent-log.component.spec.ts b/src/portal/src/lib/components/log/recent-log.component.spec.ts index decf0f802..933ddf912 100644 --- a/src/portal/src/lib/components/log/recent-log.component.spec.ts +++ b/src/portal/src/lib/components/log/recent-log.component.spec.ts @@ -10,7 +10,6 @@ import { of } from 'rxjs'; import { AuditLog } from "../../../../ng-swagger-gen/models/audit-log"; import { AuditlogService } from "../../../../ng-swagger-gen/services/auditlog.service"; import { HttpHeaders, HttpResponse } from "@angular/common/http"; -import ListAuditLogsParams = AuditlogService.ListAuditLogsParams; import { delay } from "rxjs/operators"; describe('RecentLogComponent (inline template)', () => { @@ -39,9 +38,9 @@ describe('RecentLogComponent (inline template)', () => { mockedAuditLogs.push(item); } const fakedAuditlogService = { - listAuditLogsResponse(params: ListAuditLogsParams) { - if (params && params.username) { - if (params.username === 'demo0') { + listAuditLogsResponse(params: AuditlogService.ListAuditLogsParams) { + if (params && params.q) { + if (params.q.indexOf('demo0') !== -1) { return of(new HttpResponse({ body: mockedAuditLogs.slice(0, 1), headers: new HttpHeaders({ diff --git a/src/portal/src/lib/components/log/recent-log.component.ts b/src/portal/src/lib/components/log/recent-log.component.ts index 104a9a0e6..09c53acd8 100644 --- a/src/portal/src/lib/components/log/recent-log.component.ts +++ b/src/portal/src/lib/components/log/recent-log.component.ts @@ -77,7 +77,7 @@ export class RecentLogComponent implements OnInit { pageSize: this.pageSize }; if (this.currentTerm && this.currentTerm !== "") { - params[this.defaultFilter] = this.currentTerm; + params.q = encodeURIComponent(`${this.defaultFilter}=~${this.currentTerm}`); } this.loading = true; this.logService.listAuditLogsResponse(params).pipe(finalize(() => (this.loading = false))) diff --git a/src/server/v2.0/handler/auditlog.go b/src/server/v2.0/handler/auditlog.go index b69e4c4f9..77d9a60a9 100644 --- a/src/server/v2.0/handler/auditlog.go +++ b/src/server/v2.0/handler/auditlog.go @@ -4,7 +4,6 @@ import ( "context" "github.com/go-openapi/runtime/middleware" "github.com/goharbor/harbor/src/pkg/audit" - "github.com/goharbor/harbor/src/pkg/q" "github.com/goharbor/harbor/src/server/v2.0/models" "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog" operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog" @@ -26,28 +25,9 @@ func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAud // if !a.HasPermission(ctx, rbac.ActionList, rbac.ResourceLog) { // return a.SendError(ctx, ierror.ForbiddenError(nil)) // } - keywords := make(map[string]interface{}) - query := &q.Query{ - Keywords: keywords, - } - // TODO support fuzzy match and start end time - if params.Username != nil { - query.Keywords["Username"] = *(params.Username) - } - if params.Operation != nil { - query.Keywords["Operation"] = *(params.Operation) - } - if params.Resource != nil { - query.Keywords["Resource"] = *(params.Resource) - } - if params.ResourceType != nil { - query.Keywords["ResourceType"] = *(params.ResourceType) - } - if params.Page != nil { - query.PageNumber = *(params.Page) - } - if params.PageSize != nil { - query.PageSize = *(params.PageSize) + query, err := a.BuildQuery(ctx, params.Q, params.Page, params.PageSize) + if err != nil { + return a.SendError(ctx, err) } total, err := a.auditMgr.Count(ctx, query) if err != nil { diff --git a/src/server/v2.0/handler/repository.go b/src/server/v2.0/handler/repository.go index cb7cd2b9c..ececf3aeb 100644 --- a/src/server/v2.0/handler/repository.go +++ b/src/server/v2.0/handler/repository.go @@ -148,6 +148,7 @@ func (r *repositoryAPI) DeleteRepository(ctx context.Context, params operation.D notification.AddEvent(ctx, &metadata.DeleteRepositoryEventMetadata{ Ctx: ctx, Repository: repository.Name, + ProjectID: repository.ProjectID, }) return operation.NewDeleteRepositoryOK()