12 Commits
p2 ... paging

Author SHA1 Message Date
Manuel Romero
26fa09541a less logs and oid as index 2020-05-07 09:13:55 +02:00
Manuel Romero
cfbe52efc1 paging 2020-05-06 17:37:28 +02:00
Manuel Romero
a267fedaef Adding notifications to Admin 2020-05-06 08:59:57 +02:00
Manuel Romero
d6cb0fc78f No auto refresh provisions 2020-05-05 16:34:31 +02:00
Manuel Romero
1fbbbde1a1 No needed log 2020-05-05 14:59:16 +02:00
Manuel Romero
08721bb810 populate user for ApiKey model 2020-05-05 14:25:21 +02:00
Manuel Romero
68d2bef6ba populate user for ApiKey model 2020-05-05 14:23:59 +02:00
Manuel Romero
5199cabd26 fix 2020-05-05 14:19:51 +02:00
Manuel Romero
59546838ac fix 2020-05-05 14:15:21 +02:00
Manuel Romero
4740163572 fix 2020-05-05 13:59:13 +02:00
Manuel Romero
22af7f903e reorg shell scripts and cron 2020-05-05 13:55:38 +02:00
Manuel Romero
e921182575 Getting scenarios from git 2020-05-05 13:26:49 +02:00
30 changed files with 322 additions and 100 deletions

View File

@@ -9,5 +9,5 @@
<link rel="stylesheet" href="styles.529f751cbb5308365172.css"></head>
<body>
<app-root></app-root>
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.f752a17531a45fe93c1f.js" nomodule defer></script><script src="polyfills.06ba8d1a3d9dd3a8e8b9.js" defer></script><script src="scripts.6866cf66954a0b739d41.js" defer></script><script src="main.b7d3a2d901b927013a9e.js" defer></script></body>
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.f752a17531a45fe93c1f.js" nomodule defer></script><script src="polyfills.06ba8d1a3d9dd3a8e8b9.js" defer></script><script src="scripts.6866cf66954a0b739d41.js" defer></script><script src="main.e4269137677fbcdfe029.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -83,6 +83,7 @@ services:
- REDIS_URL=redis://redis
- MONGO_URI=mongodb://root:example@mongo/qmicloud?authSource=admin
- PROJECT_PATH=${PWD}
- GIT_SCENARIOS=git::git@gitlab.com:qmi/qmi-cloud-scenarios.git
- SSHPATH=/Users/aor/.ssh
command: "sh -c 'npm run worker:dev'"
volumes:

View File

@@ -1,5 +0,0 @@
#!/bin/bash
branch=master
rm -fr ./az-tf-templates
git clone -b $branch git@gitlab.com:qmi/qmi-cloud-scenarios.git ./az-tf-templates

View File

@@ -1,6 +1,6 @@
{
"name": "qmi-cloud",
"version": "1.0.10",
"version": "1.0.12",
"scripts": {
"start": "node -r esm server/server.js",
"dev": "nodemon -r esm server/server.js",

View File

@@ -6,8 +6,8 @@ if ( myArgs.length < 3 ) {
process.exit(0);
}
var db = require('./mongo');
const sendEmail = require("./send-email");
var db = require('../mongo');
const sendEmail = require("../send-email");
const moment = require('moment');
const fetch = require('node-fetch');
@@ -36,7 +36,7 @@ async function postDestroy(provision) {
}
})
.then(res => res.json())
.then(json => console.log(json));
.then(json => console.log(json));
}

View File

@@ -11,10 +11,10 @@ const RUNNING_PERIOD = myArgs[1]; //Days
const RUNNING_LIMIT_HOURS_WARNING = 24*(RUNNING_PERIOD-1);
const RUNNING_LIMIT_HOURS_STOP = 24*RUNNING_PERIOD;
var db = require('./mongo');
const sendEmail = require("./send-email");
var db = require('../mongo');
const sendEmail = require("../send-email");
const moment = require('moment');
const azurecli = require('./azurecli');
const azurecli = require('../azurecli');
function timeRunning(p) {
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();

View File

@@ -14,7 +14,10 @@ const userSchema = new mongoose.Schema({
},
displayName: String,
upn: String,
oid: String,
oid: {
type: String,
index: true
},
role: {
type: String,
default: "user"

View File

@@ -5,7 +5,8 @@ mongoose.set('useFindAndModify', false);
const userSchema = new mongoose.Schema({
type: String,
desc: String
desc: String,
costHour: Number
});

View File

@@ -53,27 +53,42 @@ const getNewTimeRunning = function (provision) {
return Math.floor(minutesFromLastRunning + timeRunning);
};
const get = async (model, filter, extras, reply) => {
var sort = extras && extras.sort? extras.sort : {created: -1};
const get = async (model, filter, skip, limit, reply) => {
var sort = {created: -1};
try {
var exec = model.find(filter).sort(sort);
var totalDocs = await model.countDocuments(filter);
if ( extras && extras.skip ) {
exec = exec.skip(extras.skip);
}
if ( extras && extras.limit ) {
exec = exec.limit(extras.limit);
skip = skip? parseInt(skip) : 0;
exec = exec.skip(skip);
if ( limit ) {
limit = parseInt(limit);
exec = exec.limit(limit);
}
if ( model === Provision ) {
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate('destroy');
}
if ( model === ApiKey ) {
exec = exec.populate('user');
}
const entity = await exec;
return {
total: await model.countDocuments(filter),
var out = {
total: totalDocs,
count: entity.length,
results: entity
};
}
if ( limit && (skip + limit) < totalDocs) {
out.nextSkip = skip+limit;
out.nextLimit = limit;
}
return out;
} catch (err) {
throw boom.boomify(err)
}
@@ -85,6 +100,9 @@ const getById = async (model, id, reply) => {
if ( model === Provision ) {
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate('destroy');
}
if ( model === ApiKey ) {
exec = exec.populate('user');
}
const entity = await exec;
return entity;
} catch (err) {
@@ -98,6 +116,9 @@ const getOne = async (model, filter, reply) => {
if ( model === Provision ) {
exec = exec.populate('user').populate('destroy');
}
if ( model === ApiKey ) {
exec = exec.populate('user');
}
const entity = await exec;
return entity;
} catch (err) {
@@ -141,8 +162,8 @@ const del = async (model, id, reply) => {
function _m(model) {
return {
get: async (filter, extras, reply) => {
return get(model, filter, extras, reply);
get: async (filter, skip, limit, reply) => {
return get(model, filter, skip, limit, reply);
},
getById: async (id, reply) => {
return getById(model, id, reply);

View File

@@ -82,18 +82,18 @@ passport.use(new OIDCStrategy({
if ( !profile.oid ) {
return done(new Error("No oid found"), null);
}
console.log("accessToken", accessToken);
console.log("iss", iss);
console.log("sub", sub);
console.log("refreshToken", refreshToken);
console.log("jwtClaims", jwtClaims);
console.log("params", params);
console.log("profile", profile);
//console.log("accessToken", accessToken);
//console.log("iss", iss);
//console.log("sub", sub);
//console.log("refreshToken", refreshToken);
//console.log("jwtClaims", jwtClaims);
//console.log("params", params);
console.log("New Auth: profile", profile);
// asynchronous verification, for effect...
process.nextTick(function () {
_findByOid(profile.oid, async function(err, user) {
if (err) {
return done(err);
return done(err);
}
if (!user) {
// "Auto-registration"
@@ -198,13 +198,16 @@ module.exports.init = function(app){
};
async function isApiKeyAuthenticated(req) {
if (req.query && req.query.apiKey){
if (req.query && req.query.apiKey){
let key = req.query.apiKey;
var result = await db.apiKey.get({apiKey: key});
if ( result.length > 0 ) {
req.user = result[0].user;
var result = await db.apiKey.getOne({"apiKey": key});
if ( result ) {
req.user = result.user;
return true;
} else {
return false;
}
return result.length > 0;
}else {
return false;
}

View File

@@ -0,0 +1,28 @@
const express = require('express');
const router = express.Router();
const db = require('../mongo');
const passport = require('../passport');
/**
* @swagger
* /notifications:
* get:
* description: Get all notifications (Only admin)
* summary: Get all notifications (Only admin)
* produces:
* - application/json
* responses:
* 200:
* description: Notifications
*/
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
try {
const result = await db.notification.get();
return res.json(result);
} catch (error) {
next(error);
}
});
module.exports = router;

View File

@@ -22,28 +22,44 @@ const fs = require('fs-extra');
* application/json:
* schema:
* type: object
* - name: extras
* - name: skip
* in: query
* required: false
* type: object
* content:
* application/json:
* schema:
* type: object
* type: integer
* - name: limit
* in: query
* required: false
* type: integer
* responses:
* 200:
* description: JSON Array
*/
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
try {
let filter = req.query.filter? JSON.parse(req.query.filter) : {};
if ( filter.isDeleted === undefined ) {
filter.isDeleted = false;
}
let extras = req.query.extras? JSON.parse(req.query.extras) : {};
const result = await db.provision.get(filter, extras);
return res.json(result);
const result = await db.provision.get(filter, req.query.skip, req.query.limit);
var out = {
total: result.total,
count: result.count
};
if ( result.nextSkip && result.nextLimit ) {
out.nextUrl = new URL(req.protocol + '://' + req.get('Host') + req.baseUrl);
if ( req.query.filter ) {
out.nextUrl.searchParams.append("filter", req.query.filter);
}
out.nextUrl.searchParams.append("skip", result.nextSkip);
out.nextUrl.searchParams.append("limit", result.nextLimit);
}
out.results = result.results;
return res.json(out);
} catch (error) {
next(error);
}

View File

@@ -9,6 +9,7 @@ const routesApiScenarios = require('./routes/api-scenarios');
const routesApiUsers = require('./routes/api-users');
const routesApiProvisions = require('./routes/api-provisions');
const routesApiDestroyProvisions = require('./routes/api-destroyprovisions');
const routesApiNotifications = require('./routes/api-notifications');
const swaggerUi = require('swagger-ui-express');
const swaggerJsdoc = require('swagger-jsdoc');
const cookieParser = require('cookie-parser');
@@ -75,6 +76,7 @@ app.use("/api/v1/scenarios", routesApiScenarios);
app.use("/api/v1/users", routesApiUsers);
app.use("/api/v1/provisions", routesApiProvisions);
app.use("/api/v1/destroyprovisions", routesApiDestroyProvisions);
app.use("/api/v1/notifications", routesApiNotifications);
app.get('/*',(req, res, next) =>{
if (req.originalUrl.indexOf("/api-docs") !== -1 || req.originalUrl.indexOf("/arena") !== -1 ) {
@@ -147,6 +149,7 @@ const options = {
'server/routes/api-users.js',
'server/routes/api-provisions.js',
'server/routes/api-destroyprovisions.js',
'server/routes/api-notifications.js',
]
};

View File

@@ -2,10 +2,9 @@ const Docker = require('dockerode');
const docker = new Docker({
'socketPath': '/home/docker.sock'
});
const path = require('path');
const fs = require('fs');
const PROJECT_PATH = process.env.PROJECT_PATH;
const DOCKERIMAGE = "qlikgear/terraform:1.0.0";
const GIT_SCENARIOS = process.env.GIT_SCENARIOS;
const DOCKERIMAGE = "qlikgear/terraform:1.0.1";
const SSHPATH = process.env.SSHPATH;
function hook_stdout(callback) {
@@ -60,11 +59,10 @@ function _buildExec( exec, provision ) {
const init = function( provMongo ) {
const templatePath = path.join(PROJECT_PATH, 'az-tf-templates', provMongo.scenario);
const name = `qmi-tf-init-${provMongo._id}`;
console.log(`Init: will spin up container: ${name}`);
var processStream = fs.createWriteStream(provMongo.logFile, {flags:'a'});
let exec = ['terraform', 'init', '-no-color', '-from-module=/template'];
let exec = ['terraform', 'init', '-no-color', `-from-module=${GIT_SCENARIOS}//${provMongo.scenario}`];
console.log('Init: exec: '+exec.join(" "));
return docker.run(DOCKERIMAGE, exec, processStream, {
@@ -73,8 +71,7 @@ const init = function( provMongo ) {
"WorkingDir": "/app",
"HostConfig": {
"Binds": [
`${provMongo.path}:/app`,
`${templatePath}:/template`
`${provMongo.path}:/app`
]
}
}).then(function(data) {

5
shell-utils/checkdestroy.sh Executable file
View File

@@ -0,0 +1,5 @@
BASEDIR=$(dirname "$0")
d=`date`
echo "------ $d"
echo "------ TYPE: $1"
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin API_KEY="c229219ccdd72d11e8ea253fd3876d247e5f489c9c84922cabdfb0cc194d8ff398a8d8d6528d8241efc99add2207e0ec75122a1b2c5598cc340cbe6b7c3c0dbf" node $BASEDIR/../server/cronjobs/destroy5.js $1 $2 $3

View File

@@ -1,4 +1,5 @@
BASEDIR=$(dirname "$0")
d=`date`
echo "------ $d"
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin node $BASEDIR/stop5.js $1 $2 $3
echo "------ TYPE: $1"
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin node $BASEDIR/../server/cronjobs/stop5.js $1 $2 $3

6
shell-utils/mongodump.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
id=`docker ps | grep 'mongo:4.2' | awk '{ print $1 }'`
d=$(date +%Y-%m-%d-T%H:%M:%S%z)
docker exec $id sh -c 'exec mongodump --uri="mongodb://root:example@mongo/qmicloud?authSource=admin" --archive' > $1/qmicloud-$d.archive

View File

@@ -1,12 +1,6 @@
<ul style="margin-top: 80px;" class="nav nav-pills nav-fill">
<li class="nav-item">
<a class="nav-link" (click)="tabSelect($event, 'Provisions')" [ngClass]="{'active': tab === 'Provisions'}">Provisions</a>
</li>
<li class="nav-item">
<a class="nav-link" (click)="tabSelect($event, 'Users')" [ngClass]="{'active': tab === 'Users'}">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" (click)="tabSelect($event, 'Scenarios')" [ngClass]="{'active': tab === 'Scenarios'}">Scenarios</a>
<li *ngFor="let item of sections; let i = index" class="nav-item">
<a class="nav-link" (click)="tabSelect($event, item)" [ngClass]="{'active': tab === item}">{{item}}</a>
</li>
</ul>
@@ -24,4 +18,9 @@
<!--<h1>Scenarios</h1>-->
<table-scenarios></table-scenarios>
</div>
<div *ngIf="tab === 'Notifications'">
<!--<h1>Scenarios</h1>-->
<table-notifications></table-notifications>
</div>
<qmi-alert></qmi-alert>

View File

@@ -7,22 +7,18 @@ import { Component, OnInit } from '@angular/core';
})
export class AdminComponent implements OnInit {
sections = ['Provisions', 'Users', 'Scenarios', 'Notifications'];
tab : string = 'Provisions';
constructor() { }
ngOnInit() {
}
tabSelect($event, tab) {
$event.preventDefault();
$event.stopPropagation();
this.tab = tab;
}
}

View File

@@ -24,6 +24,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { TableProvisionsAdminComponent } from './tables/table-provisions.component';
import { TableScenariosComponent } from './tables/table-scenarios.component';
import { TableUsersComponent } from './tables/table-users.component';
import { TableNotificationsComponent } from './tables/table-notifications.component';
import { AlertComponent } from './alert/alert.component';
import { AlertService } from './services/alert.service';
import { ModalInfoComponent } from './alert/modalinfo.component';
@@ -60,7 +61,8 @@ export function markedOptions(): MarkedOptions {
FilterPipe,
FaqComponent,
NewProvisionConfirmComponent,
TableScenariosComponent
TableScenariosComponent,
TableNotificationsComponent
],
imports: [
BrowserModule,

View File

@@ -21,4 +21,8 @@ export class UsersService {
updateUser(userId, patchData): Observable<any> {
return this.httpClient.put(`${environment.apiVersionPath}/users/${userId}`, patchData);
}
getNotifications(): Observable<any> {
return this.httpClient.get(`${environment.apiVersionPath}/notifications`);
}
}

View File

@@ -0,0 +1,40 @@
<div class="md-form">
<input type="text" class="form-control w-25" [(ngModel)]="searchText" (keyup)="searchItems()" id="search-input2"
mdbInput>
<label for="search-input2">Search</label>
</div>
<div style="padding: 5px 0px;">
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
<span>Refreshing...</span>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
</button>
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
</button>
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
</div>
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
<thead class="sticky-top">
<tr>
<th>Date</th>
<th>Type</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of elements; let i = index">
<th *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
scope="row">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</th>
<th *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
scope="row">{{item.type}}</th>
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.message}}</td>
</tr>
</tbody>
<tfoot class="grey lighten-5 w-100">
<tr>
<td colspan="3">
<mdb-table-pagination [tableEl]="tableEl" [searchDataSource]="elements"></mdb-table-pagination>
</td>
</tr>
</tfoot>
</table>

View File

@@ -0,0 +1,82 @@
import { MdbTablePaginationComponent, MdbTableDirective } from 'angular-bootstrap-md';
import { Component, OnInit, ViewChild, HostListener, AfterViewInit, ChangeDetectorRef } from '@angular/core';
import { UsersService } from '../services/users.service';
@Component({
selector: 'table-notifications',
templateUrl: './table-notifications.component.html',
styleUrls: ['./table-notifications.component.scss']
})
export class TableNotificationsComponent implements OnInit, AfterViewInit {
@ViewChild(MdbTablePaginationComponent, { static: true }) mdbTablePagination: MdbTablePaginationComponent;
@ViewChild(MdbTableDirective, { static: true }) mdbTable: MdbTableDirective;
previous: any = [];
searchText: string = '';
maxVisibleItems: number = 25;
loading: boolean = false;
elements = [];
@HostListener('input') oninput() {
this.mdbTablePagination.searchText = this.searchText;
}
constructor(private cdRef: ChangeDetectorRef, private _usersService: UsersService) {
}
private _initElements(): void {
this.mdbTable.setDataSource(this.elements);
this.elements = this.mdbTable.getDataSource();
this.previous = this.mdbTable.getDataSource();
}
ngOnInit() {
this.refreshData();
}
refreshData() {
this.loading = true;
this.searchText = "";
var sub = this._usersService.getNotifications().subscribe( res => {
sub.unsubscribe();
this.elements = res.results;
this.loading = false;
this._initElements();
});
}
ngAfterViewInit() {
this.mdbTablePagination.setMaxVisibleItemsNumberTo(this.maxVisibleItems);
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
this.cdRef.detectChanges();
}
searchItems() {
const prev = this.mdbTable.getDataSource();
if (!this.searchText) {
this.mdbTable.setDataSource(this.previous);
this.elements = this.mdbTable.getDataSource();
}
if (this.searchText) {
this.elements = this.mdbTable.searchLocalDataBy(this.searchText);
this.mdbTable.setDataSource(prev);
}
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
this.mdbTable.searchDataObservable(this.searchText).subscribe(() => {
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
});
}
}

View File

@@ -7,7 +7,16 @@
<label for="search-input">Search</label>
</div>
<p *ngIf="elements && elements.length">Total: {{elements.length}}</p>
<div style="padding: 5px 0px;">
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
<span>Refreshing...</span>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
</button>
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
</button>
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
</div>
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
<thead class="sticky-top">

View File

@@ -15,7 +15,6 @@ import { ScenariosService } from '../services/scenarios.service';
export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterViewInit {
scenarios;
provisions;
subscription: Subscription;
filter = {
showDestroyed : false
@@ -23,6 +22,7 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
filterParams : any = {
isDestroyed: false
};
loading: boolean = false;
pagingIsDisabled: Boolean = false;
@@ -63,10 +63,10 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
p._scenario = this.scenarios.filter(s => s.name === p.scenario);
this._provisionsService.timeRunning(p);
});
if ( !this.provisions ) {
this.provisions = provisions;
if ( this.elements.length === 0 ) {
this.elements = provisions;
} else {
this.provisions.forEach( function(p, index, object) {
this.elements.forEach( function(p, index, object) {
let found = provisions.filter(a=>a._id.toString() === p._id.toString());
if ( found.length ) {
p.status = found[0].status;
@@ -81,14 +81,13 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
}.bind(this));
provisions.forEach(function(p) {
let found = this.provisions.filter(a=>a._id.toString() === p._id.toString());
let found = this.elements.filter(a=>a._id.toString() === p._id.toString());
if (found.length === 0){
this.provisions.unshift(p);
this.elements.unshift(p);
}
}.bind(this));
}
this.elements = this.provisions;
this._initElements();
}
@@ -99,9 +98,12 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
scenariosSub.unsubscribe();
this.scenarios = res.results;
this.subscription = timer(0, 8000).pipe( switchMap(() => this._provisionsService.getProvisionsAdmin(this.filterParams) ) ).subscribe(provisions => {
/*this.subscription = timer(0, 8000).pipe( switchMap(() => this._provisionsService.getProvisionsAdmin(this.filterParams) ) ).subscribe(provisions => {
this._process(provisions.results);
});
});*/
this.refreshData();
});
//this._initElements();
@@ -289,11 +291,14 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
});
}
private _refresh(): void {
this.provisions = null;
refreshData() {
this.elements = [];
this.loading = true;
this.searchText = "";
var instantSubs = this._provisionsService.getProvisionsAdmin(this.filterParams).subscribe( provisions=>{
instantSubs.unsubscribe();
this._process(provisions.results);
this.loading = false;
this._process(provisions.results);
});
}
@@ -302,7 +307,7 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
if ( !this.filter.showDestroyed ) {
this.filterParams.isDestroyed = false;
}
this._refresh();
this.refreshData();
}
}

View File

@@ -3,6 +3,16 @@
mdbInput>
<label for="search-input2">Search</label>
</div>
<div style="padding: 5px 0px;">
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
<span>Refreshing...</span>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
</button>
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
</button>
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
</div>
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
<thead class="sticky-top">
<tr>

View File

@@ -19,7 +19,7 @@ export class TableUsersComponent implements OnInit, AfterViewInit {
maxVisibleItems: number = 25;
currentUser;
loading: boolean = false;
elements = [];
@HostListener('input') oninput() {
@@ -39,12 +39,18 @@ export class TableUsersComponent implements OnInit, AfterViewInit {
}
ngOnInit() {
this.refreshData();
}
refreshData() {
this.loading = true;
this.searchText = "";
var usersSub = this._usersService.getUsers().subscribe( res => {
usersSub.unsubscribe();
this.elements = res.results;
this.loading = false;
this._initElements();
});
}
ngAfterViewInit() {

View File

@@ -1,11 +0,0 @@
#!/bin/bash
BASEDIR=$(dirname "$0")
branch=master
cd $BASEDIR/az-tf-templates
git checkout master
git checkout .
git pull origin $branch
cd $BASEDIR