You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by dy...@apache.org on 2022/05/18 10:35:39 UTC

[incubator-datalab] branch DATALAB-2785 updated (407d81834 -> 36be6ec70)

This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a change to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git


    from 407d81834 fix params for create image
     add c820988cc applied nbformat 5.3.0 version during jupyter installation
     add 39b448e1d Merge pull request #1515 from apache/DATALAB-2795
     add a7cbe5ad8 added possibility to add several users
     add 479914874 Merge pull request #1516 from apache/fix/DATALAB-2800/possibility-to-add-several-users
     add de1f23fa2 name correction
     add dbaea5d9f Merge pull request #1517 from apache/DATALAB-2783
     add 056abd7e2 [DATALAB-2768]: added connection restart to python server
     add 80e8ea9dc [DATALAB-2768]: changed retrying cycle concept
     add 13c84a9fc Merge pull request #1514 from a1expol/develop
     add de6b274f5 [DATALAB-2768]: fixed issue with 'other' group libs
     add 241cc6f57 [DATALAB-2768]: fixed issue with 'other' group libs
     add 94a159411 Merge pull request #1518 from a1expol/develop
     new acf9d94e8 added create ami button
     new ac1a9a61d Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785
     new a29aa0622 [DATALAB-2771] created create image btn
     new e6a9f09a4 resolve conflict
     add a9dd17edf fixed for all clouds
     add 2d606a001 Merge pull request #1519 from apache/DATALAB-2795
     add c4fa92fcf [DATALAB-2793]: removed broken cuda repo from azure deeplearning
     add 0fa8e198b Merge pull request #1521 from apache/DATALAB-2793
     new ae8007f5d [DATALAB-2771] created create image btn
     new 01496306b resolve conflict
     new 3f9df815d [DATALAB-2771] created create image btn
     new 9b44f4238 Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785
     new 36be6ec70 small fix

The 9 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/general/conf/datalab.ini                   |   2 +
 .../files/aws/tensor-jupyterlab_description.json   |   4 +-
 .../src/general/lib/os/debian/notebook_lib.py      |  16 +-
 .../src/general/lib/os/fab.py                      |   3 +
 .../general/scripts/os/get_list_available_pkgs.py  |  56 ++++--
 .../src/ssn/files/aws/mongo_roles.json             |   2 +-
 .../backendapi/domain/NotebookTemplate.java        |   2 +-
 .../backendapi/resources/EnvironmentResource.java  |  15 +-
 .../dto/ExploratoryImageCreateFormAdminDTO.java    |  23 +++
 .../service/impl/EnvironmentServiceImpl.java       |   6 +-
 .../src/main/resources/mongo/aws/mongo_roles.json  |   2 +-
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  47 +++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../app/administration/roles/roles.component.ts    |  41 ++--
 .../convert-action-type.pipe.ts                    |  28 +--
 .../services/applicationServiceFacade.service.ts   |   4 +-
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 20 files changed, 330 insertions(+), 222 deletions(-)
 create mode 100644 services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 02/09: Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit ac1a9a61d5dad40300ac57d8cf3b9568cee57496
Merge: 407d81834 acf9d94e8
Author: Denys Yankiv <de...@gmail.com>
AuthorDate: Thu May 12 12:58:49 2022 +0300

    Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785

 .../src/general/conf/datalab.ini                   |   2 +
 .../files/aws/tensor-jupyterlab_description.json   |   4 +-
 .../src/general/lib/os/debian/notebook_lib.py      |   8 +-
 .../general/scripts/os/get_list_available_pkgs.py  |  56 ++++--
 .../src/ssn/files/aws/mongo_roles.json             |   2 +-
 .../backendapi/domain/NotebookTemplate.java        |   2 +-
 .../src/main/resources/mongo/aws/mongo_roles.json  |   2 +-
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  42 ++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../app/administration/roles/roles.component.ts    |  41 ++--
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 14 files changed, 268 insertions(+), 192 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 06/09: [DATALAB-2771] created create image btn

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 3f9df815d76d4b2eacee215d44b8ef0c529d080f
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Tue May 3 18:18:27 2022 +0300

    [DATALAB-2771] created create image btn
---
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  47 +++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../convert-action-type.pipe.ts                    |  28 +--
 .../services/applicationServiceFacade.service.ts   |   4 +-
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 8 files changed, 215 insertions(+), 165 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index c8c793d8c..43c7534ee 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -18,14 +18,14 @@
   -->
 <div class="managment-wrapper scrolling" #pageWrapper (scroll)="scrollTable($event)" (resize)="onResize($event)">
   <div class="ani scrolling scroll-wrapper" #wrapper (scroll)="scrollTable($event)">
-    <div 
-      class="nav-buttons" 
+    <div
+      class="nav-buttons"
       [ngStyle]="{'width.px': userAgentIndex === -1 ? tableWrapperWidth - 3 : tableWrapperWidth - 7 }"
-      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}" 
+      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}"
       [hidden]="!(tableWrapper.offsetWidth - tableEl['offsetWidth'] < 0)"
     >
       <div class="button-container">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll left"
           (click)="sctollTo('left')"
           [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }"
@@ -34,7 +34,7 @@
         </button>
       </div>
       <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll right"
           (click)="sctollTo('right')"
           [ngClass]="{'not-allowed': !(isMaxRight | async)}"
@@ -61,10 +61,10 @@
           </button>
         </th>
         <td mat-cell *matCellDef="let element">
-          <ng-template 
-            [ngIf]="element.type !== 'odahu' 
-                  && element.type !== 'edge node' 
-                  && (element.status==='running' || element.status==='stopped') 
+          <ng-template
+            [ngIf]="element.type !== 'odahu'
+                  && element.type !== 'edge node'
+                  && (element.status==='running' || element.status==='stopped')
                   && !clustersInProgress(element.resources)"
           >
             <datalab-checkbox
@@ -79,10 +79,10 @@
       <ng-container matColumnDef="user">
         <th mat-header-cell *matHeaderCellDef class="user label-header">
           <span class="label">User</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -105,17 +105,17 @@
       <ng-container matColumnDef="project">
         <th mat-header-cell *matHeaderCellDef class="project label-header">
           <span class="label">Project</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
               <span *ngIf="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">filter_list</span>
               <span [hidden]="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">more_vert</span>
             </i>
-          </button> 
+          </button>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.project }}</td>
       </ng-container>
@@ -123,10 +123,10 @@
       <ng-container matColumnDef="endpoint">
         <th mat-header-cell *matHeaderCellDef class="endpoint label-header">
           <span class="label">Endpoint</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -141,10 +141,10 @@
       <ng-container matColumnDef="type">
         <th mat-header-cell *matHeaderCellDef class="type label-header">
           <span class="label">Name</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -168,10 +168,10 @@
       <ng-container matColumnDef="shape">
         <th mat-header-cell *matHeaderCellDef class="shape label-header">
           <span class="label">Shape / Resource id</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -190,10 +190,10 @@
       <ng-container matColumnDef="status">
         <th mat-header-cell *matHeaderCellDef class="status label-header">
           <span class="label">Status</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -210,10 +210,10 @@
       <ng-container matColumnDef="resources">
         <th mat-header-cell *matHeaderCellDef class="resources label-header">
           <span class="label">Compute</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -236,8 +236,8 @@
                 <div class="resource-actions">
                   <span class="not-allow">
                     <a class="start-stop-action" *ngIf="resource.image === 'docker.datalab-dataengine'">
-                      <i 
-                        class="material-icons" 
+                      <i
+                        class="material-icons"
                         (click)="toggleResourceAction(element, 'stop', resource)"
                         [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }"
                       >
@@ -246,8 +246,8 @@
                     </a>
                   </span>
                   <span class="not-allow">
-                    <a 
-                      class="remove_butt" 
+                    <a
+                      class="remove_butt"
                       (click)="toggleResourceAction(element, 'terminate', resource)"
                       [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }"
                     >
@@ -267,10 +267,10 @@
         </th>
         <td mat-cell *matCellDef="let element" class="settings actions-col">
           <span [ngClass]="{'not-allow' : selected?.length}">
-            <span 
-              #settings 
-              class="actions" 
-              (click)="actions.toggle($event, settings)" 
+            <span
+              #settings
+              class="actions"
+              (click)="actions.toggle($event, settings)"
               *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
               [ngClass]="{
                 'disabled' : (element.status !== 'running' && element.status !== 'stopped')
@@ -281,39 +281,50 @@
           <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
             <ul class="list-unstyled">
               <li
-                matTooltip="{{ element.type !== 'edge node' 
-                  ? 'Unable to stop notebook because at least one computational resource is in progress' 
+                matTooltip="{{ element.type !== 'edge node'
+                  ? 'Unable to stop notebook because at least one computational resource is in progress'
                   : 'Unable to stop edge node because at least one resource of this user is in progress' }}"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
                 [hidden]="element.name === 'edge node' && element.status === 'stopped'"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'stop')"
-                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || 
-                                              element.status === 'starting' || element.status === 'creating image' || 
+                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' ||
+                                              element.status === 'starting' || element.status === 'creating image' ||
                                               element.status === 'failed' || isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">pause_circle_outline</i>
                   <span>Stop</span>
                 </div>
               </li>
-              <li 
+              <li
                 *ngIf="element.name !== 'edge node'"
                 matTooltip="Unable to terminate notebook because at least one compute is in progress"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'terminate')"
-                  [ngClass]="{'not-allowed' : element.status !== 'running' && 
-                                              element.status !== 'stopped' || 
+                  [ngClass]="{'not-allowed' : element.status !== 'running' &&
+                                              element.status !== 'stopped' ||
                                               isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">phonelink_off</i>
                   <span>Terminate</span>
                 </div>
               </li>
+              <li
+                *ngIf="element.status !== 'terminated'  && element.image !== 'docker.datalab-superset'
+                      && element.image !== 'docker.datalab-jupyterlab'"
+              >
+                <div
+                  (click)="toggleResourceAction(element, 'create image')"
+                >
+                  <i class="material-icons">view_module</i>
+                  <span>Create Image</span>
+                </div>
+              </li>
               <li *ngIf="element.status === 'stopped'">
                 <div (click)="toggleResourceAction(element, 'start')">
                   <i class="material-icons">play_circle_outline</i>
@@ -333,9 +344,9 @@
 
       <ng-container matColumnDef="user-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
-            [type]="'users'" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
+            [type]="'users'"
             [items]="filterConfiguration.users"
             [model]="filterForm.users"
           ></multi-select-dropdown>
@@ -344,11 +355,11 @@
 
       <ng-container matColumnDef="type-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <input 
-            placeholder="Filter by name" 
-            type="text" 
+          <input
+            placeholder="Filter by name"
+            type="text"
             class="form-control filter-field"
-            [value]="filterForm.type" 
+            [value]="filterForm.type"
             (input)="onFilterNameUpdate($event.target['value'])"
           />
         </th>
@@ -356,20 +367,20 @@
 
       <ng-container matColumnDef="project-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'projects'"
-            [items]="filterConfiguration.projects" 
+            [items]="filterConfiguration.projects"
             [model]="filterForm.projects"
           ></multi-select-dropdown>
         </th>
       </ng-container>
       <ng-container matColumnDef="endpoint-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'endpoints'"
-            [items]="filterConfiguration.endpoints" 
+            [items]="filterConfiguration.endpoints"
             [model]="filterForm.endpoints"
           ></multi-select-dropdown>
         </th>
@@ -377,10 +388,10 @@
 
       <ng-container matColumnDef="shape-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'shapes'"
-            [items]="filterConfiguration.shapes" 
+            [items]="filterConfiguration.shapes"
             [model]="filterForm.shapes"
           ></multi-select-dropdown>
         </th>
@@ -388,10 +399,10 @@
 
       <ng-container matColumnDef="status-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'statuses'"
-            [items]="filterConfiguration.statuses" 
+            [items]="filterConfiguration.statuses"
             [model]="filterForm.statuses"
           ></multi-select-dropdown>
         </th>
@@ -399,10 +410,10 @@
 
       <ng-container matColumnDef="resource-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'resources'"
-            [items]="filterConfiguration.resources" 
+            [items]="filterConfiguration.resources"
             [model]="filterForm.resources"
           ></multi-select-dropdown>
         </th>
@@ -411,22 +422,22 @@
       <ng-container matColumnDef="actions-filter" sticky>
         <th mat-header-cell *matHeaderCellDef  class="actions-col filter-row-item">
           <div class="actions">
-            <button 
-              mat-icon-button 
-              class="btn reset" 
-              (click)="resetFilterConfigurations()" 
+            <button
+              mat-icon-button
+              class="btn reset"
+              (click)="resetFilterConfigurations()"
               [disabled]="!isFilterSelected"
             >
               <i class="material-icons">close</i>
             </button>
 
-            <button 
-              mat-icon-button 
-              class="btn apply" 
+            <button
+              mat-icon-button
+              class="btn apply"
               (click)="applyFilter(filterForm)"
               [disabled]="isFilterChanged"
             >
-              <i 
+              <i
                 class="material-icons"
                 [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}"
               >
@@ -439,7 +450,7 @@
 
       <ng-container matColumnDef="placeholder">
         <td mat-footer-cell *matFooterCellDef colspan="8" class="no-border">
-          <div 
+          <div
             class="info"
             *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering"
           >
@@ -450,36 +461,36 @@
       </ng-container>
 
       <ng-container matColumnDef="scrollButtons">
-        <td 
-          mat-footer-cell 
-          *matFooterCellDef 
-          colspan="9" 
+        <td
+          mat-footer-cell
+          *matFooterCellDef
+          colspan="9"
           class="buttons-wrap"
         ></td>
       </ng-container>
 
-      <tr 
-        mat-header-row 
-        *matHeaderRowDef="displayedColumns; sticky: true" 
+      <tr
+        mat-header-row
+        *matHeaderRowDef="displayedColumns; sticky: true"
         class="header-row"
       ></tr>
-      <tr 
-        [hidden]="!collapsedFilterRow" 
-        mat-header-row 
+      <tr
+        [hidden]="!collapsedFilterRow"
+        mat-header-row
         *matHeaderRowDef="displayedFilterColumns; sticky: true"
         class="filter-row"
       ></tr>
       <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 
-      <tr 
-        [hidden]="allFilteredEnvironmentData?.length" 
-        mat-footer-row *matFooterRowDef="['placeholder']" 
+      <tr
+        [hidden]="allFilteredEnvironmentData?.length"
+        mat-footer-row *matFooterRowDef="['placeholder']"
         class="info"
       ></tr>
-      <tr 
-        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" 
-        mat-footer-row 
-        *matFooterRowDef="['scrollButtons']; sticky: true" 
+      <tr
+        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length"
+        mat-footer-row
+        *matFooterRowDef="['scrollButtons']; sticky: true"
         class="info"
       ></tr>
     </table>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index a122af1c6..d757eedac 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -142,7 +142,7 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
             this.applyFilter(this.cashedFilterForm || this.filterForm);
           }
           this.progressBarService.stopProgressBar();
-        }, 
+        },
         () => {
           this.progressBarService.stopProgressBar();
         }
@@ -186,8 +186,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
 
     const containsStatus = (list, selectedItems) => {
       if (list) {
-        return list.filter((item: any) => { 
-          if (selectedItems.indexOf(item.status) !== -1) return item; 
+        return list.filter((item: any) => {
+          if (selectedItems.indexOf(item.status) !== -1) return item;
         });
       }
     };
@@ -196,30 +196,30 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
     if (config) {
       filteredData = filteredData.filter(item => {
         const isUser = config.users.length > 0 ? (config.users.indexOf(item.user) !== -1) : true;
-        const isTypeName = item.name 
-          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1 
+        const isTypeName = item.name
+          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1
           : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
-        const isStatus = config.statuses.length > 0 
-          ? (config.statuses.indexOf(item.status) !== -1) 
+        const isStatus = config.statuses.length > 0
+          ? (config.statuses.indexOf(item.status) !== -1)
           : (config.type !== 'active');
-        const isShape = config.shapes.length > 0 
+        const isShape = config.shapes.length > 0
           ? (config.shapes.indexOf(item.shape) !== -1 ||
             config.shapes.indexOf(item.gpu_type) !== -1 ||
-            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1) 
+            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1)
           : true;
-        const isProject = config.projects.length > 0 
-          ? (config.projects.indexOf(item.project) !== -1) 
+        const isProject = config.projects.length > 0
+          ? (config.projects.indexOf(item.project) !== -1)
           : true;
-        const isEndpoint = config.endpoints.length > 0 
-          ? (config.endpoints.indexOf(item.endpoint) !== -1) 
+        const isEndpoint = config.endpoints.length > 0
+          ? (config.endpoints.indexOf(item.endpoint) !== -1)
           : true;
 
         const modifiedResources = containsStatus(item.resources, config.resources);
-        let isResources = config.resources.length > 0 
-          ? (modifiedResources && modifiedResources.length > 0) 
+        let isResources = config.resources.length > 0
+          ? (modifiedResources && modifiedResources.length > 0)
           : true;
-        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { 
-          item.resources = modifiedResources; 
+        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) {
+          item.resources = modifiedResources;
         }
 
         if (config.resources && config.resources.length === 0 && config.type === 'active' ||
@@ -288,7 +288,13 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
       }
     });
 
-    this.filterConfiguration = new ManagementConfigModel(users, '', projects, [...shapes, ...gpuTypes, ...gpuCounts], statuses, resources, endpoints);
+    this.filterConfiguration = new ManagementConfigModel(
+      users,
+      '',
+      projects,
+      [...shapes, ...gpuTypes, ...gpuCounts],
+      statuses, resources, endpoints
+    );
   }
 
   public openNotebookDetails(data) {
@@ -352,7 +358,10 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
   selector: 'confirm-dialog',
   template: `
   <div class="dialog-header">
-    <h4 class="modal-title"><span class="capitalize">{{ data.action }}</span> resource</h4>
+    <h4 class="modal-title">
+      <span class="capitalize">{{ data.action }}</span>
+      <span *ngIf="data.action !== 'create image'"> resource</span>
+    </h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </div>
   <div mat-dialog-content class="content">
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
index d6c414c58..f7d3179d0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
@@ -29,7 +29,7 @@ import {
   StorageService
 } from '../../core/services';
 
-import { ActionsType, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType } from './management.model';
+import {ActionsType, ActionTypeOptions, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType} from './management.model';
 import { HTTP_STATUS_CODES } from '../../core/util';
 import { BackupDilogComponent } from './backup-dilog/backup-dilog.component';
 import { SsnMonitorComponent } from './ssn-monitor/ssn-monitor.component';
@@ -43,6 +43,7 @@ import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../share
 import { ManagementGridComponent, ReconfirmationDialogComponent } from './management-grid/management-grid.component';
 import { FolderTreeComponent } from '../../resources/bucket-browser/folder-tree/folder-tree.component';
 import { StatusTypes } from '../../core/models';
+import {AmiCreateDialogComponent} from '../../resources/exploratory/ami-create-dialog';
 
 @Component({
   selector: 'environments-management',
@@ -225,8 +226,7 @@ export class ManagementComponent implements OnInit {
     this.isActionsOpen = !this.isActionsOpen;
   }
 
-  toggleResourceAction($event): void {
-    const { environment, action, resource } = $event;
+  toggleResourceAction({ environment, action, resource = null}): void {
     this.openDialog(environment, action, resource);
   }
 
@@ -239,7 +239,7 @@ export class ManagementComponent implements OnInit {
     }
   }
 
-  public resourceAction(action) {
+  public resourceAction(action: ActionTypeOptions) {
     this.toggleResourceAction({ environment: this.selected, action: action });
   }
 
@@ -265,7 +265,14 @@ export class ManagementComponent implements OnInit {
       observer.next = (res) => {
         if (res) {
           notebooks.forEach((env) => {
-            this.getNotebookAction(env, action);
+            if (action === 'create image') {
+              env['isAdmin'] = true;
+              env['userName'] = env.user;
+              this.dialog.open(AmiCreateDialogComponent, { data: env, panelClass: 'modal-sm' })
+                .afterClosed().subscribe();
+            } else {
+              this.getNotebookAction(env, action);
+            }
           });
         }
         this.clearSelection();
@@ -306,7 +313,7 @@ export class ManagementComponent implements OnInit {
     return {...config, notebooks};
   }
 
-  private getNotebookAction(env: EnvironmentModel, action: ActionsType) {
+  private getNotebookAction(env: EnvironmentModel, action: ActionsType): void {
     this.manageEnvironmentsService.environmentManagement(env.user, action, env.project, env.name)
       .subscribe(
         () => this.buildGrid(),
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index 56ce00ff7..5e7435302 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -117,10 +117,10 @@ export class ManagementConfigModel {
 
 export interface ModalData {
   action: ActionsType;
-  resource_name?: any; 
-  user?: any, 
+  resource_name?: any;
+  user?: any;
   type: string;
-  notebooks?: any
+  notebooks?: any;
 }
 
 export enum ModalDataType {
@@ -133,5 +133,8 @@ export enum ActionsType {
   terminate = 'terminate',
   start = 'start',
   run = 'run',
-  recreate = 'recreate'
-}
\ No newline at end of file
+  recreate = 'recreate',
+  createImage = 'create image'
+}
+
+export type ActionTypeOptions = 'stop' | 'terminate' | 'start' | 'run' | 'recreate' | 'createAmi';
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
index e1459fcf3..1c47afc10 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
@@ -22,27 +22,31 @@ import { ActionsType } from '../../../administration/management/management.model
 
 const actionTypes = {
     stopped: 'Stopped',
-    terminated: ' Terminated',
-    started: 'Started'
-}
+    terminated: 'Terminated',
+    started: 'Starting',
+    creating: 'Creating Image',
+    unknown: 'Unknown type'
+};
 
 @Pipe({ name: 'convertactiontype' })
 
 export class ConvertActionTypePipe implements PipeTransform {
-  transform(value: string,): string {
-    if(value === ActionsType.stop) {
-        return actionTypes.stopped
+  transform(value: string): string {
+    if (value === ActionsType.stop) {
+        return actionTypes.stopped;
     }
 
-    if(value === ActionsType.start) {
-        return actionTypes.started
+    if (value === ActionsType.start) {
+        return actionTypes.started;
     }
 
-    if(value === ActionsType.terminate) {
-        return actionTypes.terminated
+    if (value === ActionsType.terminate) {
+        return actionTypes.terminated;
     }
-    else {
-        return 'Unknown type'
+    if (value === ActionsType.createImage) {
+      return actionTypes.creating;
+    } else {
+        return actionTypes.unknown;
     }
   }
 }
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 3bb326d3e..13167f3eb 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
@@ -475,7 +475,7 @@ export class ApplicationServiceFacade {
       data,
       {
         observe: 'response',
-        headers: { 'Content-Type': 'text/plain' }
+        headers: { 'Content-Type': 'application/json; charset=UTF-8' }
       });
   }
 
@@ -805,4 +805,4 @@ export class ApplicationServiceFacade {
       return this.http.get(body ? (url + body) : url, opt);
     }
   }
-}
\ No newline at end of file
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
index ae576f2d9..a5befe290 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
@@ -45,19 +45,19 @@
         </div>
       </form>
       <div class="text-center m-top-30 m-bott-10">
-        <button 
-          mat-raised-button 
-          type="button" 
-          class="butt action" 
+        <button
+          mat-raised-button
+          type="button"
+          class="butt action"
           (click)="dialogRef.close()"
         >
           Cancel
         </button>
-        <button 
-          mat-raised-button 
-          type="button" 
+        <button
+          mat-raised-button
+          type="button"
           [disabled]="!createAMIForm.valid"
-          (click)="assignChanges(createAMIForm.value)" 
+          (click)="assignChanges(createAMIForm.value)"
           class="butt butt-success action"
         >
           Create
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
index 315f8f3c6..455468a28 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
@@ -22,7 +22,7 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
 
-import { UserResourceService } from '../../../core/services';
+import {ManageEnvironmentsService, UserResourceService} from '../../../core/services';
 import { HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 
@@ -38,6 +38,7 @@ export class AmiCreateDialogComponent implements OnInit {
   public provider: string;
   delimitersRegex = /[-_]?/g;
   imagesList: any;
+  private isAdmin: boolean = false;
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -45,28 +46,43 @@ export class AmiCreateDialogComponent implements OnInit {
     public dialogRef: MatDialogRef<AmiCreateDialogComponent>,
     private _userResource: UserResourceService,
     private _fb: FormBuilder,
+    private manageEnvironmentsService: ManageEnvironmentsService,
   ) { }
 
   ngOnInit() {
     this.notebook = this.data;
     this.provider = this.data.cloud_provider;
+    this.checkRole();
 
     this.initFormModel();
     this._userResource.getImagesList(this.data.project).subscribe(res => this.imagesList = res);
   }
 
-  public assignChanges(data) {
-    this._userResource.createAMI(data).subscribe(
-      response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
-      error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+  public assignChanges(data): void {
+    if (this.isAdmin) {
+      const { exploratory_name, project_name, name, description } = data;
+      const imageInfo = { user: this.data.userName,   imageName: name,   description, project_name, exploratory_name};
+      this.manageEnvironmentsService.environmentManagement(imageInfo, 'createImage', project_name, exploratory_name).subscribe(
+        () => { this.dialogRef.close(true); });
+    } else {
+      this._userResource.createAMI(data).subscribe(
+        response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
+        error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+    }
+  }
+
+  private checkRole(): void {
+    if (this.data?.isAdmin) {
+      this.isAdmin = this.data.isAdmin;
+    }
   }
 
   private initFormModel(): void {
     this.createAMIForm = this._fb.group({
       name: ['', [
-        Validators.required, 
-        Validators.pattern(PATTERNS.namePattern), 
-        Validators.maxLength(10), 
+        Validators.required,
+        Validators.pattern(PATTERNS.namePattern),
+        Validators.maxLength(10),
         this.checkDuplication.bind(this)
       ]],
       description: [''],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 03/09: [DATALAB-2771] created create image btn

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit a29aa062260ea11a43103233140c05181a059303
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Tue May 3 18:18:27 2022 +0300

    [DATALAB-2771] created create image btn
---
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  47 +++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../convert-action-type.pipe.ts                    |  28 +--
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 7 files changed, 213 insertions(+), 163 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index c8c793d8c..43c7534ee 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -18,14 +18,14 @@
   -->
 <div class="managment-wrapper scrolling" #pageWrapper (scroll)="scrollTable($event)" (resize)="onResize($event)">
   <div class="ani scrolling scroll-wrapper" #wrapper (scroll)="scrollTable($event)">
-    <div 
-      class="nav-buttons" 
+    <div
+      class="nav-buttons"
       [ngStyle]="{'width.px': userAgentIndex === -1 ? tableWrapperWidth - 3 : tableWrapperWidth - 7 }"
-      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}" 
+      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}"
       [hidden]="!(tableWrapper.offsetWidth - tableEl['offsetWidth'] < 0)"
     >
       <div class="button-container">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll left"
           (click)="sctollTo('left')"
           [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }"
@@ -34,7 +34,7 @@
         </button>
       </div>
       <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll right"
           (click)="sctollTo('right')"
           [ngClass]="{'not-allowed': !(isMaxRight | async)}"
@@ -61,10 +61,10 @@
           </button>
         </th>
         <td mat-cell *matCellDef="let element">
-          <ng-template 
-            [ngIf]="element.type !== 'odahu' 
-                  && element.type !== 'edge node' 
-                  && (element.status==='running' || element.status==='stopped') 
+          <ng-template
+            [ngIf]="element.type !== 'odahu'
+                  && element.type !== 'edge node'
+                  && (element.status==='running' || element.status==='stopped')
                   && !clustersInProgress(element.resources)"
           >
             <datalab-checkbox
@@ -79,10 +79,10 @@
       <ng-container matColumnDef="user">
         <th mat-header-cell *matHeaderCellDef class="user label-header">
           <span class="label">User</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -105,17 +105,17 @@
       <ng-container matColumnDef="project">
         <th mat-header-cell *matHeaderCellDef class="project label-header">
           <span class="label">Project</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
               <span *ngIf="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">filter_list</span>
               <span [hidden]="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">more_vert</span>
             </i>
-          </button> 
+          </button>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.project }}</td>
       </ng-container>
@@ -123,10 +123,10 @@
       <ng-container matColumnDef="endpoint">
         <th mat-header-cell *matHeaderCellDef class="endpoint label-header">
           <span class="label">Endpoint</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -141,10 +141,10 @@
       <ng-container matColumnDef="type">
         <th mat-header-cell *matHeaderCellDef class="type label-header">
           <span class="label">Name</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -168,10 +168,10 @@
       <ng-container matColumnDef="shape">
         <th mat-header-cell *matHeaderCellDef class="shape label-header">
           <span class="label">Shape / Resource id</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -190,10 +190,10 @@
       <ng-container matColumnDef="status">
         <th mat-header-cell *matHeaderCellDef class="status label-header">
           <span class="label">Status</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -210,10 +210,10 @@
       <ng-container matColumnDef="resources">
         <th mat-header-cell *matHeaderCellDef class="resources label-header">
           <span class="label">Compute</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -236,8 +236,8 @@
                 <div class="resource-actions">
                   <span class="not-allow">
                     <a class="start-stop-action" *ngIf="resource.image === 'docker.datalab-dataengine'">
-                      <i 
-                        class="material-icons" 
+                      <i
+                        class="material-icons"
                         (click)="toggleResourceAction(element, 'stop', resource)"
                         [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }"
                       >
@@ -246,8 +246,8 @@
                     </a>
                   </span>
                   <span class="not-allow">
-                    <a 
-                      class="remove_butt" 
+                    <a
+                      class="remove_butt"
                       (click)="toggleResourceAction(element, 'terminate', resource)"
                       [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }"
                     >
@@ -267,10 +267,10 @@
         </th>
         <td mat-cell *matCellDef="let element" class="settings actions-col">
           <span [ngClass]="{'not-allow' : selected?.length}">
-            <span 
-              #settings 
-              class="actions" 
-              (click)="actions.toggle($event, settings)" 
+            <span
+              #settings
+              class="actions"
+              (click)="actions.toggle($event, settings)"
               *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
               [ngClass]="{
                 'disabled' : (element.status !== 'running' && element.status !== 'stopped')
@@ -281,39 +281,50 @@
           <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
             <ul class="list-unstyled">
               <li
-                matTooltip="{{ element.type !== 'edge node' 
-                  ? 'Unable to stop notebook because at least one computational resource is in progress' 
+                matTooltip="{{ element.type !== 'edge node'
+                  ? 'Unable to stop notebook because at least one computational resource is in progress'
                   : 'Unable to stop edge node because at least one resource of this user is in progress' }}"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
                 [hidden]="element.name === 'edge node' && element.status === 'stopped'"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'stop')"
-                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || 
-                                              element.status === 'starting' || element.status === 'creating image' || 
+                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' ||
+                                              element.status === 'starting' || element.status === 'creating image' ||
                                               element.status === 'failed' || isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">pause_circle_outline</i>
                   <span>Stop</span>
                 </div>
               </li>
-              <li 
+              <li
                 *ngIf="element.name !== 'edge node'"
                 matTooltip="Unable to terminate notebook because at least one compute is in progress"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'terminate')"
-                  [ngClass]="{'not-allowed' : element.status !== 'running' && 
-                                              element.status !== 'stopped' || 
+                  [ngClass]="{'not-allowed' : element.status !== 'running' &&
+                                              element.status !== 'stopped' ||
                                               isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">phonelink_off</i>
                   <span>Terminate</span>
                 </div>
               </li>
+              <li
+                *ngIf="element.status !== 'terminated'  && element.image !== 'docker.datalab-superset'
+                      && element.image !== 'docker.datalab-jupyterlab'"
+              >
+                <div
+                  (click)="toggleResourceAction(element, 'create image')"
+                >
+                  <i class="material-icons">view_module</i>
+                  <span>Create Image</span>
+                </div>
+              </li>
               <li *ngIf="element.status === 'stopped'">
                 <div (click)="toggleResourceAction(element, 'start')">
                   <i class="material-icons">play_circle_outline</i>
@@ -333,9 +344,9 @@
 
       <ng-container matColumnDef="user-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
-            [type]="'users'" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
+            [type]="'users'"
             [items]="filterConfiguration.users"
             [model]="filterForm.users"
           ></multi-select-dropdown>
@@ -344,11 +355,11 @@
 
       <ng-container matColumnDef="type-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <input 
-            placeholder="Filter by name" 
-            type="text" 
+          <input
+            placeholder="Filter by name"
+            type="text"
             class="form-control filter-field"
-            [value]="filterForm.type" 
+            [value]="filterForm.type"
             (input)="onFilterNameUpdate($event.target['value'])"
           />
         </th>
@@ -356,20 +367,20 @@
 
       <ng-container matColumnDef="project-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'projects'"
-            [items]="filterConfiguration.projects" 
+            [items]="filterConfiguration.projects"
             [model]="filterForm.projects"
           ></multi-select-dropdown>
         </th>
       </ng-container>
       <ng-container matColumnDef="endpoint-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'endpoints'"
-            [items]="filterConfiguration.endpoints" 
+            [items]="filterConfiguration.endpoints"
             [model]="filterForm.endpoints"
           ></multi-select-dropdown>
         </th>
@@ -377,10 +388,10 @@
 
       <ng-container matColumnDef="shape-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'shapes'"
-            [items]="filterConfiguration.shapes" 
+            [items]="filterConfiguration.shapes"
             [model]="filterForm.shapes"
           ></multi-select-dropdown>
         </th>
@@ -388,10 +399,10 @@
 
       <ng-container matColumnDef="status-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'statuses'"
-            [items]="filterConfiguration.statuses" 
+            [items]="filterConfiguration.statuses"
             [model]="filterForm.statuses"
           ></multi-select-dropdown>
         </th>
@@ -399,10 +410,10 @@
 
       <ng-container matColumnDef="resource-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'resources'"
-            [items]="filterConfiguration.resources" 
+            [items]="filterConfiguration.resources"
             [model]="filterForm.resources"
           ></multi-select-dropdown>
         </th>
@@ -411,22 +422,22 @@
       <ng-container matColumnDef="actions-filter" sticky>
         <th mat-header-cell *matHeaderCellDef  class="actions-col filter-row-item">
           <div class="actions">
-            <button 
-              mat-icon-button 
-              class="btn reset" 
-              (click)="resetFilterConfigurations()" 
+            <button
+              mat-icon-button
+              class="btn reset"
+              (click)="resetFilterConfigurations()"
               [disabled]="!isFilterSelected"
             >
               <i class="material-icons">close</i>
             </button>
 
-            <button 
-              mat-icon-button 
-              class="btn apply" 
+            <button
+              mat-icon-button
+              class="btn apply"
               (click)="applyFilter(filterForm)"
               [disabled]="isFilterChanged"
             >
-              <i 
+              <i
                 class="material-icons"
                 [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}"
               >
@@ -439,7 +450,7 @@
 
       <ng-container matColumnDef="placeholder">
         <td mat-footer-cell *matFooterCellDef colspan="8" class="no-border">
-          <div 
+          <div
             class="info"
             *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering"
           >
@@ -450,36 +461,36 @@
       </ng-container>
 
       <ng-container matColumnDef="scrollButtons">
-        <td 
-          mat-footer-cell 
-          *matFooterCellDef 
-          colspan="9" 
+        <td
+          mat-footer-cell
+          *matFooterCellDef
+          colspan="9"
           class="buttons-wrap"
         ></td>
       </ng-container>
 
-      <tr 
-        mat-header-row 
-        *matHeaderRowDef="displayedColumns; sticky: true" 
+      <tr
+        mat-header-row
+        *matHeaderRowDef="displayedColumns; sticky: true"
         class="header-row"
       ></tr>
-      <tr 
-        [hidden]="!collapsedFilterRow" 
-        mat-header-row 
+      <tr
+        [hidden]="!collapsedFilterRow"
+        mat-header-row
         *matHeaderRowDef="displayedFilterColumns; sticky: true"
         class="filter-row"
       ></tr>
       <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 
-      <tr 
-        [hidden]="allFilteredEnvironmentData?.length" 
-        mat-footer-row *matFooterRowDef="['placeholder']" 
+      <tr
+        [hidden]="allFilteredEnvironmentData?.length"
+        mat-footer-row *matFooterRowDef="['placeholder']"
         class="info"
       ></tr>
-      <tr 
-        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" 
-        mat-footer-row 
-        *matFooterRowDef="['scrollButtons']; sticky: true" 
+      <tr
+        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length"
+        mat-footer-row
+        *matFooterRowDef="['scrollButtons']; sticky: true"
         class="info"
       ></tr>
     </table>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index a122af1c6..d757eedac 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -142,7 +142,7 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
             this.applyFilter(this.cashedFilterForm || this.filterForm);
           }
           this.progressBarService.stopProgressBar();
-        }, 
+        },
         () => {
           this.progressBarService.stopProgressBar();
         }
@@ -186,8 +186,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
 
     const containsStatus = (list, selectedItems) => {
       if (list) {
-        return list.filter((item: any) => { 
-          if (selectedItems.indexOf(item.status) !== -1) return item; 
+        return list.filter((item: any) => {
+          if (selectedItems.indexOf(item.status) !== -1) return item;
         });
       }
     };
@@ -196,30 +196,30 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
     if (config) {
       filteredData = filteredData.filter(item => {
         const isUser = config.users.length > 0 ? (config.users.indexOf(item.user) !== -1) : true;
-        const isTypeName = item.name 
-          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1 
+        const isTypeName = item.name
+          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1
           : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
-        const isStatus = config.statuses.length > 0 
-          ? (config.statuses.indexOf(item.status) !== -1) 
+        const isStatus = config.statuses.length > 0
+          ? (config.statuses.indexOf(item.status) !== -1)
           : (config.type !== 'active');
-        const isShape = config.shapes.length > 0 
+        const isShape = config.shapes.length > 0
           ? (config.shapes.indexOf(item.shape) !== -1 ||
             config.shapes.indexOf(item.gpu_type) !== -1 ||
-            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1) 
+            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1)
           : true;
-        const isProject = config.projects.length > 0 
-          ? (config.projects.indexOf(item.project) !== -1) 
+        const isProject = config.projects.length > 0
+          ? (config.projects.indexOf(item.project) !== -1)
           : true;
-        const isEndpoint = config.endpoints.length > 0 
-          ? (config.endpoints.indexOf(item.endpoint) !== -1) 
+        const isEndpoint = config.endpoints.length > 0
+          ? (config.endpoints.indexOf(item.endpoint) !== -1)
           : true;
 
         const modifiedResources = containsStatus(item.resources, config.resources);
-        let isResources = config.resources.length > 0 
-          ? (modifiedResources && modifiedResources.length > 0) 
+        let isResources = config.resources.length > 0
+          ? (modifiedResources && modifiedResources.length > 0)
           : true;
-        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { 
-          item.resources = modifiedResources; 
+        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) {
+          item.resources = modifiedResources;
         }
 
         if (config.resources && config.resources.length === 0 && config.type === 'active' ||
@@ -288,7 +288,13 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
       }
     });
 
-    this.filterConfiguration = new ManagementConfigModel(users, '', projects, [...shapes, ...gpuTypes, ...gpuCounts], statuses, resources, endpoints);
+    this.filterConfiguration = new ManagementConfigModel(
+      users,
+      '',
+      projects,
+      [...shapes, ...gpuTypes, ...gpuCounts],
+      statuses, resources, endpoints
+    );
   }
 
   public openNotebookDetails(data) {
@@ -352,7 +358,10 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
   selector: 'confirm-dialog',
   template: `
   <div class="dialog-header">
-    <h4 class="modal-title"><span class="capitalize">{{ data.action }}</span> resource</h4>
+    <h4 class="modal-title">
+      <span class="capitalize">{{ data.action }}</span>
+      <span *ngIf="data.action !== 'create image'"> resource</span>
+    </h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </div>
   <div mat-dialog-content class="content">
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
index d6c414c58..4dd9de2ac 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
@@ -29,7 +29,7 @@ import {
   StorageService
 } from '../../core/services';
 
-import { ActionsType, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType } from './management.model';
+import {ActionsType, ActionTypeOptions, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType} from './management.model';
 import { HTTP_STATUS_CODES } from '../../core/util';
 import { BackupDilogComponent } from './backup-dilog/backup-dilog.component';
 import { SsnMonitorComponent } from './ssn-monitor/ssn-monitor.component';
@@ -43,6 +43,7 @@ import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../share
 import { ManagementGridComponent, ReconfirmationDialogComponent } from './management-grid/management-grid.component';
 import { FolderTreeComponent } from '../../resources/bucket-browser/folder-tree/folder-tree.component';
 import { StatusTypes } from '../../core/models';
+import {AmiCreateDialogComponent} from '../../resources/exploratory/ami-create-dialog';
 
 @Component({
   selector: 'environments-management',
@@ -225,8 +226,7 @@ export class ManagementComponent implements OnInit {
     this.isActionsOpen = !this.isActionsOpen;
   }
 
-  toggleResourceAction($event): void {
-    const { environment, action, resource } = $event;
+  toggleResourceAction({ environment, action, resource = null}): void {
     this.openDialog(environment, action, resource);
   }
 
@@ -239,7 +239,7 @@ export class ManagementComponent implements OnInit {
     }
   }
 
-  public resourceAction(action) {
+  public resourceAction(action: ActionTypeOptions) {
     this.toggleResourceAction({ environment: this.selected, action: action });
   }
 
@@ -265,7 +265,14 @@ export class ManagementComponent implements OnInit {
       observer.next = (res) => {
         if (res) {
           notebooks.forEach((env) => {
-            this.getNotebookAction(env, action);
+            if (action === 'Create Image') {
+              env['isAdmin'] = true;
+              env['userName'] = env.user;
+              this.dialog.open(AmiCreateDialogComponent, { data: env, panelClass: 'modal-sm' })
+                .afterClosed().subscribe();
+            } else {
+              this.getNotebookAction(env, action);
+            }
           });
         }
         this.clearSelection();
@@ -306,7 +313,7 @@ export class ManagementComponent implements OnInit {
     return {...config, notebooks};
   }
 
-  private getNotebookAction(env: EnvironmentModel, action: ActionsType) {
+  private getNotebookAction(env: EnvironmentModel, action: ActionsType): void {
     this.manageEnvironmentsService.environmentManagement(env.user, action, env.project, env.name)
       .subscribe(
         () => this.buildGrid(),
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index 56ce00ff7..5e7435302 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -117,10 +117,10 @@ export class ManagementConfigModel {
 
 export interface ModalData {
   action: ActionsType;
-  resource_name?: any; 
-  user?: any, 
+  resource_name?: any;
+  user?: any;
   type: string;
-  notebooks?: any
+  notebooks?: any;
 }
 
 export enum ModalDataType {
@@ -133,5 +133,8 @@ export enum ActionsType {
   terminate = 'terminate',
   start = 'start',
   run = 'run',
-  recreate = 'recreate'
-}
\ No newline at end of file
+  recreate = 'recreate',
+  createImage = 'create image'
+}
+
+export type ActionTypeOptions = 'stop' | 'terminate' | 'start' | 'run' | 'recreate' | 'createAmi';
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
index e1459fcf3..1c47afc10 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
@@ -22,27 +22,31 @@ import { ActionsType } from '../../../administration/management/management.model
 
 const actionTypes = {
     stopped: 'Stopped',
-    terminated: ' Terminated',
-    started: 'Started'
-}
+    terminated: 'Terminated',
+    started: 'Starting',
+    creating: 'Creating Image',
+    unknown: 'Unknown type'
+};
 
 @Pipe({ name: 'convertactiontype' })
 
 export class ConvertActionTypePipe implements PipeTransform {
-  transform(value: string,): string {
-    if(value === ActionsType.stop) {
-        return actionTypes.stopped
+  transform(value: string): string {
+    if (value === ActionsType.stop) {
+        return actionTypes.stopped;
     }
 
-    if(value === ActionsType.start) {
-        return actionTypes.started
+    if (value === ActionsType.start) {
+        return actionTypes.started;
     }
 
-    if(value === ActionsType.terminate) {
-        return actionTypes.terminated
+    if (value === ActionsType.terminate) {
+        return actionTypes.terminated;
     }
-    else {
-        return 'Unknown type'
+    if (value === ActionsType.createImage) {
+      return actionTypes.creating;
+    } else {
+        return actionTypes.unknown;
     }
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
index ae576f2d9..a5befe290 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
@@ -45,19 +45,19 @@
         </div>
       </form>
       <div class="text-center m-top-30 m-bott-10">
-        <button 
-          mat-raised-button 
-          type="button" 
-          class="butt action" 
+        <button
+          mat-raised-button
+          type="button"
+          class="butt action"
           (click)="dialogRef.close()"
         >
           Cancel
         </button>
-        <button 
-          mat-raised-button 
-          type="button" 
+        <button
+          mat-raised-button
+          type="button"
           [disabled]="!createAMIForm.valid"
-          (click)="assignChanges(createAMIForm.value)" 
+          (click)="assignChanges(createAMIForm.value)"
           class="butt butt-success action"
         >
           Create
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
index 315f8f3c6..c5caebc00 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
@@ -22,7 +22,7 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
 
-import { UserResourceService } from '../../../core/services';
+import {ManageEnvironmentsService, UserResourceService} from '../../../core/services';
 import { HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 
@@ -38,6 +38,7 @@ export class AmiCreateDialogComponent implements OnInit {
   public provider: string;
   delimitersRegex = /[-_]?/g;
   imagesList: any;
+  private isAdmin: boolean = false;
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -45,28 +46,43 @@ export class AmiCreateDialogComponent implements OnInit {
     public dialogRef: MatDialogRef<AmiCreateDialogComponent>,
     private _userResource: UserResourceService,
     private _fb: FormBuilder,
+    private manageEnvironmentsService: ManageEnvironmentsService,
   ) { }
 
   ngOnInit() {
     this.notebook = this.data;
     this.provider = this.data.cloud_provider;
+    this.checkRole();
 
     this.initFormModel();
     this._userResource.getImagesList(this.data.project).subscribe(res => this.imagesList = res);
   }
 
-  public assignChanges(data) {
-    this._userResource.createAMI(data).subscribe(
-      response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
-      error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+  public assignChanges(data): void {
+    if (this.isAdmin) {
+      const { exploratory_name, project_name, name, description } = data;
+      const imageInfo = { userName: this.data.userName,   imageName: name,   description};
+      this.manageEnvironmentsService.environmentManagement(imageInfo, 'createAmi', project_name, exploratory_name).subscribe(
+        () => { this.dialogRef.close(true); });
+    } else {
+      this._userResource.createAMI(data).subscribe(
+        response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
+        error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+    }
+  }
+
+  private checkRole(): void {
+    if (this.data?.isAdmin) {
+      this.isAdmin = this.data.isAdmin;
+    }
   }
 
   private initFormModel(): void {
     this.createAMIForm = this._fb.group({
       name: ['', [
-        Validators.required, 
-        Validators.pattern(PATTERNS.namePattern), 
-        Validators.maxLength(10), 
+        Validators.required,
+        Validators.pattern(PATTERNS.namePattern),
+        Validators.maxLength(10),
         this.checkDuplication.bind(this)
       ]],
       description: [''],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 04/09: resolve conflict

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit e6a9f09a41ae1a5847e7b9d9b642041404946af6
Merge: ac1a9a61d a29aa0622
Author: Denys Yankiv <de...@gmail.com>
AuthorDate: Thu May 12 13:54:13 2022 +0300

    resolve conflict

 .../management-grid/management-grid.component.html |  4 ++--
 .../management-grid/management-grid.component.ts   |  5 +++-
 .../administration/management/management.model.ts  |  2 +-
 .../convert-action-type.pipe.ts                    | 28 ++++++++++++----------
 4 files changed, 23 insertions(+), 16 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 05/09: [DATALAB-2771] created create image btn

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit ae8007f5dc8359543821855f3da08b42438ad224
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Tue May 3 18:18:27 2022 +0300

    [DATALAB-2771] created create image btn
---
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  47 +++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../convert-action-type.pipe.ts                    |  28 +--
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 7 files changed, 213 insertions(+), 163 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index c8c793d8c..43c7534ee 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -18,14 +18,14 @@
   -->
 <div class="managment-wrapper scrolling" #pageWrapper (scroll)="scrollTable($event)" (resize)="onResize($event)">
   <div class="ani scrolling scroll-wrapper" #wrapper (scroll)="scrollTable($event)">
-    <div 
-      class="nav-buttons" 
+    <div
+      class="nav-buttons"
       [ngStyle]="{'width.px': userAgentIndex === -1 ? tableWrapperWidth - 3 : tableWrapperWidth - 7 }"
-      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}" 
+      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}"
       [hidden]="!(tableWrapper.offsetWidth - tableEl['offsetWidth'] < 0)"
     >
       <div class="button-container">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll left"
           (click)="sctollTo('left')"
           [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }"
@@ -34,7 +34,7 @@
         </button>
       </div>
       <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll right"
           (click)="sctollTo('right')"
           [ngClass]="{'not-allowed': !(isMaxRight | async)}"
@@ -61,10 +61,10 @@
           </button>
         </th>
         <td mat-cell *matCellDef="let element">
-          <ng-template 
-            [ngIf]="element.type !== 'odahu' 
-                  && element.type !== 'edge node' 
-                  && (element.status==='running' || element.status==='stopped') 
+          <ng-template
+            [ngIf]="element.type !== 'odahu'
+                  && element.type !== 'edge node'
+                  && (element.status==='running' || element.status==='stopped')
                   && !clustersInProgress(element.resources)"
           >
             <datalab-checkbox
@@ -79,10 +79,10 @@
       <ng-container matColumnDef="user">
         <th mat-header-cell *matHeaderCellDef class="user label-header">
           <span class="label">User</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -105,17 +105,17 @@
       <ng-container matColumnDef="project">
         <th mat-header-cell *matHeaderCellDef class="project label-header">
           <span class="label">Project</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
               <span *ngIf="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">filter_list</span>
               <span [hidden]="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">more_vert</span>
             </i>
-          </button> 
+          </button>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.project }}</td>
       </ng-container>
@@ -123,10 +123,10 @@
       <ng-container matColumnDef="endpoint">
         <th mat-header-cell *matHeaderCellDef class="endpoint label-header">
           <span class="label">Endpoint</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -141,10 +141,10 @@
       <ng-container matColumnDef="type">
         <th mat-header-cell *matHeaderCellDef class="type label-header">
           <span class="label">Name</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -168,10 +168,10 @@
       <ng-container matColumnDef="shape">
         <th mat-header-cell *matHeaderCellDef class="shape label-header">
           <span class="label">Shape / Resource id</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -190,10 +190,10 @@
       <ng-container matColumnDef="status">
         <th mat-header-cell *matHeaderCellDef class="status label-header">
           <span class="label">Status</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -210,10 +210,10 @@
       <ng-container matColumnDef="resources">
         <th mat-header-cell *matHeaderCellDef class="resources label-header">
           <span class="label">Compute</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -236,8 +236,8 @@
                 <div class="resource-actions">
                   <span class="not-allow">
                     <a class="start-stop-action" *ngIf="resource.image === 'docker.datalab-dataengine'">
-                      <i 
-                        class="material-icons" 
+                      <i
+                        class="material-icons"
                         (click)="toggleResourceAction(element, 'stop', resource)"
                         [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }"
                       >
@@ -246,8 +246,8 @@
                     </a>
                   </span>
                   <span class="not-allow">
-                    <a 
-                      class="remove_butt" 
+                    <a
+                      class="remove_butt"
                       (click)="toggleResourceAction(element, 'terminate', resource)"
                       [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }"
                     >
@@ -267,10 +267,10 @@
         </th>
         <td mat-cell *matCellDef="let element" class="settings actions-col">
           <span [ngClass]="{'not-allow' : selected?.length}">
-            <span 
-              #settings 
-              class="actions" 
-              (click)="actions.toggle($event, settings)" 
+            <span
+              #settings
+              class="actions"
+              (click)="actions.toggle($event, settings)"
               *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
               [ngClass]="{
                 'disabled' : (element.status !== 'running' && element.status !== 'stopped')
@@ -281,39 +281,50 @@
           <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
             <ul class="list-unstyled">
               <li
-                matTooltip="{{ element.type !== 'edge node' 
-                  ? 'Unable to stop notebook because at least one computational resource is in progress' 
+                matTooltip="{{ element.type !== 'edge node'
+                  ? 'Unable to stop notebook because at least one computational resource is in progress'
                   : 'Unable to stop edge node because at least one resource of this user is in progress' }}"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
                 [hidden]="element.name === 'edge node' && element.status === 'stopped'"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'stop')"
-                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || 
-                                              element.status === 'starting' || element.status === 'creating image' || 
+                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' ||
+                                              element.status === 'starting' || element.status === 'creating image' ||
                                               element.status === 'failed' || isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">pause_circle_outline</i>
                   <span>Stop</span>
                 </div>
               </li>
-              <li 
+              <li
                 *ngIf="element.name !== 'edge node'"
                 matTooltip="Unable to terminate notebook because at least one compute is in progress"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'terminate')"
-                  [ngClass]="{'not-allowed' : element.status !== 'running' && 
-                                              element.status !== 'stopped' || 
+                  [ngClass]="{'not-allowed' : element.status !== 'running' &&
+                                              element.status !== 'stopped' ||
                                               isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">phonelink_off</i>
                   <span>Terminate</span>
                 </div>
               </li>
+              <li
+                *ngIf="element.status !== 'terminated'  && element.image !== 'docker.datalab-superset'
+                      && element.image !== 'docker.datalab-jupyterlab'"
+              >
+                <div
+                  (click)="toggleResourceAction(element, 'create image')"
+                >
+                  <i class="material-icons">view_module</i>
+                  <span>Create Image</span>
+                </div>
+              </li>
               <li *ngIf="element.status === 'stopped'">
                 <div (click)="toggleResourceAction(element, 'start')">
                   <i class="material-icons">play_circle_outline</i>
@@ -333,9 +344,9 @@
 
       <ng-container matColumnDef="user-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
-            [type]="'users'" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
+            [type]="'users'"
             [items]="filterConfiguration.users"
             [model]="filterForm.users"
           ></multi-select-dropdown>
@@ -344,11 +355,11 @@
 
       <ng-container matColumnDef="type-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <input 
-            placeholder="Filter by name" 
-            type="text" 
+          <input
+            placeholder="Filter by name"
+            type="text"
             class="form-control filter-field"
-            [value]="filterForm.type" 
+            [value]="filterForm.type"
             (input)="onFilterNameUpdate($event.target['value'])"
           />
         </th>
@@ -356,20 +367,20 @@
 
       <ng-container matColumnDef="project-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'projects'"
-            [items]="filterConfiguration.projects" 
+            [items]="filterConfiguration.projects"
             [model]="filterForm.projects"
           ></multi-select-dropdown>
         </th>
       </ng-container>
       <ng-container matColumnDef="endpoint-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'endpoints'"
-            [items]="filterConfiguration.endpoints" 
+            [items]="filterConfiguration.endpoints"
             [model]="filterForm.endpoints"
           ></multi-select-dropdown>
         </th>
@@ -377,10 +388,10 @@
 
       <ng-container matColumnDef="shape-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'shapes'"
-            [items]="filterConfiguration.shapes" 
+            [items]="filterConfiguration.shapes"
             [model]="filterForm.shapes"
           ></multi-select-dropdown>
         </th>
@@ -388,10 +399,10 @@
 
       <ng-container matColumnDef="status-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'statuses'"
-            [items]="filterConfiguration.statuses" 
+            [items]="filterConfiguration.statuses"
             [model]="filterForm.statuses"
           ></multi-select-dropdown>
         </th>
@@ -399,10 +410,10 @@
 
       <ng-container matColumnDef="resource-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'resources'"
-            [items]="filterConfiguration.resources" 
+            [items]="filterConfiguration.resources"
             [model]="filterForm.resources"
           ></multi-select-dropdown>
         </th>
@@ -411,22 +422,22 @@
       <ng-container matColumnDef="actions-filter" sticky>
         <th mat-header-cell *matHeaderCellDef  class="actions-col filter-row-item">
           <div class="actions">
-            <button 
-              mat-icon-button 
-              class="btn reset" 
-              (click)="resetFilterConfigurations()" 
+            <button
+              mat-icon-button
+              class="btn reset"
+              (click)="resetFilterConfigurations()"
               [disabled]="!isFilterSelected"
             >
               <i class="material-icons">close</i>
             </button>
 
-            <button 
-              mat-icon-button 
-              class="btn apply" 
+            <button
+              mat-icon-button
+              class="btn apply"
               (click)="applyFilter(filterForm)"
               [disabled]="isFilterChanged"
             >
-              <i 
+              <i
                 class="material-icons"
                 [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}"
               >
@@ -439,7 +450,7 @@
 
       <ng-container matColumnDef="placeholder">
         <td mat-footer-cell *matFooterCellDef colspan="8" class="no-border">
-          <div 
+          <div
             class="info"
             *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering"
           >
@@ -450,36 +461,36 @@
       </ng-container>
 
       <ng-container matColumnDef="scrollButtons">
-        <td 
-          mat-footer-cell 
-          *matFooterCellDef 
-          colspan="9" 
+        <td
+          mat-footer-cell
+          *matFooterCellDef
+          colspan="9"
           class="buttons-wrap"
         ></td>
       </ng-container>
 
-      <tr 
-        mat-header-row 
-        *matHeaderRowDef="displayedColumns; sticky: true" 
+      <tr
+        mat-header-row
+        *matHeaderRowDef="displayedColumns; sticky: true"
         class="header-row"
       ></tr>
-      <tr 
-        [hidden]="!collapsedFilterRow" 
-        mat-header-row 
+      <tr
+        [hidden]="!collapsedFilterRow"
+        mat-header-row
         *matHeaderRowDef="displayedFilterColumns; sticky: true"
         class="filter-row"
       ></tr>
       <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 
-      <tr 
-        [hidden]="allFilteredEnvironmentData?.length" 
-        mat-footer-row *matFooterRowDef="['placeholder']" 
+      <tr
+        [hidden]="allFilteredEnvironmentData?.length"
+        mat-footer-row *matFooterRowDef="['placeholder']"
         class="info"
       ></tr>
-      <tr 
-        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" 
-        mat-footer-row 
-        *matFooterRowDef="['scrollButtons']; sticky: true" 
+      <tr
+        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length"
+        mat-footer-row
+        *matFooterRowDef="['scrollButtons']; sticky: true"
         class="info"
       ></tr>
     </table>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index a122af1c6..d757eedac 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -142,7 +142,7 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
             this.applyFilter(this.cashedFilterForm || this.filterForm);
           }
           this.progressBarService.stopProgressBar();
-        }, 
+        },
         () => {
           this.progressBarService.stopProgressBar();
         }
@@ -186,8 +186,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
 
     const containsStatus = (list, selectedItems) => {
       if (list) {
-        return list.filter((item: any) => { 
-          if (selectedItems.indexOf(item.status) !== -1) return item; 
+        return list.filter((item: any) => {
+          if (selectedItems.indexOf(item.status) !== -1) return item;
         });
       }
     };
@@ -196,30 +196,30 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
     if (config) {
       filteredData = filteredData.filter(item => {
         const isUser = config.users.length > 0 ? (config.users.indexOf(item.user) !== -1) : true;
-        const isTypeName = item.name 
-          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1 
+        const isTypeName = item.name
+          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1
           : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
-        const isStatus = config.statuses.length > 0 
-          ? (config.statuses.indexOf(item.status) !== -1) 
+        const isStatus = config.statuses.length > 0
+          ? (config.statuses.indexOf(item.status) !== -1)
           : (config.type !== 'active');
-        const isShape = config.shapes.length > 0 
+        const isShape = config.shapes.length > 0
           ? (config.shapes.indexOf(item.shape) !== -1 ||
             config.shapes.indexOf(item.gpu_type) !== -1 ||
-            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1) 
+            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1)
           : true;
-        const isProject = config.projects.length > 0 
-          ? (config.projects.indexOf(item.project) !== -1) 
+        const isProject = config.projects.length > 0
+          ? (config.projects.indexOf(item.project) !== -1)
           : true;
-        const isEndpoint = config.endpoints.length > 0 
-          ? (config.endpoints.indexOf(item.endpoint) !== -1) 
+        const isEndpoint = config.endpoints.length > 0
+          ? (config.endpoints.indexOf(item.endpoint) !== -1)
           : true;
 
         const modifiedResources = containsStatus(item.resources, config.resources);
-        let isResources = config.resources.length > 0 
-          ? (modifiedResources && modifiedResources.length > 0) 
+        let isResources = config.resources.length > 0
+          ? (modifiedResources && modifiedResources.length > 0)
           : true;
-        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { 
-          item.resources = modifiedResources; 
+        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) {
+          item.resources = modifiedResources;
         }
 
         if (config.resources && config.resources.length === 0 && config.type === 'active' ||
@@ -288,7 +288,13 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
       }
     });
 
-    this.filterConfiguration = new ManagementConfigModel(users, '', projects, [...shapes, ...gpuTypes, ...gpuCounts], statuses, resources, endpoints);
+    this.filterConfiguration = new ManagementConfigModel(
+      users,
+      '',
+      projects,
+      [...shapes, ...gpuTypes, ...gpuCounts],
+      statuses, resources, endpoints
+    );
   }
 
   public openNotebookDetails(data) {
@@ -352,7 +358,10 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
   selector: 'confirm-dialog',
   template: `
   <div class="dialog-header">
-    <h4 class="modal-title"><span class="capitalize">{{ data.action }}</span> resource</h4>
+    <h4 class="modal-title">
+      <span class="capitalize">{{ data.action }}</span>
+      <span *ngIf="data.action !== 'create image'"> resource</span>
+    </h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </div>
   <div mat-dialog-content class="content">
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
index d6c414c58..f7d3179d0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
@@ -29,7 +29,7 @@ import {
   StorageService
 } from '../../core/services';
 
-import { ActionsType, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType } from './management.model';
+import {ActionsType, ActionTypeOptions, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType} from './management.model';
 import { HTTP_STATUS_CODES } from '../../core/util';
 import { BackupDilogComponent } from './backup-dilog/backup-dilog.component';
 import { SsnMonitorComponent } from './ssn-monitor/ssn-monitor.component';
@@ -43,6 +43,7 @@ import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../share
 import { ManagementGridComponent, ReconfirmationDialogComponent } from './management-grid/management-grid.component';
 import { FolderTreeComponent } from '../../resources/bucket-browser/folder-tree/folder-tree.component';
 import { StatusTypes } from '../../core/models';
+import {AmiCreateDialogComponent} from '../../resources/exploratory/ami-create-dialog';
 
 @Component({
   selector: 'environments-management',
@@ -225,8 +226,7 @@ export class ManagementComponent implements OnInit {
     this.isActionsOpen = !this.isActionsOpen;
   }
 
-  toggleResourceAction($event): void {
-    const { environment, action, resource } = $event;
+  toggleResourceAction({ environment, action, resource = null}): void {
     this.openDialog(environment, action, resource);
   }
 
@@ -239,7 +239,7 @@ export class ManagementComponent implements OnInit {
     }
   }
 
-  public resourceAction(action) {
+  public resourceAction(action: ActionTypeOptions) {
     this.toggleResourceAction({ environment: this.selected, action: action });
   }
 
@@ -265,7 +265,14 @@ export class ManagementComponent implements OnInit {
       observer.next = (res) => {
         if (res) {
           notebooks.forEach((env) => {
-            this.getNotebookAction(env, action);
+            if (action === 'create image') {
+              env['isAdmin'] = true;
+              env['userName'] = env.user;
+              this.dialog.open(AmiCreateDialogComponent, { data: env, panelClass: 'modal-sm' })
+                .afterClosed().subscribe();
+            } else {
+              this.getNotebookAction(env, action);
+            }
           });
         }
         this.clearSelection();
@@ -306,7 +313,7 @@ export class ManagementComponent implements OnInit {
     return {...config, notebooks};
   }
 
-  private getNotebookAction(env: EnvironmentModel, action: ActionsType) {
+  private getNotebookAction(env: EnvironmentModel, action: ActionsType): void {
     this.manageEnvironmentsService.environmentManagement(env.user, action, env.project, env.name)
       .subscribe(
         () => this.buildGrid(),
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index 56ce00ff7..5e7435302 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -117,10 +117,10 @@ export class ManagementConfigModel {
 
 export interface ModalData {
   action: ActionsType;
-  resource_name?: any; 
-  user?: any, 
+  resource_name?: any;
+  user?: any;
   type: string;
-  notebooks?: any
+  notebooks?: any;
 }
 
 export enum ModalDataType {
@@ -133,5 +133,8 @@ export enum ActionsType {
   terminate = 'terminate',
   start = 'start',
   run = 'run',
-  recreate = 'recreate'
-}
\ No newline at end of file
+  recreate = 'recreate',
+  createImage = 'create image'
+}
+
+export type ActionTypeOptions = 'stop' | 'terminate' | 'start' | 'run' | 'recreate' | 'createAmi';
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
index e1459fcf3..1c47afc10 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-action-type-pipe/convert-action-type.pipe.ts
@@ -22,27 +22,31 @@ import { ActionsType } from '../../../administration/management/management.model
 
 const actionTypes = {
     stopped: 'Stopped',
-    terminated: ' Terminated',
-    started: 'Started'
-}
+    terminated: 'Terminated',
+    started: 'Starting',
+    creating: 'Creating Image',
+    unknown: 'Unknown type'
+};
 
 @Pipe({ name: 'convertactiontype' })
 
 export class ConvertActionTypePipe implements PipeTransform {
-  transform(value: string,): string {
-    if(value === ActionsType.stop) {
-        return actionTypes.stopped
+  transform(value: string): string {
+    if (value === ActionsType.stop) {
+        return actionTypes.stopped;
     }
 
-    if(value === ActionsType.start) {
-        return actionTypes.started
+    if (value === ActionsType.start) {
+        return actionTypes.started;
     }
 
-    if(value === ActionsType.terminate) {
-        return actionTypes.terminated
+    if (value === ActionsType.terminate) {
+        return actionTypes.terminated;
     }
-    else {
-        return 'Unknown type'
+    if (value === ActionsType.createImage) {
+      return actionTypes.creating;
+    } else {
+        return actionTypes.unknown;
     }
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
index ae576f2d9..a5befe290 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
@@ -45,19 +45,19 @@
         </div>
       </form>
       <div class="text-center m-top-30 m-bott-10">
-        <button 
-          mat-raised-button 
-          type="button" 
-          class="butt action" 
+        <button
+          mat-raised-button
+          type="button"
+          class="butt action"
           (click)="dialogRef.close()"
         >
           Cancel
         </button>
-        <button 
-          mat-raised-button 
-          type="button" 
+        <button
+          mat-raised-button
+          type="button"
           [disabled]="!createAMIForm.valid"
-          (click)="assignChanges(createAMIForm.value)" 
+          (click)="assignChanges(createAMIForm.value)"
           class="butt butt-success action"
         >
           Create
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
index 315f8f3c6..455468a28 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
@@ -22,7 +22,7 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
 
-import { UserResourceService } from '../../../core/services';
+import {ManageEnvironmentsService, UserResourceService} from '../../../core/services';
 import { HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 
@@ -38,6 +38,7 @@ export class AmiCreateDialogComponent implements OnInit {
   public provider: string;
   delimitersRegex = /[-_]?/g;
   imagesList: any;
+  private isAdmin: boolean = false;
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -45,28 +46,43 @@ export class AmiCreateDialogComponent implements OnInit {
     public dialogRef: MatDialogRef<AmiCreateDialogComponent>,
     private _userResource: UserResourceService,
     private _fb: FormBuilder,
+    private manageEnvironmentsService: ManageEnvironmentsService,
   ) { }
 
   ngOnInit() {
     this.notebook = this.data;
     this.provider = this.data.cloud_provider;
+    this.checkRole();
 
     this.initFormModel();
     this._userResource.getImagesList(this.data.project).subscribe(res => this.imagesList = res);
   }
 
-  public assignChanges(data) {
-    this._userResource.createAMI(data).subscribe(
-      response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
-      error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+  public assignChanges(data): void {
+    if (this.isAdmin) {
+      const { exploratory_name, project_name, name, description } = data;
+      const imageInfo = { user: this.data.userName,   imageName: name,   description, project_name, exploratory_name};
+      this.manageEnvironmentsService.environmentManagement(imageInfo, 'createImage', project_name, exploratory_name).subscribe(
+        () => { this.dialogRef.close(true); });
+    } else {
+      this._userResource.createAMI(data).subscribe(
+        response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
+        error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+    }
+  }
+
+  private checkRole(): void {
+    if (this.data?.isAdmin) {
+      this.isAdmin = this.data.isAdmin;
+    }
   }
 
   private initFormModel(): void {
     this.createAMIForm = this._fb.group({
       name: ['', [
-        Validators.required, 
-        Validators.pattern(PATTERNS.namePattern), 
-        Validators.maxLength(10), 
+        Validators.required,
+        Validators.pattern(PATTERNS.namePattern),
+        Validators.maxLength(10),
         this.checkDuplication.bind(this)
       ]],
       description: [''],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 09/09: small fix

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 36be6ec7059d020f299dbd7042903fba802f10a3
Author: Denys Yankiv <de...@gmail.com>
AuthorDate: Wed May 18 13:34:30 2022 +0300

    small fix
---
 .../resources/dto/ExploratoryImageCreateFormAdminDTO.java           | 3 ++-
 .../datalab/backendapi/service/impl/EnvironmentServiceImpl.java     | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java
index 69498d362..9c8b545b7 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java
@@ -11,7 +11,8 @@ public class ExploratoryImageCreateFormAdminDTO {
     @NotBlank
     private String user;
     @NotBlank
-    private final String name;
+    @JsonProperty("imageName")
+    private String name;
     @NotBlank
     @JsonProperty("exploratory_name")
     private String notebookName;
diff --git a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/EnvironmentServiceImpl.java b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/EnvironmentServiceImpl.java
index 7c9ebfc29..99aa23bac 100644
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/EnvironmentServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/service/impl/EnvironmentServiceImpl.java
@@ -111,14 +111,14 @@ public class EnvironmentServiceImpl implements EnvironmentService {
 
     @ProjectAdmin
     @Override
-    public void createImage(@User UserInfo userInfo, String user, String project, String exploratoryName, String imageName, String description) {
-        imageExploratoryService.createImage(securityService.getUserInfoOffline(user), project,exploratoryName, imageName, description, null);
+    public void createImage(@User UserInfo userInfo, String user, @Project String project, String exploratoryName, String imageName, String description) {
+        imageExploratoryService.createImage(securityService.getUserInfoOffline(user), project, exploratoryName, imageName, description, null);
     }
 
     @ProjectAdmin
     @Override
     public void startExploratory(@User UserInfo userInfo, String user, @Project String project, String exploratoryName) {
-        exploratoryService.start(securityService.getServiceAccountInfo(user),exploratoryName,project,null);
+        exploratoryService.start(securityService.getServiceAccountInfo(user), exploratoryName, project, null);
     }
 
     @Override


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 08/09: Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 9b44f4238b073d039368de29d35606192580040d
Merge: 01496306b 3f9df815d
Author: Denys Yankiv <de...@gmail.com>
AuthorDate: Tue May 17 09:49:19 2022 +0300

    Merge remote-tracking branch 'origin/feat/DATALAB-2771/add-create-image-btn-for-admin' into DATALAB-2785

 .../webapp/src/app/core/services/applicationServiceFacade.service.ts  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 01/09: added create ami button

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit acf9d94e8134b42d4c2677dc27b179eb554390be
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Tue May 3 18:18:27 2022 +0300

    added create ami button
---
 .../management-grid/management-grid.component.html | 221 +++++++++++----------
 .../management-grid/management-grid.component.ts   |  42 ++--
 .../management/management.component.ts             |  19 +-
 .../administration/management/management.model.ts  |  13 +-
 .../ami-create-dialog.component.html               |  16 +-
 .../ami-create-dialog.component.ts                 |  32 ++-
 6 files changed, 193 insertions(+), 150 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index c8c793d8c..e280017f4 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -18,14 +18,14 @@
   -->
 <div class="managment-wrapper scrolling" #pageWrapper (scroll)="scrollTable($event)" (resize)="onResize($event)">
   <div class="ani scrolling scroll-wrapper" #wrapper (scroll)="scrollTable($event)">
-    <div 
-      class="nav-buttons" 
+    <div
+      class="nav-buttons"
       [ngStyle]="{'width.px': userAgentIndex === -1 ? tableWrapperWidth - 3 : tableWrapperWidth - 7 }"
-      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}" 
+      [ngClass]="{'border': tableEl['offsetHeight'] < tableWrapper.offsetHeight - 48}"
       [hidden]="!(tableWrapper.offsetWidth - tableEl['offsetWidth'] < 0)"
     >
       <div class="button-container">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll left"
           (click)="sctollTo('left')"
           [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }"
@@ -34,7 +34,7 @@
         </button>
       </div>
       <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}">
-        <button 
+        <button
           mat-mini-fab aria-label="Scroll right"
           (click)="sctollTo('right')"
           [ngClass]="{'not-allowed': !(isMaxRight | async)}"
@@ -61,10 +61,10 @@
           </button>
         </th>
         <td mat-cell *matCellDef="let element">
-          <ng-template 
-            [ngIf]="element.type !== 'odahu' 
-                  && element.type !== 'edge node' 
-                  && (element.status==='running' || element.status==='stopped') 
+          <ng-template
+            [ngIf]="element.type !== 'odahu'
+                  && element.type !== 'edge node'
+                  && (element.status==='running' || element.status==='stopped')
                   && !clustersInProgress(element.resources)"
           >
             <datalab-checkbox
@@ -79,10 +79,10 @@
       <ng-container matColumnDef="user">
         <th mat-header-cell *matHeaderCellDef class="user label-header">
           <span class="label">User</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -105,17 +105,17 @@
       <ng-container matColumnDef="project">
         <th mat-header-cell *matHeaderCellDef class="project label-header">
           <span class="label">Project</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
               <span *ngIf="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">filter_list</span>
               <span [hidden]="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">more_vert</span>
             </i>
-          </button> 
+          </button>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.project }}</td>
       </ng-container>
@@ -123,10 +123,10 @@
       <ng-container matColumnDef="endpoint">
         <th mat-header-cell *matHeaderCellDef class="endpoint label-header">
           <span class="label">Endpoint</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -141,10 +141,10 @@
       <ng-container matColumnDef="type">
         <th mat-header-cell *matHeaderCellDef class="type label-header">
           <span class="label">Name</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -168,10 +168,10 @@
       <ng-container matColumnDef="shape">
         <th mat-header-cell *matHeaderCellDef class="shape label-header">
           <span class="label">Shape / Resource id</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -190,10 +190,10 @@
       <ng-container matColumnDef="status">
         <th mat-header-cell *matHeaderCellDef class="status label-header">
           <span class="label">Status</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -210,10 +210,10 @@
       <ng-container matColumnDef="resources">
         <th mat-header-cell *matHeaderCellDef class="resources label-header">
           <span class="label">Compute</span>
-          <button 
-            mat-icon-button 
-            aria-label="More" 
-            class="ar" 
+          <button
+            mat-icon-button
+            aria-label="More"
+            class="ar"
             (click)="toggleFilterRow()"
           >
             <i class="material-icons">
@@ -236,8 +236,8 @@
                 <div class="resource-actions">
                   <span class="not-allow">
                     <a class="start-stop-action" *ngIf="resource.image === 'docker.datalab-dataengine'">
-                      <i 
-                        class="material-icons" 
+                      <i
+                        class="material-icons"
                         (click)="toggleResourceAction(element, 'stop', resource)"
                         [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }"
                       >
@@ -246,8 +246,8 @@
                     </a>
                   </span>
                   <span class="not-allow">
-                    <a 
-                      class="remove_butt" 
+                    <a
+                      class="remove_butt"
                       (click)="toggleResourceAction(element, 'terminate', resource)"
                       [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }"
                     >
@@ -267,10 +267,10 @@
         </th>
         <td mat-cell *matCellDef="let element" class="settings actions-col">
           <span [ngClass]="{'not-allow' : selected?.length}">
-            <span 
-              #settings 
-              class="actions" 
-              (click)="actions.toggle($event, settings)" 
+            <span
+              #settings
+              class="actions"
+              (click)="actions.toggle($event, settings)"
               *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
               [ngClass]="{
                 'disabled' : (element.status !== 'running' && element.status !== 'stopped')
@@ -281,39 +281,50 @@
           <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
             <ul class="list-unstyled">
               <li
-                matTooltip="{{ element.type !== 'edge node' 
-                  ? 'Unable to stop notebook because at least one computational resource is in progress' 
+                matTooltip="{{ element.type !== 'edge node'
+                  ? 'Unable to stop notebook because at least one computational resource is in progress'
                   : 'Unable to stop edge node because at least one resource of this user is in progress' }}"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
                 [hidden]="element.name === 'edge node' && element.status === 'stopped'"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'stop')"
-                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || 
-                                              element.status === 'starting' || element.status === 'creating image' || 
+                  [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' ||
+                                              element.status === 'starting' || element.status === 'creating image' ||
                                               element.status === 'failed' || isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">pause_circle_outline</i>
                   <span>Stop</span>
                 </div>
               </li>
-              <li 
+              <li
                 *ngIf="element.name !== 'edge node'"
                 matTooltip="Unable to terminate notebook because at least one compute is in progress"
-                matTooltipPosition="above" 
+                matTooltipPosition="above"
                 [matTooltipDisabled]="!isResourcesInProgress(element)"
               >
-                <div 
+                <div
                   (click)="toggleResourceAction(element, 'terminate')"
-                  [ngClass]="{'not-allowed' : element.status !== 'running' && 
-                                              element.status !== 'stopped' || 
+                  [ngClass]="{'not-allowed' : element.status !== 'running' &&
+                                              element.status !== 'stopped' ||
                                               isResourcesInProgress(element)}"
                 >
                   <i class="material-icons">phonelink_off</i>
                   <span>Terminate</span>
                 </div>
               </li>
+              <li
+                *ngIf="element.status !== 'terminated'  && element.image !== 'docker.datalab-superset'
+                      && element.image !== 'docker.datalab-jupyterlab'"
+              >
+                <div
+                  (click)="toggleResourceAction(element, 'Create Image')"
+                >
+                  <i class="material-icons">view_module</i>
+                  <span>Create AMI</span>
+                </div>
+              </li>
               <li *ngIf="element.status === 'stopped'">
                 <div (click)="toggleResourceAction(element, 'start')">
                   <i class="material-icons">play_circle_outline</i>
@@ -333,9 +344,9 @@
 
       <ng-container matColumnDef="user-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
-            [type]="'users'" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
+            [type]="'users'"
             [items]="filterConfiguration.users"
             [model]="filterForm.users"
           ></multi-select-dropdown>
@@ -344,11 +355,11 @@
 
       <ng-container matColumnDef="type-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <input 
-            placeholder="Filter by name" 
-            type="text" 
+          <input
+            placeholder="Filter by name"
+            type="text"
             class="form-control filter-field"
-            [value]="filterForm.type" 
+            [value]="filterForm.type"
             (input)="onFilterNameUpdate($event.target['value'])"
           />
         </th>
@@ -356,20 +367,20 @@
 
       <ng-container matColumnDef="project-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'projects'"
-            [items]="filterConfiguration.projects" 
+            [items]="filterConfiguration.projects"
             [model]="filterForm.projects"
           ></multi-select-dropdown>
         </th>
       </ng-container>
       <ng-container matColumnDef="endpoint-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'endpoints'"
-            [items]="filterConfiguration.endpoints" 
+            [items]="filterConfiguration.endpoints"
             [model]="filterForm.endpoints"
           ></multi-select-dropdown>
         </th>
@@ -377,10 +388,10 @@
 
       <ng-container matColumnDef="shape-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'shapes'"
-            [items]="filterConfiguration.shapes" 
+            [items]="filterConfiguration.shapes"
             [model]="filterForm.shapes"
           ></multi-select-dropdown>
         </th>
@@ -388,10 +399,10 @@
 
       <ng-container matColumnDef="status-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'statuses'"
-            [items]="filterConfiguration.statuses" 
+            [items]="filterConfiguration.statuses"
             [model]="filterForm.statuses"
           ></multi-select-dropdown>
         </th>
@@ -399,10 +410,10 @@
 
       <ng-container matColumnDef="resource-filter" sticky>
         <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-          <multi-select-dropdown 
-            (selectionChange)="onUpdate($event)" 
+          <multi-select-dropdown
+            (selectionChange)="onUpdate($event)"
             [type]="'resources'"
-            [items]="filterConfiguration.resources" 
+            [items]="filterConfiguration.resources"
             [model]="filterForm.resources"
           ></multi-select-dropdown>
         </th>
@@ -411,22 +422,22 @@
       <ng-container matColumnDef="actions-filter" sticky>
         <th mat-header-cell *matHeaderCellDef  class="actions-col filter-row-item">
           <div class="actions">
-            <button 
-              mat-icon-button 
-              class="btn reset" 
-              (click)="resetFilterConfigurations()" 
+            <button
+              mat-icon-button
+              class="btn reset"
+              (click)="resetFilterConfigurations()"
               [disabled]="!isFilterSelected"
             >
               <i class="material-icons">close</i>
             </button>
 
-            <button 
-              mat-icon-button 
-              class="btn apply" 
+            <button
+              mat-icon-button
+              class="btn apply"
               (click)="applyFilter(filterForm)"
               [disabled]="isFilterChanged"
             >
-              <i 
+              <i
                 class="material-icons"
                 [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}"
               >
@@ -439,7 +450,7 @@
 
       <ng-container matColumnDef="placeholder">
         <td mat-footer-cell *matFooterCellDef colspan="8" class="no-border">
-          <div 
+          <div
             class="info"
             *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering"
           >
@@ -450,36 +461,36 @@
       </ng-container>
 
       <ng-container matColumnDef="scrollButtons">
-        <td 
-          mat-footer-cell 
-          *matFooterCellDef 
-          colspan="9" 
+        <td
+          mat-footer-cell
+          *matFooterCellDef
+          colspan="9"
           class="buttons-wrap"
         ></td>
       </ng-container>
 
-      <tr 
-        mat-header-row 
-        *matHeaderRowDef="displayedColumns; sticky: true" 
+      <tr
+        mat-header-row
+        *matHeaderRowDef="displayedColumns; sticky: true"
         class="header-row"
       ></tr>
-      <tr 
-        [hidden]="!collapsedFilterRow" 
-        mat-header-row 
+      <tr
+        [hidden]="!collapsedFilterRow"
+        mat-header-row
         *matHeaderRowDef="displayedFilterColumns; sticky: true"
         class="filter-row"
       ></tr>
       <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 
-      <tr 
-        [hidden]="allFilteredEnvironmentData?.length" 
-        mat-footer-row *matFooterRowDef="['placeholder']" 
+      <tr
+        [hidden]="allFilteredEnvironmentData?.length"
+        mat-footer-row *matFooterRowDef="['placeholder']"
         class="info"
       ></tr>
-      <tr 
-        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" 
-        mat-footer-row 
-        *matFooterRowDef="['scrollButtons']; sticky: true" 
+      <tr
+        [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length"
+        mat-footer-row
+        *matFooterRowDef="['scrollButtons']; sticky: true"
         class="info"
       ></tr>
     </table>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index a122af1c6..e3342b5e0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -142,7 +142,7 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
             this.applyFilter(this.cashedFilterForm || this.filterForm);
           }
           this.progressBarService.stopProgressBar();
-        }, 
+        },
         () => {
           this.progressBarService.stopProgressBar();
         }
@@ -186,8 +186,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
 
     const containsStatus = (list, selectedItems) => {
       if (list) {
-        return list.filter((item: any) => { 
-          if (selectedItems.indexOf(item.status) !== -1) return item; 
+        return list.filter((item: any) => {
+          if (selectedItems.indexOf(item.status) !== -1) return item;
         });
       }
     };
@@ -196,30 +196,30 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
     if (config) {
       filteredData = filteredData.filter(item => {
         const isUser = config.users.length > 0 ? (config.users.indexOf(item.user) !== -1) : true;
-        const isTypeName = item.name 
-          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1 
+        const isTypeName = item.name
+          ? item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1
           : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
-        const isStatus = config.statuses.length > 0 
-          ? (config.statuses.indexOf(item.status) !== -1) 
+        const isStatus = config.statuses.length > 0
+          ? (config.statuses.indexOf(item.status) !== -1)
           : (config.type !== 'active');
-        const isShape = config.shapes.length > 0 
+        const isShape = config.shapes.length > 0
           ? (config.shapes.indexOf(item.shape) !== -1 ||
             config.shapes.indexOf(item.gpu_type) !== -1 ||
-            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1) 
+            config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1)
           : true;
-        const isProject = config.projects.length > 0 
-          ? (config.projects.indexOf(item.project) !== -1) 
+        const isProject = config.projects.length > 0
+          ? (config.projects.indexOf(item.project) !== -1)
           : true;
-        const isEndpoint = config.endpoints.length > 0 
-          ? (config.endpoints.indexOf(item.endpoint) !== -1) 
+        const isEndpoint = config.endpoints.length > 0
+          ? (config.endpoints.indexOf(item.endpoint) !== -1)
           : true;
 
         const modifiedResources = containsStatus(item.resources, config.resources);
-        let isResources = config.resources.length > 0 
-          ? (modifiedResources && modifiedResources.length > 0) 
+        let isResources = config.resources.length > 0
+          ? (modifiedResources && modifiedResources.length > 0)
           : true;
-        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { 
-          item.resources = modifiedResources; 
+        if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) {
+          item.resources = modifiedResources;
         }
 
         if (config.resources && config.resources.length === 0 && config.type === 'active' ||
@@ -288,7 +288,13 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView
       }
     });
 
-    this.filterConfiguration = new ManagementConfigModel(users, '', projects, [...shapes, ...gpuTypes, ...gpuCounts], statuses, resources, endpoints);
+    this.filterConfiguration = new ManagementConfigModel(
+      users,
+      '',
+      projects,
+      [...shapes, ...gpuTypes, ...gpuCounts],
+      statuses, resources, endpoints
+    );
   }
 
   public openNotebookDetails(data) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
index d6c414c58..4dd9de2ac 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
@@ -29,7 +29,7 @@ import {
   StorageService
 } from '../../core/services';
 
-import { ActionsType, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType } from './management.model';
+import {ActionsType, ActionTypeOptions, EnvironmentModel, GeneralEnvironmentStatus, ModalData, ModalDataType} from './management.model';
 import { HTTP_STATUS_CODES } from '../../core/util';
 import { BackupDilogComponent } from './backup-dilog/backup-dilog.component';
 import { SsnMonitorComponent } from './ssn-monitor/ssn-monitor.component';
@@ -43,6 +43,7 @@ import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../share
 import { ManagementGridComponent, ReconfirmationDialogComponent } from './management-grid/management-grid.component';
 import { FolderTreeComponent } from '../../resources/bucket-browser/folder-tree/folder-tree.component';
 import { StatusTypes } from '../../core/models';
+import {AmiCreateDialogComponent} from '../../resources/exploratory/ami-create-dialog';
 
 @Component({
   selector: 'environments-management',
@@ -225,8 +226,7 @@ export class ManagementComponent implements OnInit {
     this.isActionsOpen = !this.isActionsOpen;
   }
 
-  toggleResourceAction($event): void {
-    const { environment, action, resource } = $event;
+  toggleResourceAction({ environment, action, resource = null}): void {
     this.openDialog(environment, action, resource);
   }
 
@@ -239,7 +239,7 @@ export class ManagementComponent implements OnInit {
     }
   }
 
-  public resourceAction(action) {
+  public resourceAction(action: ActionTypeOptions) {
     this.toggleResourceAction({ environment: this.selected, action: action });
   }
 
@@ -265,7 +265,14 @@ export class ManagementComponent implements OnInit {
       observer.next = (res) => {
         if (res) {
           notebooks.forEach((env) => {
-            this.getNotebookAction(env, action);
+            if (action === 'Create Image') {
+              env['isAdmin'] = true;
+              env['userName'] = env.user;
+              this.dialog.open(AmiCreateDialogComponent, { data: env, panelClass: 'modal-sm' })
+                .afterClosed().subscribe();
+            } else {
+              this.getNotebookAction(env, action);
+            }
           });
         }
         this.clearSelection();
@@ -306,7 +313,7 @@ export class ManagementComponent implements OnInit {
     return {...config, notebooks};
   }
 
-  private getNotebookAction(env: EnvironmentModel, action: ActionsType) {
+  private getNotebookAction(env: EnvironmentModel, action: ActionsType): void {
     this.manageEnvironmentsService.environmentManagement(env.user, action, env.project, env.name)
       .subscribe(
         () => this.buildGrid(),
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index 56ce00ff7..b57e7b670 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -117,10 +117,10 @@ export class ManagementConfigModel {
 
 export interface ModalData {
   action: ActionsType;
-  resource_name?: any; 
-  user?: any, 
+  resource_name?: any;
+  user?: any;
   type: string;
-  notebooks?: any
+  notebooks?: any;
 }
 
 export enum ModalDataType {
@@ -133,5 +133,8 @@ export enum ActionsType {
   terminate = 'terminate',
   start = 'start',
   run = 'run',
-  recreate = 'recreate'
-}
\ No newline at end of file
+  recreate = 'recreate',
+  createAmi = 'createAmi'
+}
+
+export type ActionTypeOptions = 'stop' | 'terminate' | 'start' | 'run' | 'recreate' | 'createAmi';
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
index ae576f2d9..a5befe290 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
@@ -45,19 +45,19 @@
         </div>
       </form>
       <div class="text-center m-top-30 m-bott-10">
-        <button 
-          mat-raised-button 
-          type="button" 
-          class="butt action" 
+        <button
+          mat-raised-button
+          type="button"
+          class="butt action"
           (click)="dialogRef.close()"
         >
           Cancel
         </button>
-        <button 
-          mat-raised-button 
-          type="button" 
+        <button
+          mat-raised-button
+          type="button"
           [disabled]="!createAMIForm.valid"
-          (click)="assignChanges(createAMIForm.value)" 
+          (click)="assignChanges(createAMIForm.value)"
           class="butt butt-success action"
         >
           Create
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
index 315f8f3c6..c5caebc00 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
@@ -22,7 +22,7 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
 
-import { UserResourceService } from '../../../core/services';
+import {ManageEnvironmentsService, UserResourceService} from '../../../core/services';
 import { HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 
@@ -38,6 +38,7 @@ export class AmiCreateDialogComponent implements OnInit {
   public provider: string;
   delimitersRegex = /[-_]?/g;
   imagesList: any;
+  private isAdmin: boolean = false;
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -45,28 +46,43 @@ export class AmiCreateDialogComponent implements OnInit {
     public dialogRef: MatDialogRef<AmiCreateDialogComponent>,
     private _userResource: UserResourceService,
     private _fb: FormBuilder,
+    private manageEnvironmentsService: ManageEnvironmentsService,
   ) { }
 
   ngOnInit() {
     this.notebook = this.data;
     this.provider = this.data.cloud_provider;
+    this.checkRole();
 
     this.initFormModel();
     this._userResource.getImagesList(this.data.project).subscribe(res => this.imagesList = res);
   }
 
-  public assignChanges(data) {
-    this._userResource.createAMI(data).subscribe(
-      response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
-      error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+  public assignChanges(data): void {
+    if (this.isAdmin) {
+      const { exploratory_name, project_name, name, description } = data;
+      const imageInfo = { userName: this.data.userName,   imageName: name,   description};
+      this.manageEnvironmentsService.environmentManagement(imageInfo, 'createAmi', project_name, exploratory_name).subscribe(
+        () => { this.dialogRef.close(true); });
+    } else {
+      this._userResource.createAMI(data).subscribe(
+        response => response.status === HTTP_STATUS_CODES.ACCEPTED && this.dialogRef.close(true),
+        error => this.toastr.error(error.message || `Image creation failed!`, 'Oops!'));
+    }
+  }
+
+  private checkRole(): void {
+    if (this.data?.isAdmin) {
+      this.isAdmin = this.data.isAdmin;
+    }
   }
 
   private initFormModel(): void {
     this.createAMIForm = this._fb.group({
       name: ['', [
-        Validators.required, 
-        Validators.pattern(PATTERNS.namePattern), 
-        Validators.maxLength(10), 
+        Validators.required,
+        Validators.pattern(PATTERNS.namePattern),
+        Validators.maxLength(10),
         this.checkDuplication.bind(this)
       ]],
       description: [''],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org


[incubator-datalab] 07/09: resolve conflict

Posted by dy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dyankiv pushed a commit to branch DATALAB-2785
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 01496306bb7a574e4d23d50600ad0e7779c3f50e
Merge: e6a9f09a4 ae8007f5d
Author: Denys Yankiv <de...@gmail.com>
AuthorDate: Tue May 17 09:48:55 2022 +0300

    resolve conflict

 .../src/general/lib/os/debian/notebook_lib.py      |  8 ++++----
 .../src/general/lib/os/fab.py                      |  3 +++
 .../backendapi/resources/EnvironmentResource.java  | 15 +++++++--------
 .../dto/ExploratoryImageCreateFormAdminDTO.java    | 22 ++++++++++++++++++++++
 .../management/management.component.ts             |  2 +-
 .../ami-create-dialog.component.ts                 |  4 ++--
 6 files changed, 39 insertions(+), 15 deletions(-)

diff --cc services/self-service/src/main/java/com/epam/datalab/backendapi/resources/EnvironmentResource.java
index 2fdfda448,9b984bb40..16fa7addc
--- a/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/EnvironmentResource.java
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/EnvironmentResource.java
@@@ -20,7 -20,6 +20,8 @@@
  package com.epam.datalab.backendapi.resources;
  
  import com.epam.datalab.auth.UserInfo;
++import com.epam.datalab.backendapi.resources.dto.ExploratoryImageCreateFormAdminDTO;
 +import com.epam.datalab.backendapi.resources.dto.ExploratoryImageCreateFormDTO;
  import com.epam.datalab.backendapi.service.EnvironmentService;
  import com.google.inject.Inject;
  import io.dropwizard.auth.Auth;
@@@ -124,19 -119,4 +125,17 @@@ public class EnvironmentResource 
          environmentService.terminateComputational(userInfo, user, projectName, exploratoryName, computationalName);
          return Response.ok().build();
      }
 +
 +    @POST
-     @Consumes(MediaType.TEXT_PLAIN)
++    @Consumes(MediaType.APPLICATION_JSON)
 +    @Produces(MediaType.APPLICATION_JSON)
 +    @Path("createImage/{projectName}/{exploratoryName}")
-     public Response createImage(@Auth UserInfo userInfo, @NotEmpty String user,
-                                 @NotEmpty String imageName,
-                                 @NotEmpty String description,
-                                 @PathParam("projectName") String projectName,
-                                 @PathParam("exploratoryName") String exploratoryName,
++    public Response createImage(@Auth UserInfo userInfo,
++                                @Valid @NotNull ExploratoryImageCreateFormAdminDTO form,
 +                                @Context UriInfo uriInfo) {
-         log.info("Admin {} is creating an image of exploratory {} of user {}", userInfo.getName(), exploratoryName, user);
-         environmentService.createImage(userInfo, user, projectName, exploratoryName, imageName, description);
++        log.info("Admin {} is creating an image of exploratory {} of user {}", userInfo.getName(), form.getNotebookName(), form.getUser());
++        environmentService.createImage(userInfo, form.getUser(), form.getProjectName(), form.getNotebookName(), form.getName(), form.getDescription());
 +        return Response.ok().build();
 +    }
++
  }
diff --cc services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java
index 000000000,000000000..69498d362
new file mode 100644
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/datalab/backendapi/resources/dto/ExploratoryImageCreateFormAdminDTO.java
@@@ -1,0 -1,0 +1,22 @@@
++package com.epam.datalab.backendapi.resources.dto;
++
++import com.fasterxml.jackson.annotation.JsonProperty;
++import lombok.Data;
++import lombok.ToString;
++import org.hibernate.validator.constraints.NotBlank;
++
++@Data
++@ToString
++public class ExploratoryImageCreateFormAdminDTO {
++    @NotBlank
++    private String user;
++    @NotBlank
++    private final String name;
++    @NotBlank
++    @JsonProperty("exploratory_name")
++    private String notebookName;
++    @NotBlank
++    @JsonProperty("project_name")
++    private String projectName;
++    private final String description;
++}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org