scheduler
This commit is contained in:
@@ -185,6 +185,13 @@
|
|||||||
*ngIf="user.url && user.sure_connect_id">
|
*ngIf="user.url && user.sure_connect_id">
|
||||||
<clr-icon shape="map"></clr-icon>
|
<clr-icon shape="map"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Scheduler button -->
|
||||||
|
<button class="btn btn-icon"
|
||||||
|
(click)="openSchedulerModal(user)"
|
||||||
|
title="Scheduler">
|
||||||
|
<clr-icon shape="clock"></clr-icon>
|
||||||
|
</button>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
|
||||||
<!-- who colmn -->
|
<!-- who colmn -->
|
||||||
@@ -1105,4 +1112,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<!-- htmlpopup -->
|
<!-- Scheduler Modal -->
|
||||||
|
<clr-modal [(clrModalOpen)]="showSchedulerModal" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
|
<h3 class="modal-title">Job Scheduler</h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="scheduler-container">
|
||||||
|
<div class="data-lake-info">
|
||||||
|
<h4>Data Lake: {{ selectedSchedulerItem?.name }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Job Information -->
|
||||||
|
<div class="job-info-section" *ngIf="schedulerJob">
|
||||||
|
<div class="job-header">
|
||||||
|
<h5>Job Information</h5>
|
||||||
|
<span class="badge" [ngClass]="getStatusBadgeClass(schedulerJob.status)">
|
||||||
|
{{ schedulerJob.status }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="job-details">
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Job Name:</label>
|
||||||
|
<span>{{ schedulerJob.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Job Type:</label>
|
||||||
|
<span>{{ schedulerJob.jobType }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Description:</label>
|
||||||
|
<span>{{ schedulerJob.description }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Job Actions -->
|
||||||
|
<div class="job-actions">
|
||||||
|
<button class="btn btn-warning" (click)="pauseJob()" *ngIf="schedulerJob.status === 'RUNNING'">
|
||||||
|
<clr-icon shape="pause"></clr-icon> Pause Job
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-success" (click)="resumeJob()" *ngIf="schedulerJob.status === 'PAUSED'">
|
||||||
|
<clr-icon shape="play"></clr-icon> Resume Job
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger" (click)="stopJob()" *ngIf="schedulerJob.status !== 'STOPPED'">
|
||||||
|
<clr-icon shape="stop"></clr-icon> Stop Job
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- No Job Found -->
|
||||||
|
<div class="no-job-section" *ngIf="schedulerJob === null">
|
||||||
|
<div class="no-job-message">
|
||||||
|
<p>No scheduled job found for this Data Lake.</p>
|
||||||
|
<p>Would you like to create a new job?</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="create-job-actions">
|
||||||
|
<button class="btn btn-primary" (click)="createJob()">
|
||||||
|
<clr-icon shape="plus"></clr-icon> Create Job
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="closeSchedulerModal()">Close</button>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
@@ -683,3 +683,248 @@
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scheduler Modal Styles */
|
||||||
|
.scheduler-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-lake-info h4 {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-info-section {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-success {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-warning {
|
||||||
|
background-color: #ff9800;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-danger {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-light {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-details {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row label {
|
||||||
|
font-weight: 500;
|
||||||
|
width: 120px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-actions .btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-section {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-message p {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-message p:first-child {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-job-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-job-actions .btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
/* Scheduler Modal Styles */
|
||||||
|
.scheduler-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-lake-info h4 {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-info-section {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-success {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-warning {
|
||||||
|
background-color: #ff9800;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-danger {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-light {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-details {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row label {
|
||||||
|
font-weight: 500;
|
||||||
|
width: 120px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-actions .btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-section {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-message p {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-job-message p:first-child {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-job-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-job-actions .btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { AlertService } from 'src/app/services/alert.service';
|
import { AlertService } from 'src/app/services/alert.service';
|
||||||
import { Data_lakeservice } from './Data_lake.service';
|
import { Data_lakeservice } from './Data_lake.service';
|
||||||
|
import { SchedulerService } from 'src/app/services/scheduler.service';
|
||||||
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators, ValidationErrors } from '@angular/forms';
|
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators, ValidationErrors } from '@angular/forms';
|
||||||
import { ExtensionService } from 'src/app/services/fnd/extension.service';
|
import { ExtensionService } from 'src/app/services/fnd/extension.service';
|
||||||
import { DashboardContentModel2 } from 'src/app/models/builder/dashboard';
|
import { DashboardContentModel2 } from 'src/app/models/builder/dashboard';
|
||||||
@@ -10,6 +11,7 @@ import { UserInfoService } from 'src/app/services/user-info.service';
|
|||||||
import { SureconnectService } from '../sureconnect/sureconnect.service';
|
import { SureconnectService } from '../sureconnect/sureconnect.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
||||||
|
import { DataLakeSchedulerService } from './dataLakescheduler.service';
|
||||||
declare var JsBarcode: any;
|
declare var JsBarcode: any;
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-Data_lake',
|
selector: 'app-Data_lake',
|
||||||
@@ -121,10 +123,16 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
fieldMappings: { [key: string]: string } = {};
|
fieldMappings: { [key: string]: string } = {};
|
||||||
selectedMappingItem: any = null;
|
selectedMappingItem: any = null;
|
||||||
|
|
||||||
|
// New properties for scheduler functionality
|
||||||
|
showSchedulerModal = false;
|
||||||
|
schedulerJob: any = null;
|
||||||
|
selectedSchedulerItem: any = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private extensionService: ExtensionService,
|
private extensionService: ExtensionService,
|
||||||
private userInfoService: UserInfoService,
|
private userInfoService: UserInfoService,
|
||||||
private mainService: Data_lakeservice,
|
private mainService: Data_lakeservice,
|
||||||
|
private schedulerService: DataLakeSchedulerService,
|
||||||
private alertService: AlertService,
|
private alertService: AlertService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
private _fb: FormBuilder,
|
private _fb: FormBuilder,
|
||||||
@@ -1560,4 +1568,132 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.fieldMappingData[index].mapped = value;
|
this.fieldMappingData[index].mapped = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method to open scheduler modal
|
||||||
|
openSchedulerModal(item: any) {
|
||||||
|
this.selectedSchedulerItem = item;
|
||||||
|
this.schedulerJob = null;
|
||||||
|
this.showSchedulerModal = true;
|
||||||
|
|
||||||
|
// Fetch job by lake ID
|
||||||
|
this.schedulerService.getJobByLakeId(item.id).subscribe(
|
||||||
|
(job: any) => {
|
||||||
|
this.schedulerJob = job;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
// If job not found, it's expected - we'll show create option
|
||||||
|
if (error.status === 404) {
|
||||||
|
this.schedulerJob = null;
|
||||||
|
} else {
|
||||||
|
console.error('Error fetching scheduler job:', error);
|
||||||
|
this.toastr.error('Failed to fetch scheduler job information');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to create a new job
|
||||||
|
createJob() {
|
||||||
|
if (!this.selectedSchedulerItem) {
|
||||||
|
this.toastr.error('No data lake selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobData = {
|
||||||
|
name: this.selectedSchedulerItem.name,
|
||||||
|
status: 'RUNNING',
|
||||||
|
description: `Scheduled job for ${this.selectedSchedulerItem.name}`,
|
||||||
|
jobType: 'SYNC',
|
||||||
|
lakeid: this.selectedSchedulerItem.id
|
||||||
|
};
|
||||||
|
|
||||||
|
this.schedulerService.createJob(jobData).subscribe(
|
||||||
|
(job: any) => {
|
||||||
|
this.schedulerJob = job;
|
||||||
|
this.toastr.success('Job created successfully');
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('Error creating job:', error);
|
||||||
|
this.toastr.error('Failed to create job');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to pause a job
|
||||||
|
pauseJob() {
|
||||||
|
if (!this.schedulerJob || !this.schedulerJob.id) {
|
||||||
|
this.toastr.error('No job selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.schedulerService.pauseJob(this.schedulerJob.id).subscribe(
|
||||||
|
(response: any) => {
|
||||||
|
this.schedulerJob.status = 'PAUSED';
|
||||||
|
this.toastr.success('Job paused successfully');
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('Error pausing job:', error);
|
||||||
|
this.toastr.error('Failed to pause job');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to resume a job
|
||||||
|
resumeJob() {
|
||||||
|
if (!this.schedulerJob || !this.schedulerJob.id) {
|
||||||
|
this.toastr.error('No job selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.schedulerService.resumeJob(this.schedulerJob.id).subscribe(
|
||||||
|
(response: any) => {
|
||||||
|
this.schedulerJob.status = 'RUNNING';
|
||||||
|
this.toastr.success('Job resumed successfully');
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('Error resuming job:', error);
|
||||||
|
this.toastr.error('Failed to resume job');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to stop a job
|
||||||
|
stopJob() {
|
||||||
|
if (!this.schedulerJob || !this.schedulerJob.id) {
|
||||||
|
this.toastr.error('No job selected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.schedulerService.stopJob(this.schedulerJob.id).subscribe(
|
||||||
|
(response: any) => {
|
||||||
|
this.schedulerJob.status = 'STOPPED';
|
||||||
|
this.toastr.success('Job stopped successfully');
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('Error stopping job:', error);
|
||||||
|
this.toastr.error('Failed to stop job');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to close scheduler modal
|
||||||
|
closeSchedulerModal() {
|
||||||
|
this.showSchedulerModal = false;
|
||||||
|
this.schedulerJob = null;
|
||||||
|
this.selectedSchedulerItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to get status badge class
|
||||||
|
getStatusBadgeClass(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'RUNNING':
|
||||||
|
return 'badge-success';
|
||||||
|
case 'PAUSED':
|
||||||
|
return 'badge-warning';
|
||||||
|
case 'STOPPED':
|
||||||
|
return 'badge-danger';
|
||||||
|
default:
|
||||||
|
return 'badge-light';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { ApiRequestService } from "src/app/services/api/api-request.service";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DataLakeSchedulerService {
|
||||||
|
private baseURL = "scheduler";
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private apiRequest: ApiRequestService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
// Get job by lake ID
|
||||||
|
getJobByLakeId(lakeId: number): Observable<any> {
|
||||||
|
const _http = `${this.baseURL}/lake/${lakeId}`;
|
||||||
|
return this.apiRequest.get(_http);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new job
|
||||||
|
createJob(jobData: any): Observable<any> {
|
||||||
|
const _http = `${this.baseURL}/create`;
|
||||||
|
return this.apiRequest.post(_http, jobData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause a job
|
||||||
|
pauseJob(jobId: number): Observable<any> {
|
||||||
|
const _http = `${this.baseURL}/pause/${jobId}`;
|
||||||
|
return this.apiRequest.post(_http, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume a job
|
||||||
|
resumeJob(jobId: number): Observable<any> {
|
||||||
|
const _http = `${this.baseURL}/resume/${jobId}`;
|
||||||
|
return this.apiRequest.post(_http, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop a job
|
||||||
|
stopJob(jobId: number): Observable<any> {
|
||||||
|
const _http = `${this.baseURL}/stop/${jobId}`;
|
||||||
|
return this.apiRequest.delete(_http);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user