mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-18 08:15:16 +01:00
Merge pull request #10790 from AllForNothing/routing-modify
Improve routing and UI for artifact pages
This commit is contained in:
commit
ca871d0eb5
@ -13,11 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { CoreModule } from '../core/core.module';
|
import { CoreModule } from '../core/core.module';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { RepositoryModule } from '../repository/repository.module';
|
|
||||||
|
|
||||||
import { PasswordSettingComponent } from './password-setting/password-setting.component';
|
import { PasswordSettingComponent } from './password-setting/password-setting.component';
|
||||||
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
|
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
|
||||||
import { SignUpComponent } from './sign-up/sign-up.component';
|
import { SignUpComponent } from './sign-up/sign-up.component';
|
||||||
@ -33,7 +30,6 @@ import { AccountSettingsModalService } from './account-settings/account-settings
|
|||||||
CoreModule,
|
CoreModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RepositoryModule
|
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
PasswordSettingComponent,
|
PasswordSettingComponent,
|
||||||
|
@ -14,13 +14,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { ProjectModule } from '../project/project.module';
|
import { ProjectModule } from '../project/project.module';
|
||||||
import { UserModule } from '../user/user.module';
|
import { UserModule } from '../user/user.module';
|
||||||
import { AccountModule } from '../account/account.module';
|
import { AccountModule } from '../account/account.module';
|
||||||
import { RepositoryModule } from '../repository/repository.module';
|
|
||||||
import { GroupModule } from '../group/group.module';
|
import { GroupModule } from '../group/group.module';
|
||||||
|
|
||||||
import { NavigatorComponent } from './navigator/navigator.component';
|
import { NavigatorComponent } from './navigator/navigator.component';
|
||||||
import { GlobalSearchComponent } from './global-search/global-search.component';
|
import { GlobalSearchComponent } from './global-search/global-search.component';
|
||||||
import { FooterComponent } from './footer/footer.component';
|
import { FooterComponent } from './footer/footer.component';
|
||||||
@ -36,7 +33,6 @@ import { SearchTriggerService } from './global-search/search-trigger.service';
|
|||||||
UserModule,
|
UserModule,
|
||||||
AccountModule,
|
AccountModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
RepositoryModule,
|
|
||||||
GroupModule
|
GroupModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -13,40 +13,26 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { SystemAdminGuard } from './shared/route/system-admin-activate.service';
|
import { SystemAdminGuard } from './shared/route/system-admin-activate.service';
|
||||||
import { AuthCheckGuard } from './shared/route/auth-user-activate.service';
|
import { AuthCheckGuard } from './shared/route/auth-user-activate.service';
|
||||||
import { SignInGuard } from './shared/route/sign-in-guard-activate.service';
|
import { SignInGuard } from './shared/route/sign-in-guard-activate.service';
|
||||||
import { MemberGuard } from './shared/route/member-guard-activate.service';
|
import { MemberGuard } from './shared/route/member-guard-activate.service';
|
||||||
import { ArtifactGuard } from './shared/route/artifact-guard-activate.service';
|
|
||||||
import { MemberPermissionGuard } from './shared/route/member-permission-guard-activate.service';
|
import { MemberPermissionGuard } from './shared/route/member-permission-guard-activate.service';
|
||||||
import { OidcGuard } from './shared/route/oidc-guard-active.service';
|
import { OidcGuard } from './shared/route/oidc-guard-active.service';
|
||||||
|
|
||||||
import { PageNotFoundComponent } from './shared/not-found/not-found.component';
|
import { PageNotFoundComponent } from './shared/not-found/not-found.component';
|
||||||
import { HarborShellComponent } from './base/harbor-shell/harbor-shell.component';
|
import { HarborShellComponent } from './base/harbor-shell/harbor-shell.component';
|
||||||
import { ConfigurationComponent } from './config/config.component';
|
import { ConfigurationComponent } from './config/config.component';
|
||||||
import { DevCenterComponent } from './dev-center/dev-center.component';
|
import { DevCenterComponent } from './dev-center/dev-center.component';
|
||||||
import { GcPageComponent } from './gc-page/gc-page.component';
|
import { GcPageComponent } from './gc-page/gc-page.component';
|
||||||
import { VulnerabilityPageComponent } from './vulnerability-page/vulnerability-page.component';
|
|
||||||
|
|
||||||
import { UserComponent } from './user/user.component';
|
import { UserComponent } from './user/user.component';
|
||||||
import { SignInComponent } from './sign-in/sign-in.component';
|
import { SignInComponent } from './sign-in/sign-in.component';
|
||||||
import { ResetPasswordComponent } from './account/password-setting/reset-password/reset-password.component';
|
import { ResetPasswordComponent } from './account/password-setting/reset-password/reset-password.component';
|
||||||
import { GroupComponent } from './group/group.component';
|
import { GroupComponent } from './group/group.component';
|
||||||
|
|
||||||
import { TotalReplicationPageComponent } from './replication/total-replication/total-replication-page.component';
|
import { TotalReplicationPageComponent } from './replication/total-replication/total-replication-page.component';
|
||||||
import { ReplicationTasksPageComponent } from './replication/replication-tasks-page/replication-tasks-page.component';
|
import { ReplicationTasksPageComponent } from './replication/replication-tasks-page/replication-tasks-page.component';
|
||||||
|
|
||||||
import { DestinationPageComponent } from './replication/destination/destination-page.component';
|
import { DestinationPageComponent } from './replication/destination/destination-page.component';
|
||||||
|
|
||||||
import { AuditLogComponent } from './log/audit-log.component';
|
import { AuditLogComponent } from './log/audit-log.component';
|
||||||
import { LogPageComponent } from './log/log-page.component';
|
import { LogPageComponent } from './log/log-page.component';
|
||||||
|
|
||||||
import { RepositoryPageComponent } from './repository/repository-page.component';
|
|
||||||
import { ArtifactListPageComponent } from './repository/artifact-list-page/artifact-list-page.component';
|
|
||||||
import { ArtifactSummaryPageComponent } from './repository/artifact-summary-page/artifact-summary-page.component';
|
|
||||||
import { LeavingRepositoryRouteDeactivate } from './shared/route/leaving-repository-deactivate.service';
|
|
||||||
|
|
||||||
import { ProjectComponent } from './project/project.component';
|
import { ProjectComponent } from './project/project.component';
|
||||||
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
||||||
import { MemberComponent } from './project/member/member.component';
|
import { MemberComponent } from './project/member/member.component';
|
||||||
@ -61,7 +47,6 @@ import { HelmChartDetailComponent } from './project/helm-chart/helm-chart-detail
|
|||||||
import { OidcOnboardComponent } from './oidc-onboard/oidc-onboard.component';
|
import { OidcOnboardComponent } from './oidc-onboard/oidc-onboard.component';
|
||||||
import { LicenseComponent } from './license/license.component';
|
import { LicenseComponent } from './license/license.component';
|
||||||
import { SummaryComponent } from './project/summary/summary.component';
|
import { SummaryComponent } from './project/summary/summary.component';
|
||||||
|
|
||||||
import { TagFeatureIntegrationComponent } from './project/tag-feature-integration/tag-feature-integration.component';
|
import { TagFeatureIntegrationComponent } from './project/tag-feature-integration/tag-feature-integration.component';
|
||||||
import { TagRetentionComponent } from './project/tag-feature-integration/tag-retention/tag-retention.component';
|
import { TagRetentionComponent } from './project/tag-feature-integration/tag-retention/tag-retention.component';
|
||||||
import { ImmutableTagComponent } from './project/tag-feature-integration/immutable-tag/immutable-tag.component';
|
import { ImmutableTagComponent } from './project/tag-feature-integration/immutable-tag/immutable-tag.component';
|
||||||
@ -72,8 +57,9 @@ import { LabelsComponent } from "./labels/labels.component";
|
|||||||
import { ProjectQuotasComponent } from "./project-quotas/project-quotas.component";
|
import { ProjectQuotasComponent } from "./project-quotas/project-quotas.component";
|
||||||
import { VulnerabilityConfigComponent } from "../lib/components/config/vulnerability/vulnerability-config.component";
|
import { VulnerabilityConfigComponent } from "../lib/components/config/vulnerability/vulnerability-config.component";
|
||||||
import { USERSTATICPERMISSION } from "../lib/services";
|
import { USERSTATICPERMISSION } from "../lib/services";
|
||||||
import { LeavingArtifactSummaryRouteDeactivate } from './shared/route/leaving-artifact-summary-deactivate.service';
|
import { RepositoryGridviewComponent } from "./project/repository/repository-gridview.component";
|
||||||
|
import { ArtifactListPageComponent } from "./project/repository/artifact-list-page/artifact-list-page.component";
|
||||||
|
import { ArtifactSummaryComponent } from "./project/repository/artifact/artifact-summary.component";
|
||||||
|
|
||||||
const harborRoutes: Routes = [
|
const harborRoutes: Routes = [
|
||||||
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
||||||
@ -99,7 +85,6 @@ const harborRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'harbor',
|
path: 'harbor',
|
||||||
component: HarborShellComponent,
|
component: HarborShellComponent,
|
||||||
// canActivate: [AuthCheckGuard],
|
|
||||||
canActivateChild: [AuthCheckGuard],
|
canActivateChild: [AuthCheckGuard],
|
||||||
children: [
|
children: [
|
||||||
{ path: '', redirectTo: 'projects', pathMatch: 'full' },
|
{ path: '', redirectTo: 'projects', pathMatch: 'full' },
|
||||||
@ -169,41 +154,6 @@ const harborRoutes: Routes = [
|
|||||||
canActivate: [SystemAdminGuard],
|
canActivate: [SystemAdminGuard],
|
||||||
canActivateChild: [SystemAdminGuard]
|
canActivateChild: [SystemAdminGuard]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'tags/:id/:repo',
|
|
||||||
component: ArtifactListPageComponent,
|
|
||||||
canActivate: [MemberGuard],
|
|
||||||
resolve: {
|
|
||||||
projectResolver: ProjectRoutingResolver
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'projects/:id/repositories/:repo',
|
|
||||||
component: ArtifactListPageComponent,
|
|
||||||
canActivate: [MemberGuard],
|
|
||||||
canDeactivate: [LeavingRepositoryRouteDeactivate],
|
|
||||||
resolve: {
|
|
||||||
projectResolver: ProjectRoutingResolver
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'projects/:id/repositories/:repo/depth/:depth',
|
|
||||||
component: ArtifactListPageComponent,
|
|
||||||
canActivate: [MemberGuard],
|
|
||||||
canDeactivate: [LeavingRepositoryRouteDeactivate],
|
|
||||||
resolve: {
|
|
||||||
projectResolver: ProjectRoutingResolver
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'projects/:id/repositories/:repo/artifacts/:digest',
|
|
||||||
component: ArtifactSummaryPageComponent,
|
|
||||||
canActivate: [MemberGuard, ArtifactGuard],
|
|
||||||
canDeactivate: [LeavingArtifactSummaryRouteDeactivate],
|
|
||||||
resolve: {
|
|
||||||
projectResolver: ProjectRoutingResolver
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'projects/:id/helm-charts/:chart/versions',
|
path: 'projects/:id/helm-charts/:chart/versions',
|
||||||
component: ListChartVersionsComponent,
|
component: ListChartVersionsComponent,
|
||||||
@ -248,7 +198,7 @@ const harborRoutes: Routes = [
|
|||||||
action: USERSTATICPERMISSION.REPOSITORY.VALUE.LIST
|
action: USERSTATICPERMISSION.REPOSITORY.VALUE.LIST
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
component: RepositoryPageComponent,
|
component: RepositoryGridviewComponent
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'helm-charts',
|
path: 'helm-charts',
|
||||||
@ -261,17 +211,6 @@ const harborRoutes: Routes = [
|
|||||||
},
|
},
|
||||||
component: ListChartsComponent
|
component: ListChartsComponent
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'repositories/:repo/tags',
|
|
||||||
canActivate: [MemberPermissionGuard],
|
|
||||||
data: {
|
|
||||||
permissionParam: {
|
|
||||||
resource: USERSTATICPERMISSION.REPOSITORY.KEY,
|
|
||||||
action: USERSTATICPERMISSION.REPOSITORY.VALUE.LIST
|
|
||||||
}
|
|
||||||
},
|
|
||||||
component: ArtifactListPageComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'members',
|
path: 'members',
|
||||||
canActivate: [MemberPermissionGuard],
|
canActivate: [MemberPermissionGuard],
|
||||||
@ -374,6 +313,38 @@ const harborRoutes: Routes = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/repositories/:repo',
|
||||||
|
component: ArtifactListPageComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/repositories/:repo/depth/:depth',
|
||||||
|
component: ArtifactListPageComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/repositories/:repo/artifacts/:digest',
|
||||||
|
component: ArtifactSummaryComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/repositories/:repo/depth/:depth/artifacts/:digest',
|
||||||
|
component: ArtifactSummaryComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'configs',
|
path: 'configs',
|
||||||
component: ConfigurationComponent,
|
component: ConfigurationComponent,
|
||||||
|
@ -4,8 +4,8 @@ import { fromEvent } from 'rxjs';
|
|||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
import { HelmChartVersion } from '../helm-chart.interface.service';
|
import { HelmChartVersion } from '../helm-chart.interface.service';
|
||||||
import { ResourceType } from '../../../shared/shared.const';
|
import { ResourceType } from '../../../shared/shared.const';
|
||||||
import { Label, Tag } from "../../../../lib/services";
|
import { Label } from "../../../../lib/services";
|
||||||
import { Artifact } from '../../../../lib/components/artifact/artifact';
|
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-chart-version-label-filter",
|
selector: "hbr-chart-version-label-filter",
|
||||||
|
@ -12,31 +12,23 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { RepositoryModule } from '../repository/repository.module';
|
|
||||||
import { ReplicationModule } from '../replication/replication.module';
|
import { ReplicationModule } from '../replication/replication.module';
|
||||||
import { SummaryModule } from './summary/summary.module';
|
import { SummaryModule } from './summary/summary.module';
|
||||||
import { TagFeatureIntegrationModule } from './tag-feature-integration/tag-feature-integration.module';
|
import { TagFeatureIntegrationModule } from './tag-feature-integration/tag-feature-integration.module';
|
||||||
import { LogModule } from '../log/log.module';
|
import { LogModule } from '../log/log.module';
|
||||||
|
|
||||||
import { ProjectComponent } from './project.component';
|
import { ProjectComponent } from './project.component';
|
||||||
import { CreateProjectComponent } from './create-project/create-project.component';
|
import { CreateProjectComponent } from './create-project/create-project.component';
|
||||||
import { ListProjectComponent } from './list-project/list-project.component';
|
import { ListProjectComponent } from './list-project/list-project.component';
|
||||||
|
|
||||||
import { ProjectDetailComponent } from './project-detail/project-detail.component';
|
import { ProjectDetailComponent } from './project-detail/project-detail.component';
|
||||||
import { MemberComponent } from './member/member.component';
|
import { MemberComponent } from './member/member.component';
|
||||||
import { AddMemberComponent } from './member/add-member/add-member.component';
|
import { AddMemberComponent } from './member/add-member/add-member.component';
|
||||||
import { AddGroupComponent } from './member/add-group/add-group.component';
|
import { AddGroupComponent } from './member/add-group/add-group.component';
|
||||||
|
|
||||||
// import { ProjectService } from '@harbor/ui';
|
|
||||||
import { MemberService } from './member/member.service';
|
import { MemberService } from './member/member.service';
|
||||||
import { RobotService } from './robot-account/robot-account.service';
|
import { RobotService } from './robot-account/robot-account.service';
|
||||||
import { ProjectRoutingResolver } from './project-routing-resolver.service';
|
import { ProjectRoutingResolver } from './project-routing-resolver.service';
|
||||||
|
|
||||||
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
||||||
import { ProjectLabelComponent } from "../project/project-label/project-label.component";
|
|
||||||
import { HelmChartModule } from './helm-chart/helm-chart.module';
|
import { HelmChartModule } from './helm-chart/helm-chart.module';
|
||||||
import { RobotAccountComponent } from './robot-account/robot-account.component';
|
import { RobotAccountComponent } from './robot-account/robot-account.component';
|
||||||
import { AddRobotComponent } from './robot-account/add-robot/add-robot.component';
|
import { AddRobotComponent } from './robot-account/add-robot/add-robot.component';
|
||||||
@ -47,11 +39,34 @@ import { AddWebhookComponent } from './webhook/add-webhook/add-webhook.component
|
|||||||
import { AddWebhookFormComponent } from './webhook/add-webhook-form/add-webhook-form.component';
|
import { AddWebhookFormComponent } from './webhook/add-webhook-form/add-webhook-form.component';
|
||||||
import { ScannerComponent } from "./scanner/scanner.component";
|
import { ScannerComponent } from "./scanner/scanner.component";
|
||||||
import { ConfigScannerService } from "../config/scanner/config-scanner.service";
|
import { ConfigScannerService } from "../config/scanner/config-scanner.service";
|
||||||
|
import { RepositoryGridviewComponent } from "./repository/repository-gridview.component";
|
||||||
|
import { ResultTipHistogramComponent } from "./repository/vulnerability-scanning/result-tip-histogram/result-tip-histogram.component";
|
||||||
|
import { ResultGridComponent } from "./repository/vulnerability-scanning/result-grid.component";
|
||||||
|
import { ResultBarChartComponent } from "./repository/vulnerability-scanning/result-bar-chart.component";
|
||||||
|
import { HistogramChartComponent } from "./repository/vulnerability-scanning/histogram-chart/histogram-chart.component";
|
||||||
|
import { ResultTipComponent } from "./repository/vulnerability-scanning/result-tip.component";
|
||||||
|
import { ArtifactListPageComponent } from "./repository/artifact-list-page/artifact-list-page.component";
|
||||||
|
import { ProjectLabelComponent } from "./project-label/project-label.component";
|
||||||
|
import { ArtifactListComponent } from "./repository/artifact-list-page/artifact-list/artifact-list.component";
|
||||||
|
import { ArtifactTagComponent } from "./repository/artifact/artifact-tag/artifact-tag.component";
|
||||||
|
import { ArtifactCommonPropertiesComponent } from "./repository/artifact/artifact-common-properties/artifact-common-properties.component";
|
||||||
|
import { ArtifactAdditionsComponent } from "./repository/artifact/artifact-additions/artifact-additions.component";
|
||||||
|
import { ArtifactSummaryComponent } from "./repository/artifact/artifact-summary.component";
|
||||||
|
import { ArtifactListTabComponent } from "./repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component";
|
||||||
|
import { BuildHistoryComponent } from "./repository/artifact/artifact-additions/build-history/build-history.component";
|
||||||
|
import { DependenciesComponent } from "./repository/artifact/artifact-additions/dependencies/dependencies.component";
|
||||||
|
import { SummaryComponent } from "./repository/artifact/artifact-additions/summary/summary.component";
|
||||||
|
import { ValuesComponent } from "./repository/artifact/artifact-additions/values/values.component";
|
||||||
|
import {
|
||||||
|
ArtifactVulnerabilitiesComponent
|
||||||
|
} from "./repository/artifact/artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component";
|
||||||
|
import { RepositoryDefaultService, RepositoryService } from "./repository/repository.service";
|
||||||
|
import { ArtifactDefaultService, ArtifactService } from "./repository/artifact/artifact.service";
|
||||||
|
import { GridViewComponent } from "./repository/gridview/grid-view.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RepositoryModule,
|
|
||||||
ReplicationModule,
|
ReplicationModule,
|
||||||
LogModule,
|
LogModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
@ -76,9 +91,38 @@ import { ConfigScannerService } from "../config/scanner/config-scanner.service";
|
|||||||
AddWebhookComponent,
|
AddWebhookComponent,
|
||||||
AddWebhookFormComponent,
|
AddWebhookFormComponent,
|
||||||
ScannerComponent,
|
ScannerComponent,
|
||||||
|
RepositoryGridviewComponent,
|
||||||
|
HistogramChartComponent,
|
||||||
|
ResultTipHistogramComponent,
|
||||||
|
ResultBarChartComponent,
|
||||||
|
ResultGridComponent,
|
||||||
|
ResultTipComponent,
|
||||||
|
ArtifactListPageComponent,
|
||||||
|
ArtifactListComponent,
|
||||||
|
ArtifactListTabComponent,
|
||||||
|
ArtifactSummaryComponent,
|
||||||
|
ArtifactCommonPropertiesComponent,
|
||||||
|
ArtifactTagComponent,
|
||||||
|
ArtifactAdditionsComponent,
|
||||||
|
BuildHistoryComponent,
|
||||||
|
DependenciesComponent,
|
||||||
|
SummaryComponent,
|
||||||
|
ValuesComponent,
|
||||||
|
ArtifactVulnerabilitiesComponent,
|
||||||
|
GridViewComponent,
|
||||||
],
|
],
|
||||||
exports: [ProjectComponent, ListProjectComponent],
|
exports: [ProjectComponent, ListProjectComponent],
|
||||||
providers: [ProjectRoutingResolver, MemberService, RobotService, WebhookService, ConfigScannerService]
|
providers: [
|
||||||
|
ProjectRoutingResolver,
|
||||||
|
MemberService,
|
||||||
|
RobotService,
|
||||||
|
WebhookService,
|
||||||
|
ConfigScannerService,
|
||||||
|
RepositoryDefaultService,
|
||||||
|
ArtifactDefaultService,
|
||||||
|
{ provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||||
|
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class ProjectModule {
|
export class ProjectModule {
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
<div>
|
||||||
|
<div class="breadcrumb" *ngIf="!withAdmiral">
|
||||||
|
<a (click)="goProBack()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
|
||||||
|
<span class="back-icon"><</span>
|
||||||
|
<a (click)="watchGoBackEvt(projectId)">{{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
||||||
|
<span *ngIf="depth"><<a (click)="backInitRepo()">{{repoName}}</a></span>
|
||||||
|
<span *ngIf="referArtifactNameArray?.length>=1" >
|
||||||
|
<span *ngFor="let digest of referArtifactNameArray;let i = index">
|
||||||
|
<<a (click)="jumpDigest(i)" >{{digest | slice:0:15}}</a></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<artifact-list [repoName]="repoName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole"
|
||||||
|
[projectId]="projectId" [memberRoleID]="projectMemberRoleId" [isGuest]="isGuest"></artifact-list>
|
||||||
|
</div>
|
@ -5,14 +5,15 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ClarityModule } from '@clr/angular';
|
import { ClarityModule } from '@clr/angular';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { SessionService } from "../../../shared/session.service";
|
||||||
|
import { AppConfigService } from "../../../app-config.service";
|
||||||
|
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
|
import { ArtifactDefaultService } from "../artifact/artifact.service";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
|
|
||||||
import { AppConfigService } from '../../app-config.service';
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
|
||||||
import { ArtifactService } from '../../../lib/services';
|
|
||||||
describe('ArtifactListPageComponent', () => {
|
describe('ArtifactListPageComponent', () => {
|
||||||
let component: ArtifactListPageComponent;
|
let component: ArtifactListPageComponent;
|
||||||
let fixture: ComponentFixture<ArtifactListPageComponent>;
|
let fixture: ComponentFixture<ArtifactListPageComponent>;
|
||||||
@ -42,7 +43,9 @@ describe('ArtifactListPageComponent', () => {
|
|||||||
const mockActivatedRoute = {
|
const mockActivatedRoute = {
|
||||||
RouterparamMap: of({ get: (key) => 'value' }),
|
RouterparamMap: of({ get: (key) => 'value' }),
|
||||||
snapshot: {
|
snapshot: {
|
||||||
params: { id: 1 },
|
params: {
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
parent: {
|
parent: {
|
||||||
params: { id: 1 },
|
params: { id: 1 },
|
||||||
|
|
||||||
@ -59,7 +62,15 @@ describe('ArtifactListPageComponent', () => {
|
|||||||
ismember: true,
|
ismember: true,
|
||||||
role_name: 'master',
|
role_name: 'master',
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
params: {
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const config: IServiceConfig = {
|
||||||
|
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||||
};
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@ -71,13 +82,14 @@ describe('ArtifactListPageComponent', () => {
|
|||||||
ClarityModule,
|
ClarityModule,
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
FormsModule,
|
FormsModule,
|
||||||
RouterTestingModule,
|
|
||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule
|
||||||
],
|
],
|
||||||
declarations: [ArtifactListPageComponent],
|
declarations: [ArtifactListPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
TranslateService,
|
TranslateService,
|
||||||
|
ArtifactDefaultService,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: SessionService, useValue: mockSessionService },
|
{ provide: SessionService, useValue: mockSessionService },
|
||||||
{ provide: AppConfigService, useValue: mockAppConfigService },
|
{ provide: AppConfigService, useValue: mockAppConfigService },
|
||||||
{ provide: Router, useValue: mockRouter },
|
{ provide: Router, useValue: mockRouter },
|
@ -13,12 +13,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { AppConfigService } from '../../app-config.service';
|
import { ArtifactListComponent } from "./artifact-list/artifact-list.component";
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { ArtifactDefaultService } from "../artifact/artifact.service";
|
||||||
import { Project } from '../../project/project';
|
import { AppConfigService } from "../../../app-config.service";
|
||||||
import { ArtifactListComponent } from "../../../lib/components/artifact-list/artifact-list.component";
|
import { SessionService } from "../../../shared/session.service";
|
||||||
import { ArtifactClickEvent, ArtifactService } from "../../../lib/services";
|
import { ArtifactClickEvent } from "../../../../lib/services";
|
||||||
import { clone } from '../../../lib/utils/utils';
|
import { Project } from "../../project";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'artifact-list-page',
|
selector: 'artifact-list-page',
|
||||||
@ -37,13 +37,20 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
|
|
||||||
@ViewChild(ArtifactListComponent, {static: false})
|
@ViewChild(ArtifactListComponent, {static: false})
|
||||||
repositoryComponent: ArtifactListComponent;
|
repositoryComponent: ArtifactListComponent;
|
||||||
|
depth: string;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactDefaultService,
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private session: SessionService) {
|
private session: SessionService) {
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.depth = this.route.snapshot.params['depth'];
|
||||||
|
if (this.depth) {
|
||||||
|
const arr: string[] = this.depth.split('-');
|
||||||
|
this.referArtifactNameArray = arr.slice(0, arr.length - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -51,9 +58,7 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
if (!this.projectId) {
|
if (!this.projectId) {
|
||||||
this.projectId = this.route.snapshot.parent.params['id'];
|
this.projectId = this.route.snapshot.parent.params['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolverData = this.route.snapshot.data;
|
let resolverData = this.route.snapshot.data;
|
||||||
|
|
||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
this.isGuest = (<Project>resolverData['projectResolver']).current_user_role_id === 3;
|
this.isGuest = (<Project>resolverData['projectResolver']).current_user_role_id === 3;
|
||||||
@ -61,11 +66,6 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.repoName = this.route.snapshot.params['repo'];
|
this.repoName = this.route.snapshot.params['repo'];
|
||||||
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
||||||
|
|
||||||
let refer = JSON.parse(sessionStorage.getItem('reference'));
|
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repoName) {
|
|
||||||
this.referArtifactNameArray = refer.referArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get withNotary(): boolean {
|
get withNotary(): boolean {
|
||||||
@ -82,17 +82,6 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
hasChanges(): boolean {
|
hasChanges(): boolean {
|
||||||
return this.repositoryComponent.hasChanges();
|
return this.repositoryComponent.hasChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
watchTagClickEvt(artifactEvt: ArtifactClickEvent): void {
|
|
||||||
//
|
|
||||||
sessionStorage.setItem('referenceSummary', JSON.stringify({ projectId: this.projectId, repo: this.repoName,
|
|
||||||
"digest": artifactEvt.digest, referArray: this.referArtifactNameArray}));
|
|
||||||
|
|
||||||
let linkUrl = ['harbor', 'projects', artifactEvt.project_id, 'repositories'
|
|
||||||
, artifactEvt.repository_name, 'artifacts', artifactEvt.digest];
|
|
||||||
this.router.navigate(linkUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
watchGoBackEvt(projectId: string| number): void {
|
watchGoBackEvt(projectId: string| number): void {
|
||||||
this.router.navigate(["harbor", "projects", projectId, "repositories"]);
|
this.router.navigate(["harbor", "projects", projectId, "repositories"]);
|
||||||
}
|
}
|
||||||
@ -100,25 +89,14 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
this.router.navigate(["harbor", "projects"]);
|
this.router.navigate(["harbor", "projects"]);
|
||||||
}
|
}
|
||||||
backInitRepo() {
|
backInitRepo() {
|
||||||
this.referArtifactNameArray = [];
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repoName]);
|
||||||
|
|
||||||
sessionStorage.removeItem('reference');
|
|
||||||
this.updateArtifactList('repoName');
|
|
||||||
}
|
}
|
||||||
jumpDigest(referArtifactNameArray: string[], index: number) {
|
jumpDigest(index: number) {
|
||||||
this.referArtifactNameArray = referArtifactNameArray.slice(index);
|
const arr: string[] = this.referArtifactNameArray.slice(0, index + 1 );
|
||||||
this.referArtifactNameArray.pop();
|
if ( arr && arr.length) {
|
||||||
this.referArtifactNameArray = referArtifactNameArray.slice(index);
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repoName, "depth", arr.join('-')]);
|
||||||
|
} else {
|
||||||
sessionStorage.setItem('reference', JSON.stringify({ projectId: this.projectId, repo: this.repoName,
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repoName]);
|
||||||
referArray: referArtifactNameArray.slice(index)}));
|
}
|
||||||
|
|
||||||
this.updateArtifactList(referArtifactNameArray.slice(index));
|
|
||||||
}
|
|
||||||
updateArtifactList(res): void {
|
|
||||||
this.artifactService.triggerUploadArtifact.next(res);
|
|
||||||
}
|
|
||||||
putArtifactReferenceArr(digestArray) {
|
|
||||||
this.referArtifactNameArray = digestArray;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-datagrid [clrDgLoading]="loading" (clrDgRefresh)="clrLoad($event)" class="datagrid-top" [class.embeded-datagrid]="isEmbedded"
|
<clr-datagrid [clrDgLoading]="loading" (clrDgRefresh)="clrDgRefresh($event)" class="datagrid-top" [class.embeded-datagrid]="isEmbedded"
|
||||||
[(clrDgSelected)]="selectedRow">
|
[(clrDgSelected)]="selectedRow">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
|
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
|
||||||
@ -146,13 +146,13 @@
|
|||||||
<img class="artifact-icon"
|
<img class="artifact-icon"
|
||||||
[src]="artifact.type==='IMAGE'||artifact.type==='CHART'||artifact.type ==='CNAB'?'images/artifact-'+artifact.type.toLowerCase()+'.svg':'images/artifact-default.svg'" />
|
[src]="artifact.type==='IMAGE'||artifact.type==='CHART'||artifact.type ==='CNAB'?'images/artifact-'+artifact.type.toLowerCase()+'.svg':'images/artifact-default.svg'" />
|
||||||
|
|
||||||
<a href="javascript:void(0)" class="max-width-100" (click)="onTagClick(artifact)"
|
<a href="javascript:void(0)" class="max-width-100" (click)="goIntoArtifactSummaryPage(artifact)"
|
||||||
title="{{artifact.digest}}">
|
title="{{artifact.digest}}">
|
||||||
{{ artifact.digest | slice:0:15}}</a>
|
{{ artifact.digest | slice:0:15}}</a>
|
||||||
<clr-tooltip *ngIf="artifact.references">
|
<clr-tooltip *ngIf="artifact?.references && artifact?.references?.length">
|
||||||
<div clrTooltipTrigger class="level-border">
|
<div clrTooltipTrigger class="level-border">
|
||||||
<div class="inner truncated ">
|
<div class="inner truncated ">
|
||||||
<a href="javascript:void(0)" (click)="refer(artifact)">
|
<a href="javascript:void(0)" (click)="goIntoIndexArtifact(artifact)">
|
||||||
<clr-icon class="icon-folder" shape="folder"></clr-icon>
|
<clr-icon class="icon-folder" shape="folder"></clr-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,44 @@
|
|||||||
@import "../../mixin";
|
@mixin text-overflow
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-wrap:break-word;
|
||||||
|
white-space: nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@mixin text-overflow-param($width) {
|
||||||
|
width: $width;
|
||||||
|
@include text-overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin grid-right-top-pos{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
right: 35px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin absolute-center($width:108px) {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: $width !important;
|
||||||
|
height: $width !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dropdown-as-action-button {
|
||||||
|
margin: .25rem .5rem .25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
.option-right {
|
.option-right {
|
||||||
padding-right: 18px;
|
padding-right: 18px;
|
||||||
@ -350,3 +390,4 @@ clr-datagrid {
|
|||||||
.eslip {
|
.eslip {
|
||||||
margin-left: -3px;
|
margin-left: -3px;
|
||||||
}
|
}
|
||||||
|
|
@ -1,32 +1,37 @@
|
|||||||
import { ComponentFixture, TestBed, async } from "@angular/core/testing";
|
import { ComponentFixture, TestBed, async } from "@angular/core/testing";
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from "@angular/core";
|
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from "@angular/core";
|
||||||
|
|
||||||
import { SharedModule } from "../../utils/shared/shared.module";
|
|
||||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
|
||||||
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
|
||||||
import { ArtifactListTabComponent } from "./artifact-list-tab.component";
|
import { ArtifactListTabComponent } from "./artifact-list-tab.component";
|
||||||
|
|
||||||
import { ErrorHandler } from "../../utils/error-handler/error-handler";
|
|
||||||
import { Label, Tag } from "../../services/interface";
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from "../../entities/service.config";
|
|
||||||
import {
|
|
||||||
ScanningResultService, ScanningResultDefaultService,
|
|
||||||
RetagService, RetagDefaultService, ProjectService, ProjectDefaultService, ArtifactService, ArtifactDefaultService
|
|
||||||
} from "../../services";
|
|
||||||
import { CopyInputComponent } from "../push-image/copy-input.component";
|
|
||||||
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
|
||||||
import { LabelDefaultService, LabelService } from "../../services/label.service";
|
|
||||||
import { UserPermissionService, UserPermissionDefaultService } from "../../services/permission.service";
|
|
||||||
import { USERSTATICPERMISSION } from "../../services/permission-static";
|
|
||||||
import { OperationService } from "../operation/operation.service";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { delay } from "rxjs/operators";
|
import { delay } from "rxjs/operators";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { HttpClientTestingModule } from "@angular/common/http/testing";
|
import { HttpClientTestingModule } from "@angular/common/http/testing";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { ChannelService } from "../../services/channel.service";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Artifact, Reference } from "./artifact";
|
import { ArtifactDefaultService, ArtifactService } from "../../../artifact/artifact.service";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import {
|
||||||
|
Label,
|
||||||
|
LabelDefaultService,
|
||||||
|
LabelService,
|
||||||
|
ProjectDefaultService,
|
||||||
|
ProjectService,
|
||||||
|
RetagDefaultService,
|
||||||
|
RetagService,
|
||||||
|
ScanningResultDefaultService,
|
||||||
|
ScanningResultService,
|
||||||
|
UserPermissionDefaultService,
|
||||||
|
UserPermissionService,
|
||||||
|
USERSTATICPERMISSION
|
||||||
|
} from "../../../../../../lib/services";
|
||||||
|
import { Artifact, Reference } from "../../../artifact/artifact";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
|
||||||
|
import { SharedModule } from "../../../../../../lib/utils/shared/shared.module";
|
||||||
|
import { LabelPieceComponent } from "../../../../../../lib/components/label-piece/label-piece.component";
|
||||||
|
import { ConfirmationDialogComponent } from "../../../../../../lib/components/confirmation-dialog";
|
||||||
|
import { ImageNameInputComponent } from "../../../../../../lib/components/image-name-input/image-name-input.component";
|
||||||
|
import { CopyInputComponent } from "../../../../../../lib/components/push-image/copy-input.component";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
|
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
||||||
|
|
||||||
describe("ArtifactListTabComponent (inline template)", () => {
|
describe("ArtifactListTabComponent (inline template)", () => {
|
||||||
|
|
||||||
@ -43,13 +48,35 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
name: "Clair"
|
name: "Clair"
|
||||||
};
|
};
|
||||||
let mockActivatedRoute = {
|
let mockActivatedRoute = {
|
||||||
|
snapshot: {
|
||||||
|
params: {
|
||||||
|
id: 1,
|
||||||
|
repo: "test",
|
||||||
|
digest: "ABC",
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
projectResolver: {
|
||||||
|
has_project_admin_role: true,
|
||||||
|
current_user_role_id: 3,
|
||||||
|
name: "demo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
data: of(
|
data: of(
|
||||||
{
|
{
|
||||||
projectResolver: {
|
projectResolver: {
|
||||||
name: 'library'
|
name: 'library'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
|
params: {
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let mockArtifacts: Artifact[] = [
|
let mockArtifacts: Artifact[] = [
|
||||||
{
|
{
|
||||||
@ -68,7 +95,8 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
artifact_id: 2,
|
artifact_id: 2,
|
||||||
pull_time: '2020-01-06T09:40:08.036866579Z',
|
pull_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
push_time: '2020-01-06T09:40:08.036866579Z',
|
push_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
},],
|
}
|
||||||
|
],
|
||||||
references: [new Reference(1), new Reference(2)],
|
references: [new Reference(1), new Reference(2)],
|
||||||
media_type: 'string',
|
media_type: 'string',
|
||||||
"digest": "sha256:4875cda368906fd670c9629b5e416ab3d6c0292015f3c3f12ef37dc9a32fc8d4",
|
"digest": "sha256:4875cda368906fd670c9629b5e416ab3d6c0292015f3c3f12ef37dc9a32fc8d4",
|
||||||
@ -178,12 +206,15 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
{resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE},
|
{resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE},
|
||||||
{resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE},
|
{resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE},
|
||||||
];
|
];
|
||||||
|
const mockRouter = {
|
||||||
|
navigate: () => { }
|
||||||
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
],
|
],
|
||||||
schemas: [
|
schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
@ -198,6 +229,8 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
ChannelService,
|
ChannelService,
|
||||||
|
ArtifactDefaultService,
|
||||||
|
{ provide: Router, useValue: mockRouter },
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
||||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
@ -215,7 +248,6 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ArtifactListTabComponent);
|
fixture = TestBed.createComponent(ArtifactListTabComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
|
||||||
comp.projectId = 1;
|
comp.projectId = 1;
|
||||||
comp.repoName = "library/nginx";
|
comp.repoName = "library/nginx";
|
||||||
comp.hasDeleteImagePermission = true;
|
comp.hasDeleteImagePermission = true;
|
||||||
@ -224,11 +256,7 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
comp.registryUrl = "http://registry.testing.com";
|
comp.registryUrl = "http://registry.testing.com";
|
||||||
comp.withNotary = false;
|
comp.withNotary = false;
|
||||||
comp.withAdmiral = false;
|
comp.withAdmiral = false;
|
||||||
|
|
||||||
|
|
||||||
let labelService: LabelService;
|
let labelService: LabelService;
|
||||||
|
|
||||||
|
|
||||||
artifactService = fixture.debugElement.injector.get(ArtifactService);
|
artifactService = fixture.debugElement.injector.get(ArtifactService);
|
||||||
spy = spyOn(artifactService, "getArtifactList").and.returnValues(of(
|
spy = spyOn(artifactService, "getArtifactList").and.returnValues(of(
|
||||||
{
|
{
|
||||||
@ -243,36 +271,14 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
.withArgs(comp.projectId, permissions )
|
.withArgs(comp.projectId, permissions )
|
||||||
.and.returnValue(of([mockHasAddLabelImagePermission, mockHasRetagImagePermission,
|
.and.returnValue(of([mockHasAddLabelImagePermission, mockHasRetagImagePermission,
|
||||||
mockHasDeleteImagePermission, mockHasScanImagePermission]));
|
mockHasDeleteImagePermission, mockHasScanImagePermission]));
|
||||||
|
|
||||||
labelService = fixture.debugElement.injector.get(LabelService);
|
labelService = fixture.debugElement.injector.get(LabelService);
|
||||||
|
|
||||||
spyLabels = spyOn(labelService, "getGLabels").and.returnValues(of(mockLabels).pipe(delay(0)));
|
spyLabels = spyOn(labelService, "getGLabels").and.returnValues(of(mockLabels).pipe(delay(0)));
|
||||||
spyLabels1 = spyOn(labelService, "getPLabels").withArgs(comp.projectId).and.returnValues(of(mockLabels1).pipe(delay(0)));
|
spyLabels1 = spyOn(labelService, "getPLabels").withArgs(comp.projectId).and.returnValues(of(mockLabels1).pipe(delay(0)));
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
it("should load data", async(() => {
|
it("should load data", async(() => {
|
||||||
expect(spy.calls.any).toBeTruthy();
|
expect(spy.calls.any).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should load project scanner", async(() => {
|
|
||||||
expect(spyScanner.calls.count()).toEqual(2);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should load and render data", () => {
|
|
||||||
;
|
|
||||||
fixture.detectChanges();
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
let de: DebugElement = fixture.debugElement.query(del => del.classes["datagrid-cell"]);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(de).toBeTruthy();
|
|
||||||
let el: HTMLElement = de.nativeElement;
|
|
||||||
expect(el).toBeTruthy();
|
|
||||||
expect(el.textContent.trim()).toEqual("sha256:4875cda3");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -13,58 +13,56 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Input,
|
Input, OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { forkJoin, Observable, Subject, throwError as observableThrowError, of } from "rxjs";
|
import { forkJoin, Observable, Subject, of, Subscription } from "rxjs";
|
||||||
import { catchError, debounceTime, distinctUntilChanged, finalize, map } from 'rxjs/operators';
|
import { catchError, debounceTime, distinctUntilChanged, finalize, map } from 'rxjs/operators';
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { Comparator, Label, State, Tag, ArtifactClickEvent, VulnerabilitySummary } from "../../services/interface";
|
import { ClrLoadingState, ClrDatagridStateInterface, ClrDatagridComparatorInterface } from "@clr/angular";
|
||||||
|
|
||||||
|
import { HttpParams } from "@angular/common/http";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import {
|
import {
|
||||||
RequestQueryParams,
|
ArtifactClickEvent,
|
||||||
RetagService,
|
Comparator, Label, LabelService, ProjectService,
|
||||||
ScanningResultService,
|
RetagService, ScanningResultService,
|
||||||
ProjectService,
|
State, Tag,
|
||||||
ArtifactService
|
UserPermissionService, USERSTATICPERMISSION, VulnerabilitySummary
|
||||||
} from "../../services";
|
} from "../../../../../../lib/services";
|
||||||
import { ErrorHandler } from "../../utils/error-handler/error-handler";
|
|
||||||
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../entities/shared.const";
|
|
||||||
|
|
||||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
|
||||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
|
||||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
calculatePage,
|
calculatePage,
|
||||||
clone,
|
clone,
|
||||||
CustomComparator,
|
CustomComparator,
|
||||||
DEFAULT_PAGE_SIZE, DEFAULT_SUPPORTED_MIME_TYPE,
|
DEFAULT_PAGE_SIZE, DEFAULT_SUPPORTED_MIME_TYPE,
|
||||||
doFiltering,
|
formatSize, VULNERABILITY_SCAN_STATUS
|
||||||
doSorting, formatSize,
|
} from "../../../../../../lib/utils/utils";
|
||||||
VULNERABILITY_SCAN_STATUS,
|
import {
|
||||||
} from "../../utils/utils";
|
ConfirmationAcknowledgement,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
import { CopyInputComponent } from "../push-image/copy-input.component";
|
ConfirmationMessage
|
||||||
import { LabelService } from "../../services/label.service";
|
} from "../../../../../../lib/components/confirmation-dialog";
|
||||||
import { UserPermissionService } from "../../services/permission.service";
|
import { ImageNameInputComponent } from "../../../../../../lib/components/image-name-input/image-name-input.component";
|
||||||
import { USERSTATICPERMISSION } from "../../services/permission-static";
|
import { CopyInputComponent } from "../../../../../../lib/components/push-image/copy-input.component";
|
||||||
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { ArtifactDefaultService } from "../../../artifact/artifact.service";
|
||||||
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
||||||
import { errorHandler as errorHandFn } from "../../utils/shared/shared.utils";
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
import { ClrLoadingState, ClrDatagridStateInterface, ClrDatagridComparatorInterface } from "@clr/angular";
|
import {
|
||||||
import { ChannelService } from "../../services/channel.service";
|
ConfirmationButtons,
|
||||||
import { Artifact, Reference } from "./artifact";
|
ConfirmationState,
|
||||||
import { HttpParams } from "@angular/common/http";
|
ConfirmationTargets
|
||||||
import { ActivatedRoute } from "@angular/router";
|
} from "../../../../../../lib/entities/shared.const";
|
||||||
|
import { operateChanges, OperateInfo, OperationState } from "../../../../../../lib/components/operation/operate";
|
||||||
|
import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils";
|
||||||
|
import { Artifact } from "../../../artifact/artifact";
|
||||||
|
import { Project } from "../../../../project";
|
||||||
|
|
||||||
export interface LabelState {
|
export interface LabelState {
|
||||||
iconsShow: boolean;
|
iconsShow: boolean;
|
||||||
@ -77,7 +75,7 @@ export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z';
|
|||||||
templateUrl: './artifact-list-tab.component.html',
|
templateUrl: './artifact-list-tab.component.html',
|
||||||
styleUrls: ['./artifact-list-tab.component.scss']
|
styleUrls: ['./artifact-list-tab.component.scss']
|
||||||
})
|
})
|
||||||
export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
signedCon: { [key: string]: any | string[] } = {};
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@ -86,18 +84,11 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
@Input() repoName: string;
|
@Input() repoName: string;
|
||||||
referArtifactArray: string[] = [];
|
referArtifactArray: string[] = [];
|
||||||
@Input() isEmbedded: boolean;
|
@Input() isEmbedded: boolean;
|
||||||
|
|
||||||
@Input() hasSignedIn: boolean;
|
@Input() hasSignedIn: boolean;
|
||||||
@Input() isGuest: boolean;
|
@Input() isGuest: boolean;
|
||||||
@Input() registryUrl: string;
|
@Input() registryUrl: string;
|
||||||
@Input() withNotary: boolean;
|
@Input() withNotary: boolean;
|
||||||
@Input() withAdmiral: boolean;
|
@Input() withAdmiral: boolean;
|
||||||
@Output() refreshRepo = new EventEmitter<boolean>();
|
|
||||||
@Output() tagClickEvent = new EventEmitter<ArtifactClickEvent>();
|
|
||||||
@Output() signatureOutput = new EventEmitter<any>();
|
|
||||||
@Output() putReferArtifactArray = new EventEmitter<string[]>();
|
|
||||||
|
|
||||||
|
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
artifactList: Artifact[] = [];
|
artifactList: Artifact[] = [];
|
||||||
availableTime = AVAILABLE_TIME;
|
availableTime = AVAILABLE_TIME;
|
||||||
@ -163,91 +154,123 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
onSendingScanCommand: boolean;
|
onSendingScanCommand: boolean;
|
||||||
|
|
||||||
|
artifactDigest: string;
|
||||||
|
depth: string;
|
||||||
|
hasInit: boolean = false;
|
||||||
|
triggerSub: Subscription;
|
||||||
|
labelNameFilterSub: Subscription;
|
||||||
|
stickLabelNameFilterSub: Subscription;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandlerService: ErrorHandler,
|
||||||
private retagService: RetagService,
|
private retagService: RetagService,
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private labelService: LabelService,
|
private labelService: LabelService,
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactDefaultService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private ref: ChangeDetectorRef,
|
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private scanningService: ScanningResultService
|
private scanningService: ScanningResultService,
|
||||||
) { }
|
private router: Router,
|
||||||
|
) {
|
||||||
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.activatedRoute.params.subscribe(params => {
|
||||||
|
this.depth = this.activatedRoute.snapshot.params['depth'];
|
||||||
|
if (this.depth) {
|
||||||
|
const arr: string[] = this.depth.split('-');
|
||||||
|
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
||||||
|
}
|
||||||
|
if (this.hasInit) {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.totalCount = 0;
|
||||||
|
const st: ClrDatagridStateInterface = {page: {from: 0, to: this.pageSize - 1, size: this.pageSize}};
|
||||||
|
this.clrLoad(st);
|
||||||
|
}
|
||||||
|
this.init();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.triggerSub) {
|
||||||
|
this.triggerSub.unsubscribe();
|
||||||
|
this.triggerSub = null;
|
||||||
|
}
|
||||||
|
if (this.labelNameFilterSub) {
|
||||||
|
this.labelNameFilterSub.unsubscribe();
|
||||||
|
this.labelNameFilterSub = null;
|
||||||
|
}
|
||||||
|
if (this.stickLabelNameFilterSub) {
|
||||||
|
this.stickLabelNameFilterSub.unsubscribe();
|
||||||
|
this.stickLabelNameFilterSub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
this.hasInit = true;
|
||||||
|
this.depth = this.activatedRoute.snapshot.params['depth'];
|
||||||
|
if (this.depth) {
|
||||||
|
const arr: string[] = this.depth.split('-');
|
||||||
|
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
||||||
|
}
|
||||||
if (!this.projectId) {
|
if (!this.projectId) {
|
||||||
this.errorHandler.error("Project ID cannot be unset.");
|
this.errorHandlerService.error("Project ID cannot be unset.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.activatedRoute.data.subscribe(res => {
|
const resolverData = this.activatedRoute.snapshot.data;
|
||||||
this.projectName = res.projectResolver.name;
|
if (resolverData) {
|
||||||
});
|
const pro: Project = <Project>resolverData['projectResolver'];
|
||||||
|
this.projectName = pro.name;
|
||||||
|
}
|
||||||
|
|
||||||
this.getProjectScanner();
|
this.getProjectScanner();
|
||||||
if (!this.repoName) {
|
if (!this.repoName) {
|
||||||
this.errorHandler.error("Repo name cannot be unset.");
|
this.errorHandlerService.error("Repo name cannot be unset.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let refer = JSON.parse(sessionStorage.getItem('reference'));
|
if (!this.triggerSub) {
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repoName) {
|
this.triggerSub = this.artifactService.TriggerArtifactChan$.subscribe(res => {
|
||||||
this.referArtifactArray = refer.referArray;
|
let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} };
|
||||||
|
this.clrLoad(st);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.artifactService.TriggerArtifactChan$.subscribe(res => {
|
|
||||||
let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} };
|
|
||||||
this.clrLoad(st);
|
|
||||||
});
|
|
||||||
this.lastFilteredTagName = '';
|
this.lastFilteredTagName = '';
|
||||||
|
if (!this.labelNameFilterSub) {
|
||||||
this.labelNameFilter
|
this.labelNameFilterSub = this.labelNameFilter
|
||||||
.pipe(debounceTime(500))
|
.pipe(debounceTime(500))
|
||||||
.pipe(distinctUntilChanged())
|
.pipe(distinctUntilChanged())
|
||||||
.subscribe((name: string) => {
|
.subscribe((name: string) => {
|
||||||
if (this.filterName.length) {
|
if (this.filterName.length) {
|
||||||
this.filterOnGoing = true;
|
this.filterOnGoing = true;
|
||||||
|
this.imageFilterLabels.forEach(data => {
|
||||||
this.imageFilterLabels.forEach(data => {
|
if (data.label.name.indexOf(this.filterName) !== -1) {
|
||||||
if (data.label.name.indexOf(this.filterName) !== -1) {
|
data.show = true;
|
||||||
data.show = true;
|
} else {
|
||||||
} else {
|
data.show = false;
|
||||||
data.show = false;
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
setTimeout(() => {
|
});
|
||||||
setInterval(() => this.ref.markForCheck(), 200);
|
}
|
||||||
}, 1000);
|
if (!this.stickLabelNameFilterSub) {
|
||||||
}
|
this.stickLabelNameFilterSub = this.stickLabelNameFilter
|
||||||
});
|
.pipe(debounceTime(500))
|
||||||
|
.pipe(distinctUntilChanged())
|
||||||
this.stickLabelNameFilter
|
.subscribe((name: string) => {
|
||||||
.pipe(debounceTime(500))
|
if (this.stickName.length) {
|
||||||
.pipe(distinctUntilChanged())
|
this.filterOnGoing = true;
|
||||||
.subscribe((name: string) => {
|
this.imageStickLabels.forEach(data => {
|
||||||
if (this.stickName.length) {
|
if (data.label.name.indexOf(this.stickName) !== -1) {
|
||||||
this.filterOnGoing = true;
|
data.show = true;
|
||||||
|
} else {
|
||||||
this.imageStickLabels.forEach(data => {
|
data.show = false;
|
||||||
if (data.label.name.indexOf(this.stickName) !== -1) {
|
}
|
||||||
data.show = true;
|
});
|
||||||
} else {
|
}
|
||||||
data.show = false;
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
setInterval(() => this.ref.markForCheck(), 200);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.getImagePermissionRule(this.projectId);
|
this.getImagePermissionRule(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public get filterLabelPieceWidth() {
|
public get filterLabelPieceWidth() {
|
||||||
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
||||||
return len > 210 ? 210 : len;
|
return len > 210 ? 210 : len;
|
||||||
@ -292,88 +315,86 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
}
|
}
|
||||||
|
// todo
|
||||||
|
clrDgRefresh(state: ClrDatagridStateInterface) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.clrLoad(state);
|
||||||
|
});
|
||||||
|
}
|
||||||
clrLoad(state: ClrDatagridStateInterface): void {
|
clrLoad(state: ClrDatagridStateInterface): void {
|
||||||
if (!state || !state.page) {
|
this.artifactList = [];
|
||||||
return;
|
this.loading = true;
|
||||||
}
|
if (!state || !state.page) {
|
||||||
this.selectedRow = [];
|
return;
|
||||||
// Keep it for future filtering and sorting
|
}
|
||||||
|
this.selectedRow = [];
|
||||||
|
// Keep it for future filtering and sorting
|
||||||
|
|
||||||
let pageNumber: number = calculatePage(state);
|
let pageNumber: number = calculatePage(state);
|
||||||
if (pageNumber <= 0) { pageNumber = 1; }
|
if (pageNumber <= 0) { pageNumber = 1; }
|
||||||
let sortBy: any = '';
|
let sortBy: any = '';
|
||||||
if (state.sort) {
|
if (state.sort) {
|
||||||
sortBy = state.sort.by as string | ClrDatagridComparatorInterface<any>;
|
sortBy = state.sort.by as string | ClrDatagridComparatorInterface<any>;
|
||||||
sortBy = sortBy.fieldName ? sortBy.fieldName : sortBy;
|
sortBy = sortBy.fieldName ? sortBy.fieldName : sortBy;
|
||||||
sortBy = state.sort.reverse ? `-${sortBy}` : sortBy;
|
sortBy = state.sort.reverse ? `-${sortBy}` : sortBy;
|
||||||
}
|
}
|
||||||
this.loading = true;
|
this.currentState = state;
|
||||||
this.currentState = state;
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
let params = new HttpParams();
|
|
||||||
params = params.set('with_label', 'true');
|
|
||||||
params = params.set('with_scan_overview', 'true');
|
|
||||||
params = params.set('with_signature', 'true');
|
|
||||||
params = params.set('with_immutable_status', 'true');
|
|
||||||
|
|
||||||
if (pageNumber && this.pageSize) {
|
|
||||||
params = params.set('page', pageNumber + '').set('page_size', this.pageSize + '');
|
|
||||||
}
|
|
||||||
if (sortBy) {
|
|
||||||
params = params.set('sort', sortBy);
|
|
||||||
}
|
|
||||||
if (state.filters && state.filters.length) {
|
|
||||||
state.filters.forEach(item => {
|
|
||||||
params = params.set(item.property, item.value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.projectService.getProject(this.projectId).subscribe(project => {
|
|
||||||
this.projectName = project.name;
|
|
||||||
let refer = JSON.parse(sessionStorage.getItem('reference'));
|
|
||||||
this.referArtifactArray = [];
|
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repoName) {
|
|
||||||
this.referArtifactArray = refer.referArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.referArtifactArray.length) {
|
|
||||||
let observableLists: Observable<Artifact>[] = [];
|
|
||||||
|
|
||||||
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
|
||||||
this.referArtifactArray[this.referArtifactArray.length - 1]).subscribe(artifact => {
|
|
||||||
this.totalCount = artifact.references.length;
|
|
||||||
artifact.references.forEach((child, index) => {
|
|
||||||
if (index >= (pageNumber - 1) * this.pageSize && index < pageNumber * this.pageSize) {
|
|
||||||
observableLists.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
|
||||||
child.child_digest));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
let params = new HttpParams();
|
||||||
|
params = params.set('with_label', 'true');
|
||||||
|
params = params.set('with_scan_overview', 'true');
|
||||||
|
params = params.set('with_signature', 'true');
|
||||||
|
params = params.set('with_immutable_status', 'true');
|
||||||
|
if (pageNumber && this.pageSize) {
|
||||||
|
params = params.set('page', pageNumber + '').set('page_size', this.pageSize + '');
|
||||||
|
}
|
||||||
|
if (sortBy) {
|
||||||
|
params = params.set('sort', sortBy);
|
||||||
|
}
|
||||||
|
if (state.filters && state.filters.length) {
|
||||||
|
state.filters.forEach(item => {
|
||||||
|
params = params.set(item.property, item.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.artifactDigest) {
|
||||||
|
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName, this.artifactDigest).subscribe(
|
||||||
|
res => {
|
||||||
|
let observableLists: Observable<Artifact>[] = [];
|
||||||
|
this.totalCount = res.references.length;
|
||||||
|
res.references.forEach((child, index) => {
|
||||||
|
if (index >= (pageNumber - 1) * this.pageSize && index < pageNumber * this.pageSize) {
|
||||||
|
observableLists.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
||||||
|
child.child_digest));
|
||||||
|
}
|
||||||
|
});
|
||||||
forkJoin(observableLists).pipe(finalize(() => {
|
forkJoin(observableLists).pipe(finalize(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})).subscribe(artifacts => {
|
})).subscribe(artifacts => {
|
||||||
this.artifactList = artifacts;
|
this.artifactList = artifacts;
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandlerService.error(error);
|
||||||
});
|
});
|
||||||
});
|
}, error => {
|
||||||
} else {
|
this.loading = false;
|
||||||
this.artifactService.getArtifactList(this.projectName, this.repoName, params).subscribe(res => {
|
|
||||||
if (res.headers) {
|
|
||||||
let xHeader: string = res.headers.get("X-Total-Count");
|
|
||||||
if (xHeader) {
|
|
||||||
this.totalCount = parseInt(xHeader, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.artifactList = res.body;
|
);
|
||||||
this.loading = false;
|
} else {
|
||||||
}, error => {
|
this.artifactService.getArtifactList(this.projectName, this.repoName, params)
|
||||||
// error
|
.pipe(finalize(() => this.loading = false))
|
||||||
this.loading = false;
|
.subscribe(res => {
|
||||||
});
|
if (res.headers) {
|
||||||
|
let xHeader: string = res.headers.get("X-Total-Count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.totalCount = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.artifactList = res.body;
|
||||||
|
}, error => {
|
||||||
|
// error
|
||||||
|
this.errorHandlerService.error(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
@ -389,7 +410,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
this.imageFilterLabels = clone(this.imageLabels);
|
this.imageFilterLabels = clone(this.imageLabels);
|
||||||
this.imageStickLabels = clone(this.imageLabels);
|
this.imageStickLabels = clone(this.imageLabels);
|
||||||
}, error => this.errorHandler.error(error));
|
}, error => this.errorHandlerService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
labelSelectedChange(artifact?: Artifact[]): void {
|
labelSelectedChange(artifact?: Artifact[]): void {
|
||||||
@ -453,7 +474,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
this.inprogress = false;
|
this.inprogress = false;
|
||||||
}, err => {
|
}, err => {
|
||||||
this.inprogress = false;
|
this.inprogress = false;
|
||||||
this.errorHandler.error(err);
|
this.errorHandlerService.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,7 +493,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
this.inprogress = false;
|
this.inprogress = false;
|
||||||
}, err => {
|
}, err => {
|
||||||
this.inprogress = false;
|
this.inprogress = false;
|
||||||
this.errorHandler.error(err);
|
this.errorHandlerService.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,48 +628,6 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadArtifactList(params?) {
|
|
||||||
this.artifactList = [];
|
|
||||||
this.loading = true;
|
|
||||||
let refer = JSON.parse(sessionStorage.getItem('reference'));
|
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repoName) {
|
|
||||||
this.referArtifactArray = refer.referArray;
|
|
||||||
}
|
|
||||||
if (this.referArtifactArray.length) {
|
|
||||||
let observableLists: Observable<Artifact>[] = [];
|
|
||||||
|
|
||||||
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
|
||||||
this.referArtifactArray[this.referArtifactArray.length - 1]).subscribe(artifact => {
|
|
||||||
artifact.references.forEach(child => {
|
|
||||||
observableLists.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
|
||||||
child.child_digest));
|
|
||||||
});
|
|
||||||
forkJoin(observableLists).subscribe(artifacts => {
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
this.artifactList = artifacts;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.artifactService.getArtifactList(this.projectName, this.repoName, params).subscribe(res => {
|
|
||||||
if (res.headers) {
|
|
||||||
let xHeader: string = res.headers.get("X-Total-Count");
|
|
||||||
if (xHeader) {
|
|
||||||
this.totalCount = parseInt(xHeader, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.artifactList = res.body;
|
|
||||||
this.loading = false;
|
|
||||||
}, error => {
|
|
||||||
// error
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sizeTransform(tagSize: string): string {
|
sizeTransform(tagSize: string): string {
|
||||||
return formatSize(tagSize);
|
return formatSize(tagSize);
|
||||||
}
|
}
|
||||||
@ -658,7 +637,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
this.retagDialogOpened = true;
|
this.retagDialogOpened = true;
|
||||||
this.retagSrcImage = this.repoName + ":" + this.selectedRow[0].digest;
|
this.retagSrcImage = this.repoName + ":" + this.selectedRow[0].digest;
|
||||||
} else {
|
} else {
|
||||||
this.errorHandler.error("One tag should be selected before retag.");
|
this.errorHandlerService.error("One tag should be selected before retag.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,7 +655,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
}))
|
}))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
||||||
this.errorHandler.info(res);
|
this.errorHandlerService.info(res);
|
||||||
if (`${this.imageNameInput.projectName.value}/${this.imageNameInput.repoName.value}` === this.repoName) {
|
if (`${this.imageNameInput.projectName.value}/${this.imageNameInput.repoName.value}` === this.repoName) {
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
if (!st) {
|
if (!st) {
|
||||||
@ -689,7 +668,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandlerService.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,7 +763,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}), catchError(error => {
|
}), catchError(error => {
|
||||||
const message = errorHandFn(error);
|
const message = errorHandler(error);
|
||||||
this.translateService.get(message).subscribe(res =>
|
this.translateService.get(message).subscribe(res =>
|
||||||
operateChanges(operMessage, OperationState.failure, res)
|
operateChanges(operMessage, OperationState.failure, res)
|
||||||
);
|
);
|
||||||
@ -802,16 +781,9 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTagClick(artifact: Artifact): void {
|
goIntoArtifactSummaryPage(artifact: Artifact): void {
|
||||||
if (artifact) {
|
const relativeRouterLink: string[] = ['artifacts', artifact.digest];
|
||||||
let evt: ArtifactClickEvent = {
|
this.router.navigate(relativeRouterLink , { relativeTo: this.activatedRoute });
|
||||||
project_id: this.projectId,
|
|
||||||
repository_name: this.repoName,
|
|
||||||
digest: artifact.digest,
|
|
||||||
artifact_id: artifact.id,
|
|
||||||
};
|
|
||||||
this.tagClickEvent.emit(evt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuccess($event: any): void {
|
onSuccess($event: any): void {
|
||||||
@ -832,7 +804,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
// Get vulnerability scanning status
|
// Get vulnerability scanning status
|
||||||
scanStatus(artifact: Artifact): string {
|
scanStatus(artifact: Artifact): string {
|
||||||
if (artifact) {
|
if (artifact) {
|
||||||
let so = this.handleScanOverview(artifact.scan_overview);
|
let so = this.handleScanOverview((<any>artifact).scan_overview);
|
||||||
if (so && so.scan_status) {
|
if (so && so.scan_status) {
|
||||||
return so.scan_status;
|
return so.scan_status;
|
||||||
}
|
}
|
||||||
@ -864,7 +836,7 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
this.getAllLabels();
|
this.getAllLabels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, error => this.errorHandler.error(error));
|
}, error => this.errorHandlerService.error(error));
|
||||||
}
|
}
|
||||||
// Trigger scan
|
// Trigger scan
|
||||||
scanNow(): void {
|
scanNow(): void {
|
||||||
@ -903,22 +875,15 @@ export class ArtifactListTabComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
goIntoIndexArtifact(artifact: Artifact) {
|
||||||
refer(artifact: Artifact) {
|
let depth: string = '';
|
||||||
this.referArtifactArray.push(artifact.digest);
|
if (this.depth) {
|
||||||
sessionStorage.setItem('reference', JSON.stringify({ projectId: this.projectId, repo: this.repoName
|
depth = this.depth + '-' + artifact.digest;
|
||||||
, referArray: this.referArtifactArray}));
|
} else {
|
||||||
|
depth = artifact.digest;
|
||||||
if (this.referArtifactArray.length) {
|
|
||||||
this.putReferArtifactArray.emit(this.referArtifactArray);
|
|
||||||
}
|
}
|
||||||
let st: ClrDatagridStateInterface = this.currentState;
|
const linkUrl = ['harbor', 'projects', this.projectId, 'repositories', this.repoName, 'depth', depth];
|
||||||
if (!st) {
|
this.router.navigate(linkUrl);
|
||||||
st = { page: {} };
|
|
||||||
}
|
|
||||||
st.page.size = this.pageSize;
|
|
||||||
st.page.from = 0;
|
|
||||||
st.page.to = this.pageSize - 1;
|
|
||||||
this.clrLoad(st);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="goBack()"></clr-icon>
|
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="goBack()"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<h2 sub-header-title class="custom-h2">{{showCurrentTitle | slice:0:15}}</h2>
|
<h2 sub-header-title class="custom-h2">{{artifactDigest ? artifactDigest : showCurrentTitle | slice:0:15}}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
||||||
<ul id="configTabs" class="nav" role="tablist">
|
<ul id="configTabs" class="nav" role="tablist">
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item" *ngIf="!artifactDigest">
|
||||||
<button id="repo-info" class="btn btn-link nav-link" aria-controls="info" [class.active]='isCurrentTabLink("repo-info")'
|
<button id="repo-info" class="btn btn-link nav-link" aria-controls="info" [class.active]='isCurrentTabLink("repo-info")'
|
||||||
type="button" (click)='tabLinkClick("repo-info")'>{{'REPOSITORY.INFO' | translate}}</button>
|
type="button" (click)='tabLinkClick("repo-info")'>{{'REPOSITORY.INFO' | translate}}</button>
|
||||||
</li>
|
</li>
|
||||||
@ -54,9 +54,9 @@
|
|||||||
</section>
|
</section>
|
||||||
<section id="image" role="tabpanel" aria-labelledby="repo-image" [hidden]='!isCurrentTabContent("image")'>
|
<section id="image" role="tabpanel" aria-labelledby="repo-image" [hidden]='!isCurrentTabContent("image")'>
|
||||||
<div id="images-container">
|
<div id="images-container">
|
||||||
<artifact-list-tab ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)"
|
<artifact-list-tab ngProjectAs="clr-dg-row-detail"
|
||||||
class="sub-grid-custom" [repoName]="repoName" artifact [registryUrl]="registryUrl" [withNotary]="withNotary" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn"
|
class="sub-grid-custom" [repoName]="repoName" artifact [registryUrl]="registryUrl" [withNotary]="withNotary" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn"
|
||||||
[isGuest]="isGuest" [projectId]="projectId" [memberRoleID]="memberRoleID" (putReferArtifactArray)="putReferArtifactArray($event)"></artifact-list-tab>
|
[isGuest]="isGuest" [projectId]="projectId" [memberRoleID]="memberRoleID"></artifact-list-tab>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
@ -0,0 +1,152 @@
|
|||||||
|
import { ComponentFixture, TestBed, async, } from '@angular/core/testing';
|
||||||
|
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ArtifactListComponent } from './artifact-list.component';
|
||||||
|
import { of } from "rxjs";
|
||||||
|
import { delay } from 'rxjs/operators';
|
||||||
|
import { ClarityModule } from '@clr/angular';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { RepositoryDefaultService, RepositoryService } from "../../repository.service";
|
||||||
|
import {
|
||||||
|
Repository,
|
||||||
|
RepositoryItem,
|
||||||
|
SystemInfo, SystemInfoDefaultService,
|
||||||
|
SystemInfoService,
|
||||||
|
} from "../../../../../lib/services";
|
||||||
|
import { ArtifactDefaultService, ArtifactService } from "../../artifact/artifact.service";
|
||||||
|
import { ChannelService } from "../../../../../lib/services/channel.service";
|
||||||
|
import { FormsModule } from "@angular/forms";
|
||||||
|
import { MarkdownModule } from "ngx-markdown";
|
||||||
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateService } from "@ngx-translate/core";
|
||||||
|
import { ErrorHandler } from "../../../../../lib/utils/error-handler";
|
||||||
|
import { HttpClientTestingModule } from "@angular/common/http/testing";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../lib/entities/service.config";
|
||||||
|
import { SharedModule } from "../../../../../lib/utils/shared/shared.module";
|
||||||
|
|
||||||
|
|
||||||
|
describe('ArtifactListComponent (inline template)', () => {
|
||||||
|
|
||||||
|
let compRepo: ArtifactListComponent;
|
||||||
|
let fixture: ComponentFixture<ArtifactListComponent>;
|
||||||
|
let repositoryService: RepositoryService;
|
||||||
|
let systemInfoService: SystemInfoService;
|
||||||
|
let artifactService: ArtifactService;
|
||||||
|
let spyRepos: jasmine.Spy;
|
||||||
|
let spySystemInfo: jasmine.Spy;
|
||||||
|
let mockActivatedRoute = {
|
||||||
|
data: of(
|
||||||
|
{
|
||||||
|
projectResolver: {
|
||||||
|
name: 'library'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
params: {
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mockChannelService = {
|
||||||
|
scanCommand$: of(1)
|
||||||
|
};
|
||||||
|
let mockSystemInfo: SystemInfo = {
|
||||||
|
'with_notary': true,
|
||||||
|
'with_admiral': false,
|
||||||
|
'admiral_endpoint': 'NA',
|
||||||
|
'auth_mode': 'db_auth',
|
||||||
|
'registry_url': '10.112.122.56',
|
||||||
|
'project_creation_restriction': 'everyone',
|
||||||
|
'self_registration': true,
|
||||||
|
'has_ca_root': false,
|
||||||
|
'harbor_version': 'v1.1.1-rc1-160-g565110d'
|
||||||
|
};
|
||||||
|
|
||||||
|
let mockRepoData: RepositoryItem[] = [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'name': 'library/busybox',
|
||||||
|
'project_id': 1,
|
||||||
|
'description': 'asdfsadf',
|
||||||
|
'pull_count': 0,
|
||||||
|
'star_count': 0,
|
||||||
|
'tags_count': 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'name': 'library/nginx',
|
||||||
|
'project_id': 1,
|
||||||
|
'description': 'asdf',
|
||||||
|
'pull_count': 0,
|
||||||
|
'star_count': 0,
|
||||||
|
'tags_count': 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockRepo: Repository = {
|
||||||
|
metadata: { xTotalCount: 2 },
|
||||||
|
data: mockRepoData
|
||||||
|
};
|
||||||
|
const fakedErrorHandler = {
|
||||||
|
error: () => {}
|
||||||
|
};
|
||||||
|
const config: IServiceConfig = {
|
||||||
|
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||||
|
};
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
ClarityModule,
|
||||||
|
SharedModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateFakeLoader,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
schemas: [
|
||||||
|
NO_ERRORS_SCHEMA
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ArtifactListComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
TranslateService,
|
||||||
|
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
||||||
|
{ provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||||
|
{ provide: ChannelService, useValue: mockChannelService },
|
||||||
|
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||||
|
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ArtifactListComponent);
|
||||||
|
compRepo = fixture.componentInstance;
|
||||||
|
compRepo.projectId = 1;
|
||||||
|
compRepo.hasProjectAdminRole = true;
|
||||||
|
compRepo.repoName = 'library/nginx';
|
||||||
|
repositoryService = fixture.debugElement.injector.get(RepositoryService);
|
||||||
|
systemInfoService = fixture.debugElement.injector.get(SystemInfoService);
|
||||||
|
artifactService = fixture.debugElement.injector.get(ArtifactService);
|
||||||
|
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(of(mockRepo).pipe(delay(0)));
|
||||||
|
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(of(mockSystemInfo).pipe(delay(0)));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
let originalTimeout;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||||
|
});
|
||||||
|
it('should create', () => {
|
||||||
|
expect(compRepo).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -13,16 +13,18 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
|
import { Component, OnInit, ViewChild, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { State } from '../../services/interface';
|
import { ArtifactClickEvent, RepositoryItem, State, SystemInfo, SystemInfoService } from "../../../../../lib/services";
|
||||||
|
|
||||||
import { RepositoryService } from '../../services/repository.service';
|
|
||||||
import {
|
import {
|
||||||
RepositoryItem, ArtifactClickEvent,
|
ConfirmationAcknowledgement,
|
||||||
SystemInfo, SystemInfoService, ArtifactService
|
ConfirmationDialogComponent,
|
||||||
} from '../../services';
|
ConfirmationMessage
|
||||||
import { ErrorHandler } from '../../utils/error-handler';
|
} from "../../../../../lib/components/confirmation-dialog";
|
||||||
import { ConfirmationState, ConfirmationTargets } from '../../entities/shared.const';
|
import { ErrorHandler } from "../../../../../lib/utils/error-handler";
|
||||||
import { ConfirmationDialogComponent, ConfirmationMessage, ConfirmationAcknowledgement } from '../confirmation-dialog';
|
import { RepositoryService } from "../../repository.service";
|
||||||
|
import { ArtifactService } from "../../artifact/artifact.service";
|
||||||
|
import { ConfirmationState, ConfirmationTargets } from "../../../../../lib/entities/shared.const";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
const TabLinkContentMap: { [index: string]: string } = {
|
const TabLinkContentMap: { [index: string]: string } = {
|
||||||
'repo-info': 'info',
|
'repo-info': 'info',
|
||||||
'repo-image': 'image'
|
'repo-image': 'image'
|
||||||
@ -33,7 +35,7 @@ const TabLinkContentMap: { [index: string]: string } = {
|
|||||||
templateUrl: './artifact-list.component.html',
|
templateUrl: './artifact-list.component.html',
|
||||||
styleUrls: ['./artifact-list.component.scss']
|
styleUrls: ['./artifact-list.component.scss']
|
||||||
})
|
})
|
||||||
export class ArtifactListComponent implements OnInit, OnDestroy {
|
export class ArtifactListComponent implements OnInit {
|
||||||
signedCon: { [key: string]: any | string[] } = {};
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() memberRoleID: number;
|
@Input() memberRoleID: number;
|
||||||
@ -60,14 +62,23 @@ export class ArtifactListComponent implements OnInit, OnDestroy {
|
|||||||
@ViewChild('confirmationDialog', { static: false })
|
@ViewChild('confirmationDialog', { static: false })
|
||||||
confirmationDlg: ConfirmationDialogComponent;
|
confirmationDlg: ConfirmationDialogComponent;
|
||||||
showCurrentTitle: string;
|
showCurrentTitle: string;
|
||||||
|
artifactDigest: string;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private repositoryService: RepositoryService,
|
private repositoryService: RepositoryService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
) { }
|
private activatedRoute: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
this.activatedRoute.params.subscribe(params => {
|
||||||
|
let depth = this.activatedRoute.snapshot.params['depth'];
|
||||||
|
if (depth) {
|
||||||
|
const arr: string[] = depth.split('-');
|
||||||
|
this.artifactDigest = depth.split('-')[arr.length - 1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public get registryUrl(): string {
|
public get registryUrl(): string {
|
||||||
return this.systemInfo ? this.systemInfo.registry_url : '';
|
return this.systemInfo ? this.systemInfo.registry_url : '';
|
||||||
@ -95,11 +106,6 @@ export class ArtifactListComponent implements OnInit, OnDestroy {
|
|||||||
this.showCurrentTitle = res[res.length - 1];
|
this.showCurrentTitle = res[res.length - 1];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let refer = JSON.parse(sessionStorage.getItem('reference'));
|
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repoName) {
|
|
||||||
this.putReferArtifactArray(refer.referArray);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieve(state?: State) {
|
retrieve(state?: State) {
|
||||||
@ -113,19 +119,9 @@ export class ArtifactListComponent implements OnInit, OnDestroy {
|
|||||||
this.systemInfoService.getSystemInfo()
|
this.systemInfoService.getSystemInfo()
|
||||||
.subscribe(systemInfo => this.systemInfo = systemInfo, error => this.errorHandler.error(error));
|
.subscribe(systemInfo => this.systemInfo = systemInfo, error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
saveSignatures(event: { [key: string]: string[] }): void {
|
|
||||||
Object.assign(this.signedCon, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
}
|
}
|
||||||
|
|
||||||
watchTagClickEvt(tagClickEvt: ArtifactClickEvent): void {
|
|
||||||
this.tagClickEvent.emit(tagClickEvt);
|
|
||||||
}
|
|
||||||
|
|
||||||
isCurrentTabLink(tabID: string): boolean {
|
isCurrentTabLink(tabID: string): boolean {
|
||||||
return this.currentTabID === tabID;
|
return this.currentTabID === tabID;
|
||||||
}
|
}
|
||||||
@ -195,15 +191,4 @@ export class ArtifactListComponent implements OnInit, OnDestroy {
|
|||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
sessionStorage.removeItem('reference');
|
|
||||||
|
|
||||||
}
|
|
||||||
putReferArtifactArray(referArtifactArray) {
|
|
||||||
if (referArtifactArray.length) {
|
|
||||||
this.showCurrentTitle = referArtifactArray[referArtifactArray.length - 1];
|
|
||||||
this.putArtifactReferenceArr.emit(referArtifactArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArtifactAdditionsComponent } from './artifact-additions.component';
|
import { ArtifactAdditionsComponent } from './artifact-additions.component';
|
||||||
import { AdditionLinks } from "../../../../../ng-swagger-gen/models/addition-links";
|
import { AdditionLinks } from "../../../../../../ng-swagger-gen/models/addition-links";
|
||||||
import { HarborLibraryModule } from "../../../harbor-library.module";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../lib/entities/service.config";
|
||||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../entities/service.config";
|
import { ProjectModule } from "../../../project.module";
|
||||||
|
|
||||||
|
|
||||||
describe('ArtifactAdditionsComponent', () => {
|
describe('ArtifactAdditionsComponent', () => {
|
||||||
const mockedAdditionLinks: AdditionLinks = {
|
const mockedAdditionLinks: AdditionLinks = {
|
||||||
@ -21,7 +21,7 @@ describe('ArtifactAdditionsComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HarborLibraryModule
|
ProjectModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit, Input, SimpleChanges } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
import { AdditionLinks } from "../../../../../ng-swagger-gen/models/addition-links";
|
|
||||||
import { ADDITIONS } from "./models";
|
import { ADDITIONS } from "./models";
|
||||||
import { AdditionLink } from "../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLinks } from "../../../../../../ng-swagger-gen/models/addition-links";
|
||||||
|
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'artifact-additions',
|
selector: 'artifact-additions',
|
@ -3,13 +3,14 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component';
|
import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component';
|
||||||
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { VulnerabilityItem } from "../../../../services";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { VulnerabilityItem } from "../../../../../../lib/services";
|
||||||
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
describe('ArtifactVulnerabilitiesComponent', () => {
|
describe('ArtifactVulnerabilitiesComponent', () => {
|
||||||
let component: ArtifactVulnerabilitiesComponent;
|
let component: ArtifactVulnerabilitiesComponent;
|
@ -1,13 +1,11 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import {
|
|
||||||
VulnerabilityItem
|
|
||||||
} from "../../../../services";
|
|
||||||
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
||||||
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../utils/utils";
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { VulnerabilityItem } from "../../../../../../lib/services";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../lib/utils/utils";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-artifact-vulnerabilities',
|
selector: 'hbr-artifact-vulnerabilities',
|
@ -1,14 +1,14 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
|
||||||
import { BuildHistoryComponent } from "./build-history.component";
|
import { BuildHistoryComponent } from "./build-history.component";
|
||||||
import { ArtifactBuildHistory } from "../models";
|
import { ArtifactBuildHistory } from "../models";
|
||||||
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
describe('BuildHistoryComponent', () => {
|
describe('BuildHistoryComponent', () => {
|
||||||
let component: BuildHistoryComponent;
|
let component: BuildHistoryComponent;
|
@ -1,9 +1,9 @@
|
|||||||
import { Component, Input, OnInit } from "@angular/core";
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { ArtifactBuildHistory } from "../models";
|
import { ArtifactBuildHistory } from "../models";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-artifact-build-history",
|
selector: "hbr-artifact-build-history",
|
@ -1,12 +1,13 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { DependenciesComponent } from "./dependencies.component";
|
import { DependenciesComponent } from "./dependencies.component";
|
||||||
import { ErrorHandler } from '../../../../utils/error-handler';
|
|
||||||
import { AdditionsService } from '../additions.service';
|
import { AdditionsService } from '../additions.service';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../../../entities/service.config';
|
|
||||||
import { ArtifactDependency } from "../models";
|
import { ArtifactDependency } from "../models";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
describe('DependenciesComponent', () => {
|
describe('DependenciesComponent', () => {
|
||||||
let component: DependenciesComponent;
|
let component: DependenciesComponent;
|
@ -4,9 +4,10 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { ArtifactDependency } from "../models";
|
import { ArtifactDependency } from "../models";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-artifact-dependencies",
|
selector: "hbr-artifact-dependencies",
|
@ -1,4 +1,4 @@
|
|||||||
import { HelmChartMaintainer } from "../../../../app/project/helm-chart/helm-chart.interface.service";
|
import { HelmChartMaintainer } from "../../../helm-chart/helm-chart.interface.service";
|
||||||
|
|
||||||
export class ArtifactBuildHistory {
|
export class ArtifactBuildHistory {
|
||||||
created: Date;
|
created: Date;
|
@ -1,12 +1,12 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
|
||||||
import { SummaryComponent } from "./summary.component";
|
import { SummaryComponent } from "./summary.component";
|
||||||
import { HarborLibraryModule } from "../../../../harbor-library.module";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../../entities/service.config";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { ProjectModule } from "../../../../project.module";
|
||||||
|
|
||||||
describe('SummaryComponent', () => {
|
describe('SummaryComponent', () => {
|
||||||
let component: SummaryComponent;
|
let component: SummaryComponent;
|
||||||
@ -165,7 +165,7 @@ describe('SummaryComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HarborLibraryModule
|
ProjectModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
@ -3,9 +3,10 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Input
|
Input
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-artifact-summary",
|
selector: "hbr-artifact-summary",
|
@ -7,9 +7,10 @@ import { MarkdownModule, MarkdownService, MarkedOptions } from 'ngx-markdown';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { ValuesComponent } from "./values.component";
|
import { ValuesComponent } from "./values.component";
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
describe('ValuesComponent', () => {
|
describe('ValuesComponent', () => {
|
||||||
let component: ValuesComponent;
|
let component: ValuesComponent;
|
@ -3,9 +3,10 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { ErrorHandler } from "../../../../utils/error-handler";
|
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-artifact-values",
|
selector: "hbr-artifact-values",
|
@ -1,10 +1,10 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArtifactCommonPropertiesComponent } from './artifact-common-properties.component';
|
import { ArtifactCommonPropertiesComponent } from './artifact-common-properties.component';
|
||||||
import { ExtraAttrs } from "../../../../../ng-swagger-gen/models/extra-attrs";
|
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateService } from "@ngx-translate/core";
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateService } from "@ngx-translate/core";
|
||||||
|
import { ExtraAttrs } from "../../../../../../ng-swagger-gen/models/extra-attrs";
|
||||||
|
|
||||||
describe('ArtifactCommonPropertiesComponent', () => {
|
describe('ArtifactCommonPropertiesComponent', () => {
|
||||||
let component: ArtifactCommonPropertiesComponent;
|
let component: ArtifactCommonPropertiesComponent;
|
@ -1,8 +1,8 @@
|
|||||||
import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
|
import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
|
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { formatSize } from "../../../utils/utils";
|
import { Artifact } from "../../../../../../ng-swagger-gen/models/artifact";
|
||||||
|
import { formatSize } from "../../../../../lib/utils/utils";
|
||||||
|
|
||||||
enum Types {
|
enum Types {
|
||||||
CREATED = 'created',
|
CREATED = 'created',
|
@ -1,13 +1,23 @@
|
|||||||
|
<div class="arrow-block" *ngIf="!withAdmiral">
|
||||||
|
<a (click)="goBackPro()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
|
||||||
|
<span class="back-icon"><</span>
|
||||||
|
<a (click)="goBackRep()">{{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
||||||
|
<span class="back-icon"><</span>
|
||||||
|
<a (click)="goBack()">{{repositoryName}}</a>
|
||||||
|
|
||||||
|
<span *ngFor="let digest of referArtifactNameArray;let i = index">
|
||||||
|
<<a (click)="jumpDigest(i)" >{{digest | slice:0:15}}</a></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper">
|
||||||
<div class="title-block arrow-block" *ngIf="withAdmiral">
|
<div class="title-block arrow-block" *ngIf="withAdmiral">
|
||||||
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="onBack()"></clr-icon>
|
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="onBack()"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<!-- <h2 class="custom-h2">{{repositoryName}}:{{artifactDetails?.digest | slice:0:15}}</h2> -->
|
|
||||||
<h2 class="custom-h2">{{artifact?.digest | slice:0:15}}</h2>
|
<h2 class="custom-h2">{{artifact?.digest | slice:0:15}}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="artifact">
|
<ng-container *ngIf="!loading">
|
||||||
<!-- common properties -->
|
<!-- common properties -->
|
||||||
<artifact-common-properties [artifactDetails]="artifact"></artifact-common-properties>
|
<artifact-common-properties [artifactDetails]="artifact"></artifact-common-properties>
|
||||||
|
|
||||||
@ -18,7 +28,7 @@
|
|||||||
<!-- Additions -->
|
<!-- Additions -->
|
||||||
<artifact-additions [additionLinks]="artifact?.addition_links"></artifact-additions>
|
<artifact-additions [additionLinks]="artifact?.addition_links"></artifact-additions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div *ngIf="!artifact" class="clr-row mt-3 center">
|
<div *ngIf="loading" class="clr-row mt-3 center">
|
||||||
<span class="spinner spinner-md"></span>
|
<span class="spinner spinner-md"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
.arrow-block a{
|
||||||
|
cursor: pointer;
|
||||||
|
color: #007cbb;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.back-icon {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-top-5px {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ArtifactSummaryComponent } from "./artifact-summary.component";
|
||||||
|
import { of } from "rxjs";
|
||||||
|
import { ClarityModule } from "@clr/angular";
|
||||||
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
|
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
|
||||||
|
import { ProjectService } from "../../../../lib/services";
|
||||||
|
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { AppConfigService } from "../../../app-config.service";
|
||||||
|
|
||||||
|
describe('ArtifactSummaryComponent', () => {
|
||||||
|
|
||||||
|
const mockedArtifact: Artifact = {
|
||||||
|
id: 123,
|
||||||
|
type: 'IMAGE'
|
||||||
|
};
|
||||||
|
|
||||||
|
const fakedProjectService = {
|
||||||
|
getProject() {
|
||||||
|
return of({name: 'test'});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fakedArtifactService = {
|
||||||
|
getArtifact() {
|
||||||
|
return of(mockedArtifact);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let component: ArtifactSummaryComponent;
|
||||||
|
let fixture: ComponentFixture<ArtifactSummaryComponent>;
|
||||||
|
const mockActivatedRoute = {
|
||||||
|
RouterparamMap: of({ get: (key) => 'value' }),
|
||||||
|
snapshot: {
|
||||||
|
params: {
|
||||||
|
id: 1,
|
||||||
|
repo: "test",
|
||||||
|
digest: "ABC",
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
projectResolver: {
|
||||||
|
has_project_admin_role: true,
|
||||||
|
current_user_role_id: 3,
|
||||||
|
name: "demo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: of({
|
||||||
|
projectResolver: {
|
||||||
|
ismember: true,
|
||||||
|
role_name: 'master',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const fakedAppConfigService = {
|
||||||
|
getConfig: () => {
|
||||||
|
return {with_admiral: false};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mockRouter = {
|
||||||
|
navigate: () => { }
|
||||||
|
};
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
ClarityModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateFakeLoader,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ArtifactSummaryComponent
|
||||||
|
],
|
||||||
|
schemas: [
|
||||||
|
NO_ERRORS_SCHEMA
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: AppConfigService, useValue: fakedAppConfigService },
|
||||||
|
{ provide: Router, useValue: mockRouter },
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: ProjectService, useValue: fakedProjectService },
|
||||||
|
{ provide: ArtifactService, useValue: fakedArtifactService },
|
||||||
|
ErrorHandler
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ArtifactSummaryComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.repositoryName = 'demo';
|
||||||
|
component.artifactDigest = 'sha: acf4234f';
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and get artifactDetails', async () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(component.artifact.type).toEqual('IMAGE');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,110 @@
|
|||||||
|
import { Component, Output, EventEmitter, OnInit } from "@angular/core";
|
||||||
|
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
|
||||||
|
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { Label } from "../../../../../ng-swagger-gen/models/label";
|
||||||
|
import { ProjectService } from "../../../../lib/services";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { AppConfigService } from "../../../app-config.service";
|
||||||
|
import { Project } from "../../project";
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "artifact-summary",
|
||||||
|
templateUrl: "./artifact-summary.component.html",
|
||||||
|
styleUrls: ["./artifact-summary.component.scss"],
|
||||||
|
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
export class ArtifactSummaryComponent implements OnInit {
|
||||||
|
tagId: string;
|
||||||
|
artifactDigest: string;
|
||||||
|
repositoryName: string;
|
||||||
|
projectId: string | number;
|
||||||
|
referArtifactNameArray: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
labels: Label;
|
||||||
|
artifact: Artifact;
|
||||||
|
@Output()
|
||||||
|
backEvt: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
projectName: string;
|
||||||
|
loading: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private projectService: ProjectService,
|
||||||
|
private artifactService: ArtifactService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private appConfigService: AppConfigService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get withAdmiral(): boolean {
|
||||||
|
return this.appConfigService.getConfig().with_admiral;
|
||||||
|
}
|
||||||
|
|
||||||
|
goBack(): void {
|
||||||
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repositoryName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
goBackRep(): void {
|
||||||
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
goBackPro(): void {
|
||||||
|
this.router.navigate(["harbor", "projects"]);
|
||||||
|
}
|
||||||
|
jumpDigest(index: number) {
|
||||||
|
const arr: string[] = this.referArtifactNameArray.slice(0, index + 1 );
|
||||||
|
if ( arr && arr.length) {
|
||||||
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repositoryName, "depth", arr.join('-')]);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repositoryName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
let depth = this.route.snapshot.params['depth'];
|
||||||
|
if (depth) {
|
||||||
|
this.referArtifactNameArray = depth.split('-');
|
||||||
|
}
|
||||||
|
this.repositoryName = this.route.snapshot.params["repo"];
|
||||||
|
this.artifactDigest = this.route.snapshot.params["digest"];
|
||||||
|
this.projectId = this.route.snapshot.params["id"];
|
||||||
|
if (this.repositoryName && this.artifactDigest) {
|
||||||
|
const resolverData = this.route.snapshot.data;
|
||||||
|
if (resolverData) {
|
||||||
|
const pro: Project = <Project>resolverData['projectResolver'];
|
||||||
|
this.projectName = pro.name;
|
||||||
|
if (this.projectName) {
|
||||||
|
this.getArtifactDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getArtifactDetails(): void {
|
||||||
|
this.loading = true;
|
||||||
|
this.artifactService.getArtifact({
|
||||||
|
repositoryName: this.repositoryName,
|
||||||
|
reference: this.artifactDigest,
|
||||||
|
projectName: this.projectName,
|
||||||
|
}).pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(response => {
|
||||||
|
this.artifact = response;
|
||||||
|
}, error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBack(): void {
|
||||||
|
this.backEvt.emit(this.repositoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshArtifact() {
|
||||||
|
this.getArtifactDetails();
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ErrorHandler } from "../../../utils/error-handler/error-handler";
|
|
||||||
|
|
||||||
import { ArtifactTagComponent } from './artifact-tag.component';
|
import { ArtifactTagComponent } from './artifact-tag.component';
|
||||||
import { SharedModule } from '../../../utils/shared/shared.module';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { OperationService } from '../../operation/operation.service';
|
|
||||||
import { TagService } from '../../../services';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../../entities/service.config';
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../lib/entities/service.config";
|
||||||
|
import { SharedModule } from "../../../../../lib/utils/shared/shared.module";
|
||||||
|
import { ErrorHandler } from "../../../../../lib/utils/error-handler";
|
||||||
|
import { TagService } from "../../../../../lib/services";
|
||||||
|
import { OperationService } from "../../../../../lib/components/operation/operation.service";
|
||||||
|
|
||||||
|
|
||||||
describe('ArtifactTagComponent', () => {
|
describe('ArtifactTagComponent', () => {
|
||||||
let component: ArtifactTagComponent;
|
let component: ArtifactTagComponent;
|
@ -1,19 +1,22 @@
|
|||||||
|
|
||||||
import { Component, OnInit, Input, ViewChild, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, ViewChild, Output, EventEmitter } from '@angular/core';
|
||||||
import { Artifact } from '../artifact';
|
|
||||||
import { TagService } from '../../../services';
|
|
||||||
import { Tag } from '../../../../../ng-swagger-gen/models/tag';
|
|
||||||
import { ConfirmationButtons, ConfirmationTargets, ConfirmationState } from '../../../entities/shared.const';
|
|
||||||
import { ConfirmationMessage, ConfirmationDialogComponent, ConfirmationAcknowledgement } from '../../confirmation-dialog';
|
|
||||||
import { Observable, of, forkJoin } from 'rxjs';
|
import { Observable, of, forkJoin } from 'rxjs';
|
||||||
import { OperateInfo, OperationState, operateChanges } from '../../operation/operate';
|
|
||||||
import { OperationService } from '../../operation/operation.service';
|
|
||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError } from 'rxjs/operators';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { errorHandler as errorHandFn } from "../../../utils/shared/shared.utils";
|
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { ErrorHandler } from '../../../utils/error-handler';
|
import { AVAILABLE_TIME } from "../../artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component";
|
||||||
import { AVAILABLE_TIME } from '../artifact-list-tab.component';
|
import {
|
||||||
|
ConfirmationAcknowledgement,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
ConfirmationMessage
|
||||||
|
} from "../../../../../lib/components/confirmation-dialog";
|
||||||
|
import { OperationService } from "../../../../../lib/components/operation/operation.service";
|
||||||
|
import { Tag, TagService } from "../../../../../lib/services";
|
||||||
|
import { ErrorHandler } from "../../../../../lib/utils/error-handler";
|
||||||
|
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../../lib/entities/shared.const";
|
||||||
|
import { operateChanges, OperateInfo, OperationState } from "../../../../../lib/components/operation/operate";
|
||||||
|
import { errorHandler } from "../../../../../lib/utils/shared/shared.utils";
|
||||||
|
import { Artifact } from "../artifact";
|
||||||
|
|
||||||
class InitTag {
|
class InitTag {
|
||||||
name = "";
|
name = "";
|
||||||
}
|
}
|
||||||
@ -43,7 +46,7 @@ export class ArtifactTagComponent implements OnInit {
|
|||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private tagService: TagService,
|
private tagService: TagService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private errorHandler: ErrorHandler
|
private errorHandlerService: ErrorHandler
|
||||||
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@ -64,7 +67,7 @@ export class ArtifactTagComponent implements OnInit {
|
|||||||
this.newTagName = new InitTag();
|
this.newTagName = new InitTag();
|
||||||
this.refreshArtifact.emit();
|
this.refreshArtifact.emit();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandlerService.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
removeTag() {
|
removeTag() {
|
||||||
@ -127,7 +130,7 @@ export class ArtifactTagComponent implements OnInit {
|
|||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}), catchError(error => {
|
}), catchError(error => {
|
||||||
const message = errorHandFn(error);
|
const message = errorHandler(error);
|
||||||
this.translateService.get(message).subscribe(res =>
|
this.translateService.get(message).subscribe(res =>
|
||||||
operateChanges(operMessage, OperationState.failure, res)
|
operateChanges(operMessage, OperationState.failure, res)
|
||||||
);
|
);
|
@ -1,9 +1,7 @@
|
|||||||
import { TestBed, inject } from '@angular/core/testing';
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
import { SharedModule } from '../utils/shared/shared.module';
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../entities/service.config';
|
import { TagDefaultService, TagService } from "../../../../lib/services";
|
||||||
import { TagService, TagDefaultService } from './tag.service';
|
|
||||||
|
|
||||||
|
|
||||||
describe('TagService', () => {
|
describe('TagService', () => {
|
||||||
|
|
@ -1,18 +1,15 @@
|
|||||||
import { Injectable, Inject } from "@angular/core";
|
import { Injectable, Inject } from "@angular/core";
|
||||||
import { HttpClient, HttpResponse } from "@angular/common/http";
|
import { HttpClient, HttpResponse } from "@angular/common/http";
|
||||||
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from "../entities/service.config";
|
|
||||||
import {
|
|
||||||
buildHttpRequestOptions,
|
|
||||||
HTTP_JSON_OPTIONS,
|
|
||||||
HTTP_GET_OPTIONS,
|
|
||||||
buildHttpRequestOptionsWithObserveResponse
|
|
||||||
} from "../utils/utils";
|
|
||||||
import { RequestQueryParams } from "./RequestQueryParams";
|
|
||||||
import { Tag, Manifest } from "./interface";
|
|
||||||
import { Artifact } from "../components/artifact/artifact";
|
|
||||||
import { map, catchError } from "rxjs/operators";
|
import { map, catchError } from "rxjs/operators";
|
||||||
import { Observable, throwError as observableThrowError, Subject } from "rxjs";
|
import { Observable, throwError as observableThrowError, Subject } from "rxjs";
|
||||||
|
import { Manifest, RequestQueryParams } from "../../../../lib/services";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
|
import {
|
||||||
|
buildHttpRequestOptionsWithObserveResponse,
|
||||||
|
HTTP_GET_OPTIONS,
|
||||||
|
HTTP_JSON_OPTIONS
|
||||||
|
} from "../../../../lib/utils/utils";
|
||||||
|
import { Artifact } from "./artifact";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,4 +1,5 @@
|
|||||||
import { Label, Tag } from "../../services";
|
import { Label, Tag } from "../../../../lib/services";
|
||||||
|
|
||||||
|
|
||||||
export class Artifact {
|
export class Artifact {
|
||||||
id: number;
|
id: number;
|
@ -10,10 +10,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { GridViewComponent } from './grid-view.component';
|
import { GridViewComponent } from './grid-view.component';
|
||||||
import { SharedModule } from '../../utils/shared/shared.module';
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe('GridViewComponent', () => {
|
describe('GridViewComponent', () => {
|
@ -24,8 +24,7 @@ import {
|
|||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { ScrollPosition } from "../../../../lib/services";
|
||||||
import { ScrollPosition } from "../../services/interface";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-gridview",
|
selector: "hbr-gridview",
|
@ -34,9 +34,9 @@
|
|||||||
<clr-dg-column [clrDgSortBy]="'pull_count'">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="'pull_count'">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||||
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
|
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
|
||||||
<clr-dg-cell><a href="javascript:void(0)" (click)="watchRepoClickEvt(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell>
|
<clr-dg-cell><a href="javascript:void(0)" (click)="goIntoRepo(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell>
|
||||||
<!-- to do -->
|
<!-- to do -->
|
||||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
<clr-dg-cell>{{r.artifact_count}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
@ -58,7 +58,7 @@
|
|||||||
[withAdmiral]="withAdmiral"
|
[withAdmiral]="withAdmiral"
|
||||||
(loadNextPageEvent)="loadNextPage()">
|
(loadNextPageEvent)="loadNextPage()">
|
||||||
<ng-template let-item="item">
|
<ng-template let-item="item">
|
||||||
<a class="card clickable" (click)="watchRepoClickEvt(item)">
|
<a class="card clickable" (click)="goIntoRepo(item)">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div [ngClass]="{'card-media-block': true, 'wrap': !withAdmiral }">
|
<div [ngClass]="{'card-media-block': true, 'wrap': !withAdmiral }">
|
||||||
<img *ngIf="withAdmiral" [src]="getImgLink(item)"/>
|
<img *ngIf="withAdmiral" [src]="getImgLink(item)"/>
|
||||||
@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
||||||
<button *ngIf="withAdmiral" type="button" class="btn btn-link" (click)="provisionItemEvent($event, item)" [disabled]="!hasProjectAdminRole">{{'REPOSITORY.DEPLOY' | translate}}</button>
|
<!--<button *ngIf="withAdmiral" type="button" class="btn btn-link" (click)="provisionItemEvent($event, item)" [disabled]="!hasProjectAdminRole">{{'REPOSITORY.DEPLOY' | translate}}</button>-->
|
||||||
<button type="button" class="btn btn-link" (click)="$event.stopPropagation()" [disabled]="!hasDeleteRepositoryPermission" clrDropdownTrigger>
|
<button type="button" class="btn btn-link" (click)="$event.stopPropagation()" [disabled]="!hasDeleteRepositoryPermission" clrDropdownTrigger>
|
||||||
{{'REPOSITORY.ACTION' | translate}}
|
{{'REPOSITORY.ACTION' | translate}}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon shape="caret down"></clr-icon>
|
@ -1,4 +1,44 @@
|
|||||||
@import '../../mixin';
|
@mixin text-overflow
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-wrap:break-word;
|
||||||
|
white-space: nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@mixin text-overflow-param($width) {
|
||||||
|
width: $width;
|
||||||
|
@include text-overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin grid-right-top-pos{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
right: 35px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin absolute-center($width:108px) {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: $width !important;
|
||||||
|
height: $width !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dropdown-as-action-button {
|
||||||
|
margin: .25rem .5rem .25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
.rightPos{
|
.rightPos{
|
||||||
@include grid-right-top-pos;
|
@include grid-right-top-pos;
|
@ -2,27 +2,30 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { SharedModule } from '../../utils/shared/shared.module';
|
import { of } from "rxjs";
|
||||||
import { RepositoryGridviewComponent } from './repository-gridview.component';
|
import { RepositoryService as NewRepositoryService } from "../../../../ng-swagger-gen/services/repository.service";
|
||||||
import { ErrorHandler } from '../../utils/error-handler/error-handler';
|
import { RepositoryGridviewComponent } from "./repository-gridview.component";
|
||||||
import { Repository, RepositoryItem, SystemInfo } from '../../services/interface';
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
|
|
||||||
import { RepositoryService } from '../../services/repository.service';
|
|
||||||
import { TagService, TagDefaultService } from '../../services/tag.service';
|
|
||||||
import { SystemInfoService } from '../../services/system-info.service';
|
|
||||||
import { OperationService } from "../operation/operation.service";
|
|
||||||
import {
|
import {
|
||||||
ProjectDefaultService,
|
ProjectDefaultService,
|
||||||
ProjectService,
|
ProjectService,
|
||||||
RequestQueryParams,
|
Repository,
|
||||||
RetagDefaultService,
|
RepositoryItem,
|
||||||
RetagService
|
RequestQueryParams, RetagDefaultService, RetagService,
|
||||||
} from "../../services";
|
SystemInfo, SystemInfoService,
|
||||||
import { UserPermissionService } from "../../services/permission.service";
|
TagDefaultService,
|
||||||
import { of } from "rxjs";
|
TagService, UserPermissionService
|
||||||
import { HarborLibraryModule } from "../../harbor-library.module";
|
} from "../../../lib/services";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../lib/entities/service.config";
|
||||||
import { delay } from 'rxjs/operators';
|
import { delay } from 'rxjs/operators';
|
||||||
import { RepositoryService as NewRepositoryService } from "../../../../ng-swagger-gen/services/repository.service";
|
import { SharedModule } from "../../../lib/utils/shared/shared.module";
|
||||||
|
import { ErrorHandler } from "../../../lib/utils/error-handler";
|
||||||
|
import { RepositoryService } from "./repository.service";
|
||||||
|
import { OperationService } from "../../../lib/components/operation/operation.service";
|
||||||
|
import { ProjectModule } from "../project.module";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { Repository as NewRepository } from "../../../../ng-swagger-gen/models/repository";
|
||||||
|
import { StrictHttpResponse as __StrictHttpResponse } from '../../../../ng-swagger-gen/strict-http-response';
|
||||||
|
|
||||||
describe('RepositoryComponentGridview (inline template)', () => {
|
describe('RepositoryComponentGridview (inline template)', () => {
|
||||||
|
|
||||||
let compRepo: RepositoryGridviewComponent;
|
let compRepo: RepositoryGridviewComponent;
|
||||||
@ -38,15 +41,14 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
"has_ca_root": false,
|
"has_ca_root": false,
|
||||||
"harbor_version": "v1.1.1-rc1-160-g565110d"
|
"harbor_version": "v1.1.1-rc1-160-g565110d"
|
||||||
};
|
};
|
||||||
let mockRepoData: RepositoryItem[] = [
|
let mockRepoData: NewRepository[] = [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "library/busybox",
|
"name": "library/busybox",
|
||||||
"project_id": 1,
|
"project_id": 1,
|
||||||
"description": "asdfsadf",
|
"description": "asdfsadf",
|
||||||
"pull_count": 0,
|
"pull_count": 0,
|
||||||
"star_count": 0,
|
"artifact_count": 1
|
||||||
"tags_count": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@ -54,30 +56,22 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
"project_id": 1,
|
"project_id": 1,
|
||||||
"description": "asdf",
|
"description": "asdf",
|
||||||
"pull_count": 0,
|
"pull_count": 0,
|
||||||
"star_count": 0,
|
"artifact_count": 1
|
||||||
"tags_count": 1
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
let mockRepoNginxData: RepositoryItem[] = [
|
let mockRepoNginxData: NewRepository[] = [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "library/nginx",
|
"name": "library/nginx",
|
||||||
"project_id": 1,
|
"project_id": 1,
|
||||||
"description": "asdf",
|
"description": "asdf",
|
||||||
"pull_count": 0,
|
"pull_count": 0,
|
||||||
"star_count": 0,
|
"artifact_count": 1
|
||||||
"tags_count": 1
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
let mockRepo: Repository = {
|
let mockRepo: NewRepository[] = mockRepoData;
|
||||||
metadata: { xTotalCount: 2 },
|
let mockNginxRepo: NewRepository[] = mockRepoNginxData;
|
||||||
data: mockRepoData
|
|
||||||
};
|
|
||||||
let mockNginxRepo: Repository = {
|
|
||||||
metadata: { xTotalCount: 2 },
|
|
||||||
data: mockRepoNginxData
|
|
||||||
};
|
|
||||||
let config: IServiceConfig = {
|
let config: IServiceConfig = {
|
||||||
repositoryBaseEndpoint: '/api/repository/testing',
|
repositoryBaseEndpoint: '/api/repository/testing',
|
||||||
systemInfoEndpoint: '/api/systeminfo/testing',
|
systemInfoEndpoint: '/api/systeminfo/testing',
|
||||||
@ -94,11 +88,11 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const fakedRepositoryService = {
|
const fakedRepositoryService = {
|
||||||
getRepositories(projectId: number, name: string, param?: RequestQueryParams) {
|
listRepositoriesResponse(params: NewRepositoryService.ListRepositoriesParams) {
|
||||||
if (name === 'nginx') {
|
if (params.name === 'nginx') {
|
||||||
return of(mockNginxRepo);
|
return of({headers: new Map(), body: mockNginxRepo});
|
||||||
}
|
}
|
||||||
return of(mockRepo).pipe(delay(0));
|
return of({headers: new Map(), body: mockRepo}).pipe(delay(0));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const fakedUserPermissionService = {
|
const fakedUserPermissionService = {
|
||||||
@ -106,15 +100,24 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
return of(true);
|
return of(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const fakedActivatedRoute = {
|
||||||
|
snapshot: {
|
||||||
|
parent: {
|
||||||
|
params: {
|
||||||
|
id: "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RouterTestingModule,
|
RouterTestingModule,
|
||||||
HarborLibraryModule
|
ProjectModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: ActivatedRoute, useValue: fakedActivatedRoute },
|
||||||
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: RepositoryService, useValue: fakedRepositoryService },
|
{ provide: RepositoryService, useValue: fakedRepositoryService },
|
||||||
@ -150,30 +153,4 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
it('should create', async(() => {
|
it('should create', async(() => {
|
||||||
expect(compRepo).toBeTruthy();
|
expect(compRepo).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should load and render data', async(() => {
|
|
||||||
fixtureRepo.whenStable().then(() => {
|
|
||||||
fixtureRepo.detectChanges();
|
|
||||||
let deRepo: DebugElement = fixtureRepo.debugElement.query(del => del.classes['datagrid-cell']);
|
|
||||||
expect(deRepo).toBeTruthy();
|
|
||||||
let elRepo: HTMLElement = deRepo.nativeElement;
|
|
||||||
expect(elRepo).toBeTruthy();
|
|
||||||
expect(elRepo.textContent).toEqual('library/busybox');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should filter data by keyword', async () => {
|
|
||||||
fixtureRepo.detectChanges();
|
|
||||||
await fixtureRepo.whenStable();
|
|
||||||
compRepo.doSearchRepoNames('nginx');
|
|
||||||
fixtureRepo.detectChanges();
|
|
||||||
await fixtureRepo.whenStable();
|
|
||||||
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('.datagrid-cell'));
|
|
||||||
expect(de).toBeTruthy();
|
|
||||||
expect(compRepo.repositories.length).toEqual(1);
|
|
||||||
expect(de.length).toEqual(4);
|
|
||||||
let el: HTMLElement = de[1].nativeElement;
|
|
||||||
expect(el).toBeTruthy();
|
|
||||||
expect(el.textContent).toEqual('library/nginx');
|
|
||||||
});
|
|
||||||
});
|
});
|
@ -0,0 +1,455 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
OnInit,
|
||||||
|
ViewChild,
|
||||||
|
EventEmitter,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
|
Inject, OnDestroy
|
||||||
|
} from "@angular/core";
|
||||||
|
import { forkJoin, Subscription } from "rxjs";
|
||||||
|
import { debounceTime, distinctUntilChanged, switchMap } from "rxjs/operators";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { map, catchError } from "rxjs/operators";
|
||||||
|
import { Observable, throwError as observableThrowError } from "rxjs";
|
||||||
|
import { ClrDatagridStateInterface } from "@clr/angular";
|
||||||
|
import {
|
||||||
|
RepositoryService as NewRepositoryService
|
||||||
|
} from "../../../../ng-swagger-gen/services/repository.service";
|
||||||
|
import {
|
||||||
|
Repository,
|
||||||
|
RepositoryItem, RequestQueryParams,
|
||||||
|
SystemInfo,
|
||||||
|
SystemInfoService,
|
||||||
|
TagService, UserPermissionService, USERSTATICPERMISSION
|
||||||
|
} from "../../../lib/services";
|
||||||
|
import { FilterComponent } from "../../../lib/components/filter/filter.component";
|
||||||
|
import { calculatePage, clone, DEFAULT_PAGE_SIZE } from "../../../lib/utils/utils";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../lib/entities/service.config";
|
||||||
|
import { ErrorHandler } from "../../../lib/utils/error-handler";
|
||||||
|
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../lib/entities/shared.const";
|
||||||
|
import { operateChanges, OperateInfo, OperationState } from "../../../lib/components/operation/operate";
|
||||||
|
import {
|
||||||
|
ConfirmationAcknowledgement,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
ConfirmationMessage
|
||||||
|
} from "../../../lib/components/confirmation-dialog";
|
||||||
|
import { OperationService } from "../../../lib/components/operation/operation.service";
|
||||||
|
import { errorHandler } from "../../../lib/utils/shared/shared.utils";
|
||||||
|
import { Project } from "../project";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { SessionService } from "../../shared/session.service";
|
||||||
|
import { RepositoryDefaultService } from "./repository.service";
|
||||||
|
import { GridViewComponent } from "./gridview/grid-view.component";
|
||||||
|
import { Repository as NewRepository } from "../../../../ng-swagger-gen/models/repository";
|
||||||
|
import { StrictHttpResponse as __StrictHttpResponse } from '../../../../ng-swagger-gen/strict-http-response';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "hbr-repository-gridview",
|
||||||
|
templateUrl: "./repository-gridview.component.html",
|
||||||
|
styleUrls: ["./repository-gridview.component.scss"],
|
||||||
|
})
|
||||||
|
export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
|
downloadLink: string;
|
||||||
|
@Input() urlPrefix: string;
|
||||||
|
projectId: number;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
projectName: string;
|
||||||
|
mode = 'standalone';
|
||||||
|
@Output() repoProvisionEvent = new EventEmitter<NewRepository>();
|
||||||
|
@Output() addInfoEvent = new EventEmitter<NewRepository>();
|
||||||
|
|
||||||
|
lastFilteredRepoName: string;
|
||||||
|
repositories: NewRepository[] = [];
|
||||||
|
repositoriesCopy: NewRepository[] = [];
|
||||||
|
systemInfo: SystemInfo;
|
||||||
|
selectedRow: NewRepository[] = [];
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
isCardView: boolean;
|
||||||
|
cardHover = false;
|
||||||
|
listHover = false;
|
||||||
|
|
||||||
|
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||||
|
currentPage = 1;
|
||||||
|
totalCount = 0;
|
||||||
|
currentState: ClrDatagridStateInterface;
|
||||||
|
|
||||||
|
@ViewChild("confirmationDialog", {static: false})
|
||||||
|
confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
@ViewChild("gridView", {static: false}) gridView: GridViewComponent;
|
||||||
|
hasCreateRepositoryPermission: boolean;
|
||||||
|
hasDeleteRepositoryPermission: boolean;
|
||||||
|
@ViewChild(FilterComponent, {static: true})
|
||||||
|
filterComponent: FilterComponent;
|
||||||
|
searchSub: Subscription;
|
||||||
|
|
||||||
|
constructor(@Inject(SERVICE_CONFIG) private configInfo: IServiceConfig,
|
||||||
|
private errorHandlerService: ErrorHandler,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private repositoryService: RepositoryDefaultService,
|
||||||
|
private newRepoService: NewRepositoryService,
|
||||||
|
private systemInfoService: SystemInfoService,
|
||||||
|
private tagService: TagService,
|
||||||
|
private operationService: OperationService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private session: SessionService,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
if (this.configInfo && this.configInfo.systemInfoEndpoint) {
|
||||||
|
this.downloadLink = this.configInfo.systemInfoEndpoint + "/getcert";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get registryUrl(): string {
|
||||||
|
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||||
|
}
|
||||||
|
public get withAdmiral(): boolean {
|
||||||
|
return this.mode === "admiral";
|
||||||
|
}
|
||||||
|
|
||||||
|
get canDownloadCert(): boolean {
|
||||||
|
return this.systemInfo && this.systemInfo.has_ca_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
goIntoRepo(repoEvt: RepositoryItem): void {
|
||||||
|
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
|
||||||
|
this.router.navigate(linkUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes["projectId"] && changes["projectId"].currentValue) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.projectId = this.route.snapshot.parent.params['id'];
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if (resolverData) {
|
||||||
|
let pro: Project = <Project>resolverData['projectResolver'];
|
||||||
|
this.hasProjectAdminRole = pro.has_project_admin_role;
|
||||||
|
this.projectName = pro.name;
|
||||||
|
}
|
||||||
|
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Get system info for tag views
|
||||||
|
this.getSystemInfo();
|
||||||
|
this.isCardView = this.mode === "admiral";
|
||||||
|
this.lastFilteredRepoName = "";
|
||||||
|
this.getHelmChartVersionPermission(this.projectId);
|
||||||
|
if (!this.searchSub) {
|
||||||
|
this.searchSub = this.filterComponent.filterTerms.pipe(
|
||||||
|
debounceTime(500),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap(repoName => {
|
||||||
|
this.lastFilteredRepoName = repoName;
|
||||||
|
this.currentPage = 1;
|
||||||
|
// Pagination
|
||||||
|
let params: NewRepositoryService.ListRepositoriesParams = {
|
||||||
|
projectName: this.projectName,
|
||||||
|
page: this.currentPage,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
name: this.lastFilteredRepoName
|
||||||
|
};
|
||||||
|
this.loading = true;
|
||||||
|
return this.newRepoService.listRepositoriesResponse(params);
|
||||||
|
})
|
||||||
|
).subscribe((repo: __StrictHttpResponse<Array<NewRepository>>) => {
|
||||||
|
this.totalCount = +repo.headers.get('x-total-count');
|
||||||
|
this.repositories = repo.body;
|
||||||
|
this.loading = false;
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.errorHandlerService.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getSystemInfo() {
|
||||||
|
this.systemInfoService.getSystemInfo()
|
||||||
|
.subscribe(systemInfo => (this.systemInfo = systemInfo)
|
||||||
|
, error => this.errorHandlerService.error(error));
|
||||||
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.searchSub) {
|
||||||
|
this.searchSub.unsubscribe();
|
||||||
|
this.searchSub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||||
|
this.loading = true;
|
||||||
|
// forkJoin(...repArr).subscribe(() => {
|
||||||
|
if (message &&
|
||||||
|
message.source === ConfirmationTargets.REPOSITORY &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED) {
|
||||||
|
let repoLists = message.data;
|
||||||
|
if (repoLists && repoLists.length) {
|
||||||
|
let observableLists: any[] = [];
|
||||||
|
repoLists.forEach(repo => {
|
||||||
|
observableLists.push(this.delOperate(repo));
|
||||||
|
});
|
||||||
|
forkJoin(observableLists).subscribe((item) => {
|
||||||
|
this.selectedRow = [];
|
||||||
|
this.refresh();
|
||||||
|
let st: ClrDatagridStateInterface = this.getStateAfterDeletion();
|
||||||
|
if (!st) {
|
||||||
|
this.refresh();
|
||||||
|
} else {
|
||||||
|
this.clrLoad(st);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.errorHandlerService.error(error);
|
||||||
|
this.loading = false;
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delOperate(repo: NewRepository): Observable<any> {
|
||||||
|
// init operation info
|
||||||
|
let operMessage = new OperateInfo();
|
||||||
|
operMessage.name = 'OPERATION.DELETE_REPO';
|
||||||
|
operMessage.data.id = repo.id;
|
||||||
|
operMessage.state = OperationState.progressing;
|
||||||
|
operMessage.data.name = repo.name;
|
||||||
|
this.operationService.publishInfo(operMessage);
|
||||||
|
return this.newRepoService
|
||||||
|
.deleteRepository({
|
||||||
|
repositoryName: repo.name,
|
||||||
|
projectName: this.projectName
|
||||||
|
})
|
||||||
|
.pipe(map(
|
||||||
|
response => {
|
||||||
|
this.translateService.get('BATCH.DELETED_SUCCESS').subscribe(res => {
|
||||||
|
operateChanges(operMessage, OperationState.success);
|
||||||
|
});
|
||||||
|
}), catchError(error => {
|
||||||
|
const message = errorHandler(error);
|
||||||
|
this.translateService.get(message).subscribe(res =>
|
||||||
|
operateChanges(operMessage, OperationState.failure, res)
|
||||||
|
);
|
||||||
|
return observableThrowError(message);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearchRepoNames(repoName: string) {
|
||||||
|
this.lastFilteredRepoName = repoName;
|
||||||
|
this.currentPage = 1;
|
||||||
|
let st: ClrDatagridStateInterface = this.currentState;
|
||||||
|
if (!st || !st.page) {
|
||||||
|
st = {page: {}};
|
||||||
|
}
|
||||||
|
st.page.size = this.pageSize;
|
||||||
|
st.page.from = 0;
|
||||||
|
st.page.to = this.pageSize - 1;
|
||||||
|
this.clrLoad(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRepos(repoLists: NewRepository[]) {
|
||||||
|
if (repoLists && repoLists.length) {
|
||||||
|
let repoNames: string[] = [];
|
||||||
|
repoLists.forEach(repo => {
|
||||||
|
repoNames.push(repo.name);
|
||||||
|
});
|
||||||
|
this.confirmationDialogSet(
|
||||||
|
'REPOSITORY.DELETION_TITLE_REPO',
|
||||||
|
'',
|
||||||
|
repoNames.join(','),
|
||||||
|
repoLists,
|
||||||
|
'REPOSITORY.DELETION_SUMMARY_REPO',
|
||||||
|
ConfirmationButtons.DELETE_CANCEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmationDialogSet(summaryTitle: string, signature: string,
|
||||||
|
repoName: string, repoLists: NewRepository[],
|
||||||
|
summaryKey: string, button: ConfirmationButtons): void {
|
||||||
|
this.translateService.get(summaryKey,
|
||||||
|
{
|
||||||
|
repoName: repoName,
|
||||||
|
signedImages: signature,
|
||||||
|
}).subscribe((res: string) => {
|
||||||
|
summaryKey = res;
|
||||||
|
let message = new ConfirmationMessage(
|
||||||
|
summaryTitle,
|
||||||
|
summaryKey,
|
||||||
|
repoName,
|
||||||
|
repoLists,
|
||||||
|
ConfirmationTargets.REPOSITORY,
|
||||||
|
button);
|
||||||
|
this.confirmationDialog.open(message);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
itemAddInfoEvent(evt: any, repo: NewRepository): void {
|
||||||
|
evt.stopPropagation();
|
||||||
|
let repoCopy = clone(repo);
|
||||||
|
repoCopy.name = this.registryUrl + ":443/" + repoCopy.name;
|
||||||
|
this.addInfoEvent.emit(repoCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteItemEvent(evt: any, item: NewRepository): void {
|
||||||
|
evt.stopPropagation();
|
||||||
|
this.deleteRepos([item]);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.doSearchRepoNames("");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadNextPage() {
|
||||||
|
this.currentPage = this.currentPage + 1;
|
||||||
|
// Pagination
|
||||||
|
let params: NewRepositoryService.ListRepositoriesParams = {
|
||||||
|
projectName: this.projectName,
|
||||||
|
page: this.currentPage,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
name: this.lastFilteredRepoName
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
this.newRepoService.listRepositoriesResponse(
|
||||||
|
params
|
||||||
|
)
|
||||||
|
.subscribe((repo: __StrictHttpResponse<Array<NewRepository>>) => {
|
||||||
|
this.totalCount = +repo.headers.get('x-total-count');
|
||||||
|
this.repositoriesCopy = repo.body;
|
||||||
|
this.repositories = this.repositories.concat(this.repositoriesCopy);
|
||||||
|
this.loading = false;
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.errorHandlerService.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clrLoad(state: ClrDatagridStateInterface): void {
|
||||||
|
if (!state || !state.page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.selectedRow = [];
|
||||||
|
// Keep it for future filtering and sorting
|
||||||
|
this.currentState = state;
|
||||||
|
|
||||||
|
let pageNumber: number = calculatePage(state);
|
||||||
|
if (pageNumber <= 0) {
|
||||||
|
pageNumber = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
let params: NewRepositoryService.ListRepositoriesParams = {
|
||||||
|
projectName: this.projectName,
|
||||||
|
page: pageNumber,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
name: this.lastFilteredRepoName
|
||||||
|
};
|
||||||
|
if (state.filters && state.filters.length) {
|
||||||
|
state.filters.forEach(item => {
|
||||||
|
params[item.property] = item.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (state.sort && state.sort.by) {
|
||||||
|
// params = params.set(`sort`, `${(state.sort.reverse ? `-` : ``)}${state.sort.by as string}`);
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
this.newRepoService.listRepositoriesResponse(
|
||||||
|
params
|
||||||
|
)
|
||||||
|
.subscribe((repo: __StrictHttpResponse<Array<NewRepository>>) => {
|
||||||
|
|
||||||
|
this.totalCount = +repo.headers.get('x-total-count');
|
||||||
|
this.repositories = repo.body;
|
||||||
|
|
||||||
|
this.signedCon = {};
|
||||||
|
this.loading = false;
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.errorHandlerService.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateAfterDeletion(): ClrDatagridStateInterface {
|
||||||
|
let total: number = this.totalCount - 1;
|
||||||
|
if (total <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalPages: number = Math.ceil(total / this.pageSize);
|
||||||
|
let targetPageNumber: number = this.currentPage;
|
||||||
|
|
||||||
|
if (this.currentPage > totalPages) {
|
||||||
|
targetPageNumber = totalPages; // Should == currentPage -1
|
||||||
|
}
|
||||||
|
|
||||||
|
let st: ClrDatagridStateInterface = this.currentState;
|
||||||
|
if (!st) {
|
||||||
|
st = {page: {}};
|
||||||
|
}
|
||||||
|
st.page.size = this.pageSize;
|
||||||
|
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
||||||
|
st.page.to = targetPageNumber * this.pageSize - 1;
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
getImgLink(repo: NewRepository): string {
|
||||||
|
return "/container-image-icons?container-image=" + repo.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
showCard(cardView: boolean) {
|
||||||
|
if (this.isCardView === cardView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isCardView = cardView;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = true;
|
||||||
|
} else {
|
||||||
|
this.listHover = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeave(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
this.cardHover = false;
|
||||||
|
} else {
|
||||||
|
this.listHover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isHovering(itemName: string) {
|
||||||
|
if (itemName === "card") {
|
||||||
|
return this.cardHover;
|
||||||
|
} else {
|
||||||
|
return this.listHover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelmChartVersionPermission(projectId: number): void {
|
||||||
|
|
||||||
|
let hasCreateRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.CREATE);
|
||||||
|
let hasDeleteRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE);
|
||||||
|
forkJoin(hasCreateRepositoryPermission, hasDeleteRepositoryPermission).subscribe(permissions => {
|
||||||
|
this.hasCreateRepositoryPermission = permissions[0] as boolean;
|
||||||
|
this.hasDeleteRepositoryPermission = permissions[1] as boolean;
|
||||||
|
}, error => this.errorHandlerService.error(error));
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
import { TestBed, inject } from '@angular/core/testing';
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
import { RepositoryService, RepositoryDefaultService } from './repository.service';
|
import { RepositoryService, RepositoryDefaultService } from './repository.service';
|
||||||
import { SharedModule } from '../utils/shared/shared.module';
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../lib/entities/service.config";
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../entities/service.config';
|
import { SharedModule } from "../../../lib/utils/shared/shared.module";
|
||||||
|
|
||||||
|
|
||||||
describe('RepositoryService', () => {
|
describe('RepositoryService', () => {
|
||||||
const mockConfig: IServiceConfig = {
|
const mockConfig: IServiceConfig = {
|
@ -1,12 +1,10 @@
|
|||||||
import { RequestQueryParams } from './RequestQueryParams';
|
|
||||||
import { Repository, RepositoryItem } from './interface';
|
|
||||||
import { Injectable, Inject } from '@angular/core';
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { HttpClient, HttpResponse } from '@angular/common/http';
|
import { HttpClient, HttpResponse } from '@angular/common/http';
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../entities/service.config';
|
|
||||||
import { buildHttpRequestOptions, buildHttpRequestOptionsWithObserveResponse, HTTP_JSON_OPTIONS } from '../utils/utils';
|
|
||||||
import { map, catchError } from "rxjs/operators";
|
import { map, catchError } from "rxjs/operators";
|
||||||
import { Observable, throwError as observableThrowError } from "rxjs";
|
import { Observable, throwError as observableThrowError } from "rxjs";
|
||||||
|
import { Repository, RepositoryItem, RequestQueryParams } from "../../../lib/services";
|
||||||
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../lib/entities/service.config";
|
||||||
|
import { buildHttpRequestOptionsWithObserveResponse, HTTP_JSON_OPTIONS } from "../../../lib/utils/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define service methods for handling the repository related things.
|
* Define service methods for handling the repository related things.
|
@ -1,23 +1,21 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { VulnerabilitySummary } from '../../services';
|
|
||||||
|
|
||||||
import { ResultBarChartComponent } from './result-bar-chart.component';
|
import { ResultBarChartComponent } from './result-bar-chart.component';
|
||||||
import { ResultTipComponent } from './result-tip.component';
|
import { ResultTipComponent } from './result-tip.component';
|
||||||
import {
|
|
||||||
ScanningResultService,
|
|
||||||
ScanningResultDefaultService,
|
|
||||||
ArtifactService,
|
|
||||||
ArtifactDefaultService,
|
|
||||||
JobLogService,
|
|
||||||
JobLogDefaultService
|
|
||||||
} from '../../services';
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
|
|
||||||
import { ErrorHandler } from '../../utils/error-handler';
|
|
||||||
import { SharedModule } from '../../utils/shared/shared.module';
|
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../../utils/utils';
|
|
||||||
import { ResultTipHistogramComponent } from "./result-tip-histogram/result-tip-histogram.component";
|
import { ResultTipHistogramComponent } from "./result-tip-histogram/result-tip-histogram.component";
|
||||||
import { HistogramChartComponent } from "./histogram-chart/histogram-chart.component";
|
import { HistogramChartComponent } from "./histogram-chart/histogram-chart.component";
|
||||||
import { ChannelService } from "../../services/channel.service";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
|
import {
|
||||||
|
JobLogDefaultService,
|
||||||
|
JobLogService,
|
||||||
|
ScanningResultDefaultService,
|
||||||
|
ScanningResultService,
|
||||||
|
VulnerabilitySummary
|
||||||
|
} from "../../../../lib/services";
|
||||||
|
import { VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
||||||
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
|
import { ArtifactDefaultService, ArtifactService } from "../artifact/artifact.service";
|
||||||
|
|
||||||
describe('ResultBarChartComponent (inline template)', () => {
|
describe('ResultBarChartComponent (inline template)', () => {
|
||||||
let component: ResultBarChartComponent;
|
let component: ResultBarChartComponent;
|
||||||
@ -53,6 +51,7 @@ describe('ResultBarChartComponent (inline template)', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
ChannelService,
|
ChannelService,
|
||||||
|
ArtifactDefaultService,
|
||||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||||
{ provide: ArtifactService, useValue: ArtifactDefaultService },
|
{ provide: ArtifactService, useValue: ArtifactDefaultService },
|
||||||
{ provide: ScanningResultService, useValue: ScanningResultDefaultService },
|
{ provide: ScanningResultService, useValue: ScanningResultDefaultService },
|
@ -6,19 +6,14 @@ import {
|
|||||||
ChangeDetectorRef, Output, EventEmitter,
|
ChangeDetectorRef, Output, EventEmitter,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Subscription , timer} from "rxjs";
|
import { Subscription , timer} from "rxjs";
|
||||||
|
|
||||||
import { clone, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from '../../utils/utils';
|
|
||||||
import {
|
|
||||||
VulnerabilitySummary,
|
|
||||||
TagService,
|
|
||||||
ScanningResultService,
|
|
||||||
ScannerVo, ArtifactService
|
|
||||||
} from '../../services';
|
|
||||||
import { ErrorHandler } from '../../utils/error-handler';
|
|
||||||
import { JobLogService } from "../../services";
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ChannelService } from "../../services/channel.service";
|
import { ScannerVo, ScanningResultService, VulnerabilitySummary } from "../../../../lib/services";
|
||||||
import { Artifact } from '../artifact/artifact';
|
import { ArtifactDefaultService } from "../artifact/artifact.service";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
|
import { clone, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
||||||
|
import { Artifact } from "../artifact/artifact";
|
||||||
|
|
||||||
|
|
||||||
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
||||||
const RETRY_TIMES: number = 3;
|
const RETRY_TIMES: number = 3;
|
||||||
@ -39,18 +34,15 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
stateCheckTimer: Subscription;
|
stateCheckTimer: Subscription;
|
||||||
scanSubscription: Subscription;
|
scanSubscription: Subscription;
|
||||||
timerHandler: any;
|
timerHandler: any;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
// private tagService: TagService,
|
private artifactService: ArtifactDefaultService,
|
||||||
private artifactService: ArtifactService,
|
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
private ref: ChangeDetectorRef,
|
private ref: ChangeDetectorRef,
|
||||||
// private jobLogService: JobLogService,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
@ -1,17 +1,19 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { VulnerabilityItem } from '../../services';
|
|
||||||
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { ResultGridComponent } from './result-grid.component';
|
import { ResultGridComponent } from './result-grid.component';
|
||||||
import { ScanningResultService, ScanningResultDefaultService } from '../../services/scanning.service';
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
|
|
||||||
import { ErrorHandler } from '../../utils/error-handler';
|
|
||||||
import { SharedModule } from '../../utils/shared/shared.module';
|
|
||||||
import {ChannelService} from "../../services/channel.service";
|
|
||||||
import { UserPermissionService, UserPermissionDefaultService } from "../../services/permission.service";
|
|
||||||
import { USERSTATICPERMISSION } from "../../services/permission-static";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SEVERITY } from "../../utils/utils";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
import { FilterComponent } from "../filter/filter.component";
|
import {
|
||||||
|
ScanningResultDefaultService,
|
||||||
|
ScanningResultService,
|
||||||
|
UserPermissionDefaultService,
|
||||||
|
UserPermissionService, USERSTATICPERMISSION, VulnerabilityItem
|
||||||
|
} from "../../../../lib/services";
|
||||||
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
|
import { FilterComponent } from "../../../../lib/components/filter/filter.component";
|
||||||
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SEVERITY } from "../../../../lib/utils/utils";
|
||||||
describe('ResultGridComponent (inline template)', () => {
|
describe('ResultGridComponent (inline template)', () => {
|
||||||
let component: ResultGridComponent;
|
let component: ResultGridComponent;
|
||||||
let fixture: ComponentFixture<ResultGridComponent>;
|
let fixture: ComponentFixture<ResultGridComponent>;
|
@ -1,17 +1,16 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
import {
|
|
||||||
ScanningResultService,
|
|
||||||
VulnerabilityItem
|
|
||||||
} from '../../services';
|
|
||||||
import { ErrorHandler } from '../../utils/error-handler';
|
|
||||||
import { forkJoin } from "rxjs";
|
import { forkJoin } from "rxjs";
|
||||||
|
|
||||||
import { ChannelService } from "../../services/channel.service";
|
|
||||||
import { UserPermissionService } from "../../services";
|
|
||||||
import { USERSTATICPERMISSION } from "../../services";
|
|
||||||
import { DEFAULT_SUPPORTED_MIME_TYPE, SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from '../../utils/utils';
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
||||||
|
import {
|
||||||
|
ScanningResultService,
|
||||||
|
UserPermissionService,
|
||||||
|
USERSTATICPERMISSION,
|
||||||
|
VulnerabilityItem
|
||||||
|
} from "../../../../lib/services";
|
||||||
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
|
import { DEFAULT_SUPPORTED_MIME_TYPE, SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../lib/utils/utils";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-vulnerabilities-grid',
|
selector: 'hbr-vulnerabilities-grid',
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { ScannerVo, VulnerabilitySummary } from "../../../services";
|
|
||||||
import { VULNERABILITY_SCAN_STATUS, VULNERABILITY_SEVERITY } from "../../../utils/utils";
|
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { ScannerVo, VulnerabilitySummary } from "../../../../../lib/services";
|
||||||
|
import { VULNERABILITY_SCAN_STATUS, VULNERABILITY_SEVERITY } from "../../../../../lib/utils/utils";
|
||||||
|
|
||||||
const MIN = 60;
|
const MIN = 60;
|
||||||
const MIN_STR = "min ";
|
const MIN_STR = "min ";
|
@ -1,12 +1,10 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { VulnerabilitySummary } from '../../services';
|
|
||||||
|
|
||||||
import { ResultTipComponent } from './result-tip.component';
|
import { ResultTipComponent } from './result-tip.component';
|
||||||
import { SharedModule } from '../../utils/shared/shared.module';
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||||
|
import { UserPermissionDefaultService, UserPermissionService, VulnerabilitySummary } from "../../../../lib/services";
|
||||||
|
import { VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
||||||
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
|
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../../utils/utils';
|
|
||||||
import { UserPermissionService, UserPermissionDefaultService } from "../../services/permission.service";
|
|
||||||
|
|
||||||
describe('ResultTipComponent (inline template)', () => {
|
describe('ResultTipComponent (inline template)', () => {
|
||||||
let component: ResultTipComponent;
|
let component: ResultTipComponent;
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { VulnerabilitySummary, VulnerabilitySeverity } from '../../services';
|
import { VulnerabilitySeverity, VulnerabilitySummary } from "../../../../lib/services";
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../../utils/utils';
|
import { VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
||||||
|
|
||||||
export const MIN_TIP_WIDTH = 5;
|
export const MIN_TIP_WIDTH = 5;
|
||||||
export const MAX_TIP_WIDTH = 100;
|
export const MAX_TIP_WIDTH = 100;
|
@ -1,15 +0,0 @@
|
|||||||
<div>
|
|
||||||
<div class="breadcrumb" *ngIf="!withAdmiral">
|
|
||||||
<a (click)="goProBack()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
|
|
||||||
<span class="back-icon"><</span>
|
|
||||||
<a (click)="watchGoBackEvt(projectId)">{{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
|
||||||
<span *ngIf="referArtifactNameArray.length===1"><<a (click)="backInitRepo()">{{repoName}}</a></span>
|
|
||||||
<span *ngIf="referArtifactNameArray.length>=2" >
|
|
||||||
<span *ngFor="let digest of referArtifactNameArray;let i = index">
|
|
||||||
<<a (click)="jumpDigest(referArtifactNameArray,i)" >{{digest | slice:0:15}}</a></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<artifact-list [repoName]="repoName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole"
|
|
||||||
[projectId]="projectId" [memberRoleID]="projectMemberRoleId" [isGuest]="isGuest"
|
|
||||||
(tagClickEvent)="watchTagClickEvt($event)" (backEvt)="watchGoBackEvt($event)" (putArtifactReferenceArr)="putArtifactReferenceArr($event)"></artifact-list>
|
|
||||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||||||
<div>
|
|
||||||
<div class="arrow-block" *ngIf="!withAdmiral">
|
|
||||||
<a (click)="goBackPro()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
|
|
||||||
<span class="back-icon"><</span>
|
|
||||||
<a (click)="goBackRep()">{{'REPOSITORY.REPOSITORIES'| translate}}</a>
|
|
||||||
<span class="back-icon"><</span>
|
|
||||||
<a (click)="goBack(repositoryName)">{{'REPOSITORY.ARTIFACTS'| translate}}</a>
|
|
||||||
|
|
||||||
<span *ngFor="let digest of referArtifactNameArray;let i = index">
|
|
||||||
<<a (click)="jumpDigest(referArtifactNameArray,i)" >{{digest | slice:0:15}}</a></span>
|
|
||||||
</div>
|
|
||||||
<artifact-summary (backEvt)="goBack($event)"
|
|
||||||
[artifactDigest]="artifactDigest"
|
|
||||||
[withAdmiral]="withAdmiral"
|
|
||||||
[projectId]="projectId"
|
|
||||||
[repositoryName]="repositoryName"></artifact-summary>
|
|
||||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||||||
.arrow-block a{
|
|
||||||
cursor: pointer;
|
|
||||||
color: #007cbb;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 2px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
.back-icon {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { ArtifactSummaryPageComponent } from './artifact-summary-page.component';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { ClarityModule } from '@clr/angular';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import {AppConfigService} from "../../app-config.service";
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
|
||||||
describe('ArtifactSummaryPageComponent', () => {
|
|
||||||
let component: ArtifactSummaryPageComponent;
|
|
||||||
let fixture: ComponentFixture<ArtifactSummaryPageComponent>;
|
|
||||||
const mockSessionService = {
|
|
||||||
getCurrentUser: () => { }
|
|
||||||
};
|
|
||||||
const mockAppConfigService = {
|
|
||||||
getConfig: () => {
|
|
||||||
return {
|
|
||||||
registry_storage_provider_name : ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const mockRouter = {
|
|
||||||
navigate: () => { }
|
|
||||||
};
|
|
||||||
const mockActivatedRoute = {
|
|
||||||
RouterparamMap: of({ get: (key) => 'value' }),
|
|
||||||
snapshot: {
|
|
||||||
params: {
|
|
||||||
id: 1,
|
|
||||||
repo: "ere",
|
|
||||||
tag: "33"
|
|
||||||
},
|
|
||||||
parent: {
|
|
||||||
params: { id: 1 },
|
|
||||||
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
projectResolver: {
|
|
||||||
has_project_admin_role: true,
|
|
||||||
current_user_role_id: 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: of({
|
|
||||||
projectResolver: {
|
|
||||||
ismember: true,
|
|
||||||
role_name: 'master',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
schemas: [
|
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
ClarityModule,
|
|
||||||
TranslateModule.forRoot(),
|
|
||||||
FormsModule,
|
|
||||||
RouterTestingModule,
|
|
||||||
NoopAnimationsModule,
|
|
||||||
HttpClientTestingModule
|
|
||||||
],
|
|
||||||
declarations: [ArtifactSummaryPageComponent],
|
|
||||||
providers: [
|
|
||||||
TranslateService,
|
|
||||||
{ provide: SessionService, useValue: mockSessionService },
|
|
||||||
{ provide: AppConfigService, useValue: mockAppConfigService },
|
|
||||||
{ provide: Router, useValue: mockRouter },
|
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ArtifactSummaryPageComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,72 +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.
|
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import {AppConfigService} from "../../app-config.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'artifact-summary-page',
|
|
||||||
templateUrl: 'artifact-summary-page.component.html',
|
|
||||||
styleUrls: ["artifact-summary-page.component.scss"]
|
|
||||||
})
|
|
||||||
export class ArtifactSummaryPageComponent implements OnInit, OnDestroy {
|
|
||||||
tagId: string;
|
|
||||||
artifactDigest: string;
|
|
||||||
repositoryName: string;
|
|
||||||
projectId: string | number;
|
|
||||||
referArtifactNameArray: string[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private appConfigService: AppConfigService,
|
|
||||||
private router: Router
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.repositoryName = this.route.snapshot.params["repo"];
|
|
||||||
this.artifactDigest = this.route.snapshot.params["digest"];
|
|
||||||
this.projectId = this.route.snapshot.params["id"];
|
|
||||||
|
|
||||||
let refer = JSON.parse(sessionStorage.getItem('referenceSummary'));
|
|
||||||
if (refer && refer.projectId === this.projectId && refer.repo === this.repositoryName && refer.digest === this.artifactDigest) {
|
|
||||||
this.referArtifactNameArray = refer.referArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
get withAdmiral(): boolean {
|
|
||||||
return this.appConfigService.getConfig().with_admiral;
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(repositoryName: string): void {
|
|
||||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories", repositoryName]);
|
|
||||||
}
|
|
||||||
goBackRep(): void {
|
|
||||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories"]);
|
|
||||||
}
|
|
||||||
goBackPro(): void {
|
|
||||||
this.router.navigate(["harbor", "projects"]);
|
|
||||||
}
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
sessionStorage.removeItem('referenceSummary');
|
|
||||||
|
|
||||||
}
|
|
||||||
jumpDigest(referArtifactNameArray: string[], index: number) {
|
|
||||||
sessionStorage.removeItem('referenceSummary');
|
|
||||||
sessionStorage.setItem('reference', JSON.stringify({ projectId: this.projectId, repo: this.repositoryName,
|
|
||||||
referArray: referArtifactNameArray.slice(index)}));
|
|
||||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories", this.repositoryName]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
<div>
|
|
||||||
<hbr-repository-gridview [projectId]="projectId" [projectName]="projectName" [hasSignedIn]="hasSignedIn"
|
|
||||||
[hasProjectAdminRole]="hasProjectAdminRole" [mode]="mode"
|
|
||||||
(repoClickEvent)="watchRepoClickEvent($event)"></hbr-repository-gridview>
|
|
||||||
</div>
|
|
@ -1,67 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { RepositoryPageComponent } from './repository-page.component';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { ClarityModule } from '@clr/angular';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { SessionService } from '../shared/session.service';
|
|
||||||
describe('RepositoryPageComponent', () => {
|
|
||||||
let component: RepositoryPageComponent;
|
|
||||||
let fixture: ComponentFixture<RepositoryPageComponent>;
|
|
||||||
const mockActivatedRoute = {
|
|
||||||
RouterparamMap: of({ get: (key) => 'value' }),
|
|
||||||
snapshot: {
|
|
||||||
parent: {
|
|
||||||
params: { id: 1 },
|
|
||||||
data: {
|
|
||||||
projectResolver: {
|
|
||||||
ismember: true,
|
|
||||||
name: 'library',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const mockSessionService = {
|
|
||||||
getCurrentUser: () => { }
|
|
||||||
};
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
schemas: [
|
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
ClarityModule,
|
|
||||||
TranslateModule.forRoot(),
|
|
||||||
FormsModule,
|
|
||||||
RouterTestingModule,
|
|
||||||
NoopAnimationsModule,
|
|
||||||
HttpClientTestingModule
|
|
||||||
],
|
|
||||||
declarations: [RepositoryPageComponent],
|
|
||||||
providers: [
|
|
||||||
TranslateService,
|
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
|
||||||
{ provide: SessionService, useValue: mockSessionService },
|
|
||||||
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(RepositoryPageComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,52 +0,0 @@
|
|||||||
// 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 { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { Project } from '../project/project';
|
|
||||||
import { SessionService } from '../shared/session.service';
|
|
||||||
import { RepositoryItem } from "../../lib/services";
|
|
||||||
@Component({
|
|
||||||
selector: 'repository',
|
|
||||||
templateUrl: 'repository-page.component.html'
|
|
||||||
})
|
|
||||||
export class RepositoryPageComponent implements OnInit {
|
|
||||||
projectId: number;
|
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
hasSignedIn: boolean;
|
|
||||||
projectName: string;
|
|
||||||
mode = 'standalone';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private session: SessionService,
|
|
||||||
private router: Router,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.projectId = this.route.snapshot.parent.params['id'];
|
|
||||||
let resolverData = this.route.snapshot.parent.data;
|
|
||||||
if (resolverData) {
|
|
||||||
let pro: Project = <Project>resolverData['projectResolver'];
|
|
||||||
this.hasProjectAdminRole = pro.has_project_admin_role;
|
|
||||||
this.projectName = pro.name;
|
|
||||||
}
|
|
||||||
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
watchRepoClickEvent(repoEvt: RepositoryItem): void {
|
|
||||||
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
|
|
||||||
this.router.navigate(linkUrl);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +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.
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
|
|
||||||
import { SharedModule } from '../shared/shared.module';
|
|
||||||
|
|
||||||
import { RepositoryPageComponent } from './repository-page.component';
|
|
||||||
import { ArtifactListPageComponent } from './artifact-list-page/artifact-list-page.component';
|
|
||||||
import { TopRepoComponent } from './top-repo/top-repo.component';
|
|
||||||
import { ArtifactSummaryPageComponent } from './artifact-summary-page/artifact-summary-page.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
SharedModule,
|
|
||||||
RouterModule
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
RepositoryPageComponent,
|
|
||||||
ArtifactListPageComponent,
|
|
||||||
TopRepoComponent,
|
|
||||||
ArtifactSummaryPageComponent
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
RepositoryPageComponent,
|
|
||||||
TopRepoComponent,
|
|
||||||
ArtifactSummaryPageComponent
|
|
||||||
],
|
|
||||||
providers: []
|
|
||||||
})
|
|
||||||
export class RepositoryModule { }
|
|
@ -1,12 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ArtifactGuardActivateService } from './artifact-guard-activate.service';
|
|
||||||
|
|
||||||
describe('ArtifactGuardActivateService', () => {
|
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
const service: ArtifactGuardActivateService = TestBed.get(ArtifactGuardActivateService);
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,74 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ArtifactGuardActivateService {
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
import {
|
|
||||||
CanActivate, Router,
|
|
||||||
ActivatedRouteSnapshot,
|
|
||||||
RouterStateSnapshot,
|
|
||||||
CanActivateChild
|
|
||||||
} from '@angular/router';
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import { map, catchError, switchMap } from 'rxjs/operators';
|
|
||||||
import { ProjectService, ArtifactService } from "../../../lib/services";
|
|
||||||
import { CommonRoutes } from "../../../lib/entities/shared.const";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ArtifactGuard implements CanActivate, CanActivateChild {
|
|
||||||
constructor(
|
|
||||||
private sessionService: SessionService,
|
|
||||||
private artifactService: ArtifactService,
|
|
||||||
private projectService: ProjectService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
|
|
||||||
const projectId = route.params['id'];
|
|
||||||
const repoName = route.params['repo'];
|
|
||||||
const digest = route.params['digest'];
|
|
||||||
return this.projectService.getProject(projectId).pipe(
|
|
||||||
switchMap((project) => {
|
|
||||||
return this.hasArtifactPerm(project.name, repoName, digest);
|
|
||||||
}),
|
|
||||||
catchError(err => {
|
|
||||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
|
||||||
return of(false);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
|
|
||||||
return this.canActivate(route, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasArtifactPerm(projectName: string, repoName: string, digest): Observable<boolean> {
|
|
||||||
// Note: current user will have the permission to visit the project when the user can get response from GET /projects/:id API.
|
|
||||||
return this.artifactService.getArtifactFromDigest(projectName, repoName, digest).pipe(
|
|
||||||
() => {
|
|
||||||
return of(true);
|
|
||||||
},
|
|
||||||
catchError(err => {
|
|
||||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
|
||||||
return of(false);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import { TestBed, inject } from '@angular/core/testing';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { LeavingArtifactSummaryRouteDeactivate } from './leaving-artifact-summary-deactivate.service';
|
|
||||||
import { ConfirmationDialogService } from '../confirmation-dialog/confirmation-dialog.service';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
|
|
||||||
describe('LeavingArtifactSummaryRouteDeactivate', () => {
|
|
||||||
let fakeConfirmationDialogService = {
|
|
||||||
confirmationConfirm$: of({
|
|
||||||
state: 1,
|
|
||||||
source: 2
|
|
||||||
})
|
|
||||||
};
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [
|
|
||||||
RouterTestingModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
LeavingArtifactSummaryRouteDeactivate,
|
|
||||||
{ provide: ConfirmationDialogService, useValue: fakeConfirmationDialogService }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', inject([LeavingArtifactSummaryRouteDeactivate], (service: LeavingArtifactSummaryRouteDeactivate) => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
}));
|
|
||||||
});
|
|
@ -1,45 +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.
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import {
|
|
||||||
CanDeactivate, Router,
|
|
||||||
ActivatedRouteSnapshot,
|
|
||||||
RouterStateSnapshot
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { ConfirmationDialogService } from '../confirmation-dialog/confirmation-dialog.service';
|
|
||||||
|
|
||||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
|
||||||
import { ConfirmationState, ConfirmationTargets } from '../shared.const';
|
|
||||||
import { ArtifactListPageComponent } from '../../repository/artifact-list-page/artifact-list-page.component';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class LeavingArtifactSummaryRouteDeactivate implements CanDeactivate<ArtifactListPageComponent> {
|
|
||||||
constructor(
|
|
||||||
private router: Router,
|
|
||||||
private confirmation: ConfirmationDialogService) { }
|
|
||||||
|
|
||||||
canDeactivate(
|
|
||||||
tagRepo: ArtifactListPageComponent,
|
|
||||||
route: ActivatedRouteSnapshot,
|
|
||||||
state: RouterStateSnapshot): Observable<boolean> | boolean {
|
|
||||||
// Confirmation before leaving config route
|
|
||||||
return new Observable((observer) => {
|
|
||||||
sessionStorage.removeItem('referenceSummary');
|
|
||||||
|
|
||||||
return observer.next(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user