You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by hs...@apache.org on 2022/06/02 09:58:40 UTC
[incubator-datalab] 01/03: initial commit
This is an automated email from the ASF dual-hosted git repository.
hshpak pushed a commit to branch feat/DATALAB-2811/view-list-of-all-images
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git
commit 7017c1f7ceb4c28faf38f55c6361861afb3ae87b
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Tue May 24 12:11:44 2022 +0300
initial commit
---
.../com/epam/datalab/model/exploratory/Image.java | 3 +
.../backendapi/dao/ImageExploratoryDAO.java | 2 +
.../backendapi/dao/ImageExploratoryDAOImpl.java | 12 ++
.../resources/ImageExploratoryResource.java | 20 ++
.../dto/{ImageInfoRecord.java => ImageFilter.java} | 38 +++-
.../backendapi/resources/dto/ImageInfoRecord.java | 7 +
.../resources/dto/ProjectImagesInfo.java} | 32 +--
.../service/ImageExploratoryService.java | 5 +
.../service/impl/ImageExploratoryServiceImpl.java | 35 +++
.../resources/webapp/src/app/app.routing.module.ts | 32 +--
.../resources/webapp/src/app/core/core.module.ts | 7 +-
.../capitalize-first-letter.pipe.ts} | 26 ++-
.../{ => capitalize-first-letter-pipe}/index.ts | 17 +-
.../resources/webapp/src/app/core/pipes/index.ts | 1 +
.../src/app/core/services/appRouting.service.ts | 2 +-
.../services/applicationServiceFacade.service.ts | 21 +-
.../webapp/src/app/core/services/index.ts | 1 +
.../app/core/services/user-images-page.service.ts} | 32 +--
.../src/app/core/services/userResource.service.ts | 15 +-
.../reporting-grid/reporting-grid.component.ts | 15 +-
.../bucket-browser/bucket-browser.component.ts | 17 +-
.../src/app/resources/images/images.component.html | 240 +++++++++++++++++++++
.../src/app/resources/images/images.component.scss | 102 +++++++++
.../src/app/resources/images/images.component.ts | 135 ++++++++++++
.../src/app/resources/images/images.config.ts | 10 +
.../src/app/resources/images/images.model.ts | 20 ++
.../webapp/src/app/resources/images/index.ts | 2 +
.../resources-grid/resources-grid.component.ts | 9 +-
.../resources-grid/resources-grid.model.ts | 7 -
.../src/app/resources/resources.component.html | 28 +--
.../src/app/resources/resources.component.ts | 2 +-
.../webapp/src/app/resources/resources.module.ts | 10 +-
.../src/app/shared/bubble/bubble.component.ts | 29 +--
.../src/app/shared/navbar/navbar.component.html | 113 +++++-----
.../src/app/shared/navbar/navbar.component.ts | 5 +-
.../webapp/src/app/shared/navbar/navbar.config.ts | 25 +--
.../src/main/resources/webapp/src/styles.scss | 1 +
.../resources/ImageExploratoryResourceTest.java | 16 +-
.../service/impl/BillingServiceImplTest.java | 15 +-
.../impl/ImageExploratoryServiceImplTest.java | 5 +-
40 files changed, 900 insertions(+), 214 deletions(-)
diff --git a/services/datalab-model/src/main/java/com/epam/datalab/model/exploratory/Image.java b/services/datalab-model/src/main/java/com/epam/datalab/model/exploratory/Image.java
index c8d3b2c35..322d5511a 100644
--- a/services/datalab-model/src/main/java/com/epam/datalab/model/exploratory/Image.java
+++ b/services/datalab-model/src/main/java/com/epam/datalab/model/exploratory/Image.java
@@ -24,6 +24,7 @@ import com.epam.datalab.model.library.Library;
import lombok.Builder;
import lombok.Data;
+import java.time.Instant;
import java.util.List;
import java.util.Map;
@@ -40,6 +41,8 @@ public class Image {
private final String fullName;
private final String externalName;
private final String application;
+ private final String instanceName;
+ private final String cloudProvider;
private final String dockerImage;
private final List<Library> libraries;
private final Map<String, List<Library>> computationalLibraries;
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAO.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAO.java
index d8e89874a..4d97437e6 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAO.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAO.java
@@ -38,6 +38,8 @@ public interface ImageExploratoryDAO {
List<ImageInfoRecord> getImages(String user, String dockerImage, String project, String endpoint, ImageStatus... statuses);
+ List<ImageInfoRecord> getImagesOfUser(String user, String project);
+
List<ImageInfoRecord> getImagesForProject(String project);
Optional<ImageInfoRecord> getImage(String user, String name, String project, String endpoint);
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAOImpl.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAOImpl.java
index 234a15e52..95f4e0a1a 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAOImpl.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/dao/ImageExploratoryDAOImpl.java
@@ -54,6 +54,7 @@ public class ImageExploratoryDAOImpl extends BaseDAO implements ImageExploratory
private static final String DOCKER_IMAGE = "dockerImage";
private static final String PROJECT = "project";
private static final String ENDPOINT = "endpoint";
+ private static final String CREATION_DATE = "creationDate";
@Override
public boolean exist(String image, String project) {
@@ -79,6 +80,13 @@ public class ImageExploratoryDAOImpl extends BaseDAO implements ImageExploratory
ImageInfoRecord.class);
}
+ @Override
+ public List<ImageInfoRecord> getImagesOfUser(String user, String project) {
+ return find(MongoCollections.IMAGES,
+ imageUserProjectCondition(user, project),
+ ImageInfoRecord.class);
+ }
+
@Override
public List<ImageInfoRecord> getImagesForProject(String project) {
return find(MongoCollections.IMAGES,
@@ -146,6 +154,10 @@ public class ImageExploratoryDAOImpl extends BaseDAO implements ImageExploratory
return and(eq(IMAGE_NAME, image), eq(PROJECT, project));
}
+ private Bson imageUserProjectCondition(String user, String project) {
+ return and(eq(USER, user), eq(PROJECT, project));
+ }
+
private Document getUpdatedFields(Image image) {
return new Document(STATUS, image.getStatus().toString())
.append(IMAGE_FULL_NAME, image.getFullName())
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ImageExploratoryResource.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ImageExploratoryResource.java
index 95dcdb469..8c5c76861 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ImageExploratoryResource.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/ImageExploratoryResource.java
@@ -22,7 +22,9 @@ package com.epam.datalab.backendapi.resources;
import com.epam.datalab.auth.UserInfo;
import com.epam.datalab.backendapi.domain.RequestId;
import com.epam.datalab.backendapi.resources.dto.ExploratoryImageCreateFormDTO;
+import com.epam.datalab.backendapi.resources.dto.ImageFilter;
import com.epam.datalab.backendapi.resources.dto.ImageInfoRecord;
+import com.epam.datalab.backendapi.resources.dto.ProjectImagesInfo;
import com.epam.datalab.backendapi.service.ImageExploratoryService;
import com.google.inject.Inject;
import io.dropwizard.auth.Auth;
@@ -98,6 +100,24 @@ public class ImageExploratoryResource {
return Response.ok(images).build();
}
+
+ @GET
+ @Path("user")
+ public Response getImagesForUser(@Auth UserInfo ui) {
+ log.debug("Getting images for user {}", ui.getName());
+ final List<ProjectImagesInfo> images = imageExploratoryService.getImagesOfUser(ui);
+ return Response.ok(images).build();
+ }
+
+ @POST
+ @Path("user")
+ public Response getImagesForUser(@Auth UserInfo ui, @Valid @NotNull ImageFilter imageFilter) {
+ log.debug("Getting images for user {} with filter {}", ui.getName(), imageFilter);
+ final List<ProjectImagesInfo> images = imageExploratoryService.getImagesOfUserWithFilter(ui ,imageFilter);
+ return Response.ok(images).build();
+ }
+
+
@GET
@Path("{name}")
public Response getImage(@Auth UserInfo ui,
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageFilter.java
similarity index 58%
copy from services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
copy to services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageFilter.java
index 18692c34c..09c50ef58 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageFilter.java
@@ -19,19 +19,39 @@
package com.epam.datalab.backendapi.resources.dto;
+import com.epam.datalab.cloud.CloudProvider;
import com.epam.datalab.dto.exploratory.ImageStatus;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+
+import java.util.List;
@Data
+@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ImageInfoRecord {
- private final String name;
- private final String description;
- private final String project;
- private final String endpoint;
- private final String user;
- private final String application;
- private final String fullName;
- private final ImageStatus status;
+public class ImageFilter {
+ @NonNull
+ private String imageName;
+ @NonNull
+ @JsonProperty("date_start")
+ private String dateStart;
+ @NonNull
+ @JsonProperty("date_end")
+ private String dateEnd;
+ @NonNull
+ private List<CloudProvider> cloudProviders;
+ @NonNull
+ private List<ImageStatus> statuses;
+// @NonNull
+// private List<> sharingStatuses;
+ @NonNull
+ private List<String> templateNames;
+ @NonNull
+ private List<String> instanceNames;
+ @NonNull
+ private List<String> projects;
+
}
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
index 18692c34c..00e5c94c6 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
@@ -19,19 +19,26 @@
package com.epam.datalab.backendapi.resources.dto;
+import com.epam.datalab.cloud.CloudProvider;
import com.epam.datalab.dto.exploratory.ImageStatus;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
+import java.time.LocalDateTime;
+
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ImageInfoRecord {
private final String name;
+ private final String creationDate;
private final String description;
private final String project;
private final String endpoint;
private final String user;
private final String application;
+ private final String instanceName;
+ private final CloudProvider cloudProvider;
private final String fullName;
private final ImageStatus status;
+ private final String sharedStatus;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ProjectImagesInfo.java
similarity index 64%
copy from services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
copy to services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ProjectImagesInfo.java
index 38df6d2c5..06da51f28 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ProjectImagesInfo.java
@@ -17,19 +17,23 @@
* under the License.
*/
-export const sideBarNamesConfig: Record<string, string> = {
- resourses: 'Resources',
- reports: 'Reports',
- audit: 'Audit',
- billing: 'Billing',
- administration: 'Administration',
- users: 'Users',
- projects: 'Projects',
- resources: 'Resources',
- configuration: 'Configuration'
-}
+package com.epam.datalab.backendapi.resources.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
-export interface UserInfo {
- email: string;
- name: string;
+@AllArgsConstructor
+@Builder
+@EqualsAndHashCode
+@ToString
+public class ProjectImagesInfo {
+ @JsonProperty
+ private String project;
+ @JsonProperty
+ private List<ImageInfoRecord> images;
}
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ImageExploratoryService.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ImageExploratoryService.java
index fae72a33f..99973f1a6 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ImageExploratoryService.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/ImageExploratoryService.java
@@ -20,7 +20,9 @@
package com.epam.datalab.backendapi.service;
import com.epam.datalab.auth.UserInfo;
+import com.epam.datalab.backendapi.resources.dto.ImageFilter;
import com.epam.datalab.backendapi.resources.dto.ImageInfoRecord;
+import com.epam.datalab.backendapi.resources.dto.ProjectImagesInfo;
import com.epam.datalab.model.exploratory.Image;
import java.util.List;
@@ -36,4 +38,7 @@ public interface ImageExploratoryService {
ImageInfoRecord getImage(String user, String name, String project, String endpoint);
List<ImageInfoRecord> getImagesForProject(String project);
+
+ List<ProjectImagesInfo> getImagesOfUser(UserInfo user);
+ List<ProjectImagesInfo> getImagesOfUserWithFilter(UserInfo user, ImageFilter imageFilter);
}
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImpl.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImpl.java
index 8c6802167..c0cd6e54d 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImpl.java
@@ -30,7 +30,9 @@ import com.epam.datalab.backendapi.dao.ExploratoryLibDAO;
import com.epam.datalab.backendapi.dao.ImageExploratoryDAO;
import com.epam.datalab.backendapi.domain.EndpointDTO;
import com.epam.datalab.backendapi.domain.ProjectDTO;
+import com.epam.datalab.backendapi.resources.dto.ImageFilter;
import com.epam.datalab.backendapi.resources.dto.ImageInfoRecord;
+import com.epam.datalab.backendapi.resources.dto.ProjectImagesInfo;
import com.epam.datalab.backendapi.service.EndpointService;
import com.epam.datalab.backendapi.service.ImageExploratoryService;
import com.epam.datalab.backendapi.service.ProjectService;
@@ -53,6 +55,7 @@ import com.google.inject.Singleton;
import com.google.inject.name.Named;
import lombok.extern.slf4j.Slf4j;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
@@ -104,6 +107,8 @@ public class ImageExploratoryServiceImpl implements ImageExploratoryService {
.computationalLibraries(fetchComputationalLibs(libraries))
.dockerImage(userInstance.getImageName())
.exploratoryId(userInstance.getId())
+ .instanceName(userInstance.getExploratoryName())
+ .cloudProvider(userInstance.getCloudProvider())
.project(userInstance.getProject())
.endpoint(userInstance.getEndpoint())
.build());
@@ -154,6 +159,36 @@ public class ImageExploratoryServiceImpl implements ImageExploratoryService {
return imageExploratoryDao.getImagesForProject(project);
}
+ @Override
+ public List<ProjectImagesInfo> getImagesOfUser(UserInfo user) {
+ log.debug("Loading list of images for user {}", user.getName());
+ return projectService.getUserProjects(user, Boolean.FALSE)
+ .stream()
+ .map( p-> {
+ List<ImageInfoRecord> images = imageExploratoryDao.getImagesOfUser(user.getName(), p.getName());
+ return ProjectImagesInfo.builder()
+ .project(p.getName())
+ .images(images)
+ .build();
+ })
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public List<ProjectImagesInfo> getImagesOfUserWithFilter(UserInfo user, ImageFilter imageFilter) {
+ log.debug("Loading list of images for user {}", user.getName());
+ return projectService.getUserProjects(user, Boolean.FALSE)
+ .stream()
+ .map( p-> {
+ List<ImageInfoRecord> images = imageExploratoryDao.getImagesOfUser(user.getName(), p.getName());
+ return ProjectImagesInfo.builder()
+ .project(p.getName())
+ .images(images)
+ .build();
+ })
+ .collect(Collectors.toList());
+ }
+
private Map<String, List<Library>> fetchComputationalLibs(List<Library> libraries) {
return libraries.stream()
.filter(resourceTypePredicate(ResourceType.COMPUTATIONAL))
diff --git a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
index d166b7d40..af905a56e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
@@ -37,12 +37,13 @@ import {ProjectAdminGuard} from './core/services/projectAdmin.guard';
import {ReportingComponent} from './reports/reporting/reporting.component';
import {OdahuComponent} from './administration/odahu/odahu.component';
import {AuditComponent} from './reports/audit/audit.component';
+import {ImagesComponent} from './resources/images/images.component';
const routes: Routes = [
{
path: 'login',
component: LoginComponent
- },
+ },
{
path: '',
canActivate: [CheckParamsGuard],
@@ -50,19 +51,24 @@ const routes: Routes = [
children: [
{
path: '',
- redirectTo: 'resources_list',
+ redirectTo: 'instances',
pathMatch: 'full'
- },
+ },
{
- path: 'resources_list',
+ path: 'instances',
component: ResourcesComponent,
canActivate: [AuthorizationGuard]
- },
+ },
+ {
+ path: 'images',
+ component: ImagesComponent,
+ canActivate: [AuthorizationGuard]
+ },
{
path: 'billing_report',
component: ReportingComponent,
canActivate: [AuthorizationGuard, CloudProviderGuard]
- },
+ },
{
path: 'projects',
component: ProjectComponent,
@@ -76,12 +82,12 @@ const routes: Routes = [
path: 'roles',
component: RolesComponent,
canActivate: [AuthorizationGuard, AdminGuard],
- },
+ },
{
path: 'environment_management',
component: ManagementComponent,
canActivate: [AuthorizationGuard, AdminGuard]
- },
+ },
{
path: 'configuration',
component: ConfigurationComponent,
@@ -91,12 +97,12 @@ const routes: Routes = [
path: 'swagger',
component: SwaggerComponent,
canActivate: [AuthorizationGuard]
- },
+ },
{
path: 'help/publickeyguide',
component: PublicKeyGuideComponent,
canActivate: [AuthorizationGuard]
- },
+ },
{
path: 'help/accessnotebookguide',
component: AccessNotebookGuideComponent,
@@ -108,16 +114,16 @@ const routes: Routes = [
canActivate: [AuthorizationGuard, AuditGuard],
},
]
- },
+ },
{
path: 'terminal/:id/:endpoint',
component: WebterminalComponent
- },
+ },
{
path: '403',
component: AccessDeniedComponent,
canActivate: [AuthorizationGuard]
- },
+ },
{
path: '**',
component: NotFoundComponent
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
index 20a45126c..33dc379fd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
@@ -48,9 +48,9 @@ import { NoCacheInterceptor } from './interceptors/nocache.interceptor';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import {ConfigurationService} from './services/configutration.service';
-import {AuditGuard, OdahuDeploymentService} from './services';
-import {ProjectAdminGuard} from './services/projectAdmin.guard';
+import { ConfigurationService } from './services/configutration.service';
+import { AuditGuard, OdahuDeploymentService, UserImagesPageService } from './services';
+import { ProjectAdminGuard } from './services/projectAdmin.guard';
@NgModule({
imports: [CommonModule],
@@ -90,6 +90,7 @@ export class CoreModule {
UserAccessKeyService,
ConfigurationService,
OdahuDeploymentService,
+ UserImagesPageService,
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: [] },
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
similarity index 68%
copy from services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
index 38df6d2c5..df57fee83 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
@@ -17,19 +17,17 @@
* under the License.
*/
-export const sideBarNamesConfig: Record<string, string> = {
- resourses: 'Resources',
- reports: 'Reports',
- audit: 'Audit',
- billing: 'Billing',
- administration: 'Administration',
- users: 'Users',
- projects: 'Projects',
- resources: 'Resources',
- configuration: 'Configuration'
-}
-export interface UserInfo {
- email: string;
- name: string;
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'capitalizeFirstLetter' })
+
+export class CapitalizeFirstLetterPipe implements PipeTransform {
+ transform(value: string): string {
+ if (!value) {
+ return '';
+ }
+ const firstLetter = value. substring(0, 1). toUpperCase();
+ return `${firstLetter}${value.substring(1).toLowerCase()}`;
+ }
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
similarity index 70%
copy from services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
index 399ce1d31..d333ad504 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
@@ -17,9 +17,14 @@
* under the License.
*/
-export * from './keys-pipe';
-export * from './underscoreless-pipe';
-export * from './lib-sort-pipe';
-export * from './replace-breaks-pipe';
-export * from './highlight.pipe';
-export * from './convert-action-pipe';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CapitalizeFirstLetterPipe } from './capitalize-first-letter.pipe';
+
+@NgModule({
+ imports: [CommonModule],
+ declarations: [CapitalizeFirstLetterPipe],
+ exports: [CapitalizeFirstLetterPipe]
+})
+
+export class CapitalizeFirstLetterPipeModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
index 399ce1d31..bc2e2c7b6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
@@ -23,3 +23,4 @@ export * from './lib-sort-pipe';
export * from './replace-breaks-pipe';
export * from './highlight.pipe';
export * from './convert-action-pipe';
+export * from './capitalize-first-letter-pipe';
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
index 1a8075973..3dd07cbd7 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
@@ -34,7 +34,7 @@ export class AppRoutingService {
}
redirectToHomePage(): void {
- this.router.navigate(['/resources_list']);
+ this.router.navigate(['/instances']);
}
redirectToHealthStatusPage(): void {
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
index c4fb49bd7..cd39b0e67 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
@@ -17,13 +17,13 @@
* under the License.
*/
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
-import { HttpClient } from '@angular/common/http';
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs';
+import {HttpClient} from '@angular/common/http';
-import { Dictionary } from '../collections';
-import { environment } from '../../../environments/environment';
-import { HTTPMethod } from '../util';
+import {Dictionary} from '../collections';
+import {environment} from '../../../environments/environment';
+import {HTTPMethod} from '../util';
// we can now access environment.apiUrl
const API_URL = environment.apiUrl;
@@ -81,6 +81,7 @@ export class ApplicationServiceFacade {
private static readonly AUDIT = 'audit';
private static readonly CONFIG = 'config';
private static readonly QUOTA = 'quota';
+ private static readonly IMAGE_PAGE = 'image_page';
private requestRegistry: Dictionary<string>;
@@ -180,6 +181,12 @@ export class ApplicationServiceFacade {
null);
}
+ buildGetUserImagePage(): Observable<any> {
+ return this.buildRequest(HTTPMethod.GET,
+ this.requestRegistry.Item(ApplicationServiceFacade.IMAGE_PAGE),
+ null);
+ }
+
public buildGetTemplatesRequest(params): Observable<any> {
return this.buildRequest(HTTPMethod.GET,
this.requestRegistry.Item(ApplicationServiceFacade.TEMPLATES) + params,
@@ -714,6 +721,8 @@ export class ApplicationServiceFacade {
// Exploratory Environment
this.requestRegistry.Add(ApplicationServiceFacade.PROVISIONED_RESOURCES,
'/api/infrastructure/info');
+ this.requestRegistry.Add(ApplicationServiceFacade.IMAGE_PAGE,
+ '/api/infrastructure_provision/exploratory_environment/image/user');
this.requestRegistry.Add(ApplicationServiceFacade.EXPLORATORY_ENVIRONMENT,
'/api/infrastructure_provision/exploratory_environment');
this.requestRegistry.Add(ApplicationServiceFacade.TEMPLATES,
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
index d84741540..68ce7e151 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
@@ -40,3 +40,4 @@ export * from './storage.service';
export * from './project.service';
export * from './odahu-deployment.service';
export * from './endpoint.service';
+export * from './user-images-page.service';
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
similarity index 56%
copy from services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
copy to services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
index 18692c34c..05e9e9077 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ImageInfoRecord.java
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
@@ -17,21 +17,23 @@
* under the License.
*/
-package com.epam.datalab.backendapi.resources.dto;
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+import { ErrorUtils } from '../util';
-import com.epam.datalab.dto.exploratory.ImageStatus;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.Data;
+import { ApplicationServiceFacade } from './applicationServiceFacade.service';
+import { ProjectModel } from '../../resources/images';
-@Data
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class ImageInfoRecord {
- private final String name;
- private final String description;
- private final String project;
- private final String endpoint;
- private final String user;
- private final String application;
- private final String fullName;
- private final ImageStatus status;
+@Injectable()
+export class UserImagesPageService {
+ constructor(private applicationServiceFacade: ApplicationServiceFacade) { }
+
+
+ getUserImagePageInfo(): Observable<ProjectModel[]> {
+ return this.applicationServiceFacade.buildGetUserImagePage()
+ .pipe(
+ catchError(ErrorUtils.handleServiceError)
+ );
+ }
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
index 3f2d5f8d6..05061c533 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
@@ -23,6 +23,7 @@ import { catchError, map } from 'rxjs/operators';
import { ErrorUtils } from '../util/';
import { ApplicationServiceFacade } from './applicationServiceFacade.service';
+import {ProjectModel} from '../../resources/images/images.model';
@Injectable()
export class UserResourceService {
@@ -109,9 +110,9 @@ export class UserResourceService {
}
public suspendComputationalResource(
- projectName: string,
- notebookName: string,
- computationalResourceName: string,
+ projectName: string,
+ notebookName: string,
+ computationalResourceName: string,
provider: string
): Observable<{}> {
const body = JSON.stringify('/' + projectName + '/' + notebookName + '/' + computationalResourceName + '/terminate');
@@ -123,10 +124,10 @@ export class UserResourceService {
}
public toggleStopStartAction(
- project: string,
- notebook: string,
- resource: string,
- action,
+ project: string,
+ notebook: string,
+ resource: string,
+ action,
provider: string
): Observable<{}> {
const url = `/${project}/${notebook}/${resource}/${action}`;
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
index 513ac45c1..9e45d9133 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
@@ -62,13 +62,13 @@ export class ReportingGridComponent implements OnInit {
isFiltered: boolean = false;
active: object = {};
displayedColumns: string[] = [
- 'name', 'user', 'project',
- 'type', 'status', 'shape',
+ 'name', 'user', 'project',
+ 'type', 'status', 'shape',
'service', 'empty', 'charge'
];
displayedFilterColumns: string[] = [
- 'name-filter', 'user-filter', 'project-filter',
- 'type-filter', 'status-filter', 'shape-filter',
+ 'name-filter', 'user-filter', 'project-filter',
+ 'type-filter', 'status-filter', 'shape-filter',
'service-filter', 'empty-filter', 'actions'
];
filtered: any;
@@ -106,7 +106,7 @@ export class ReportingGridComponent implements OnInit {
ngOnInit() {
this.userAgentIndex = window.navigator.userAgent.indexOf('Firefox');
-
+
window.setTimeout(() => {
this.isScrollButtonsVisible = this.tableWrapper.nativeElement.offsetWidth - this.table._elementRef.nativeElement.offsetWidth < 0;
this.checkMaxRight();
@@ -128,7 +128,6 @@ export class ReportingGridComponent implements OnInit {
refreshData(fullReport, report) {
this.reportData = [...report];
- console.log(fullReport);
this.fullReport = fullReport;
this.checkFilters();
}
@@ -168,7 +167,7 @@ export class ReportingGridComponent implements OnInit {
return 0;
});
}
-
+
this.refreshData(this.fullReport, report);
this.removeSorting();
this.active[sortItem + direction] = true;
@@ -223,7 +222,7 @@ export class ReportingGridComponent implements OnInit {
const arg = this.tableWrapper.nativeElement.offsetWidth +
this.tableWrapper.nativeElement.scrollLeft + 2 <= this.table._elementRef.nativeElement.offsetWidth;
return this.isMaxRight.next(arg);
-
+
}
public onFilterNameUpdate(targetElement: any) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
index 010e5d22e..05f822f4f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
@@ -127,9 +127,8 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
}
public toggleSelectedFile(file, type): void {
- console.log(file, type);
- type === 'file'
- ? file.isSelected = !file.isSelected
+ type === 'file'
+ ? file.isSelected = !file.isSelected
: file.isFolderSelected = !file.isFolderSelected;
this.selected = this.folderItems.filter(item => item.isSelected);
this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
@@ -260,8 +259,8 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
this.objectPath = event.pathObject;
this.path = event.path;
this.originFolderItems = this.folderItems.map(v => v);
- this.pathInsideBucket = this.path.indexOf('/') !== -1
- ? this.path.slice(this.path.indexOf('/') + 1) + '/'
+ this.pathInsideBucket = this.path.indexOf('/') !== -1
+ ? this.path.slice(this.path.indexOf('/') + 1) + '/'
: '';
this.folderItems.forEach(item => item.isSelected = false);
}
@@ -327,7 +326,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
if (!file) {
file = waitUploading[0];
}
-
+
file.status = 'uploading';
this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
this.isQueueFull = this.addedFiles.some(v => v.status === 'waiting');
@@ -350,7 +349,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
this.sendFile(this.addedFiles.find(v => v.status === 'waiting'));
this.bucketDataService.refreshBucketdata(this.bucketName, this.endpoint);
}
- },
+ },
error => {
window.clearInterval(file.interval);
file.status = 'failed';
@@ -412,7 +411,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
selected[0].progress = 0;
}, 1000);
}
- },
+ },
error => {
this.toastr.error(error.message || 'File downloading error!', 'Oops!');
selected[0]['isDownloading'] = false;
@@ -461,7 +460,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
public copyPath(): void {
const selected = this.folderItems.filter(item => item.isSelected || item.isFolderSelected)[0];
const pathToItem = `${this.pathInsideBucket}${selected.item}${selected.isFolderSelected ? '/' : ''}`;
-
+
const cloud = this.getCloud();
const protocol = HelpUtils.getBucketProtocol(cloud);
if (cloud !== 'azure') {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
new file mode 100644
index 000000000..595cd2893
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
@@ -0,0 +1,240 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+
+<section class="image-list--wrapper">
+ <nav class="image-list__nav-bar">
+ <div class="selection">
+ <div class="mat-reset">
+ <div class="control selector-wrapper"
+ [ngClass]="{'disabled-select': !isProjectsMoreThanOne}"
+ >
+ <mat-form-field>
+ <mat-label>Select project</mat-label>
+
+ <mat-select
+ disableOptionCentering
+ panelClass="top-select scrolling"
+ [disabled]="!projectList.length"
+ >
+ <mat-option
+ *ngIf="isProjectsMoreThanOne"
+ (click)="onSelectClick('')"
+ >
+ Show all
+ </mat-option>
+ <mat-option
+ *ngFor="let project of projectList"
+ [value]="project"
+ (click)="onSelectClick(project)"
+ >
+ {{ project }}
+ </mat-option>
+ <mat-option *ngIf="!projectList?.length" class="multiple-select ml-10" disabled>
+ Projects list is empty
+ </mat-option>
+ </mat-select>
+ <button class="caret" [disabled]="false">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+
+ <div class="button--wrapper">
+ <span class="action-button--wrapper">
+ <button
+ type="button"
+ class="butt action-button"
+ mat-raised-button
+ [disabled]="true"
+ (click)="onActionClick()"
+ >
+ Actions
+ <i class="material-icons" >{{ !isActionsOpen ? 'expand_more' : 'expand_less' }}</i>
+ </button>
+ </span>
+ <span>
+ <button mat-raised-button class="butt">
+ <i class="material-icons highlight">autorenew</i>
+ Refresh
+ </button>
+ </span>
+ </div>
+ </nav>
+ <mat-divider></mat-divider>
+
+ <table mat-table [dataSource]="dataSource" class="mat-elevation-z8 demo-table data-grid">
+ <!-- Position Column -->
+ <ng-container matColumnDef="checkbox">
+ <th mat-header-cell *matHeaderCellDef class="image-checkbox--wrapper">
+ <div class="header-cell--wrapper">
+ <span>
+ <datalab-checkbox
+ (click)="allCheckboxToggle()"
+ [checked]="checkboxSelected"
+ class="image-checkbox"
+ ></datalab-checkbox>
+ </span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element" class="image-checkbox--wrapper">
+ <datalab-checkbox
+ (click)="onCheckboxClick(element)"
+ class="image-checkbox"
+ [checked]="element.isSelected"
+ ></datalab-checkbox>
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="imageName">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.imageName}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element"> {{element.name}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="creationDate">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.creationDate}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element">
+ <span class="date-item"> {{element.creationDate | date: 'yyyy-MM-dd'}} </span>
+ <span> {{element.creationDate | date: 'HH:mm:ss'}} </span>
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="provider">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.provider}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element"> {{element.cloudProvider}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="imageStatus">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.imageStatus}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element" ngClass="{{ element.status.toLowerCase() || ''}}">
+ {{element.status | capitalizeFirstLetter}}
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="sharedStatus">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.sharedStatus}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element">
+ <div class="shared-status--wrapper">
+ <span class="shared-status"> {{element.shared}} </span>
+ <span class="currency_details" >
+ <i class="material-icons">help_outline</i>
+ </span>
+ </div>
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="templateName">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.templateName}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element"> {{element.application}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="instanceName">
+ <th mat-header-cell *matHeaderCellDef>
+ <div class="header-cell--wrapper">
+ <span>{{tableHeaderCellTitles.instanceName}}</span>
+ <i class="material-icons header-cell__dots">
+ <span>more_vert</span>
+ </i>
+ </div>
+ </th>
+ <td mat-cell *matCellDef="let element"> {{element.instanceName}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="actions">
+ <th mat-header-cell *matHeaderCellDef> {{tableHeaderCellTitles.actions}} </th>
+ <td mat-cell *matCellDef="let element" class="settings actions-col">
+
+ <div class="button--wrapper">
+ <span class="currency_details" >
+ <i class="material-icons">help_outline</i>
+ </span>
+ <span #settings class="actions" (click)="actions.toggle($event, settings)"></span>
+ </div>
+ <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
+ <ul class="list-unstyled">
+ <li
+ matTooltip="Unable to terminate notebook because at least one compute is in progress"
+ matTooltipPosition="above"
+ >
+ <div>
+ <i class="material-icons">phonelink_off</i>
+ <span>Terminate</span>
+ </div>
+ </li>
+ <li>
+ <div>
+ <i class="material-icons">create</i>
+ <span>Share</span>
+ </div>
+ </li>
+ </ul>
+ </bubble-up>
+ </td>
+ </ng-container>
+
+ <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+ <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+ </table>
+</section>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
new file mode 100644
index 000000000..e203266ef
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
@@ -0,0 +1,102 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.image-list {
+ &--wrapper {
+ padding: 12px 15px;
+ }
+
+ &__nav-bar {
+ display: flex;
+ justify-content: space-between;
+ }
+}
+
+.action-button {
+ padding: 0 38px 0 45px;
+
+ &--wrapper {
+ margin-right: 10px;
+ }
+}
+
+.demo-table {
+ width: 100%;
+}
+
+.mat-column-demo-position {
+ width: 32px;
+ border-right: 1px solid currentColor;
+ padding-right: 24px;
+ text-align: center;
+}
+
+.mat-column-demo-name {
+ padding-left: 16px;
+ font-size: 20px;
+}
+
+.mat-column-demo-weight {
+ font-style: italic;
+}
+
+.mat-column-demo-symbol {
+ width: 32px;
+ text-align: center;
+ font-weight: bold;
+}
+
+.image-checkbox {
+ &--wrapper {
+ padding-left: 10px !important;
+ }
+}
+
+.currency_details {
+ display: flex;
+ color: #35afd5;
+ cursor: pointer;
+ transition: all 0.45s ease-in-out;
+}
+
+.material-icons {
+ font-size: 18px;
+}
+
+.button--wrapper,
+.header-cell--wrapper {
+ display: flex;
+ justify-content: space-between;
+}
+
+.shared-status {
+ padding-right: 16px;
+
+ &--wrapper {
+ display: flex;
+ }
+}
+
+.header-cell__dots {
+ margin-right: 10px;
+}
+
+.date-item {
+ margin-right: 10px;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
new file mode 100644
index 000000000..cd39142fa
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Component, OnInit } from '@angular/core';
+
+import { ToastrService } from 'ngx-toastr';
+
+import { GeneralEnvironmentStatus } from '../../administration/management/management.model';
+import { HealthStatusService, UserImagesPageService } from '../../core/services';
+import { ImageModel, ProjectModel } from './images.model';
+import { Image_Table_Column_Headers } from './images.config';
+
+
+interface Aaa {
+ isSelected?: boolean;
+ imageName: string;
+ creationDate: string;
+ provider: string;
+ imageStatus: string;
+ sharedStatus: 'Private' | 'Shared';
+ templateName: string;
+ instanceName: string;
+ actions: object;
+}
+
+
+const tableTitles = <const>['checkbox', 'imageName', 'creationDate', 'provider', 'imageStatus', 'sharedStatus', 'templateName', 'instanceName', 'actions'];
+
+@Component({
+ selector: 'datalab-images',
+ templateUrl: './images.component.html',
+ styleUrls: [
+ './images.component.scss',
+ '../resources-grid/resources-grid.component.scss',
+ '../resources.component.scss'
+ ]
+})
+
+export class ImagesComponent implements OnInit {
+ isActionsOpen: boolean = false;
+ healthStatus: GeneralEnvironmentStatus;
+ tableHeaderCellTitles: typeof Image_Table_Column_Headers = Image_Table_Column_Headers;
+ displayedColumns: typeof tableTitles = tableTitles;
+ dataSource: ImageModel[] = [];
+ checkboxSelected: boolean = false;
+ projectList: string[] = [];
+ private cashedImageListData: ProjectModel[] = [];
+
+ constructor(
+ private healthStatusService: HealthStatusService,
+ public toastr: ToastrService,
+ private userImagesPageService: UserImagesPageService
+ ) { }
+
+ ngOnInit(): void {
+ this.getEnvironmentHealthStatus();
+ this.getUserImagePageInfo();
+ }
+
+ onCheckboxClick(element: ImageModel) {
+ element.isSelected = !element.isSelected;
+ }
+
+ allCheckboxToggle(): void {
+ this.checkboxSelected = !this.checkboxSelected;
+
+ if (this.checkboxSelected) {
+ this.dataSource.forEach(image => image.isSelected = true);
+ } else {
+ this.dataSource.forEach(image => image.isSelected = false);
+ }
+ }
+
+ onActionClick(): void {
+ this.isActionsOpen = !this.isActionsOpen;
+ }
+
+ onSelectClick(projectName: string): void {
+ if (!projectName) {
+ this.dataSource = this.getImageList();
+ }
+ const { images } = this.cashedImageListData.find(({project}) => project === projectName);
+ this.dataSource = [...images];
+ }
+
+ private getImageList() {
+ return this.cashedImageListData.reduce((acc, {images}) => [...acc, ...images], []);
+ }
+
+ private getEnvironmentHealthStatus() {
+ this.healthStatusService.getEnvironmentHealthStatus().subscribe(
+ (result: GeneralEnvironmentStatus) => {
+ this.healthStatus = result;
+ },
+ error => this.toastr.error(error.message, 'Oops!')
+ );
+ }
+
+ private getUserImagePageInfo(): void {
+ this.userImagesPageService.getUserImagePageInfo().subscribe(imageListData => this.initImageTable(imageListData));
+ }
+
+ private initImageTable(imagePageList: ProjectModel[]) {
+ this.cashedImageListData = imagePageList;
+ this.getProjectList(imagePageList);
+ this.dataSource = this.getImageList();
+ }
+
+ private getProjectList(imagePageList: ProjectModel[]): void {
+ if (!imagePageList) {
+ return;
+ }
+ imagePageList.forEach(({project}) => this.projectList.push(project));
+ }
+
+ get isProjectsMoreThanOne () {
+ return this.projectList.length > 1;
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
new file mode 100644
index 000000000..f2ac9f810
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
@@ -0,0 +1,10 @@
+export enum Image_Table_Column_Headers {
+ imageName = 'Image name',
+ creationDate = 'Creation date',
+ provider = 'Provider',
+ imageStatus = 'Image status',
+ sharedStatus = 'Shared status',
+ templateName = 'Template name',
+ instanceName = 'Instance name',
+ actions = 'Actions',
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
new file mode 100644
index 000000000..b5b725a96
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
@@ -0,0 +1,20 @@
+export interface ProjectModel {
+ project: string;
+ images: ImageModel[];
+}
+
+export interface ImageModel {
+ application: string;
+ cloudProvider: 'AWS' | 'GCP' | 'Azure';
+ creationDate: string;
+ description: string;
+ endpoint: string;
+ fullName: string;
+ instanceName: string;
+ name: string;
+ project: string;
+ shared: 'private' | 'shared';
+ status: 'created' | 'creating' | 'terminated' | 'terminating' | 'failed';
+ user: string;
+ isSelected?: boolean;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/index.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/index.ts
new file mode 100644
index 000000000..365e9989d
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/index.ts
@@ -0,0 +1,2 @@
+export * from './images.config';
+export * from './images.model';
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index 55c824d95..28b79ecd8 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -32,14 +32,13 @@ import { ProjectService, UserResourceService, OdahuDeploymentService } from '../
import { ExploratoryModel } from './resources-grid.model';
import { FilterConfigurationModel } from './filter-configuration.model';
import { GeneralEnvironmentStatus } from '../../administration/management/management.model';
-import { ConfirmationDialogType } from '../../shared';
+import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../shared';
import { SortUtils, CheckUtils } from '../../core/util';
import { DetailDialogComponent } from '../exploratory/detail-dialog';
import { AmiCreateDialogComponent } from '../exploratory/ami-create-dialog';
import { InstallLibrariesComponent } from '../exploratory/install-libraries';
import { ComputationalResourceCreateDialogComponent } from '../computational/computational-resource-create-dialog/computational-resource-create-dialog.component';
import { CostDetailsDialogComponent } from '../exploratory/cost-details-dialog';
-import { ConfirmationDialogComponent } from '../../shared/modal-dialog/confirmation-dialog';
import { SchedulerComponent } from '../scheduler';
import { DICTIONARY } from '../../../dictionary/global.dictionary';
import { ProgressBarService } from '../../core/services/progress-bar.service';
@@ -195,9 +194,9 @@ export class ResourcesGridComponent implements OnInit {
this.buildGrid();
}
- public containsNotebook(notebook_name: string, envoirmentNames: Array<string>): boolean {
- if (notebook_name && envoirmentNames.length ) {
- return envoirmentNames
+ public containsNotebook(notebook_name: string, environmentNames: Array<string>): boolean {
+ if (notebook_name && environmentNames.length ) {
+ return environmentNames
.some(item => CheckUtils.delimitersFiltering(notebook_name) === CheckUtils.delimitersFiltering(item));
}
return false;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
index 8c8d8d7d8..64e5855a4 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
@@ -148,10 +148,3 @@ export class ExploratoryModel {
}
}
}
-
-// export interface Exploratory {
-// project: string;
-// endpoints: [];
-// projectEndpoints: [];
-// exploratory: ExploratoryModel[];
-// }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
index 131aa638d..23117f7f2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
@@ -26,9 +26,9 @@
[matTooltipClass]="'full-size-tooltip'"
[matTooltipDisabled]="healthStatus?.projectAssigned && resourcesGrid.activeProjectsList?.length !== 0"
>
- <button
- mat-raised-button
- class="butt butt-create"
+ <button
+ mat-raised-button
+ class="butt butt-create"
(click)="createEnvironment()"
[disabled]="!healthStatus?.projectAssigned || !resourcesGrid.activeProjectsList?.length"
>
@@ -36,26 +36,26 @@
</button>
</span>
<div class="mat-reset">
- <div class="control selector-wrapper" *ngIf="projects?.length"
+ <div class="control selector-wrapper" *ngIf="projects?.length"
[ngClass]="{'disabled-select': !isProjectsMoreThanOne}"
>
<mat-form-field>
<mat-label>Select project</mat-label>
- <mat-select
+ <mat-select
disableOptionCentering
- [(value)]="resourcesGrid.activeProject"
+ [(value)]="resourcesGrid.activeProject"
panelClass="top-select scrolling"
[disabled]="!isProjectsMoreThanOne"
>
- <mat-option
- *ngIf="projects?.length > 1"
+ <mat-option
+ *ngIf="projects?.length > 1"
(click)="setActiveProject('')"
>
Show all
</mat-option>
- <mat-option
- *ngFor="let project of projects"
+ <mat-option
+ *ngFor="let project of projects"
[value]="project"
(click)="setActiveProject(project)"
>
@@ -72,15 +72,15 @@
</div>
<div>
- <span
+ <span
matTooltip="{{!this.bucketStatus?.view ? 'You have not permission to open bucket browser' : 'You have not any bucket'}}"
matTooltipPosition="above"
matTooltipDisabled="{{resourcesGrid.bucketsList?.length > 0 && this.bucketStatus?.view}}"
[matTooltipClass]="'full-size-tooltip'"
>
- <button
- mat-raised-button
- class="butt butt-tool"
+ <button
+ mat-raised-button
+ class="butt butt-tool"
(click)="bucketBrowser(this.bucketStatus?.view)"
[disabled]="!this.bucketStatus?.view || resourcesGrid.bucketsList?.length === 0"
>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
index e876331bf..6b0a1b764 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
@@ -86,7 +86,7 @@ export class ResourcesComponent implements OnInit {
bucketStatus: this.bucketStatus,
buckets: this.resourcesGrid.bucketsList
},
- panelClass: 'modal-fullscreen'
+ panelClass: 'modal-fullscreen'
})
.afterClosed().subscribe();
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index 47aea4c70..996c347d4 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -31,6 +31,10 @@ import { MatTreeModule } from '@angular/material/tree';
import { BucketDataService } from './bucket-browser/bucket-data.service';
import { ConvertFileSizePipeModule } from '../core/pipes/convert-file-size';
import { BucketBrowserModule } from './bucket-browser/bucket-browser.module';
+import { ImagesComponent } from './images/images.component';
+import {CheckboxModule} from '../shared/checkbox';
+import {BubbleModule} from '../shared';
+import { CapitalizeFirstLetterPipeModule } from '../core/pipes';
@NgModule({
imports: [
@@ -42,12 +46,16 @@ import { BucketBrowserModule } from './bucket-browser/bucket-browser.module';
MaterialModule,
MatTreeModule,
ConvertFileSizePipeModule,
- BucketBrowserModule
+ BucketBrowserModule,
+ CheckboxModule,
+ BubbleModule,
+ CapitalizeFirstLetterPipeModule
],
declarations: [
ResourcesComponent,
ManageUngitComponent,
ConfirmDeleteAccountDialogComponent,
+ ImagesComponent,
],
entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialogComponent],
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/bubble/bubble.component.ts b/services/self-service/src/main/resources/webapp/src/app/shared/bubble/bubble.component.ts
index 02e8ddef8..86bcfaaeb 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/bubble/bubble.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/bubble/bubble.component.ts
@@ -17,17 +17,17 @@
* under the License.
*/
-import {
- Component,
- Input,
- Output,
- EventEmitter,
+import {
+ Component,
+ Input,
+ Output,
+ EventEmitter,
HostBinding,
- ChangeDetectorRef,
- ElementRef,
+ ChangeDetectorRef,
+ ElementRef,
OnDestroy,
- ViewEncapsulation,
- HostListener
+ ViewEncapsulation,
+ HostListener
} from '@angular/core';
import { BubblesCollector, BubbleService } from './bubble.service';
@@ -101,11 +101,14 @@ export class BubbleComponent implements OnDestroy {
this.changeDirection = !this.isInViewport(bubbleElem);
let isBubbleOutOfWrapper;
-
- if(document.querySelector('.wrapper')) {
- isBubbleOutOfWrapper = bubbleElem.getBoundingClientRect().bottom > document.querySelector('.wrapper').getBoundingClientRect().bottom;
+
+ if (document.querySelector('.wrapper')) {
+ isBubbleOutOfWrapper = bubbleElem.getBoundingClientRect()
+ .bottom > document.querySelector('.wrapper')
+ .getBoundingClientRect()
+ .bottom;
}
-
+
(this.changeDirection || isBubbleOutOfWrapper) && this.bubbleService.updatePosition(element, bubbleElem, this.alternative);
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
index 8c52c46f9..60e635d99 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
@@ -37,11 +37,11 @@
<!-- <a *ngIf="healthStatus.status" [routerLink]="['/environment_management']" class="statusbar">
<span class="material-icons" ngClass="{{healthStatus.status || ''}}">radio_button_checked</span>
</a> -->
-
- <a
- *ngIf="metadata"
- class="statusbar about-btn--wrapper"
- #info
+
+ <a
+ *ngIf="metadata"
+ class="statusbar about-btn--wrapper"
+ #info
(click)="actions.toggle($event, info)">
<span class="about-btn">About</span>
</a>
@@ -72,7 +72,7 @@
<a class="help-link" href="https://github.com/apache/incubator-datalab/blob/master/USER_GUIDE.md" target="_blank">Help</a>
</span>
- <span
+ <span
class="material-icons account-icon--nav-bar account-icon" #login (click)="loginInfo.toggle($event, login)">
account_circle
</span>
@@ -85,53 +85,70 @@
<span class="user-mail">{{userData.email}}</span>
<button type="button" class="logout-btn" (click)="logout_btnClick()">
Log out from account
- </button>
+ </button>
</div>
</bubble-up>
</div>
</div>
<mat-sidenav-container class="example-container" autosize >
- <mat-sidenav
- #drawer
- mode="side"
- opened
- role="navigation"
- [style.width]="isExpanded ? '220px' : '60px'"
- disableClose
+ <mat-sidenav
+ #drawer
+ mode="side"
+ opened
+ role="navigation"
+ [style.width]="isExpanded ? '220px' : '60px'"
+ disableClose
*ngIf="healthStatus"
>
<mat-nav-list >
<nav>
<div>
- <a
- class="nav-item"
- [routerLink]="['/resources_list']"
- [routerLinkActive]="['active']"
- [routerLinkActiveOptions]="{exact:true}"
- >
- <span *ngIf="isExpanded; else resources">{{sideBarNames.resourses}}</span>
- <ng-template #resources><i class="material-icons">dashboard</i></ng-template>
+ <a class="nav-item has-children">
+ <span *ngIf="isExpanded">{{sideBarNames.resources}}</span>
+ <a
+ class="sub-nav-item"
+ [style.margin-left.px]="isExpanded ? '30' : '0'"
+ [routerLink]="['/instances']"
+ [routerLinkActive]="['active']"
+ [routerLinkActiveOptions]="{exact:true}"
+ >
+ <span *ngIf="isExpanded; else instances">{{sideBarNames.instances}}</span>
+ <ng-template #instances><i class="material-icons">laptop</i></ng-template>
+ </a>
+
+ <a
+ class="sub-nav-item"
+ [style.margin-left.px]="isExpanded ? '30' : '0'"
+ [routerLink]="['/images']"
+ [routerLinkActive]="['active']"
+ [routerLinkActiveOptions]="{exact:true}"
+ >
+ <span *ngIf="isExpanded; else images">{{sideBarNames.images}}</span>
+ <ng-template #images><i class="material-icons">photo</i></ng-template>
+ </a>
+
</a>
+
<a class="nav-item has-children" *ngIf="healthStatus?.billingEnabled || healthStatus?.auditEnabled">
<span *ngIf="isExpanded">{{sideBarNames.reports}}</span>
- <a
- *ngIf="healthStatus?.auditEnabled"
- class="sub-nav-item"
- [routerLink]="['/audit']"
+ <a
+ *ngIf="healthStatus?.auditEnabled"
+ class="sub-nav-item"
+ [routerLink]="['/audit']"
[style.margin-left.px]="isExpanded ? '30' : '0'"
- [routerLinkActive]="['active']"
+ [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{exact:true}"
>
<span *ngIf="isExpanded; else audit">{{sideBarNames.audit}}</span>
<ng-template #audit><i class="material-icons">library_books</i></ng-template>
</a>
- <a
- *ngIf="healthStatus?.billingEnabled"
- class="sub-nav-item"
+ <a
+ *ngIf="healthStatus?.billingEnabled"
+ class="sub-nav-item"
[routerLink]="['/billing_report']"
- [routerLinkActive]="['active']"
- [routerLinkActiveOptions]="{exact:true}"
+ [routerLinkActive]="['active']"
+ [routerLinkActiveOptions]="{exact:true}"
[style.margin-left.px]="isExpanded ? '30' : '0'"
>
<span *ngIf="isExpanded; else billing">{{sideBarNames.billing}}</span>
@@ -141,21 +158,21 @@
<a class="nav-item has-children" *ngIf="healthStatus?.admin || healthStatus?.projectAdmin">
<span *ngIf="isExpanded">{{sideBarNames.administration}}</span>
- <a
- class="sub-nav-item"
- [style.margin-left.px]="isExpanded ? '30' : '0'"
+ <a
+ class="sub-nav-item"
+ [style.margin-left.px]="isExpanded ? '30' : '0'"
[routerLink]="['/roles']"
- [routerLinkActive]="['active']"
+ [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{exact:true}"
>
<span *ngIf="isExpanded; else roles">{{sideBarNames.users}}</span>
<ng-template #roles><i class="material-icons">account_box</i></ng-template>
</a>
- <a
- class="sub-nav-item"
- [style.margin-left.px]="isExpanded ? '30' : '0'"
+ <a
+ class="sub-nav-item"
+ [style.margin-left.px]="isExpanded ? '30' : '0'"
[routerLink]="['/projects']"
- [routerLinkActive]="['active']"
+ [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{exact:true}"
>
<span *ngIf="isExpanded; else projects">{{sideBarNames.projects}}</span>
@@ -166,21 +183,21 @@
<!-- <span *ngIf="isExpanded; else odahu">Odahu deployment</span>-->
<!-- <ng-template #odahu><i class="material-icons">get_app</i></ng-template>-->
<!-- </a>-->
- <a
- class="sub-nav-item"
+ <a
+ class="sub-nav-item"
[style.margin-left.px]="isExpanded ? '30' : '0'"
- [routerLink]="['/environment_management']"
+ [routerLink]="['/environment_management']"
[routerLinkActive]="['active']"
[routerLinkActiveOptions]="{exact:true}"
>
<span *ngIf="isExpanded; else env">{{sideBarNames.resources}}</span>
<ng-template #env><i class="material-icons">settings</i></ng-template>
</a>
- <a
- *ngIf="healthStatus?.admin"
- class="sub-nav-item"
+ <a
+ *ngIf="healthStatus?.admin"
+ class="sub-nav-item"
[style.margin-left.px]="isExpanded ? '30' : '0'"
- [routerLink]="['/configuration']"
+ [routerLink]="['/configuration']"
[routerLinkActive]="['active']"
[routerLinkActiveOptions]="{exact:true}"
>
@@ -188,7 +205,7 @@
<ng-template #env><i class="material-icons">build_circle</i></ng-template>
</a>
</a>
-
+
</div>
<!-- <div>-->
<!-- <a class="nav-item" [routerLink]="['/swagger']" [routerLinkActive]="['active']"-->
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.ts b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.ts
index c88094060..4c906b3ef 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.ts
@@ -42,7 +42,7 @@ import {
} from '@angular/animations';
import {skip, take} from 'rxjs/operators';
import {ProgressBarService} from '../../core/services/progress-bar.service';
-import { sideBarNamesConfig, UserInfo } from './navbar.config';
+import {Sidebar_Names_Config, UserInfo} from './navbar.config';
interface Quota {
projectQuotas: {};
@@ -93,7 +93,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
isExpanded: boolean = true;
healthStatus: GeneralEnvironmentStatus;
subscriptions: Subscription = new Subscription();
- sideBarNames!: Record<string, string>;
+ sideBarNames: typeof Sidebar_Names_Config = Sidebar_Names_Config;
userData!: UserInfo;
commitMaxLength: number = 22;
@@ -109,7 +109,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
) { }
ngOnInit() {
- this.sideBarNames = sideBarNamesConfig;
this.applicationSecurityService.loggedInStatus.subscribe(response => {
this.subscriptions.unsubscribe();
this.subscriptions.closed = false;
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
index 38df6d2c5..44102f602 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.config.ts
@@ -17,19 +17,20 @@
* under the License.
*/
-export const sideBarNamesConfig: Record<string, string> = {
- resourses: 'Resources',
- reports: 'Reports',
- audit: 'Audit',
- billing: 'Billing',
- administration: 'Administration',
- users: 'Users',
- projects: 'Projects',
- resources: 'Resources',
- configuration: 'Configuration'
+export enum Sidebar_Names_Config {
+ reports = 'Reports',
+ audit = 'Audit',
+ billing = 'Billing',
+ administration = 'Administration',
+ users = 'Users',
+ projects = 'Projects',
+ resources = 'Resources',
+ configuration = 'Configuration',
+ instances = 'Instances',
+ images = 'Images'
}
export interface UserInfo {
- email: string;
- name: string;
+ email: string;
+ name: string;
}
diff --git a/services/self-service/src/main/resources/webapp/src/styles.scss b/services/self-service/src/main/resources/webapp/src/styles.scss
index 00a27bbe2..5bc005390 100644
--- a/services/self-service/src/main/resources/webapp/src/styles.scss
+++ b/services/self-service/src/main/resources/webapp/src/styles.scss
@@ -121,6 +121,7 @@ mat-chip.mat-chip strong {
text-align: left;
}
+.created,
.running,
.starting,
.installed,
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ImageExploratoryResourceTest.java b/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ImageExploratoryResourceTest.java
index f62b1e3c6..6f7a0e224 100644
--- a/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ImageExploratoryResourceTest.java
+++ b/services/self-service/src/test/java/com/epam/datalab/backendapi/resources/ImageExploratoryResourceTest.java
@@ -24,6 +24,7 @@ import com.epam.datalab.backendapi.domain.RequestId;
import com.epam.datalab.backendapi.resources.dto.ExploratoryImageCreateFormDTO;
import com.epam.datalab.backendapi.resources.dto.ImageInfoRecord;
import com.epam.datalab.backendapi.service.ImageExploratoryService;
+import com.epam.datalab.cloud.CloudProvider;
import com.epam.datalab.dto.exploratory.ImageStatus;
import com.epam.datalab.exceptions.ResourceAlreadyExistException;
import com.epam.datalab.exceptions.ResourceNotFoundException;
@@ -39,6 +40,7 @@ import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@@ -275,8 +277,18 @@ public class ImageExploratoryResourceTest extends TestBase {
}
private List<ImageInfoRecord> getImageList() {
- ImageInfoRecord imageInfoRecord = new ImageInfoRecord("someName", "someDescription", "someProject", "someEndpoint", "someUser", "someApp",
- "someFullName", ImageStatus.CREATED);
+ ImageInfoRecord imageInfoRecord = new ImageInfoRecord("someName",
+ "2020-02-02",
+ "someDescription",
+ "someProject",
+ "someEndpoint",
+ "someUser",
+ "someApp",
+ "someInstance",
+ CloudProvider.AWS,
+ "someFullName",
+ ImageStatus.CREATED,
+ "private");
return Collections.singletonList(imageInfoRecord);
}
}
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/BillingServiceImplTest.java b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/BillingServiceImplTest.java
index 5c9ec461b..b2345a8c8 100644
--- a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/BillingServiceImplTest.java
+++ b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/BillingServiceImplTest.java
@@ -56,6 +56,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import javax.ws.rs.core.GenericType;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -745,7 +746,19 @@ public class BillingServiceImplTest extends TestBase {
private List<ImageInfoRecord> getImageInfoRecords() {
return Collections.singletonList(
- new ImageInfoRecord(IMAGE_NAME, IMAGE_DESCRIPTION, PROJECT, ENDPOINT, USER, IMAGE_APPLICATION, IMAGE_FULL_NAME, ImageStatus.CREATED)
+ new ImageInfoRecord(
+ IMAGE_NAME,
+ "2020-02-02",
+ IMAGE_DESCRIPTION,
+ PROJECT,
+ ENDPOINT,
+ USER,
+ IMAGE_APPLICATION,
+ EXPLORATORY_NAME,
+ CloudProvider.GENERAL,
+ IMAGE_FULL_NAME,
+ ImageStatus.CREATED,
+ "private")
);
}
}
\ No newline at end of file
diff --git a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImplTest.java b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImplTest.java
index 78eb81f55..1af955aa8 100644
--- a/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImplTest.java
+++ b/services/self-service/src/test/java/com/epam/datalab/backendapi/service/impl/ImageExploratoryServiceImplTest.java
@@ -52,6 +52,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
+import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -308,8 +309,8 @@ public class ImageExploratoryServiceImplTest {
}
private ImageInfoRecord getImageInfoRecord() {
- return new ImageInfoRecord("someName", "someDescription", "someProject", "someEndpoint", "someUser", "someApp",
- "someFullName", ImageStatus.CREATED);
+ return new ImageInfoRecord("someName", "2020-02-02","someDescription", "someProject", "someEndpoint", "someUser", "someApp",
+ "someInstance",CloudProvider.GENERAL,"someFullName", ImageStatus.CREATED, "private");
}
private Image fetchImage() {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org