chore: adding comments part1
This commit is contained in:
parent
d7f8201246
commit
c054ea500d
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ client/src/environments/version.ts
|
||||
client/cypress/screenshots
|
||||
client/cypress/results
|
||||
client/cypress/videos
|
||||
client/documentation
|
||||
cypress.env.json
|
||||
sasjsbuild
|
||||
sasjsresults
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { QueryClause } from './models/TableData'
|
||||
|
||||
/**
|
||||
* Filtering cache info, to be reused when filtering modal is re-open
|
||||
*/
|
||||
interface FilterCache {
|
||||
cols: any[]
|
||||
vals: any[]
|
||||
@ -10,12 +13,18 @@ interface FilterCache {
|
||||
query: QueryClause[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtering cache info in the open viewboxes, to be reused when filtering modal is re-open
|
||||
*/
|
||||
interface ViewboxCache {
|
||||
[key: number]: {
|
||||
filter: FilterCache
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial values when no cached values stored
|
||||
*/
|
||||
export const initFilter: { filter: FilterCache } = {
|
||||
filter: {
|
||||
cols: <any[]>[],
|
||||
@ -28,6 +37,13 @@ export const initFilter: { filter: FilterCache } = {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached filtering values across whole app (editor, viewer, viewboxes)
|
||||
* Cached lineage libraries, tables
|
||||
* Cached metadata tree
|
||||
* Cached usernav tree
|
||||
* Cached viyaApi collections, search and selected endpoint
|
||||
*/
|
||||
export const globals: {
|
||||
rootParam: string
|
||||
editor: any
|
||||
|
@ -57,24 +57,15 @@ export class AppComponent {
|
||||
private elementRef: ElementRef
|
||||
) {
|
||||
this.parseDcAdapterSettings()
|
||||
|
||||
/**
|
||||
* Prints app info in the console such as:
|
||||
* - Adapter versions
|
||||
* - App version
|
||||
* - Build timestamp
|
||||
*
|
||||
*/
|
||||
;(window as any).appinfo = () => {
|
||||
const licenseKeyData = this.licenceService.getLicenseKeyData()
|
||||
|
||||
if (licenseKeyData) {
|
||||
const expiry_date = moment(
|
||||
licenseKeyData.valid_until,
|
||||
'YYYY-MM-DD'
|
||||
).startOf('day')
|
||||
const current_date = moment().startOf('day')
|
||||
const daysToExpiry = expiry_date.diff(current_date, 'days')
|
||||
|
||||
licenseKeyData.valid_until += ` (${daysToExpiry} ${
|
||||
daysToExpiry === 1 ? 'day' : 'days'
|
||||
} remaining)`
|
||||
|
||||
if (isNaN(daysToExpiry)) licenseKeyData.valid_until = 'Unlimited'
|
||||
}
|
||||
|
||||
console.table({
|
||||
'Adapter version': VERSION.adapterVersion || 'n/a',
|
||||
'App version': (VERSION.tag || '').replace('v', ''),
|
||||
@ -87,7 +78,12 @@ export class AppComponent {
|
||||
|
||||
this.subscribeToLicenseEvents()
|
||||
|
||||
/**
|
||||
* Fetches git tag ang git hash from `version.ts` file
|
||||
* It's placed in the user drop down.
|
||||
*/
|
||||
this.commitVer = (VERSION.tag || '').replace('v', '') + '.' + VERSION.hash
|
||||
|
||||
router.events.subscribe((val) => {
|
||||
this.routeUrl = this.router.url
|
||||
|
||||
@ -127,8 +123,10 @@ export class AppComponent {
|
||||
this.subscribeToAppActive()
|
||||
this.subscribeToDemoLimitModal()
|
||||
|
||||
/* In Viya streaming apps, content is served within an iframe. This code
|
||||
makes that iframe "full screen" so it looks like a regular window. */
|
||||
/**
|
||||
* In Viya streaming apps, content is served within an iframe. This code
|
||||
* makes that iframe "full screen" so it looks like a regular window.
|
||||
*/
|
||||
if (window.frameElement) {
|
||||
window.frameElement.setAttribute(
|
||||
'style',
|
||||
@ -143,6 +141,9 @@ export class AppComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses adapter settings that are found in the <sasjs> tag inside index.html
|
||||
*/
|
||||
private parseDcAdapterSettings() {
|
||||
const sasjsElement = document.querySelector('sasjs')
|
||||
|
||||
@ -180,9 +181,14 @@ export class AppComponent {
|
||||
this.appService.sasServiceInit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens licence page with the active licence problem
|
||||
* Problem details are encoded in the url
|
||||
*/
|
||||
public licenceProblemDetails(url: string) {
|
||||
this.router.navigateByUrl(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on string provided we return true, false or null
|
||||
* True -> Compute API
|
||||
@ -199,6 +205,12 @@ export class AppComponent {
|
||||
return value === 'true' || false
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for an `demo limit` event that will show the `Feature locked modal`
|
||||
* For exmaple when in editor upload feature is not enabled
|
||||
* When user tries to upload the excel, editor component will trgger this event
|
||||
* And stop the execution of file upload code.
|
||||
*/
|
||||
public subscribeToDemoLimitModal() {
|
||||
this.eventService.onDemoLimitModalShow.subscribe((featureName: string) => {
|
||||
this.demoLimitNotice = {
|
||||
@ -208,6 +220,10 @@ export class AppComponent {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for licence events so banner can be displayed.
|
||||
* App is free tier, licence will expire, is expired or is invalid
|
||||
*/
|
||||
public subscribeToLicenseEvents() {
|
||||
this.licenceService.isAppFreeTier.subscribe((isAppFreeTier: boolean) => {
|
||||
this.freeTierBanner = isAppFreeTier
|
||||
@ -227,6 +243,10 @@ export class AppComponent {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for an event that will activate od deactivate full application.
|
||||
* Based on licence key prcoessing result
|
||||
*/
|
||||
public subscribeToAppActive() {
|
||||
this.licenceService.isAppActivated.subscribe((value: any) => {
|
||||
this.appActive = value
|
||||
@ -248,31 +268,51 @@ export class AppComponent {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* When startupservice request is finished with valid response, this event will
|
||||
* make sure loading screen is gone.
|
||||
*/
|
||||
public subscribeToStartupData() {
|
||||
this.eventService.onStartupDataLoaded.subscribe(() => {
|
||||
this.startupDataLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens requests modal when requested from event service
|
||||
*/
|
||||
public subscribeToRequestsModal() {
|
||||
this.eventService.onRequestsModalOpen.subscribe((value: boolean) => {
|
||||
this.requestsModal = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes abort modal with matching ID (there could be multiple abort modals open)
|
||||
*/
|
||||
public closeAbortModal(abortId: number) {
|
||||
let abortIndex = this.sasjsAborts.findIndex((abort) => abort.id === abortId)
|
||||
this.sasjsAborts.splice(abortIndex, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles sidebar when requested from event service
|
||||
*/
|
||||
public toggleSidebar() {
|
||||
this.eventService.toggleSidebar()
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not current route includes the route from param
|
||||
* @param route route to check
|
||||
*/
|
||||
public isMainRoute(route: string): boolean {
|
||||
return this.router.url.includes(route)
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a page for updating the licence.
|
||||
*/
|
||||
public openLicencingPage() {
|
||||
this.router.navigateByUrl('/licensing/update')
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ import { DeployModule } from './deploy/deploy.module'
|
||||
import { LicensingModule } from './licensing/licensing.module'
|
||||
import { SystemModule } from './system/system.module'
|
||||
|
||||
/**
|
||||
* Defining routes
|
||||
*/
|
||||
export const ROUTES: Routes = [
|
||||
{ path: '', redirectTo: 'home', pathMatch: 'full' },
|
||||
{
|
||||
@ -25,6 +28,9 @@ export const ROUTES: Routes = [
|
||||
loadChildren: () => ViewerModule
|
||||
},
|
||||
{
|
||||
/**
|
||||
* Load review module (approve, history, submitted)
|
||||
*/
|
||||
path: 'review',
|
||||
component: ReviewRouteComponent,
|
||||
children: [
|
||||
@ -41,6 +47,9 @@ export const ROUTES: Routes = [
|
||||
},
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{
|
||||
/**
|
||||
* Load editor module with subroutes
|
||||
*/
|
||||
path: 'editor',
|
||||
loadChildren: () => EditorModule
|
||||
},
|
||||
@ -59,6 +68,9 @@ export const ROUTES: Routes = [
|
||||
{ path: '**', component: NotFoundComponent }
|
||||
]
|
||||
|
||||
/**
|
||||
* Exporting routes
|
||||
*/
|
||||
export const ROUTING: ModuleWithProviders<RouterModule> = RouterModule.forRoot(
|
||||
ROUTES,
|
||||
{ useHash: true }
|
||||
|
@ -78,6 +78,9 @@ export class DeployComponent implements OnInit {
|
||||
this.setDeployDefaults()
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting default values used for deploy request
|
||||
*/
|
||||
public setDeployDefaults() {
|
||||
this.dcPath = this.dcAdapterSettings?.dcPath || ''
|
||||
this.selectedAdminGroup = this.dcAdapterSettings?.adminGroup || ''
|
||||
@ -86,6 +89,9 @@ export class DeployComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepting terms of service shows next screen
|
||||
*/
|
||||
public termsAgreeChange() {
|
||||
if (!this.autodeploy) {
|
||||
this.getAdminGroups()
|
||||
@ -94,6 +100,9 @@ export class DeployComponent implements OnInit {
|
||||
this.step++
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches admin groups from VIYA to be selected for a backend deploy
|
||||
*/
|
||||
public getAdminGroups() {
|
||||
fetch(
|
||||
this.sasJsConfig.serverUrl + '/identities/groups?sortBy=name&limit=5000',
|
||||
|
@ -55,6 +55,13 @@ export class AutomaticComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
/**
|
||||
* Executes sas.json file to deploy the backend
|
||||
* Method will first try to run the `auto deploy`
|
||||
* If that fails the rest of the code is ignored.
|
||||
* If request is successfull, method will continue to try
|
||||
* to create database if checkbox is toggled on
|
||||
*/
|
||||
public async executeJson() {
|
||||
this.autodeploying = true
|
||||
this.isSubmittingJson = true
|
||||
@ -99,6 +106,9 @@ export class AutomaticComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the `makedata` request sending the ADMIN and DCPATH values
|
||||
*/
|
||||
public createDatabase() {
|
||||
let data = {
|
||||
fromjs: [
|
||||
|
@ -52,6 +52,9 @@ export class ManualComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
/**
|
||||
* FIXME: Remove
|
||||
*/
|
||||
public async executableContext() {
|
||||
// getExecutableContexts now need AuthConfig parameter which we don't have on web
|
||||
// this.contextsLoading = true
|
||||
@ -65,10 +68,16 @@ export class ManualComponent implements OnInit {
|
||||
// this.contextsLoading = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes sas.json file attached to the input
|
||||
*/
|
||||
public clearUploadInput(event: Event) {
|
||||
this.deployService.clearUploadInput(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads attached SAS file to be sent to sas for execution (backend deploy)
|
||||
*/
|
||||
public onSasFileChange(event: any) {
|
||||
this.preloadedFile = false
|
||||
|
||||
@ -93,12 +102,18 @@ export class ManualComponent implements OnInit {
|
||||
fileReader.readAsText(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads attached JSON file to be sent to sas for execution (backend deploy)
|
||||
*/
|
||||
public async onJsonFileChange(event: any) {
|
||||
let file = event.target.files[0]
|
||||
|
||||
this.jsonFile = await this.deployService.readFile(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appending precode lines to the attached sas or json file for backend deploy
|
||||
*/
|
||||
public addPrecodeLines() {
|
||||
let headerLines = [
|
||||
`%let context=${this.selectedContext};`,
|
||||
@ -110,6 +125,9 @@ export class ManualComponent implements OnInit {
|
||||
this.linesOfCode.unshift(...headerLines)
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloadng file with precode included
|
||||
*/
|
||||
public downloadSasPrecodeFile() {
|
||||
let linesAsText = this.linesOfCode.join('\n')
|
||||
let filename = this.fileName.split('.')[0]
|
||||
@ -117,6 +135,9 @@ export class ManualComponent implements OnInit {
|
||||
this.downloadFile(linesAsText, filename, 'sas')
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for downloading log and repsonse as a file
|
||||
*/
|
||||
public downloadFile(
|
||||
content: any,
|
||||
filename: string,
|
||||
@ -125,10 +146,17 @@ export class ManualComponent implements OnInit {
|
||||
this.deployService.downloadFile(content, filename, extension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving dcpath to localstorage
|
||||
* FIXME: maybe it'snot necessary
|
||||
*/
|
||||
public saveDcPath() {
|
||||
localStorage.setItem('deploy_dc_loc', this.dcPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send sas.json to be executed (deploying backend)
|
||||
*/
|
||||
public async executeJson() {
|
||||
this.isSubmittingJson = true
|
||||
|
||||
@ -162,6 +190,9 @@ export class ManualComponent implements OnInit {
|
||||
this.isSubmittingJson = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Send sas file to be executed (deploying backend)
|
||||
*/
|
||||
public async executeSAS() {
|
||||
this.executingScript = true
|
||||
this.jobLog = ''
|
||||
@ -194,6 +225,10 @@ export class ManualComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Running makedata service
|
||||
* @param newTab open and run in new tab
|
||||
*/
|
||||
public createDatabase(newTab: boolean = true) {
|
||||
if (newTab) {
|
||||
let url =
|
||||
|
@ -5,7 +5,6 @@ import { ServerType } from '@sasjs/utils/types/serverType'
|
||||
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
|
||||
import { SASGroup } from 'src/app/models/sas/public-getgroups.model'
|
||||
import { SASjsApiServerInfo } from 'src/app/models/sasjs-api/SASjsApiServerInfo.model'
|
||||
import { HelperService } from 'src/app/services/helper.service'
|
||||
import { SasService } from 'src/app/services/sas.service'
|
||||
import { SasjsService } from 'src/app/services/sasjs.service'
|
||||
|
||||
@ -51,6 +50,9 @@ export class SasjsConfiguratorComponent implements OnInit {
|
||||
this.getServerInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fethes the sasjs server instance info
|
||||
*/
|
||||
getServerInfo() {
|
||||
this.sasjsService
|
||||
.getServerInfo()
|
||||
@ -59,6 +61,9 @@ export class SasjsConfiguratorComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches user groups from the `usernav/usergroupsbymember` service
|
||||
*/
|
||||
getUserGroups() {
|
||||
this.loading = true
|
||||
|
||||
@ -99,6 +104,9 @@ export class SasjsConfiguratorComponent implements OnInit {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating database
|
||||
*/
|
||||
makeData() {
|
||||
// const _debug = "&_debug=131"; //debug on
|
||||
const _debug = ' ' //debug off
|
||||
|
@ -14,7 +14,9 @@ export class DragNdropDirective {
|
||||
@Output() fileDropped = new EventEmitter<any>()
|
||||
@Output() fileDraggedOver = new EventEmitter<any>()
|
||||
|
||||
// Dragover listener
|
||||
/**
|
||||
* Dragover listener
|
||||
*/
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver(event: any) {
|
||||
event.preventDefault()
|
||||
@ -26,7 +28,9 @@ export class DragNdropDirective {
|
||||
}
|
||||
}
|
||||
|
||||
// Dragleave listener
|
||||
/**
|
||||
* Dragleave listener
|
||||
*/
|
||||
@HostListener('dragleave', ['$event'])
|
||||
public onDragLeave(event: any) {
|
||||
event.preventDefault()
|
||||
@ -34,7 +38,9 @@ export class DragNdropDirective {
|
||||
this.fileOver = false
|
||||
}
|
||||
|
||||
// Drop listener
|
||||
/**
|
||||
* Drop listener
|
||||
*/
|
||||
@HostListener('drop', ['$event'])
|
||||
public ondrop(event: any) {
|
||||
event.preventDefault()
|
||||
@ -48,6 +54,9 @@ export class DragNdropDirective {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wether dragging element contain files
|
||||
*/
|
||||
private containsFiles(event: any) {
|
||||
if (event && event.dataTransfer && event.dataTransfer.types) {
|
||||
for (let i = 0; i < event.dataTransfer.types.length; i++) {
|
||||
|
@ -22,6 +22,9 @@ export class FileDropDirective {
|
||||
this.element = element
|
||||
}
|
||||
|
||||
/**
|
||||
* Dragging element drop event
|
||||
*/
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop(event: DragEvent): void {
|
||||
this._preventAndStop(event)
|
||||
@ -39,6 +42,9 @@ export class FileDropDirective {
|
||||
this.fileDrop.emit(fileList)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dragging element drag over event
|
||||
*/
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver(event: DragEvent): void {
|
||||
this._preventAndStop(event)
|
||||
@ -59,6 +65,10 @@ export class FileDropDirective {
|
||||
this.fileOver.emit(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent propagation trough elements and stop default behavior
|
||||
* For particular event
|
||||
*/
|
||||
protected _preventAndStop(event: MouseEvent): void {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -21,6 +21,9 @@ export class FileSelectDirective {
|
||||
this.element = element
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if files exist in the input after input change
|
||||
*/
|
||||
isEmptyAfterSelection(): boolean {
|
||||
return !!this.element.nativeElement.attributes.multiple
|
||||
}
|
||||
|
@ -368,6 +368,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
this.setRestrictions()
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare feature restrictions based on licence key
|
||||
*/
|
||||
private parseRestrictions() {
|
||||
this.restrictions.restrictAddRecord =
|
||||
this.licenceState.value.addRecord === false
|
||||
@ -377,6 +380,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
this.licenceState.value.fileUpload === false
|
||||
}
|
||||
|
||||
/**
|
||||
* Applying prepared restrictions
|
||||
* @param overrideRestrictions can be used to apply and override specific restrictions
|
||||
*/
|
||||
private setRestrictions(overrideRestrictions?: EditorRestrictions) {
|
||||
if (overrideRestrictions) {
|
||||
this.restrictions = {
|
||||
@ -396,6 +403,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabling add row button based on wether rows limit is present
|
||||
*/
|
||||
private checkRowLimit() {
|
||||
if (this.columnLevelSecurityFlag) return
|
||||
|
||||
@ -410,12 +420,19 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetting filter variables
|
||||
*/
|
||||
public resetFilter() {
|
||||
if (this.queryFilterCompList.first) {
|
||||
this.queryFilterCompList.first.resetFilter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Openning file upload modal
|
||||
* If feature is locked, `feature locked` modal will be shown
|
||||
*/
|
||||
public onShowUploadModal() {
|
||||
if (this.restrictions.restrictFileUpload) {
|
||||
this.eventService.showDemoLimitModal('File Upload')
|
||||
@ -433,6 +450,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
if (!this.uploadPreview) this.showUploadModal = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by FileDropDirective
|
||||
* @param e true if file is dragged over the drop zone
|
||||
*/
|
||||
public fileOverBase(e: boolean): void {
|
||||
this.hasBaseDropZoneOver = e
|
||||
}
|
||||
@ -614,6 +635,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
return returnObj
|
||||
}
|
||||
|
||||
/**
|
||||
* When excel is password protected we will display the password promppt for user to type password in.
|
||||
* @returns Password user input or undefined if discarded by user
|
||||
*/
|
||||
public promptExcelPassword(): Promise<string | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.filePasswordModal = true
|
||||
@ -639,6 +664,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses attached file, to be uploaded
|
||||
* If attached file is CSV it will be send to backend straight away
|
||||
* If attached file is EXCEL it will be displayed in the table, in preview mode
|
||||
* @param event file drop event
|
||||
* @param dropped whether it's dropped or added by browse button
|
||||
*/
|
||||
public getFileDesc(event: any, dropped: boolean = false) {
|
||||
this.excelUploadState = 'Loading'
|
||||
this.excelFileParsing = true
|
||||
@ -1004,6 +1036,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits attached excel file that is in preview mode
|
||||
*/
|
||||
public submitExcel() {
|
||||
if (this.licenceState.value.submit_rows_limit !== Infinity) {
|
||||
this.submitLimitNotice = true
|
||||
@ -1013,6 +1048,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
this.getFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will run validations and upload all of the pending files that are in the uploader queue
|
||||
*/
|
||||
public getFile() {
|
||||
if (this.checkInvalid()) {
|
||||
this.eventService.showAbortModal(null, 'Invalid values are present.')
|
||||
@ -1085,6 +1123,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* After excel file is attached and parsed, this function will display it's content in the HOT table in read only mode
|
||||
*/
|
||||
public getPendingExcelPreview() {
|
||||
this.queryTextSaved = this.queryText
|
||||
this.queryText = ''
|
||||
@ -1176,6 +1217,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
// )
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the attached excel file
|
||||
* @param discardData wheter to discard data parsed from the file or to keep it in the table after dropping a attached excel file
|
||||
*/
|
||||
public discardPendingExcel(discardData?: boolean) {
|
||||
this.hotInstance.updateSettings({
|
||||
maxRows: this.licenceState.value.editor_rows_allowed
|
||||
@ -1199,6 +1244,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops attached excel file, keeps it's data in the DC table
|
||||
* User can now edit the table and submit. Witout the file present.
|
||||
*/
|
||||
public previewTableEditConfirm() {
|
||||
this.discardPendingExcel()
|
||||
this.convertToCorrectTypes(this.dataSource)
|
||||
|
5
client/src/app/models/ErrorBody.ts
Normal file
5
client/src/app/models/ErrorBody.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface ErrorBody {
|
||||
message: string
|
||||
details: any
|
||||
raw: any
|
||||
}
|
@ -137,6 +137,11 @@ export class QueryComponent
|
||||
public whereClause: string | undefined
|
||||
public logicOperators: Array<string> = ['AND', 'OR']
|
||||
|
||||
/**
|
||||
* Temporary values array used in pickers
|
||||
* because they need particular format to work
|
||||
* before sending values to backend, values are parsed
|
||||
*/
|
||||
public queryDateTime: QueryDateTime[] = []
|
||||
|
||||
public currentClauseIndex: number = -1
|
||||
@ -158,6 +163,11 @@ export class QueryComponent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets temporary values selected with DATETIME or TIME picker
|
||||
* Those values are used for picker to work with format it lieks
|
||||
* Later before sending values to backend, values are parsed
|
||||
*/
|
||||
getQueryDateTime(clauseIndex: number, queryIndex: number): QueryDateTime {
|
||||
let existingQueryDateTime = this.queryDateTime.find(
|
||||
(x) => x.clauseIndex === clauseIndex && x.queryIndex === queryIndex
|
||||
@ -178,10 +188,16 @@ export class QueryComponent
|
||||
return existingQueryDateTime
|
||||
}
|
||||
|
||||
/**
|
||||
* When toggling pickers feature we reset the temp picker values array
|
||||
*/
|
||||
usePickersChange() {
|
||||
this.queryDateTime = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all variables used for filtering
|
||||
*/
|
||||
public resetFilter() {
|
||||
this.whereString = undefined
|
||||
this.whereClause = undefined
|
||||
@ -210,6 +226,10 @@ export class QueryComponent
|
||||
this.whereClauseFn(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* `Globals` are used to store filtering state (variables) as a caching feature
|
||||
* until browser reloads
|
||||
*/
|
||||
public setToGlobals() {
|
||||
if (!this.caching) return
|
||||
|
||||
@ -237,6 +257,10 @@ export class QueryComponent
|
||||
console.log('globals', globals)
|
||||
}
|
||||
|
||||
/**
|
||||
* `Globals` are used to store filtering state (variables) as a caching feature
|
||||
* until browser reloads
|
||||
*/
|
||||
public getFromGlobals() {
|
||||
if (!this.caching) return
|
||||
|
||||
@ -269,6 +293,11 @@ export class QueryComponent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets filtering multiple caluses group logic (and / or)
|
||||
*
|
||||
* @param groupLogic to set
|
||||
*/
|
||||
public setGroupLogic(groupLogic: any) {
|
||||
this.groupLogic = groupLogic
|
||||
this.clauses.groupLogic = groupLogic
|
||||
|
@ -17,6 +17,17 @@ export class HelperService {
|
||||
console.log('Is IE or Edge?', this.isMicrosoft)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JavaScript date object to a SAS Date or Datetime, given the logic below:
|
||||
*
|
||||
* A JS Date contains the number of _milliseconds_ since 01/01/1970
|
||||
* A SAS Date contains the number of _days_ since 01/01/1960
|
||||
* A SAS Datetime contains the number of _seconds_ since 01/01/1960
|
||||
*
|
||||
* @param jsDate JS Date to be converted. The type is instance of `Date`
|
||||
* @param unit Unit in which to return the SAS Date / datetime, eg `sasdate | sasdatetime`
|
||||
* @returns SAS Date value based on `unit` param
|
||||
*/
|
||||
public convertJsDateToSasDate(
|
||||
jsDate: string | Date,
|
||||
unit: string = 'days'
|
||||
@ -63,6 +74,17 @@ export class HelperService {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a SAS Date or Datetime to a JavaScript date object, given the logic below:
|
||||
*
|
||||
* A JS Date contains the number of _milliseconds_ since 01/01/1970
|
||||
* A SAS Date contains the number of _days_ since 01/01/1960
|
||||
* A SAS Datetime contains the number of _seconds_ since 01/01/1960
|
||||
*
|
||||
* @param sasValue SAS Date or Datetime to be converted. The type could be `number` or `string.
|
||||
* @param unit Unit from which to convert the SAS Date / Datetime, eg `sasdate | sasdatetime`
|
||||
* @returns JavaScript Date object
|
||||
*/
|
||||
public convertSasDaysToJsDate(
|
||||
sasValue: number | string,
|
||||
unit: string = 'days'
|
||||
@ -87,6 +109,11 @@ export class HelperService {
|
||||
return new Date(msNegativeTenYears + sasValue * msInDay)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array all elements in the clarity tree
|
||||
* @param arrToFilter sub array in the tree to be filtered for example `tables`
|
||||
*/
|
||||
public treeOnFilter(array: any, arrToFilter: string) {
|
||||
let search = array['searchString'] ? array['searchString'] : ''
|
||||
let arrToFilterArray = arrToFilter.split('.')[0]
|
||||
|
@ -40,6 +40,16 @@ export class SasStoreService {
|
||||
private loggerService: LoggerService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Wrapper for making request to service
|
||||
* Should be removed, as it's redundant now
|
||||
* TODO: Refactor to call editors/getdata directly
|
||||
* @param tableData
|
||||
* @param tableName
|
||||
* @param program
|
||||
* @param libds
|
||||
* @returns
|
||||
*/
|
||||
public async callService(
|
||||
tableData: Array<any>,
|
||||
tableName: string,
|
||||
@ -60,6 +70,15 @@ export class SasStoreService {
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling editors/stagedata - saving table data, sending request to backend
|
||||
* @param tableParams params to send to backend
|
||||
* @param tableData data to be updated
|
||||
* @param tableName name of the table to be updated
|
||||
* @param program service against which we send request
|
||||
* @param $dataFormats column data formats recieved from backend, sending it back
|
||||
* @returns adapter.request() response
|
||||
*/
|
||||
public async updateTable(
|
||||
tableParams: any,
|
||||
tableData: any,
|
||||
@ -86,6 +105,13 @@ export class SasStoreService {
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Sending request to 'approvers/getapprovals' to fetch approvals list
|
||||
* @param tableData sending to backend table data
|
||||
* @param tableName sending to backend table name
|
||||
* @param program service to run request on
|
||||
* @returns HTTP Response
|
||||
*/
|
||||
public async getApprovals(
|
||||
tableData: any,
|
||||
tableName: string,
|
||||
@ -96,6 +122,13 @@ export class SasStoreService {
|
||||
let res: any = await this.sasService.request(program, tables)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Interceptor for loading of the submitted details
|
||||
* @param detail submitter
|
||||
* @param index submitter index
|
||||
* @param data submit data
|
||||
*/
|
||||
public async sendDetails(detail: any, index: any, data: any) {
|
||||
let details = Object.assign({ sub: true }, detail)
|
||||
let subData = data[index]
|
||||
@ -105,11 +138,20 @@ export class SasStoreService {
|
||||
}
|
||||
this.submittDetail.next(allData)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns All submits
|
||||
*/
|
||||
public async getSubmitts() {
|
||||
let res: any = await this.sasService.request('editors/getsubmits', null)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns All libraries
|
||||
*/
|
||||
public async viewLibs() {
|
||||
return this.sasService.request('public/viewlibs', null)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { DcAdapterSettings } from '../models/DcAdapterSettings'
|
||||
import { AppStoreService } from './app-store.service'
|
||||
import { LoggerService } from './logger.service'
|
||||
import { RequestWrapperOptions } from '../models/RequestWrapperOptions'
|
||||
import { ErrorBody } from '../models/ErrorBody'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -39,6 +40,11 @@ export class SasService {
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Same as `setup` function in the sasjs.service, this is the constructor replacement.
|
||||
* This function is being called by `app.service`.
|
||||
* Because of timing and dependency issues
|
||||
*/
|
||||
public sasServiceInit() {
|
||||
this.dcAdapterSettings = this.appStoreService.getDcAdapterSettings()
|
||||
|
||||
@ -80,6 +86,16 @@ export class SasService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runing a backend request against a service.
|
||||
* Function also handles the displaying of success or error modals.
|
||||
*
|
||||
* @param url service to run reuqest against
|
||||
* @param data to be sent to backend service
|
||||
* @param config additional parameters to force eg. { debug: false }
|
||||
* @param wrapperOptions used to suppress error or success abort modals after request is finished
|
||||
* @returns
|
||||
*/
|
||||
public request(
|
||||
url: string,
|
||||
data: any,
|
||||
@ -197,6 +213,14 @@ export class SasService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to the backend, using the adapter upload function.
|
||||
*
|
||||
* @param sasService Service to which the file will be sent
|
||||
* @param files Files to be sent
|
||||
* @param params Aditional parameters eg. { debug: false }
|
||||
* @returns HTTP Response
|
||||
*/
|
||||
public uploadFile(sasService: string, files: UploadFile[], params: any) {
|
||||
return this.sasjsAdapter.uploadFile(sasService, files, params)
|
||||
}
|
||||
@ -487,9 +511,3 @@ export class SasService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ErrorBody {
|
||||
message: string
|
||||
details: any
|
||||
raw: any
|
||||
}
|
||||
|
@ -25,6 +25,12 @@ export class SasjsService {
|
||||
private appStoreService: AppStoreService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* This function is replacing the constructor.
|
||||
* The reason for this is timing issues, other services eg. sas.service, app-store.service
|
||||
* must be initialized before this bit of code is executed.
|
||||
* This function is being called by `sas.service`
|
||||
*/
|
||||
setup() {
|
||||
const adapterConfig = this.appStoreService.getDcAdapterSettings()
|
||||
|
||||
@ -32,10 +38,18 @@ export class SasjsService {
|
||||
this.driveUrl = `${this.url}/drive`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns Sasjs/server information
|
||||
*/
|
||||
getServerInfo(): Observable<SASjsApiServerInfo> {
|
||||
return this.http.get<SASjsApiServerInfo>(`${this.url}/info`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets file contents on a given path
|
||||
* @param filePath path to the file
|
||||
*/
|
||||
getFileFromDrive(filePath: string) {
|
||||
return this.http.get(
|
||||
`${this.driveUrl}/file/?_filePath=${filePath}`,
|
||||
@ -43,6 +57,11 @@ export class SasjsService {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets folder contents on a given path
|
||||
* @param folderPath path to the folder
|
||||
* @returns
|
||||
*/
|
||||
getFolderContentsFromDrive(
|
||||
folderPath: string
|
||||
): Observable<SASjsApiDriveFolderContents> {
|
||||
|
@ -41,6 +41,14 @@ export class InfoModalComponent implements OnInit {
|
||||
this.data = newData
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheter or not to show the `Open configurator button`
|
||||
* Button used for navigating to the `configuration` page
|
||||
* Only for SAS9
|
||||
* @param sasService backend service that caused this info modal to be shown
|
||||
* Decision is made based on that service path
|
||||
* @returns
|
||||
*/
|
||||
showConfiguratorButton(sasService: string | null) {
|
||||
const sasjsConfig = this.sasService.getSasjsConfig()
|
||||
|
||||
@ -54,6 +62,9 @@ export class InfoModalComponent implements OnInit {
|
||||
this.onConfirmModalClick.emit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Only on SAS9, opening a backend configurator/deploy page
|
||||
*/
|
||||
openConfigurator() {
|
||||
this.eventService.startupDataLoaded()
|
||||
this.router.navigateByUrl('/deploy')
|
||||
|
@ -201,16 +201,28 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Open viewboxes modal
|
||||
*/
|
||||
public newViewbox() {
|
||||
this.viewboxOpen = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetting filter variables
|
||||
*/
|
||||
public resetFilter() {
|
||||
if (this.queryFilterCompList.first) {
|
||||
this.queryFilterCompList.first.resetFilter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searching table against particular string, data is comming from backend.
|
||||
* There is also a toggle that will search for a numeric values
|
||||
*
|
||||
* @param inputElement input from which search string is captured
|
||||
*/
|
||||
public async searchTable(inputElement: any) {
|
||||
this.searchLoading = true
|
||||
|
||||
@ -249,6 +261,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
this.searchLoading = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Re sending request to backend and re-setting data in the HOT
|
||||
*/
|
||||
public reloadTableData() {
|
||||
this.viewData(this.urlFilterPk || 0)
|
||||
}
|
||||
@ -278,6 +293,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: Should be removed, not used
|
||||
*/
|
||||
public filterFn(input: string) {
|
||||
let libraries = this.libraries
|
||||
this.libraries = libraries.filter(
|
||||
@ -286,6 +304,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from backend, against `getrawdata` service, link is created and open in new tab
|
||||
*/
|
||||
public downloadData() {
|
||||
let storage = this.sasjsConfig.serverUrl
|
||||
let metaData = this.sasjsConfig.appLoc
|
||||
@ -320,6 +341,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
this.openDownload = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from backend, against `getddl` service, link is created and open in new tab
|
||||
*/
|
||||
public downloadDDL() {
|
||||
let libref = this.lib
|
||||
let ds = this.table
|
||||
@ -346,15 +370,27 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
this.openDownload = false
|
||||
}
|
||||
|
||||
/**
|
||||
* When clicked on textare in the Web Query Modal, this function will
|
||||
* select all text inside.
|
||||
* @param evt textarea which contains the web query text
|
||||
*/
|
||||
public onCliCommandFocus(evt: any): void {
|
||||
evt.preventDefault()
|
||||
evt.target.select()
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the edit page of a viewing table
|
||||
*/
|
||||
public editTable() {
|
||||
this.router.navigateByUrl('/editor/' + this.libTab)
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to show/hide the edit table button
|
||||
* @returns Wheter currently viewed table is edtiable
|
||||
*/
|
||||
public tableEditExists() {
|
||||
let editTables: any = {}
|
||||
editTables = globals.editor.libsAndTables
|
||||
@ -367,12 +403,18 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
||||
return editTables[currentLibrary].includes(currentTable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the lineage of a viewing table
|
||||
*/
|
||||
public goToLineage() {
|
||||
let routeUri = this.tableuri!.split('\\')[1]
|
||||
let lineageUrl = `/view/lineage/${routeUri}/REVERSE`
|
||||
this.router.navigateByUrl(lineageUrl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays web query modal
|
||||
*/
|
||||
public showWebQuery() {
|
||||
this.webQuery = true
|
||||
let filter_pk: number
|
||||
|
Loading…
Reference in New Issue
Block a user