Compare commits
52 Commits
v6.16.1
...
c8b6fdbfdb
Author | SHA1 | Date | |
---|---|---|---|
c8b6fdbfdb | |||
|
b495c41626 | ||
|
7f4be474c6 | ||
7f6f68fcbb | |||
|
03fd7db033 | ||
|
9dc5c66f7b | ||
|
aa1b08632e | ||
|
6bbe354c9e | ||
|
8ff429793b | ||
|
70d010127a | ||
|
696717c509 | ||
|
71c308d052 | ||
|
bed5b320ad | ||
b0e827412e | |||
|
63e9af402e | ||
fd55105f62 | |||
|
c2e3b362e7 | ||
|
5d2d60d040 | ||
0e59f5406f | |||
|
e7cb471c0b | ||
|
0465089207 | ||
|
4f2f59907c | ||
|
7d85328d41 | ||
f2a9329196 | |||
|
7a8231615c | ||
|
0db6b25327 | ||
e91f6f01a6 | |||
|
0b4042af60 | ||
|
519d8953b5 | ||
14a616fc1b | |||
bfe5a8626f | |||
|
4ecd186e5c | ||
|
8c60473c15 | ||
bb126eba5b | |||
|
d1998422d2 | ||
|
69f687a85f | ||
|
2aa19d1dca | ||
|
e44a25dcc3 | ||
|
efb5ffa906 | ||
|
b4c586a859 | ||
|
e874143a95 | ||
|
e4dbab8b16 | ||
|
f6d7d6f90c | ||
|
063c90caf4 | ||
|
2011c2eee7 | ||
|
24545f2acd | ||
a7c81245ff | |||
|
4f2c993b2d | ||
|
e5f8e500c1 | ||
|
a61e2de140 | ||
881d2b060e | |||
|
4830c6d219 |
@@ -1,11 +1,23 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Avoid commits to the master branch
|
# Using `--silent` helps for showing any errs in the first line of the response
|
||||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
# The first line is picked up by the VS Code GIT UI popup when rc is not 0
|
||||||
REGEX="^(master|development)$"
|
|
||||||
|
|
||||||
if [[ "$BRANCH" =~ $REGEX ]]; then
|
if npm run --silent lint:check:silent ; then
|
||||||
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
|
exit 0
|
||||||
echo "If so, commit with -n to bypass the pre-commit hook."
|
else
|
||||||
exit 1
|
npm run --silent lint:fix:silent
|
||||||
|
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
## Avoid large commits
|
||||||
|
# https://www.backblaze.com/blog/how-many-bytes-are-in-a-megabyte-really/
|
||||||
|
size_limit=$((2 * 2**20)) # 2mbs
|
||||||
|
# https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---disk-usage
|
||||||
|
commit_size=$(git rev-list --disk-usage HEAD^..HEAD)
|
||||||
|
test "$commit_size" -lt "$size_limit" || (
|
||||||
|
echo "Commit size is too large: $commit_size > $size_limit"
|
||||||
|
echo "Force commit using --no-verify"
|
||||||
|
exit 1
|
||||||
|
)
|
@@ -10,7 +10,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.14.0
|
node-version: 20.15.1
|
||||||
|
|
||||||
- name: Install Google Chrome
|
- name: Install Google Chrome
|
||||||
run: |
|
run: |
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.14.0
|
node-version: 20.15.1
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: |
|
run: |
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||||
cat ./cypress.config.ts
|
cat ./cypress.config.ts
|
||||||
# Start frontend and run cypress
|
# Start frontend and run cypress
|
||||||
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
|
||||||
|
|
||||||
- name: Zip Cypress videos
|
- name: Zip Cypress videos
|
||||||
if: always()
|
if: always()
|
||||||
|
53
CHANGELOG.md
53
CHANGELOG.md
@@ -1,3 +1,56 @@
|
|||||||
|
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* makedata vars ([e7cb471](https://git.datacontroller.io/dc/dc/commit/e7cb471c0b60058b03fe8cbed5e3e2e70dd72e26))
|
||||||
|
* viya deploy makedata missing params ([7a82316](https://git.datacontroller.io/dc/dc/commit/7a8231615cb56710351fae5868e8fdeed54d180c))
|
||||||
|
|
||||||
|
## [7.0.2](https://git.datacontroller.io/dc/dc/compare/v7.0.1...v7.0.2) (2025-06-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **viya deploy:** run makedata in new window to ensure logs are available for the user ([0b4042a](https://git.datacontroller.io/dc/dc/commit/0b4042af6011fdc65cfaaa5d4b1d8f48cd67f3b3))
|
||||||
|
|
||||||
|
## [7.0.1](https://git.datacontroller.io/dc/dc/compare/v7.0.0...v7.0.1) (2025-06-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* refresh process ([4ecd186](https://git.datacontroller.io/dc/dc/commit/4ecd186e5cb22dd436f2d7f1200956f4e3f27425))
|
||||||
|
|
||||||
|
# [7.0.0](https://git.datacontroller.io/dc/dc/compare/v6.16.2...v7.0.0) (2025-06-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bumping adapter to re-enable JES API method ([e874143](https://git.datacontroller.io/dc/dc/commit/e874143a95d0ac2e56c0793e04b979c27f96d74b))
|
||||||
|
* commit git hooks checking lint ([69f687a](https://git.datacontroller.io/dc/dc/commit/69f687a85f1cc562346b6167813d617cb9bd3404))
|
||||||
|
* ensuring apploc is not case sensitive. Closes [#171](https://git.datacontroller.io/dc/dc/issues/171) ([24545f2](https://git.datacontroller.io/dc/dc/commit/24545f2acdd5bd73cbe062526f2bd043269cc6a3))
|
||||||
|
* export unregistered formats ([f6d7d6f](https://git.datacontroller.io/dc/dc/commit/f6d7d6f90c978ac8c071471dfb67a60834424de5)), closes [#158](https://git.datacontroller.io/dc/dc/issues/158)
|
||||||
|
* reload startupservice after user approves the MPE_TABLES page ([e5f8e50](https://git.datacontroller.io/dc/dc/commit/e5f8e500c125ee233c6f7af5ad0077c0ed6abfcb))
|
||||||
|
* showing catalog_cnt in libinfo ([e44a25d](https://git.datacontroller.io/dc/dc/commit/e44a25dcc39ba4b9714257c60da84c2dfa613a85)), closes [#160](https://git.datacontroller.io/dc/dc/issues/160)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* adding 4 new tables for catalogs ([e4dbab8](https://git.datacontroller.io/dc/dc/commit/e4dbab8b1654b24e610e4b0603d1cf2b02a451e2))
|
||||||
|
* capturing catalog specific information, closes [#159](https://git.datacontroller.io/dc/dc/issues/159) ([b4c586a](https://git.datacontroller.io/dc/dc/commit/b4c586a859929e0122cd46449e43d4ca597b8b2b))
|
||||||
|
* viewer added catalog_cnt ([2aa19d1](https://git.datacontroller.io/dc/dc/commit/2aa19d1dca747f41274a032cde78d8ba73d66224))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* Introduction of 4 new tables for capturing information related to catalogs and their objects. Migration script prepared and available in the DB folder (usual place)
|
||||||
|
|
||||||
|
## [6.16.2](https://git.datacontroller.io/dc/dc/compare/v6.16.1...v6.16.2) (2025-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* streaming viya deploy `isStreaming` function stability fix ([4830c6d](https://git.datacontroller.io/dc/dc/commit/4830c6d2191cb47abcc7919bc1d49e55595e6121))
|
||||||
|
|
||||||
## [6.16.1](https://git.datacontroller.io/dc/dc/compare/v6.16.0...v6.16.1) (2025-06-06)
|
## [6.16.1](https://git.datacontroller.io/dc/dc/compare/v6.16.0...v6.16.1) (2025-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
11
README.md
11
README.md
@@ -23,7 +23,16 @@ _Problems with the above include:_
|
|||||||
|
|
||||||
Data Controller for SAS® solves all these issues in a simple-to-install, user-friendly, secure, documented, battle-tested web application. Available on Viya, SAS 9 EBI, and [SASjs Server](https://server.sasjs.io).
|
Data Controller for SAS® solves all these issues in a simple-to-install, user-friendly, secure, documented, battle-tested web application. Available on Viya, SAS 9 EBI, and [SASjs Server](https://server.sasjs.io).
|
||||||
|
|
||||||
For more information:
|
An individual Viya deploy can be done in just 2 lines of #SAS code!
|
||||||
|
|
||||||
|
```sas
|
||||||
|
filename dc url "https://git.datacontroller.io/dc/dc/releases/download/latest/viya.sas";
|
||||||
|
%inc dc;
|
||||||
|
```
|
||||||
|
|
||||||
|
For a multi-user deploy, using a shared system account, please see [deploy docs](https://docs.datacontroller.io/deploy-viya/).
|
||||||
|
|
||||||
|
For further information:
|
||||||
|
|
||||||
* Main site: https://datacontroller.io
|
* Main site: https://datacontroller.io
|
||||||
* Docs: https://docs.datacontroller.io
|
* Docs: https://docs.datacontroller.io
|
||||||
|
@@ -10,7 +10,7 @@ const check = (cwd) => {
|
|||||||
onlyAllow:
|
onlyAllow:
|
||||||
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
|
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
|
||||||
excludePackages:
|
excludePackages:
|
||||||
'@cds/city@1.1.0;@handsontable/angular@15.3.0;handsontable@15.3.0;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
|
'@cds/city@1.1.0;@handsontable/angular@16.0.1;handsontable@16.0.1;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
|
||||||
},
|
},
|
||||||
(error, json) => {
|
(error, json) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
1573
client/package-lock.json
generated
1573
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -48,8 +48,8 @@
|
|||||||
"@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
|
"@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
|
||||||
"@clr/icons": "^13.0.2",
|
"@clr/icons": "^13.0.2",
|
||||||
"@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
|
"@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
|
||||||
"@handsontable/angular": "^15.3.0",
|
"@handsontable/angular": "^16.0.1",
|
||||||
"@sasjs/adapter": "^4.12.0",
|
"@sasjs/adapter": "^4.12.2",
|
||||||
"@sasjs/utils": "^3.4.0",
|
"@sasjs/utils": "^3.4.0",
|
||||||
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
|
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
|
||||||
"@types/d3-graphviz": "^2.6.7",
|
"@types/d3-graphviz": "^2.6.7",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"d3-graphviz": "^5.0.2",
|
"d3-graphviz": "^5.0.2",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"handsontable": "^15.3.0",
|
"handsontable": "^16.0.1",
|
||||||
"https-browserify": "1.0.0",
|
"https-browserify": "1.0.0",
|
||||||
"hyperformula": "^2.5.0",
|
"hyperformula": "^2.5.0",
|
||||||
"iconv-lite": "^0.5.0",
|
"iconv-lite": "^0.5.0",
|
||||||
|
@@ -78,6 +78,17 @@ export class AutomaticComponent implements OnInit {
|
|||||||
runMakeData: null
|
runMakeData: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sasjsConfig = this.sasService.getSasjsConfig()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* makedata service will be run in a new window
|
||||||
|
* This is needed to ensure that the user can see the logs
|
||||||
|
* and the progress of the service execution.
|
||||||
|
* If this is set to `false`, the service will be run in the same window
|
||||||
|
* using the adapter request method.
|
||||||
|
*/
|
||||||
|
public deployInNewWindow: boolean = true
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
private deployService: DeployService,
|
private deployService: DeployService,
|
||||||
@@ -156,8 +167,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
public async getAdminGroups() {
|
public async getAdminGroups() {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
this.adminGroupsLoading = true
|
this.adminGroupsLoading = true
|
||||||
|
;(this.sasViyaService
|
||||||
this.sasViyaService
|
|
||||||
.getAdminGroups()
|
.getAdminGroups()
|
||||||
.subscribe((res: ViyaApiIdentities) => {
|
.subscribe((res: ViyaApiIdentities) => {
|
||||||
this.adminGroupsLoading = false
|
this.adminGroupsLoading = false
|
||||||
@@ -177,7 +187,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
this.eventService.showAbortModal('admin groups', err)
|
this.eventService.showAbortModal('admin groups', err)
|
||||||
|
|
||||||
reject(err)
|
reject(err)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +256,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async runAutoDeploy(executeJson: boolean = false) {
|
public async runAutoDeploy(executeJson: boolean = false) {
|
||||||
this.autodeploying = true
|
if (!this.deployInNewWindow) this.autodeploying = true
|
||||||
|
|
||||||
if (executeJson) {
|
if (executeJson) {
|
||||||
this.executeJson()
|
this.executeJson()
|
||||||
@@ -255,7 +265,7 @@ export class AutomaticComponent implements OnInit {
|
|||||||
if (this.recreateDatabase) {
|
if (this.recreateDatabase) {
|
||||||
this.createDatabase()
|
this.createDatabase()
|
||||||
} else {
|
} else {
|
||||||
this.autodeployDone = true
|
if (!this.deployInNewWindow) this.autodeployDone = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,51 +306,86 @@ export class AutomaticComponent implements OnInit {
|
|||||||
debug: true
|
debug: true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sasJs
|
if (this.deployInNewWindow) {
|
||||||
.request(`services/admin/makedata`, data, overrideConfig, () => {
|
this.runMakedataInNewWindow({
|
||||||
this.sasService.shouldLogin.next(true)
|
contextName: selectedComputeContextName,
|
||||||
|
admin: this.selectedAdminGroup,
|
||||||
|
dcPath: this.dcPath
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
} else {
|
||||||
this.autodeployDone = true
|
this.sasJs
|
||||||
|
.request(`services/admin/makedata`, data, overrideConfig, () => {
|
||||||
|
this.sasService.shouldLogin.next(true)
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
this.autodeployDone = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.makeDataResponse = JSON.stringify(res)
|
this.makeDataResponse = JSON.stringify(res)
|
||||||
} catch {
|
} catch {
|
||||||
this.makeDataResponse = res
|
this.makeDataResponse = res
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.result && res.result.length > 0) {
|
if (res.result && res.result.length > 0) {
|
||||||
this.autoDeployStatus.runMakeData = true
|
this.autoDeployStatus.runMakeData = true
|
||||||
} else {
|
} else {
|
||||||
|
this.autoDeployStatus.runMakeData = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof res.sasjsAbort !== 'undefined') {
|
||||||
|
const abortRes = res
|
||||||
|
const abortMsg = abortRes.sasjsAbort[0].MSG
|
||||||
|
const macMsg = abortRes.sasjsAbort[0].MAC
|
||||||
|
|
||||||
|
this.eventService.showAbortModal('makedata', abortMsg, {
|
||||||
|
SYSWARNINGTEXT: abortRes.SYSWARNINGTEXT,
|
||||||
|
SYSERRORTEXT: abortRes.SYSERRORTEXT,
|
||||||
|
MAC: macMsg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.helperService.isStreamingViya())
|
||||||
|
this.updateIndexHtmlComputeContext()
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.eventService.showAbortModal('makedata', JSON.stringify(err))
|
||||||
this.autoDeployStatus.runMakeData = false
|
this.autoDeployStatus.runMakeData = false
|
||||||
}
|
this.autodeployDone = true
|
||||||
|
|
||||||
if (typeof res.sasjsAbort !== 'undefined') {
|
try {
|
||||||
const abortRes = res
|
this.makeDataResponse = JSON.stringify(err)
|
||||||
const abortMsg = abortRes.sasjsAbort[0].MSG
|
} catch {
|
||||||
const macMsg = abortRes.sasjsAbort[0].MAC
|
this.makeDataResponse = err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.eventService.showAbortModal('makedata', abortMsg, {
|
public runMakedataInNewWindow(params: {
|
||||||
SYSWARNINGTEXT: abortRes.SYSWARNINGTEXT,
|
contextName: string
|
||||||
SYSERRORTEXT: abortRes.SYSERRORTEXT,
|
admin: string
|
||||||
MAC: macMsg
|
dcPath: string
|
||||||
})
|
}) {
|
||||||
}
|
let serverUrl = this.sasjsConfig.serverUrl
|
||||||
|
let appLoc = this.sasjsConfig.appLoc
|
||||||
|
const execPath = this.sasService.getExecutionPath()
|
||||||
|
let contextname = `&_contextname=${params.contextName}`
|
||||||
|
let admin = `&admin=${params.admin}`
|
||||||
|
let dcPath = `&dcpath=${params.dcPath}`
|
||||||
|
let debug = `&_debug=131`
|
||||||
|
|
||||||
if (this.helperService.isStreamingViya())
|
let programUrl =
|
||||||
this.updateIndexHtmlComputeContext()
|
serverUrl +
|
||||||
})
|
execPath +
|
||||||
.catch((err: any) => {
|
'/?_program=' +
|
||||||
this.eventService.showAbortModal('makedata', JSON.stringify(err))
|
appLoc +
|
||||||
this.autoDeployStatus.runMakeData = false
|
'/services/admin/makedata' +
|
||||||
this.autodeployDone = true
|
contextname +
|
||||||
|
admin +
|
||||||
|
dcPath +
|
||||||
|
debug
|
||||||
|
|
||||||
try {
|
window.open(programUrl)
|
||||||
this.makeDataResponse = JSON.stringify(err)
|
|
||||||
} catch {
|
|
||||||
this.makeDataResponse = err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
@@ -71,7 +72,7 @@ import { ParseResult } from '../models/ParseResult.interface'
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class EditorComponent implements OnInit, AfterViewInit {
|
export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
@ViewChildren('uploadStater')
|
@ViewChildren('uploadStater')
|
||||||
uploadStaterCompList: QueryList<UploadStaterComponent> = new QueryList()
|
uploadStaterCompList: QueryList<UploadStaterComponent> = new QueryList()
|
||||||
@ViewChildren('queryFilter')
|
@ViewChildren('queryFilter')
|
||||||
@@ -124,7 +125,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
hidden: true,
|
hidden: true,
|
||||||
columns: [],
|
columns: [],
|
||||||
height: '100%',
|
height: 'calc(100vh - 160px)',
|
||||||
minSpareRows: 1,
|
minSpareRows: 1,
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@@ -350,6 +351,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
|
|
||||||
|
private ariaObserver: MutationObserver | undefined
|
||||||
|
private ariaCheckInterval: any | undefined
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private licenceService: LicenceService,
|
private licenceService: LicenceService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
@@ -896,6 +900,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reSetCellValidationValues()
|
this.reSetCellValidationValues()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table edit
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 100)
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2262,7 +2271,173 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Clean up the MutationObserver
|
||||||
|
if (this.ariaObserver) {
|
||||||
|
this.ariaObserver.disconnect()
|
||||||
|
this.ariaObserver = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the interval
|
||||||
|
if (this.ariaCheckInterval) {
|
||||||
|
clearInterval(this.ariaCheckInterval)
|
||||||
|
this.ariaCheckInterval = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes ARIA accessibility issues in the Handsontable component
|
||||||
|
* This addresses the accessibility report issues with treegrid and presentation roles
|
||||||
|
*/
|
||||||
|
private fixAriaAccessibility() {
|
||||||
|
// Use a more aggressive approach to find and fix all ARIA issues
|
||||||
|
const fixAriaIssues = () => {
|
||||||
|
// Specifically target Handsontable wrapper elements that are causing issues
|
||||||
|
const hotWrappers = document.querySelectorAll(
|
||||||
|
'.ht-wrapper, .wtHolder, [id^="ht_"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
hotWrappers.forEach((wrapper) => {
|
||||||
|
// Remove problematic ARIA attributes from Handsontable wrappers
|
||||||
|
wrapper.removeAttribute('role')
|
||||||
|
wrapper.removeAttribute('aria-rowcount')
|
||||||
|
wrapper.removeAttribute('aria-colcount')
|
||||||
|
wrapper.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find all elements with problematic ARIA roles in the entire document
|
||||||
|
const allTreegridElements = document.querySelectorAll('[role="treegrid"]')
|
||||||
|
const allPresentationElements = document.querySelectorAll(
|
||||||
|
'[role="presentation"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fix treegrid role issues - remove them completely as they're causing problems
|
||||||
|
allTreegridElements.forEach((element) => {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
element.removeAttribute('aria-colcount')
|
||||||
|
element.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fix presentation role issues - remove them if they contain interactive elements
|
||||||
|
allPresentationElements.forEach((element) => {
|
||||||
|
const hasInteractiveChildren =
|
||||||
|
element.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [tabindex], [onclick], [contenteditable]'
|
||||||
|
).length > 0
|
||||||
|
if (hasInteractiveChildren) {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also fix any elements with aria-rowcount="-1" which is problematic
|
||||||
|
const negativeRowCountElements = document.querySelectorAll(
|
||||||
|
'[aria-rowcount="-1"]'
|
||||||
|
)
|
||||||
|
negativeRowCountElements.forEach((element) => {
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure proper table structure
|
||||||
|
const tableElements = document.querySelectorAll('table')
|
||||||
|
tableElements.forEach((table) => {
|
||||||
|
if (!table.getAttribute('role')) {
|
||||||
|
table.setAttribute('role', 'table')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure table headers have proper scope
|
||||||
|
const headerCells = table.querySelectorAll('th')
|
||||||
|
headerCells.forEach((th) => {
|
||||||
|
if (!th.getAttribute('scope')) {
|
||||||
|
th.setAttribute('scope', 'col')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add proper ARIA labels to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [contenteditable]'
|
||||||
|
)
|
||||||
|
interactiveElements.forEach((element) => {
|
||||||
|
if (
|
||||||
|
!element.getAttribute('aria-label') &&
|
||||||
|
!element.getAttribute('aria-labelledby')
|
||||||
|
) {
|
||||||
|
const textContent = element.textContent?.trim()
|
||||||
|
if (textContent) {
|
||||||
|
element.setAttribute('aria-label', textContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fix immediately
|
||||||
|
fixAriaIssues()
|
||||||
|
|
||||||
|
// Run it again after a short delay to catch any dynamically created elements
|
||||||
|
setTimeout(fixAriaIssues, 100)
|
||||||
|
setTimeout(fixAriaIssues, 500)
|
||||||
|
setTimeout(fixAriaIssues, 1000)
|
||||||
|
setTimeout(fixAriaIssues, 2000)
|
||||||
|
|
||||||
|
// Set up a periodic check to ensure accessibility fixes are maintained
|
||||||
|
if (!this.ariaCheckInterval) {
|
||||||
|
this.ariaCheckInterval = setInterval(fixAriaIssues, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a MutationObserver to continuously monitor for new problematic elements
|
||||||
|
if (!this.ariaObserver) {
|
||||||
|
this.ariaObserver = new MutationObserver((mutations) => {
|
||||||
|
let shouldFix = false
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (
|
||||||
|
mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'role' ||
|
||||||
|
mutation.attributeName === 'aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as Element
|
||||||
|
if (
|
||||||
|
element.hasAttribute('role') ||
|
||||||
|
element.hasAttribute('aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldFix) {
|
||||||
|
setTimeout(fixAriaIssues, 50)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start observing the entire document for changes
|
||||||
|
this.ariaObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [
|
||||||
|
'role',
|
||||||
|
'aria-rowcount',
|
||||||
|
'aria-colcount',
|
||||||
|
'aria-multiselectable'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initSetup(response: EditorsGetDataServiceResponse) {
|
initSetup(response: EditorsGetDataServiceResponse) {
|
||||||
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
|
||||||
@@ -2597,6 +2772,17 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
hot.addHook('afterRender', (isForced: boolean) => {
|
hot.addHook('afterRender', (isForced: boolean) => {
|
||||||
this.eventService.dispatchEvent('resize')
|
this.eventService.dispatchEvent('resize')
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after each render
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add a more frequent accessibility fix hook
|
||||||
|
hot.addHook('afterChange', () => {
|
||||||
|
// Fix ARIA accessibility issues after any data change
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 50)
|
||||||
})
|
})
|
||||||
|
|
||||||
hot.addHook('afterCreateRow', (source: any, change: any) => {
|
hot.addHook('afterCreateRow', (source: any, change: any) => {
|
||||||
@@ -2660,5 +2846,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hot.render()
|
hot.render()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -99,6 +99,11 @@ export class MetadataComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.pageSize = 5
|
this.pageSize = 5
|
||||||
|
|
||||||
|
// Initialize filters for accessibility
|
||||||
|
this.typeFilter = new TypeFilter()
|
||||||
|
this.nameFilter = new NameFilter()
|
||||||
|
this.valueFilter = new ValueFilter()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
globals.metadata.metaDataList &&
|
globals.metadata.metaDataList &&
|
||||||
globals.metadata.metaRepositories &&
|
globals.metadata.metaRepositories &&
|
||||||
|
@@ -8,4 +8,5 @@ export interface Libinfo {
|
|||||||
LIBID: string
|
LIBID: string
|
||||||
LIBSIZE: number
|
LIBSIZE: number
|
||||||
TABLE_CNT: number
|
TABLE_CNT: number
|
||||||
|
CATALOG_CNT: number
|
||||||
}
|
}
|
||||||
|
@@ -1,30 +1,14 @@
|
|||||||
import { BaseSASResponse } from './common/BaseSASResponse'
|
import { BaseSASResponse } from './common/BaseSASResponse'
|
||||||
|
|
||||||
export interface EditorsStageDataSASResponse extends BaseSASResponse {
|
export interface EditorsStageDataSASResponse extends BaseSASResponse {
|
||||||
SYSDATE: string
|
|
||||||
SYSTIME: string
|
|
||||||
sasparams: Sasparam[]
|
sasparams: Sasparam[]
|
||||||
_DEBUG: string
|
|
||||||
_PROGRAM: string
|
|
||||||
AUTOEXEC: string
|
AUTOEXEC: string
|
||||||
MF_GETUSER: string
|
|
||||||
SYSCC: string
|
|
||||||
SYSENCODING: string
|
SYSENCODING: string
|
||||||
SYSERRORTEXT: string
|
|
||||||
SYSHOSTINFOLONG: string
|
SYSHOSTINFOLONG: string
|
||||||
SYSHOSTNAME: string
|
|
||||||
SYSPROCESSID: string
|
SYSPROCESSID: string
|
||||||
SYSPROCESSMODE: string
|
SYSPROCESSMODE: string
|
||||||
SYSPROCESSNAME: string
|
SYSPROCESSNAME: string
|
||||||
SYSJOBID: string
|
|
||||||
SYSSCPL: string
|
|
||||||
SYSSITE: string
|
|
||||||
SYSTCPIPHOSTNAME: string
|
SYSTCPIPHOSTNAME: string
|
||||||
SYSUSERID: string
|
|
||||||
SYSVLONG: string
|
|
||||||
SYSWARNINGTEXT: string
|
|
||||||
END_DTTM: string
|
|
||||||
MEMSIZE: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Sasparam {
|
export interface Sasparam {
|
||||||
|
28
client/src/app/models/sas/public-getchangeinfo.model.ts
Normal file
28
client/src/app/models/sas/public-getchangeinfo.model.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { BaseSASResponse } from './common/BaseSASResponse'
|
||||||
|
|
||||||
|
export interface PublicGetChangeinfo extends BaseSASResponse {
|
||||||
|
jsparams: Jsparam[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Jsparam {
|
||||||
|
TABLE_ID: string
|
||||||
|
SUBMIT_STATUS_CD: string
|
||||||
|
BASE_LIB: string
|
||||||
|
BASE_DS: string
|
||||||
|
SUBMITTED_BY_NM: string
|
||||||
|
SUBMITTED_ON: number
|
||||||
|
SUBMITTED_REASON_TXT: string
|
||||||
|
INPUT_OBS: number
|
||||||
|
INPUT_VARS: number
|
||||||
|
NUM_OF_APPROVALS_REQUIRED: number
|
||||||
|
NUM_OF_APPROVALS_REMAINING: number
|
||||||
|
REVIEWED_BY_NM: string
|
||||||
|
REVIEWED_ON?: any
|
||||||
|
TABLE_NM: string
|
||||||
|
BASE_TABLE: string
|
||||||
|
REVIEWED_ON_DTTM: string
|
||||||
|
SUBMITTED_ON_DTTM: string
|
||||||
|
LIB_ENGINE: string
|
||||||
|
ALLOW_RESTORE: string
|
||||||
|
REASON: string
|
||||||
|
}
|
@@ -13,6 +13,8 @@ import {
|
|||||||
AuditorsPostdataSASResponse,
|
AuditorsPostdataSASResponse,
|
||||||
Param
|
Param
|
||||||
} from '../../models/sas/auditors-postdata.model'
|
} from '../../models/sas/auditors-postdata.model'
|
||||||
|
import { PublicGetChangeinfo } from 'src/app/models/sas/public-getchangeinfo.model'
|
||||||
|
import { SasService } from 'src/app/services'
|
||||||
|
|
||||||
interface ChangesObj {
|
interface ChangesObj {
|
||||||
ind: any
|
ind: any
|
||||||
@@ -76,9 +78,11 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
public diffsLimit: boolean = false
|
public diffsLimit: boolean = false
|
||||||
public recordsLimit: number = 100
|
public recordsLimit: number = 100
|
||||||
|
public refreshStartupserviceAfterApprove: boolean = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sasStoreService: SasStoreService,
|
private sasStoreService: SasStoreService,
|
||||||
|
private sasService: SasService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
private router: ActivatedRoute,
|
private router: ActivatedRoute,
|
||||||
private route: Router
|
private route: Router
|
||||||
@@ -162,6 +166,9 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
|
|||||||
await this.sasStoreService
|
await this.sasStoreService
|
||||||
.approveTable(approveParams, 'SASControlTable', 'auditors/postdata')
|
.approveTable(approveParams, 'SASControlTable', 'auditors/postdata')
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
|
// If we are approving MPE_TABLES we will arm the trigger for the reload of startup data to se the updated tables
|
||||||
|
if (this.refreshStartupserviceAfterApprove)
|
||||||
|
this.sasService.reloadStartupData()
|
||||||
this.route.navigateByUrl('/review/history')
|
this.route.navigateByUrl('/review/history')
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
@@ -176,7 +183,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
|
|||||||
public async callChangesInfo(tableId: any) {
|
public async callChangesInfo(tableId: any) {
|
||||||
await this.sasStoreService
|
await this.sasStoreService
|
||||||
.getChangeInfo(tableId)
|
.getChangeInfo(tableId)
|
||||||
.then((res: any) => {
|
.then((res: PublicGetChangeinfo) => {
|
||||||
this.tableDetails = res.jsparams[0]
|
this.tableDetails = res.jsparams[0]
|
||||||
this.jsParams = res.jsparams[0]
|
this.jsParams = res.jsparams[0]
|
||||||
|
|
||||||
@@ -189,6 +196,11 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.keysArray = keysArray
|
this.keysArray = keysArray
|
||||||
|
|
||||||
|
// If we are approving MPE_TABLES we will arm the trigger for the reload of startup data to se the updated tables
|
||||||
|
// After user approved if armed, reload will be triggered
|
||||||
|
if (res.jsparams[0].BASE_DS === 'MPE_TABLES')
|
||||||
|
this.refreshStartupserviceAfterApprove = true
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
this.acceptLoading = false
|
this.acceptLoading = false
|
||||||
|
@@ -33,12 +33,34 @@
|
|||||||
<div class="clr-col-md-12" ng-if="loaded">
|
<div class="clr-col-md-12" ng-if="loaded">
|
||||||
<div *ngIf="approveList && remained !== 0">
|
<div *ngIf="approveList && remained !== 0">
|
||||||
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
||||||
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
|
<clr-dg-column [clrDgField]="'submitter'">
|
||||||
<clr-dg-column [clrDgField]="'baseTable'">BASE TABLE</clr-dg-column>
|
SUBMITTER
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
<clr-dg-string-filter
|
||||||
<clr-dg-column [clrDgField]="'submitReason'"
|
[clrDgStringFilter]="submitterFilter"
|
||||||
>SUBMIT REASON</clr-dg-column
|
aria-label="Filter submitter"
|
||||||
>
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'baseTable'">
|
||||||
|
BASE TABLE
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="baseTableFilter"
|
||||||
|
aria-label="Filter base table"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
|
SUBMITTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column>ACTION</clr-dg-column>
|
<clr-dg-column>ACTION</clr-dg-column>
|
||||||
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
||||||
|
|
||||||
@@ -51,15 +73,19 @@
|
|||||||
<clr-dg-cell>{{ approveItem.submitReason }}</clr-dg-cell>
|
<clr-dg-cell>{{ approveItem.submitReason }}</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div
|
<div
|
||||||
class="clr-row"
|
class="clr-row d-flex justify-content-around"
|
||||||
role="tooltip"
|
role="toolbar"
|
||||||
class="d-flex justify-content-around"
|
aria-label="Table actions"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="column-center links tooltip tooltip-md tooltip-bottom-left color-green"
|
class="column-center links tooltip tooltip-md tooltip-bottom-left color-green"
|
||||||
(click)="getClicked(i)"
|
(click)="getClicked(i)"
|
||||||
>
|
>
|
||||||
<clr-icon shape="check" size="24"></clr-icon>
|
<clr-icon
|
||||||
|
shape="check"
|
||||||
|
size="24"
|
||||||
|
aria-hidden="true"
|
||||||
|
></clr-icon>
|
||||||
<span class="tooltip-content">Go to review page screen</span>
|
<span class="tooltip-content">Go to review page screen</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
@@ -70,10 +96,12 @@
|
|||||||
*ngIf="!approveItem.rejectLoading"
|
*ngIf="!approveItem.rejectLoading"
|
||||||
shape="ban"
|
shape="ban"
|
||||||
size="22"
|
size="22"
|
||||||
|
aria-hidden="true"
|
||||||
></clr-icon>
|
></clr-icon>
|
||||||
<clr-spinner
|
<clr-spinner
|
||||||
*ngIf="approveItem.rejectLoading"
|
*ngIf="approveItem.rejectLoading"
|
||||||
[clrSmall]="true"
|
[clrSmall]="true"
|
||||||
|
aria-hidden="true"
|
||||||
></clr-spinner>
|
></clr-spinner>
|
||||||
<span class="tooltip-content">Reject</span>
|
<span class="tooltip-content">Reject</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -81,7 +109,11 @@
|
|||||||
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
|
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
|
||||||
(click)="getTable(approveItem.tableId)"
|
(click)="getTable(approveItem.tableId)"
|
||||||
>
|
>
|
||||||
<clr-icon shape="code" size="28"></clr-icon>
|
<clr-icon
|
||||||
|
shape="code"
|
||||||
|
size="28"
|
||||||
|
aria-hidden="true"
|
||||||
|
></clr-icon>
|
||||||
<span class="tooltip-content">Go to staged data screen</span>
|
<span class="tooltip-content">Go to staged data screen</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -8,6 +8,7 @@ import { SasStoreService } from '../../services/sas-store.service'
|
|||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { SasService } from '../../services/sas.service'
|
import { SasService } from '../../services/sas.service'
|
||||||
import { EventService } from '../../services/event.service'
|
import { EventService } from '../../services/event.service'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
interface ApproveData {
|
interface ApproveData {
|
||||||
tableId: string
|
tableId: string
|
||||||
@@ -19,6 +20,32 @@ interface ApproveData {
|
|||||||
rejectLoading?: boolean
|
rejectLoading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubmitterFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitter.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseTableFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.baseTable.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter implements ClrDatagridStringFilterInterface<ApproveData> {
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<ApproveData>
|
||||||
|
{
|
||||||
|
accepts(data: ApproveData, search: string): boolean {
|
||||||
|
return data.submitReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-approve',
|
selector: 'app-approve',
|
||||||
templateUrl: './approve.component.html',
|
templateUrl: './approve.component.html',
|
||||||
@@ -35,6 +62,12 @@ export class ApproveComponent implements OnInit {
|
|||||||
public loaded: boolean = false
|
public loaded: boolean = false
|
||||||
public itemsNum: number = 10
|
public itemsNum: number = 10
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public submitterFilter = new SubmitterFilter()
|
||||||
|
public baseTableFilter = new BaseTableFilter()
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sasStoreService: SasStoreService,
|
private sasStoreService: SasStoreService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
|
@@ -85,16 +85,48 @@
|
|||||||
class="datagrid-history datagrid-custom-footer"
|
class="datagrid-history datagrid-custom-footer"
|
||||||
*ngIf="loaded"
|
*ngIf="loaded"
|
||||||
>
|
>
|
||||||
<clr-dg-column [clrDgField]="'basetable'">BASE_TABLE</clr-dg-column>
|
<clr-dg-column [clrDgField]="'basetable'">
|
||||||
<clr-dg-column [clrDgField]="'status'">STATUS</clr-dg-column>
|
BASE_TABLE
|
||||||
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
|
<clr-dg-string-filter
|
||||||
<clr-dg-column [clrDgField]="'submittedReason'"
|
[clrDgStringFilter]="baseTableFilter"
|
||||||
>SUBMIT REASON</clr-dg-column
|
aria-label="Filter base table"
|
||||||
>
|
></clr-dg-string-filter>
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'reviewed'"
|
<clr-dg-column [clrDgField]="'status'">
|
||||||
>APPROVED / REJECTED</clr-dg-column
|
STATUS
|
||||||
>
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="statusFilter"
|
||||||
|
aria-label="Filter status"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitter'">
|
||||||
|
SUBMITTER
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitterFilter"
|
||||||
|
aria-label="Filter submitter"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submittedReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
|
SUBMITTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'reviewed'">
|
||||||
|
APPROVED / REJECTED
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="reviewedFilter"
|
||||||
|
aria-label="Filter reviewed date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
<clr-dg-column>DOWNLOAD</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row
|
<clr-dg-row
|
||||||
|
@@ -8,6 +8,55 @@ import {
|
|||||||
EventService,
|
EventService,
|
||||||
SasService
|
SasService
|
||||||
} from 'src/app/services'
|
} from 'src/app/services'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
|
interface HistoryData {
|
||||||
|
tableId: string
|
||||||
|
basetable: string
|
||||||
|
status: string
|
||||||
|
submitter: string
|
||||||
|
submittedReason: string
|
||||||
|
submitted: string
|
||||||
|
reviewed: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseTableFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.basetable.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.status.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitterFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submitter.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<HistoryData>
|
||||||
|
{
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submittedReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReviewedFilter implements ClrDatagridStringFilterInterface<HistoryData> {
|
||||||
|
accepts(data: HistoryData, search: string): boolean {
|
||||||
|
return data.reviewed.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-history',
|
selector: 'app-history',
|
||||||
@@ -29,6 +78,14 @@ export class HistoryComponent implements OnInit {
|
|||||||
public approveData: any = {}
|
public approveData: any = {}
|
||||||
public sasjsConfig: SASjsConfig = new SASjsConfig()
|
public sasjsConfig: SASjsConfig = new SASjsConfig()
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public baseTableFilter = new BaseTableFilter()
|
||||||
|
public statusFilter = new StatusFilter()
|
||||||
|
public submitterFilter = new SubmitterFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public reviewedFilter = new ReviewedFilter()
|
||||||
|
|
||||||
public histParams: { HIST: number; STARTROW: number; NOBS: number } = {
|
public histParams: { HIST: number; STARTROW: number; NOBS: number } = {
|
||||||
HIST: 0,
|
HIST: 0,
|
||||||
STARTROW: 1,
|
STARTROW: 1,
|
||||||
|
@@ -44,10 +44,20 @@
|
|||||||
<div *ngIf="submitterList && remained !== 0">
|
<div *ngIf="submitterList && remained !== 0">
|
||||||
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
|
||||||
<clr-dg-column>BASE TABLE</clr-dg-column>
|
<clr-dg-column>BASE TABLE</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
|
<clr-dg-column [clrDgField]="'submitted'">
|
||||||
<clr-dg-column [clrDgField]="'submitReason'"
|
SUBMITTED
|
||||||
>SUBMIT REASON</clr-dg-column
|
<clr-dg-string-filter
|
||||||
>
|
[clrDgStringFilter]="submittedFilter"
|
||||||
|
aria-label="Filter submitted date"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'submitReason'">
|
||||||
|
SUBMIT REASON
|
||||||
|
<clr-dg-string-filter
|
||||||
|
[clrDgStringFilter]="submitReasonFilter"
|
||||||
|
aria-label="Filter submit reason"
|
||||||
|
></clr-dg-string-filter>
|
||||||
|
</clr-dg-column>
|
||||||
<clr-dg-column class="d-flex justify-content-center"
|
<clr-dg-column class="d-flex justify-content-center"
|
||||||
>ACTION</clr-dg-column
|
>ACTION</clr-dg-column
|
||||||
>
|
>
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { SasStoreService, EventService, SasService } from '../../services'
|
import { SasStoreService, EventService, SasService } from '../../services'
|
||||||
|
import { ClrDatagridStringFilterInterface } from '@clr/angular'
|
||||||
|
|
||||||
interface SubmitterData {
|
interface SubmitterData {
|
||||||
tableId: string
|
tableId: string
|
||||||
@@ -16,6 +17,22 @@ interface SubmitterData {
|
|||||||
approver: string
|
approver: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubmittedFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<SubmitterData>
|
||||||
|
{
|
||||||
|
accepts(data: SubmitterData, search: string): boolean {
|
||||||
|
return data.submitted.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmitReasonFilter
|
||||||
|
implements ClrDatagridStringFilterInterface<SubmitterData>
|
||||||
|
{
|
||||||
|
accepts(data: SubmitterData, search: string): boolean {
|
||||||
|
return data.submitReason.toLowerCase().indexOf(search.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-submitter',
|
selector: 'app-submitter',
|
||||||
templateUrl: './submitter.component.html',
|
templateUrl: './submitter.component.html',
|
||||||
@@ -37,6 +54,10 @@ export class SubmitterComponent implements OnInit, AfterViewInit {
|
|||||||
private _readySub!: Subscription
|
private _readySub!: Subscription
|
||||||
private _backToSub!: Subscription
|
private _backToSub!: Subscription
|
||||||
|
|
||||||
|
// Filter instances for datagrid accessibility
|
||||||
|
public submittedFilter = new SubmittedFilter()
|
||||||
|
public submitReasonFilter = new SubmitReasonFilter()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sasStoreService: SasStoreService,
|
private sasStoreService: SasStoreService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
|
@@ -321,8 +321,8 @@ export class HelperService {
|
|||||||
if (serverType !== 'SASVIYA') return false
|
if (serverType !== 'SASVIYA') return false
|
||||||
|
|
||||||
if (
|
if (
|
||||||
location.search.includes('?_file=') &&
|
location.search.toLowerCase().includes('?_file=') &&
|
||||||
location.pathname.includes('/SASJobExecution')
|
location.pathname.toLowerCase().includes('/sasjobexecution')
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
@@ -333,6 +333,10 @@ export class SasService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public reloadStartupData() {
|
||||||
|
this.loadStartupServiceEmitter.emit()
|
||||||
|
}
|
||||||
|
|
||||||
public getLicenseSiteId(): string[] {
|
public getLicenseSiteId(): string[] {
|
||||||
return this.license_site_id.value || []
|
return this.license_site_id.value || []
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,10 @@
|
|||||||
rejected: tableDetails?.REVIEW_STATUS_ID === 'REJECTED',
|
rejected: tableDetails?.REVIEW_STATUS_ID === 'REJECTED',
|
||||||
accepted: tableDetails?.REVIEW_STATUS_ID === 'APPROVED'
|
accepted: tableDetails?.REVIEW_STATUS_ID === 'APPROVED'
|
||||||
}"
|
}"
|
||||||
|
[attr.aria-label]="
|
||||||
|
'Review status: ' + tableDetails?.REVIEW_STATUS_ID
|
||||||
|
"
|
||||||
|
role="status"
|
||||||
>
|
>
|
||||||
{{ tableDetails?.REVIEW_STATUS_ID }}
|
{{ tableDetails?.REVIEW_STATUS_ID }}
|
||||||
</span>
|
</span>
|
||||||
@@ -61,6 +65,7 @@
|
|||||||
class="btn btn-sm btn-outline text-center mr-5i"
|
class="btn btn-sm btn-outline text-center mr-5i"
|
||||||
(click)="viewerTableScreen()"
|
(click)="viewerTableScreen()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="View base table"
|
||||||
>
|
>
|
||||||
View base table
|
View base table
|
||||||
</button>
|
</button>
|
||||||
@@ -74,6 +79,7 @@
|
|||||||
"
|
"
|
||||||
(click)="approveTableScreen()"
|
(click)="approveTableScreen()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="Approve table"
|
||||||
>
|
>
|
||||||
Approve
|
Approve
|
||||||
</button>
|
</button>
|
||||||
@@ -81,14 +87,16 @@
|
|||||||
class="btn btn-sm btn-info-outline text-center mr-5i"
|
class="btn btn-sm btn-info-outline text-center mr-5i"
|
||||||
(click)="goBack()"
|
(click)="goBack()"
|
||||||
[disabled]="revertingChanges"
|
[disabled]="revertingChanges"
|
||||||
|
aria-label="Edit base table"
|
||||||
>
|
>
|
||||||
Edit base table
|
Edit base table
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-success text-center mr-5i min-w-0"
|
class="btn btn-sm btn-success text-center mr-5i min-w-0"
|
||||||
(click)="download(tableDetails?.TABLE_ID)"
|
(click)="download(tableDetails?.TABLE_ID)"
|
||||||
|
aria-label="Download audit file"
|
||||||
>
|
>
|
||||||
<clr-icon shape="download"></clr-icon>
|
<clr-icon shape="download" aria-hidden="true"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
@@ -98,6 +106,7 @@
|
|||||||
clrTooltipTrigger
|
clrTooltipTrigger
|
||||||
[clrLoading]="revertingChanges"
|
[clrLoading]="revertingChanges"
|
||||||
class="btn btn-sm btn-danger text-center mt-20"
|
class="btn btn-sm btn-danger text-center mt-20"
|
||||||
|
aria-label="Revert this and all subsequent changes"
|
||||||
>
|
>
|
||||||
REVERT
|
REVERT
|
||||||
|
|
||||||
@@ -128,6 +137,8 @@
|
|||||||
[afterGetColHeader]="hotTable.afterGetColHeader"
|
[afterGetColHeader]="hotTable.afterGetColHeader"
|
||||||
stretchH="all"
|
stretchH="all"
|
||||||
[cells]="hotTable.cells"
|
[cells]="hotTable.cells"
|
||||||
|
[settings]="hotTable.settings"
|
||||||
|
aria-label="Staged data table"
|
||||||
>
|
>
|
||||||
<!--[licenseKey]=null-->
|
<!--[licenseKey]=null-->
|
||||||
</hot-table>
|
</hot-table>
|
||||||
|
@@ -1,4 +1,10 @@
|
|||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core'
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation,
|
||||||
|
ViewChild,
|
||||||
|
AfterViewInit
|
||||||
|
} from '@angular/core'
|
||||||
import { SasStoreService } from '../services/sas-store.service'
|
import { SasStoreService } from '../services/sas-store.service'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
@@ -19,7 +25,7 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class StageComponent implements OnInit {
|
export class StageComponent implements OnInit, AfterViewInit {
|
||||||
public table_id: any
|
public table_id: any
|
||||||
public jsParams: any
|
public jsParams: any
|
||||||
public keysArray: any
|
public keysArray: any
|
||||||
@@ -32,12 +38,26 @@ export class StageComponent implements OnInit {
|
|||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
height: 500,
|
height: 500,
|
||||||
settings: {},
|
settings: {
|
||||||
|
// Disable problematic ARIA attributes that cause accessibility issues
|
||||||
|
ariaTags: false,
|
||||||
|
// Use grid role instead of treegrid for better accessibility
|
||||||
|
tableClassName: 'htCenter',
|
||||||
|
// Disable focus management to avoid focus catcher issues
|
||||||
|
outsideClickDeselects: false,
|
||||||
|
// Use simpler accessibility mode
|
||||||
|
autoWrapRow: false,
|
||||||
|
autoWrapCol: false
|
||||||
|
},
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
|
maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
|
||||||
afterGetColHeader: (column, th, headerLevel) => {
|
afterGetColHeader: (column, th, headerLevel) => {
|
||||||
// Dark mode
|
// Dark mode
|
||||||
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
||||||
|
},
|
||||||
|
afterInit: () => {
|
||||||
|
// Fix accessibility issues with focus catcher inputs
|
||||||
|
this.fixFocusCatcherAccessibility()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +189,13 @@ export class StageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// Additional accessibility fixes after view is initialized
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixFocusCatcherAccessibility()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
revertChanges() {
|
revertChanges() {
|
||||||
this.revertingChanges = true
|
this.revertingChanges = true
|
||||||
|
|
||||||
@@ -204,4 +231,27 @@ export class StageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fixFocusCatcherAccessibility() {
|
||||||
|
// Add labels to focus catcher inputs to fix accessibility issues
|
||||||
|
setTimeout(() => {
|
||||||
|
const focusCatchers = document.querySelectorAll('.htFocusCatcher')
|
||||||
|
focusCatchers.forEach((input: any, index: number) => {
|
||||||
|
if (input) {
|
||||||
|
// Add proper accessibility attributes
|
||||||
|
input.setAttribute('aria-label', `Table focus catcher ${index + 1}`)
|
||||||
|
input.setAttribute('aria-hidden', 'true')
|
||||||
|
input.setAttribute('tabindex', '-1')
|
||||||
|
input.setAttribute('role', 'presentation')
|
||||||
|
// Add a hidden label element
|
||||||
|
const label = document.createElement('label')
|
||||||
|
label.setAttribute('for', input.id || `htFocusCatcher${index}`)
|
||||||
|
label.setAttribute('aria-hidden', 'true')
|
||||||
|
label.style.display = 'none'
|
||||||
|
label.textContent = `Table focus catcher ${index + 1}`
|
||||||
|
input.parentNode?.insertBefore(label, input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -598,6 +598,12 @@
|
|||||||
{{ libinfo[0] ? libinfo[0].TABLE_CNT : '' }}
|
{{ libinfo[0] ? libinfo[0].TABLE_CNT : '' }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr *ngIf="libinfo[0].CATALOG_CNT !== null">
|
||||||
|
<td class="m-0">CATALOG_CNT:</td>
|
||||||
|
<td class="m-0 font-bold">
|
||||||
|
{{ libinfo[0] ? libinfo[0].CATALOG_CNT : '' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
@@ -616,6 +622,7 @@
|
|||||||
|
|
||||||
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
|
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
|
||||||
<hot-table
|
<hot-table
|
||||||
|
#hotInstance
|
||||||
hotId="hotInstance"
|
hotId="hotInstance"
|
||||||
id="hotTable"
|
id="hotTable"
|
||||||
className="htDark"
|
className="htDark"
|
||||||
|
@@ -3,9 +3,11 @@ import {
|
|||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
|
OnDestroy,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewEncapsulation
|
ViewEncapsulation,
|
||||||
|
ViewChild
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { SasStoreService } from '../services/sas-store.service'
|
import { SasStoreService } from '../services/sas-store.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
@@ -49,10 +51,15 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
|
|||||||
},
|
},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
export class ViewerComponent
|
||||||
|
implements AfterContentInit, AfterViewInit, OnDestroy
|
||||||
|
{
|
||||||
@ViewChildren('queryFilter')
|
@ViewChildren('queryFilter')
|
||||||
queryFilterCompList: QueryList<QueryComponent> = new QueryList()
|
queryFilterCompList: QueryList<QueryComponent> = new QueryList()
|
||||||
|
|
||||||
|
@ViewChild('hotInstance', { static: false })
|
||||||
|
hotInstanceViewChild!: Handsontable
|
||||||
|
|
||||||
public libraries!: Array<any>
|
public libraries!: Array<any>
|
||||||
public librariesPaging: boolean = false
|
public librariesPaging: boolean = false
|
||||||
public librariesSearch: string = ''
|
public librariesSearch: string = ''
|
||||||
@@ -107,11 +114,14 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
public Infinity = Infinity
|
public Infinity = Infinity
|
||||||
|
|
||||||
|
private ariaObserver: MutationObserver | undefined
|
||||||
|
private ariaCheckInterval: any | undefined
|
||||||
|
|
||||||
public hotTable: HotTableInterface = {
|
public hotTable: HotTableInterface = {
|
||||||
data: [],
|
data: [],
|
||||||
colHeaders: [],
|
colHeaders: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
height: '100%',
|
height: 'calc(100vh - 182px)',
|
||||||
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
|
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
|
||||||
settings: {},
|
settings: {},
|
||||||
licenseKey: undefined,
|
licenseKey: undefined,
|
||||||
@@ -530,7 +540,6 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
`#search_${library.LIBRARYREF}`
|
`#search_${library.LIBRARYREF}`
|
||||||
)
|
)
|
||||||
|
|
||||||
this.loggerService.log('[libTreeSearchInput]', libTreeSearchInput)
|
|
||||||
if (libTreeSearchInput) libTreeSearchInput.focus()
|
if (libTreeSearchInput) libTreeSearchInput.focus()
|
||||||
|
|
||||||
if (library && library.libinfo) this.libinfo = library.libinfo
|
if (library && library.libinfo) this.libinfo = library.libinfo
|
||||||
@@ -819,28 +828,35 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
this.versions = res.versions || []
|
this.versions = res.versions || []
|
||||||
this.setDSNote()
|
this.setDSNote()
|
||||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||||
let columns: any[] = []
|
|
||||||
let colArr = []
|
|
||||||
|
|
||||||
for (let key in res.viewdata[0]) {
|
// Initialize columns only if we have data
|
||||||
if (key) {
|
if (res.viewdata && res.viewdata.length > 0) {
|
||||||
colArr.push(key)
|
let columns: any[] = []
|
||||||
|
let colArr = []
|
||||||
|
|
||||||
|
for (let key in res.viewdata[0]) {
|
||||||
|
if (key) {
|
||||||
|
colArr.push(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < colArr.length; index++) {
|
||||||
|
columns.push({ data: colArr[index] })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hotTable.colHeaders = colArr
|
||||||
|
this.hotTable.columns = columns
|
||||||
|
} else {
|
||||||
|
// Set empty arrays if no data
|
||||||
|
this.hotTable.colHeaders = []
|
||||||
|
this.hotTable.columns = []
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let index = 0; index < colArr.length; index++) {
|
// Set cells function
|
||||||
columns.push({ data: colArr[index] })
|
this.hotTable.cells = () => {
|
||||||
|
return { readOnly: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
let cells = function () {
|
|
||||||
let cellProperties = { readOnly: true }
|
|
||||||
return cellProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotTable.colHeaders = colArr
|
|
||||||
this.hotTable.columns = columns
|
|
||||||
this.hotTable.cells = cells
|
|
||||||
|
|
||||||
this.tableFlag = false
|
this.tableFlag = false
|
||||||
let ds = []
|
let ds = []
|
||||||
ds = libDataset.split('.')
|
ds = libDataset.split('.')
|
||||||
@@ -911,6 +927,11 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
//That is intorduced by HOT update
|
//That is intorduced by HOT update
|
||||||
if (!this.noData && !this.noDataReqErr && libDataset) this.setupHot()
|
if (!this.noData && !this.noDataReqErr && libDataset) this.setupHot()
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after data loading
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1500)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is hacky fix for closing the nav drodpwon, when clicking on the handsontable area.
|
* This is hacky fix for closing the nav drodpwon, when clicking on the handsontable area.
|
||||||
* Without it, hot table does not steal focus, so it doesn't close the nav.
|
* Without it, hot table does not steal focus, so it doesn't close the nav.
|
||||||
@@ -1065,7 +1086,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
if (this.hotInstance) {
|
if (this.hotInstance) {
|
||||||
this.hotInstance.updateSettings({
|
this.hotInstance.updateSettings({
|
||||||
height: this.hotTable.height,
|
height: this.hotTable.height,
|
||||||
modifyColWidth: function (width: any, col: any) {
|
modifyColWidth: (width: any, col: any) => {
|
||||||
if (width > 500) return 500
|
if (width > 500) return 500
|
||||||
else return width
|
else return width
|
||||||
},
|
},
|
||||||
@@ -1081,8 +1102,26 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
th.classList.add(globals.handsontable.darkTableHeaderClass)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add hooks for accessibility fixes
|
||||||
|
this.hotInstance.addHook('afterRender', () => {
|
||||||
|
// Fix ARIA accessibility issues after each render
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.hotInstance.addHook('afterChange', () => {
|
||||||
|
// Fix ARIA accessibility issues after any data change
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix ARIA accessibility issues after table setup
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 500)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1148,7 +1187,173 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {
|
||||||
|
// Fix ARIA accessibility issues after table initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fixAriaAccessibility()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// Clean up the MutationObserver
|
||||||
|
if (this.ariaObserver) {
|
||||||
|
this.ariaObserver.disconnect()
|
||||||
|
this.ariaObserver = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the interval
|
||||||
|
if (this.ariaCheckInterval) {
|
||||||
|
clearInterval(this.ariaCheckInterval)
|
||||||
|
this.ariaCheckInterval = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes ARIA accessibility issues in the Handsontable component
|
||||||
|
* This addresses the accessibility report issues with treegrid and presentation roles
|
||||||
|
*/
|
||||||
|
private fixAriaAccessibility() {
|
||||||
|
// Use a more aggressive approach to find and fix all ARIA issues
|
||||||
|
const fixAriaIssues = () => {
|
||||||
|
// Specifically target Handsontable wrapper elements that are causing issues
|
||||||
|
const hotWrappers = document.querySelectorAll(
|
||||||
|
'.ht-wrapper, .wtHolder, [id^="ht_"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
hotWrappers.forEach((wrapper) => {
|
||||||
|
// Remove problematic ARIA attributes from Handsontable wrappers
|
||||||
|
wrapper.removeAttribute('role')
|
||||||
|
wrapper.removeAttribute('aria-rowcount')
|
||||||
|
wrapper.removeAttribute('aria-colcount')
|
||||||
|
wrapper.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find all elements with problematic ARIA roles in the entire document
|
||||||
|
const allTreegridElements = document.querySelectorAll('[role="treegrid"]')
|
||||||
|
const allPresentationElements = document.querySelectorAll(
|
||||||
|
'[role="presentation"]'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fix treegrid role issues - remove them completely as they're causing problems
|
||||||
|
allTreegridElements.forEach((element) => {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
element.removeAttribute('aria-colcount')
|
||||||
|
element.removeAttribute('aria-multiselectable')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fix presentation role issues - remove them if they contain interactive elements
|
||||||
|
allPresentationElements.forEach((element) => {
|
||||||
|
const hasInteractiveChildren =
|
||||||
|
element.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [tabindex], [onclick], [contenteditable]'
|
||||||
|
).length > 0
|
||||||
|
if (hasInteractiveChildren) {
|
||||||
|
element.removeAttribute('role')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also fix any elements with aria-rowcount="-1" which is problematic
|
||||||
|
const negativeRowCountElements = document.querySelectorAll(
|
||||||
|
'[aria-rowcount="-1"]'
|
||||||
|
)
|
||||||
|
negativeRowCountElements.forEach((element) => {
|
||||||
|
element.removeAttribute('aria-rowcount')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure proper table structure
|
||||||
|
const tableElements = document.querySelectorAll('table')
|
||||||
|
tableElements.forEach((table) => {
|
||||||
|
if (!table.getAttribute('role')) {
|
||||||
|
table.setAttribute('role', 'table')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure table headers have proper scope
|
||||||
|
const headerCells = table.querySelectorAll('th')
|
||||||
|
headerCells.forEach((th) => {
|
||||||
|
if (!th.getAttribute('scope')) {
|
||||||
|
th.setAttribute('scope', 'col')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add proper ARIA labels to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll(
|
||||||
|
'button, input, select, textarea, [contenteditable]'
|
||||||
|
)
|
||||||
|
interactiveElements.forEach((element) => {
|
||||||
|
if (
|
||||||
|
!element.getAttribute('aria-label') &&
|
||||||
|
!element.getAttribute('aria-labelledby')
|
||||||
|
) {
|
||||||
|
const textContent = element.textContent?.trim()
|
||||||
|
if (textContent) {
|
||||||
|
element.setAttribute('aria-label', textContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fix immediately
|
||||||
|
fixAriaIssues()
|
||||||
|
|
||||||
|
// Run it again after a short delay to catch any dynamically created elements
|
||||||
|
setTimeout(fixAriaIssues, 100)
|
||||||
|
setTimeout(fixAriaIssues, 500)
|
||||||
|
setTimeout(fixAriaIssues, 1000)
|
||||||
|
setTimeout(fixAriaIssues, 2000)
|
||||||
|
|
||||||
|
// Set up a periodic check to ensure accessibility fixes are maintained
|
||||||
|
if (!this.ariaCheckInterval) {
|
||||||
|
this.ariaCheckInterval = setInterval(fixAriaIssues, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a MutationObserver to continuously monitor for new problematic elements
|
||||||
|
if (!this.ariaObserver) {
|
||||||
|
this.ariaObserver = new MutationObserver((mutations) => {
|
||||||
|
let shouldFix = false
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (
|
||||||
|
mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'role' ||
|
||||||
|
mutation.attributeName === 'aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const element = node as Element
|
||||||
|
if (
|
||||||
|
element.hasAttribute('role') ||
|
||||||
|
element.hasAttribute('aria-rowcount')
|
||||||
|
) {
|
||||||
|
shouldFix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldFix) {
|
||||||
|
setTimeout(fixAriaIssues, 50)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start observing the entire document for changes
|
||||||
|
this.ariaObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [
|
||||||
|
'role',
|
||||||
|
'aria-rowcount',
|
||||||
|
'aria-colcount',
|
||||||
|
'aria-multiselectable'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async ngAfterContentInit() {
|
async ngAfterContentInit() {
|
||||||
if (this.hotTable.data.length > 0) {
|
if (this.hotTable.data.length > 0) {
|
||||||
|
@@ -1042,12 +1042,12 @@ app-approve {
|
|||||||
// HISTORY.COMPONENT
|
// HISTORY.COMPONENT
|
||||||
app-history {
|
app-history {
|
||||||
.rejected {
|
.rejected {
|
||||||
color: #f83126;
|
color: #92201a;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
.accepted {
|
.accepted {
|
||||||
color: #3fc424;
|
color: #105c26;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,6 +1206,8 @@ app-viewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hot-table {
|
hot-table {
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
|
||||||
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
|
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
|
||||||
&.primaryKeyHeaderStyle {
|
&.primaryKeyHeaderStyle {
|
||||||
background-color: #306b00b0 !important;
|
background-color: #306b00b0 !important;
|
||||||
@@ -3442,6 +3444,10 @@ app-approve-details {
|
|||||||
width: 175px
|
width: 175px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#rejectBtn {
|
||||||
|
background-color: #a62f16 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.formatted-values-toggle {
|
.formatted-values-toggle {
|
||||||
min-width: 75px
|
min-width: 75px
|
||||||
}
|
}
|
||||||
@@ -3630,12 +3636,12 @@ app-excel-password-modal {
|
|||||||
// STAGE.COMPONENT
|
// STAGE.COMPONENT
|
||||||
app-stage {
|
app-stage {
|
||||||
.rejected {
|
.rejected {
|
||||||
color: #f83126;
|
color: #92201a;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
.accepted {
|
.accepted {
|
||||||
color: #3fc424;
|
color: #105c26;
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3644,6 +3650,25 @@ app-stage {
|
|||||||
margin-top:10px;
|
margin-top:10px;
|
||||||
color: #007cbb;
|
color: #007cbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accessibility fixes for handsontable focus catchers
|
||||||
|
.htFocusCatcher {
|
||||||
|
// Hide from screen readers but maintain functionality
|
||||||
|
position: absolute !important;
|
||||||
|
left: -9999px !important;
|
||||||
|
width: 1px !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
clip: rect(0, 0, 0, 0) !important;
|
||||||
|
border: 0 !important;
|
||||||
|
margin: -1px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
|
// Ensure it's not focusable by screen readers
|
||||||
|
&:focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body[cds-theme="dark"] {
|
body[cds-theme="dark"] {
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.14.7",
|
"version": "7.0.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.14.7",
|
"version": "7.0.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"@semantic-release/npm": "11.0.0",
|
"@semantic-release/npm": "11.0.0",
|
||||||
"@semantic-release/release-notes-generator": "^11.0.4",
|
"@semantic-release/release-notes-generator": "^11.0.4",
|
||||||
"commit-and-tag-version": "^11.2.2",
|
"commit-and-tag-version": "^11.2.2",
|
||||||
"prettier": "3.2.5"
|
"prettier": "3.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -6690,9 +6690,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.2.5",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.16.1",
|
"version": "7.0.3",
|
||||||
"description": "Data Controller",
|
"description": "Data Controller",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"@semantic-release/release-notes-generator": "^11.0.4",
|
"@semantic-release/release-notes-generator": "^11.0.4",
|
||||||
"commit-and-tag-version": "^11.2.2",
|
"commit-and-tag-version": "^11.2.2",
|
||||||
"prettier": "3.2.5"
|
"prettier": "3.6.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "cd client && npm i && cd ../sas && npm i",
|
"install": "cd client && npm i && cd ../sas && npm i",
|
||||||
@@ -18,8 +18,11 @@
|
|||||||
"release": "commit-and-tag-version",
|
"release": "commit-and-tag-version",
|
||||||
"lint": "npm run lint:fix",
|
"lint": "npm run lint:fix",
|
||||||
"lint:fix": "npx prettier --write \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
"lint:fix": "npx prettier --write \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
||||||
|
"lint:fix:silent": "npx prettier --log-level silent --write \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
||||||
"lint:check": "npx prettier --check \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
"lint:check": "npx prettier --check \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
||||||
"jo": "echo"
|
"lint:check:silent": "npx prettier --log-level silent --check \"client/{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\" \"client/cypress/integration/*.tests.ts\"",
|
||||||
|
"jo": "echo",
|
||||||
|
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks && git config core.autocrlf false || true"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
32
sas/package-lock.json
generated
32
sas/package-lock.json
generated
@@ -6,8 +6,8 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "dc-sas",
|
"name": "dc-sas",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.12.7",
|
"@sasjs/cli": "^4.12.9",
|
||||||
"@sasjs/core": "^4.58.2"
|
"@sasjs/core": "^4.59.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@coolaj86/urequest": {
|
"node_modules/@coolaj86/urequest": {
|
||||||
@@ -30,9 +30,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/adapter": {
|
"node_modules/@sasjs/adapter": {
|
||||||
"version": "4.11.3",
|
"version": "4.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.12.1.tgz",
|
||||||
"integrity": "sha512-KF6G4vzs4l4efjpCD02og3kB44uFfJ1u2UWu749VdHtLKNN9l+PO26/moR+YAmRmmz2I9sC3X09fZE1nlN6zgw==",
|
"integrity": "sha512-0217oZIkrecOyQygRe6Azgc1C4TIcjB5noi15fU4Rd5GsfDpleKVfQQdzYZ7im/vesWlLIVl/yUT67MMaQe0ew==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -45,14 +45,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/cli": {
|
"node_modules/@sasjs/cli": {
|
||||||
"version": "4.12.7",
|
"version": "4.12.9",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.7.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.9.tgz",
|
||||||
"integrity": "sha512-KcXSR+3dRgINOLiN+7oJbzWsNQu7qm1YQ7eaVqiHTZI429BSgZez9+7p1bq09R4otHN8IzMAgLP9se/r9p9yJA==",
|
"integrity": "sha512-SxwBoSus5vBc2jysShPazCKGQfFzPlkVmsLDckhvXO7LVU+F0+OVjloE+l43gom5uPpnsLxKCWu8GuoshoBHjA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "4.11.3",
|
"@sasjs/adapter": "4.12.1",
|
||||||
"@sasjs/core": "4.58.1",
|
"@sasjs/core": "4.59.1",
|
||||||
"@sasjs/lint": "2.4.3",
|
"@sasjs/lint": "2.4.3",
|
||||||
"@sasjs/utils": "3.5.2",
|
"@sasjs/utils": "3.5.2",
|
||||||
"adm-zip": "0.5.10",
|
"adm-zip": "0.5.10",
|
||||||
@@ -77,15 +77,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
|
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
|
||||||
"version": "4.58.1",
|
"version": "4.59.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.58.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.1.tgz",
|
||||||
"integrity": "sha512-Qp6KAtp1VZcmN5HLGSIUE9H41qpFuihWLbjNygOYp+NRs/Y8VagpHrYeyIQbh3cSgchiJEMXudLql8hoU06wpg==",
|
"integrity": "sha512-52GNI4nIll5YivI8uobWrucE6TkHcTjcbKTr/YPC9eTauC4sh0V0MptebfAJ5E6vE5P2WevNZGr42KdDpckLpg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/core": {
|
"node_modules/@sasjs/core": {
|
||||||
"version": "4.58.2",
|
"version": "4.59.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.58.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.5.tgz",
|
||||||
"integrity": "sha512-P/DMCHfFrZT+50DIT7CiYBSjxOo5m0AHBLNKHGFOMfQnEymOKekQPk2Xzw5wkQyg8gjp2yBKhRwhpni5rvJFgQ==",
|
"integrity": "sha512-PAPinHjuFi3P/MxkZHGSF0iLWoBFnpxon7/SshHPcBRmSQwmFOvh9X8xdE8ZClqY9XDOwFZ6sqeOQ7GYZliLfw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/lint": {
|
"node_modules/@sasjs/lint": {
|
||||||
|
@@ -22,13 +22,13 @@
|
|||||||
"serverdata": "sasjs request services/admin/makedata -d deploy/makeDataServer.json -l sasjsresults/makedata_server.log -o sasjsresults/makedata_server.json -t server",
|
"serverdata": "sasjs request services/admin/makedata -d deploy/makeDataServer.json -l sasjsresults/makedata_server.log -o sasjsresults/makedata_server.json -t server",
|
||||||
"viya": "npm run viyacbd && sasjs test",
|
"viya": "npm run viyacbd && sasjs test",
|
||||||
"viyacbd": "sasjs folder delete /Public/app/viya && sasjs cbd -t viya && npm run viyadata",
|
"viyacbd": "sasjs folder delete /Public/app/viya && sasjs cbd -t viya && npm run viyadata",
|
||||||
"viyadata": "sasjs request services/admin/makedata -d deploy/makeDataViya.json -l sasjsresults/makedata.log -o sasjsresults/output.json",
|
"viyadata": "sasjs request services/admin/makedata -d deploy/makeDataViya.json -l sasjsresults/makedata.log -o sasjsresults/output.json -t viya",
|
||||||
"v4data": "sasjs request services/admin/makedata -d deploy/makeDataV4.json -l sasjsresults/makedatav4.log -o sasjsresults/outputv4.json -t v4",
|
"v4data": "sasjs request services/admin/makedata -d deploy/makeDataV4.json -l sasjsresults/makedatav4.log -o sasjsresults/outputv4.json -t v4",
|
||||||
"sasdocs": "sasjs doc && ./sasjs/utils/deploydocs.sh"
|
"sasdocs": "sasjs doc && ./sasjs/utils/deploydocs.sh"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.12.7",
|
"@sasjs/cli": "^4.12.9",
|
||||||
"@sasjs/core": "^4.58.2"
|
"@sasjs/core": "^4.59.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
sas/sasjs/db/datactrl/mpe_datacatalog_cats.ddl
Normal file
17
sas/sasjs/db/datactrl/mpe_datacatalog_cats.ddl
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
@file mpe_datacatalog_CATS.ddl
|
||||||
|
@brief ddl file
|
||||||
|
@details
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
create table &curlib..mpe_datacatalog_CATS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
constraint pk_mpe_datacatalog_CATS
|
||||||
|
primary key(libref,memname,tx_to));
|
21
sas/sasjs/db/datactrl/mpe_datacatalog_objs.ddl
Normal file
21
sas/sasjs/db/datactrl/mpe_datacatalog_objs.ddl
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
@file mpe_datacatalog_CATS.ddl
|
||||||
|
@brief ddl file
|
||||||
|
@details
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
create table &curlib..mpe_datacatalog_OBJS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
objname char(32) label='Object Name',
|
||||||
|
objtype char(8) label='Object Type',
|
||||||
|
objdesc char(256) label='Object Description',
|
||||||
|
alias char(32) label='Object Alias',
|
||||||
|
constraint pk_mpe_datacatalog_OBJS
|
||||||
|
primary key(libref,memname,objname,objtype,tx_to));
|
20
sas/sasjs/db/datactrl/mpe_datastatus_cats.ddl
Normal file
20
sas/sasjs/db/datactrl/mpe_datastatus_cats.ddl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
@file mpe_datacatalog_CATS.ddl
|
||||||
|
@brief ddl file
|
||||||
|
@details
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
create table &curlib..mpe_datastatus_CATS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
nobjs num label='Number of objects',
|
||||||
|
created num format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified',
|
||||||
|
constraint pk_mpe_datastatus_CATS
|
||||||
|
primary key(libref,memname,tx_to));
|
@@ -14,5 +14,6 @@ create table &curlib..mpe_datastatus_libs(
|
|||||||
libref char(8) label='Library Name',
|
libref char(8) label='Library Name',
|
||||||
libsize num format=SIZEKMG. label='Size of file',
|
libsize num format=SIZEKMG. label='Size of file',
|
||||||
table_cnt num label='Number of Tables',
|
table_cnt num label='Number of Tables',
|
||||||
|
catalog_cnt num label='Number of Catalogs',
|
||||||
constraint pk_mpe_datastatus_libs
|
constraint pk_mpe_datastatus_libs
|
||||||
primary key(libref,tx_to));
|
primary key(libref,tx_to));
|
||||||
|
22
sas/sasjs/db/datactrl/mpe_datastatus_objs.ddl
Normal file
22
sas/sasjs/db/datactrl/mpe_datastatus_objs.ddl
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
@file mpe_datacatalog_CATS.ddl
|
||||||
|
@brief ddl file
|
||||||
|
@details
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
create table &curlib..mpe_datastatus_OBJS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
objname char(32) label='Object Name',
|
||||||
|
objtype char(8) label='Object Type',
|
||||||
|
created num format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified',
|
||||||
|
level num label='Library Concatenation Level',
|
||||||
|
constraint pk_mpe_datastatus_OBJS
|
||||||
|
primary key(libref,memname,objname,objtype,tx_to));
|
@@ -14,8 +14,8 @@ create table &curlib..mpe_datastatus_tabs(
|
|||||||
libref char(8) label='Library Name',
|
libref char(8) label='Library Name',
|
||||||
dsn char(64) label='Member Name',
|
dsn char(64) label='Member Name',
|
||||||
filesize num format=SIZEKMG. label='Size of file',
|
filesize num format=SIZEKMG. label='Size of file',
|
||||||
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
|
crdate num format=DATETIME. label='Date Created',
|
||||||
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
|
modate num format=DATETIME. label='Date Modified',
|
||||||
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
|
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
|
||||||
constraint pk_mpe_datastatus_tabs
|
constraint pk_mpe_datastatus_tabs
|
||||||
primary key(libref,dsn,tx_to));
|
primary key(libref,dsn,tx_to));
|
||||||
|
@@ -29,7 +29,7 @@ create table dc.mpe_datacatalog_TABS(
|
|||||||
nvar num label='Number of Variables',
|
nvar num label='Number of Variables',
|
||||||
compress char(8) label='Compression Routine',
|
compress char(8) label='Compression Routine',
|
||||||
pk_fields char(512)
|
pk_fields char(512)
|
||||||
label='Primary Key Fields (identified by being in a constraint that is both Unique and Not Null)',
|
label='Primary Key Fields (in a constraint that is both Unique and Not Null)',
|
||||||
constraint pk
|
constraint pk
|
||||||
primary key(libref,dsn,tx_to));
|
primary key(libref,dsn,tx_to));
|
||||||
|
|
||||||
@@ -67,8 +67,8 @@ create table dc.mpe_datacatalog_vars(
|
|||||||
libref char(8) label='Library Name',
|
libref char(8) label='Library Name',
|
||||||
dsn char(64) label='Member Name',
|
dsn char(64) label='Member Name',
|
||||||
filesize num format=SIZEKMG. label='Size of file',
|
filesize num format=SIZEKMG. label='Size of file',
|
||||||
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
|
crdate num format=DATETIME. label='Date Created',
|
||||||
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
|
modate num format=DATETIME. label='Date Modified',
|
||||||
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
|
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
|
||||||
constraint pk
|
constraint pk
|
||||||
primary key(libref,dsn,tx_to));
|
primary key(libref,dsn,tx_to));
|
71
sas/sasjs/db/migrations/20250611_v7.0_release.sas
Normal file
71
sas/sasjs/db/migrations/20250611_v7.0_release.sas
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief migration script to move from v6.8.2 to v7.0 of data controller
|
||||||
|
|
||||||
|
BREAKING CHANGE - 1 new column and 4 additional tables for capturing catalogs
|
||||||
|
|
||||||
|
Be sure to run this using the correct system account
|
||||||
|
(eg the regular DC account)
|
||||||
|
|
||||||
|
On SAS 9 you may wish to run proc metalib or refresh in DI Studio afterwards
|
||||||
|
to see the new tables in the VIEW page
|
||||||
|
|
||||||
|
proc metalib;
|
||||||
|
omr (library="&DC_LIBNAME");
|
||||||
|
folder="&root/data";
|
||||||
|
update_rule=(delete);
|
||||||
|
run;
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%let dclib=YOURDCLIB;
|
||||||
|
|
||||||
|
libname &dclib "/YOUR/DATACONTROLLER/LIBRARY/PATH";
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.BACKUP as select * from &dclib..mpe_datastatus_libs;
|
||||||
|
alter table &dclib..mpe_datastatus_libs add catalog_cnt num;
|
||||||
|
|
||||||
|
create table &dclib..mpe_datacatalog_CATS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
constraint pk_mpe_datacatalog_CATS
|
||||||
|
primary key(libref,memname,tx_to));
|
||||||
|
|
||||||
|
create table &dclib..mpe_datacatalog_OBJS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
objname char(32) label='Object Name',
|
||||||
|
objtype char(8) label='Object Type',
|
||||||
|
objdesc char(256) label='Object Description',
|
||||||
|
alias char(32) label='Object Alias',
|
||||||
|
constraint pk_mpe_datacatalog_OBJS
|
||||||
|
primary key(libref,memname,objname,objtype,tx_to));
|
||||||
|
|
||||||
|
create table &dclib..mpe_datastatus_CATS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
nobjs num label='Number of objects',
|
||||||
|
created num format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified',
|
||||||
|
constraint pk_mpe_datastatus_CATS
|
||||||
|
primary key(libref,memname,tx_to));
|
||||||
|
|
||||||
|
create table &dclib..mpe_datastatus_OBJS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
objname char(32) label='Object Name',
|
||||||
|
objtype char(8) label='Object Type',
|
||||||
|
created num format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified',
|
||||||
|
level num label='Library Concatenation Level',
|
||||||
|
constraint pk_mpe_datastatus_OBJS
|
||||||
|
primary key(libref,memname,objname,objtype,tx_to));
|
@@ -14,14 +14,31 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mpe_dsmeta(libds, outds=dsmeta);
|
%macro mpe_dsmeta(libds, outds=dsmeta);
|
||||||
%local ddsd ddld notes lenstmt;
|
%local ddsd ddld notes lenstmt memname;
|
||||||
%let lenstmt=length ods_table $18 name $100 value $1000;
|
%let lenstmt=length ods_table $18 name $100 value $1000;
|
||||||
%let libds=%upcase(&libds);
|
%let libds=%upcase(&libds);
|
||||||
%mp_dsmeta(&libds, outds=&outds)
|
|
||||||
|
%if "%scan(&libds,2,-)" ne "FC" %then %do;
|
||||||
|
%let memname=%scan(&libds,2,.);
|
||||||
|
%mp_dsmeta(&libds, outds=&outds)
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%let memname=%scan(&libds,2,.-);
|
||||||
|
data &outds;
|
||||||
|
&lenstmt;
|
||||||
|
set sashelp.vcatalg;
|
||||||
|
ods_table=cats(objtype);
|
||||||
|
name=cats(objname);
|
||||||
|
value=catx(' ',objdesc,'(modified:',put(modified,datetime19.),')');
|
||||||
|
where libname="%scan(&libds,1,.)" and memname="&memname";
|
||||||
|
keep ods_table name value;
|
||||||
|
run;
|
||||||
|
proc sort; by ods_table name;run;
|
||||||
|
%end;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
set &mpelib..mpe_datadictionary;
|
set &mpelib..mpe_datadictionary;
|
||||||
where &dc_dttmtfmt < tx_to & dd_source=%upcase("&libds") & dd_type='TABLE';
|
where &dc_dttmtfmt < tx_to & dd_source="&memname" & dd_type='TABLE';
|
||||||
call symputx('ddsd',dd_shortdesc,'l');
|
call symputx('ddsd',dd_shortdesc,'l');
|
||||||
call symputx('ddld',dd_longdesc,'l');
|
call symputx('ddld',dd_longdesc,'l');
|
||||||
run;
|
run;
|
||||||
|
@@ -34,4 +34,15 @@ run;
|
|||||||
outds=work.test_results
|
outds=work.test_results
|
||||||
)
|
)
|
||||||
|
|
||||||
|
%mpe_dsmeta(FMTONLY.DCFMTS-FC,outds=test2)
|
||||||
|
data work.test2;
|
||||||
|
set work.test2;
|
||||||
|
putlog (_all_)(=);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.test2,
|
||||||
|
desc=Test 2 - records returned,
|
||||||
|
test=EQUALS 5,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -106,6 +106,19 @@ proc datasets lib=&lib noprint;
|
|||||||
/nomiss unique;
|
/nomiss unique;
|
||||||
quit;
|
quit;
|
||||||
proc sql;
|
proc sql;
|
||||||
|
create table &lib..mpe_datacatalog_CATS(
|
||||||
|
TX_FROM float ¬null format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name'
|
||||||
|
);quit;
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify mpe_datacatalog_CATS;
|
||||||
|
index create
|
||||||
|
pk_mpe_datacatalog_CATS=(tx_to libref memname)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
proc sql;
|
||||||
create table &lib..mpe_datacatalog_libs(
|
create table &lib..mpe_datacatalog_libs(
|
||||||
TX_FROM num ¬null format=datetime19.3,
|
TX_FROM num ¬null format=datetime19.3,
|
||||||
TX_TO num ¬null format=datetime19.3,
|
TX_TO num ¬null format=datetime19.3,
|
||||||
@@ -125,6 +138,23 @@ proc datasets lib=&lib noprint;
|
|||||||
/nomiss unique;
|
/nomiss unique;
|
||||||
quit;
|
quit;
|
||||||
proc sql;
|
proc sql;
|
||||||
|
create table &lib..mpe_datacatalog_OBJS(
|
||||||
|
TX_FROM num ¬null format=datetime19.,
|
||||||
|
TX_TO num ¬null format=datetime19.,
|
||||||
|
libref char(8) ¬null label='Library Name',
|
||||||
|
memname char(64) ¬null label='Member Name',
|
||||||
|
objname char(32) ¬null label='Object Name',
|
||||||
|
objtype char(8) ¬null label='Object Type',
|
||||||
|
objdesc char(256) label='Object Description',
|
||||||
|
alias char(32) label='Object Alias'
|
||||||
|
);quit;
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify mpe_datacatalog_OBJS;
|
||||||
|
index create
|
||||||
|
pk_mpe_datacatalog_OBJS=(libref memname objname objtype tx_to)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
proc sql;
|
||||||
create table &lib..mpe_datacatalog_TABS(
|
create table &lib..mpe_datacatalog_TABS(
|
||||||
TX_FROM num ¬null format=datetime19.3,
|
TX_FROM num ¬null format=datetime19.3,
|
||||||
TX_TO num ¬null format=datetime19.3,
|
TX_TO num ¬null format=datetime19.3,
|
||||||
@@ -137,7 +167,7 @@ create table &lib..mpe_datacatalog_TABS(
|
|||||||
nvar num label='Number of Variables',
|
nvar num label='Number of Variables',
|
||||||
compress char(8) label='Compression Routine',
|
compress char(8) label='Compression Routine',
|
||||||
pk_fields char(512)
|
pk_fields char(512)
|
||||||
label='Primary Key Fields (identified by being in a constraint that is both Unique and Not Null)'
|
label='Primary Key Fields - in a constraint being both Unique and Not Null'
|
||||||
);quit;
|
);quit;
|
||||||
proc datasets lib=&lib noprint;
|
proc datasets lib=&lib noprint;
|
||||||
modify mpe_datacatalog_TABS;
|
modify mpe_datacatalog_TABS;
|
||||||
@@ -169,12 +199,29 @@ proc datasets lib=&lib noprint;
|
|||||||
/nomiss unique;
|
/nomiss unique;
|
||||||
quit;
|
quit;
|
||||||
proc sql;
|
proc sql;
|
||||||
|
create table &lib..mpe_datastatus_CATS(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
TX_TO float format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
nobjs num ¬null label='Number of objects',
|
||||||
|
created num ¬null format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified'
|
||||||
|
);quit;
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify mpe_datastatus_CATS;
|
||||||
|
index create
|
||||||
|
pk_mpe_datastatus_cats=(libref memname tx_to)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
proc sql;
|
||||||
create table &lib..mpe_datastatus_libs(
|
create table &lib..mpe_datastatus_libs(
|
||||||
TX_FROM num ¬null format=datetime19.3,
|
TX_FROM num ¬null format=datetime19.3,
|
||||||
TX_TO num ¬null format=datetime19.3,
|
TX_TO num ¬null format=datetime19.3,
|
||||||
libref char(8) label='Library Name',
|
libref char(8) label='Library Name',
|
||||||
libsize num format=SIZEKMG. label='Size of library',
|
libsize num format=SIZEKMG. label='Size of library',
|
||||||
table_cnt num label='Number of Tables'
|
table_cnt num label='Number of Tables',
|
||||||
|
catalog_cnt num label='Number of Catalogs'
|
||||||
);quit;
|
);quit;
|
||||||
proc datasets lib=&lib noprint;
|
proc datasets lib=&lib noprint;
|
||||||
modify mpe_datastatus_libs;
|
modify mpe_datastatus_libs;
|
||||||
@@ -183,14 +230,32 @@ proc datasets lib=&lib noprint;
|
|||||||
/nomiss unique;
|
/nomiss unique;
|
||||||
quit;
|
quit;
|
||||||
proc sql;
|
proc sql;
|
||||||
|
create table &lib..mpe_datastatus_OBJS(
|
||||||
|
TX_FROM float ¬null format=datetime19.,
|
||||||
|
TX_TO float ¬null format=datetime19.,
|
||||||
|
libref char(8) label='Library Name',
|
||||||
|
memname char(64) label='Member Name',
|
||||||
|
objname char(32) label='Object Name',
|
||||||
|
objtype char(8) label='Object Type',
|
||||||
|
created num ¬null format=DATETIME. label='Date Created',
|
||||||
|
modified num format=DATETIME. label='Date Modified',
|
||||||
|
level num label='Library Concatenation Level'
|
||||||
|
);quit;
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify mpe_datastatus_OBJS;
|
||||||
|
index create
|
||||||
|
pk_mpe_datastatus_OBJS=(libref memname objname objtype tx_to)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
proc sql;
|
||||||
create table &lib..mpe_datastatus_tabs(
|
create table &lib..mpe_datastatus_tabs(
|
||||||
TX_FROM num ¬null format=datetime19.3,
|
TX_FROM num ¬null format=datetime19.3,
|
||||||
TX_TO num ¬null format=datetime19.3,
|
TX_TO num ¬null format=datetime19.3,
|
||||||
libref char(8) label='Library Name',
|
libref char(8) label='Library Name',
|
||||||
dsn char(64) label='Member Name',
|
dsn char(64) label='Member Name',
|
||||||
filesize num format=SIZEKMG. label='Size of file',
|
filesize num format=SIZEKMG. label='Size of file',
|
||||||
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
|
crdate num format=DATETIME. label='Date Created',
|
||||||
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
|
modate num format=DATETIME. label='Date Modified',
|
||||||
nobs num label='Number of Physical (Actual, inc. deleted) Observations'
|
nobs num label='Number of Physical (Actual, inc. deleted) Observations'
|
||||||
);quit;
|
);quit;
|
||||||
proc datasets lib=&lib noprint;
|
proc datasets lib=&lib noprint;
|
||||||
|
26
sas/sasjs/macros/mpe_makedatamodel.test.sas
Normal file
26
sas/sasjs/macros/mpe_makedatamodel.test.sas
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mpe_refreshtables macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mpe_makedatamodel.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
|
4GL Apps Ltd.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%mpe_makedatamodel(lib=WORK)
|
||||||
|
%mp_assertscope(COMPARE,
|
||||||
|
desc=Checking macro variables against previous snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc = 0),
|
||||||
|
desc=Checking error condition
|
||||||
|
)
|
140
sas/sasjs/macros/mpe_refreshcatalogs.sas
Normal file
140
sas/sasjs/macros/mpe_refreshcatalogs.sas
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
@file mpe_refreshtables.sas
|
||||||
|
@brief Refreshes the data catalog
|
||||||
|
@details Assumes library is already assigned.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mpe_refreshcatalogs(sashelp)
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li bitemporal_dataloader.sas
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mpe_refreshcatalogs(lib,cat=#all);
|
||||||
|
%let lib=%upcase(&lib);
|
||||||
|
%let cat=%upcase(&cat);
|
||||||
|
|
||||||
|
%put running &sysmacroname &lib for &cat;
|
||||||
|
proc sql;
|
||||||
|
create table work.catdata as
|
||||||
|
select libname as libref,
|
||||||
|
memname,
|
||||||
|
objname,
|
||||||
|
objtype,
|
||||||
|
objdesc,
|
||||||
|
created,
|
||||||
|
modified,
|
||||||
|
alias,
|
||||||
|
level
|
||||||
|
from dictionary.catalogs
|
||||||
|
where upcase(libname)="&lib"
|
||||||
|
%if &cat ne #ALL %then %do;
|
||||||
|
and upcase(memname)="&cat"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=&_program
|
||||||
|
,msg=%str(syscc=&syscc afer &lib objects extraction)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* load mpe_datacatalog_CATS */
|
||||||
|
proc sql;
|
||||||
|
create table datacats as select distinct libref,memname from catdata;
|
||||||
|
|
||||||
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
|
,base_dsn=mpe_datacatalog_CATS
|
||||||
|
,append_dsn=datacats
|
||||||
|
,PK=LIBREF MEMNAME
|
||||||
|
,etlsource=&sysmacroname
|
||||||
|
,loadtype=TXTEMPORAL
|
||||||
|
,tech_from=TX_FROM
|
||||||
|
,tech_to=TX_TO
|
||||||
|
%if &cat = #ALL %then %do;
|
||||||
|
,close_vars=LIBREF
|
||||||
|
%end;
|
||||||
|
,dclib=&mpelib
|
||||||
|
)
|
||||||
|
|
||||||
|
/* load mpe_datacatalog_objsS */
|
||||||
|
proc sql;
|
||||||
|
create table dataobjs as
|
||||||
|
select distinct libref,
|
||||||
|
memname,
|
||||||
|
objname,
|
||||||
|
objtype,
|
||||||
|
objdesc,
|
||||||
|
alias
|
||||||
|
from catdata;
|
||||||
|
quit;
|
||||||
|
|
||||||
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
|
,base_dsn=mpe_datacatalog_OBJS
|
||||||
|
,append_dsn=dataobjs
|
||||||
|
,PK=LIBREF MEMNAME OBJNAME OBJTYPE
|
||||||
|
,etlsource=&sysmacroname
|
||||||
|
,loadtype=TXTEMPORAL
|
||||||
|
,tech_from=TX_FROM
|
||||||
|
,tech_to=TX_TO
|
||||||
|
%if &cat = #ALL %then %do;
|
||||||
|
,close_vars=LIBREF MEMNAME
|
||||||
|
%end;
|
||||||
|
,dclib=&mpelib
|
||||||
|
)
|
||||||
|
|
||||||
|
%put load mpe_datastatus_OBJS;
|
||||||
|
proc sql;
|
||||||
|
create table statusobjs as
|
||||||
|
select distinct libref,
|
||||||
|
memname,
|
||||||
|
objname,
|
||||||
|
objtype,
|
||||||
|
created,
|
||||||
|
modified,
|
||||||
|
level
|
||||||
|
from catdata;
|
||||||
|
|
||||||
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
|
,base_dsn=mpe_datastatus_OBJS
|
||||||
|
,append_dsn=statusobjs
|
||||||
|
,PK=LIBREF MEMNAME OBJNAME OBJTYPE
|
||||||
|
,etlsource=&sysmacroname
|
||||||
|
,loadtype=TXTEMPORAL
|
||||||
|
,tech_from=TX_FROM
|
||||||
|
,tech_to=TX_TO
|
||||||
|
%if &cat = #ALL %then %do;
|
||||||
|
,close_vars=LIBREF MEMNAME
|
||||||
|
%end;
|
||||||
|
,dclib=&mpelib
|
||||||
|
)
|
||||||
|
|
||||||
|
%put load mpe_datastatus_cats;
|
||||||
|
proc sql;
|
||||||
|
create table statuscats as
|
||||||
|
select libref,
|
||||||
|
memname,
|
||||||
|
count(*) as nobjs,
|
||||||
|
min(created) as created,
|
||||||
|
max(modified) as modified
|
||||||
|
from catdata
|
||||||
|
group by 1,2;
|
||||||
|
|
||||||
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
|
,base_dsn=mpe_datastatus_cats
|
||||||
|
,append_dsn=statuscats
|
||||||
|
,PK=LIBREF MEMNAME
|
||||||
|
,etlsource=&sysmacroname
|
||||||
|
,loadtype=TXTEMPORAL
|
||||||
|
,tech_from=TX_FROM
|
||||||
|
,tech_to=TX_TO
|
||||||
|
%if &cat = #ALL %then %do;
|
||||||
|
,close_vars=LIBREF
|
||||||
|
%end;
|
||||||
|
,dclib=&mpelib
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
%mend mpe_refreshcatalogs;
|
38
sas/sasjs/macros/mpe_refreshcatalogs.test.sas
Normal file
38
sas/sasjs/macros/mpe_refreshcatalogs.test.sas
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mpe_refreshcatalogs macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mpe_refreshcatalogs.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
@li mp_ds2md.sas
|
||||||
|
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
|
4GL Apps Ltd.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%mpe_refreshcatalogs(FMTONLY)
|
||||||
|
%mp_assertscope(COMPARE,
|
||||||
|
desc=Checking macro variables against previous snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
/* make sure that the process picks up the catalog */
|
||||||
|
proc sql noprint;
|
||||||
|
create table work.test1 as
|
||||||
|
select *
|
||||||
|
from &mpelib..mpe_datacatalog_cats(where=(&dc_dttmtfmt. lt tx_to))
|
||||||
|
where libref="FMTONLY";
|
||||||
|
%let test1=0;
|
||||||
|
select count(*) into: test1 from work.test1;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test1>0),
|
||||||
|
desc=Checking fmtonly.dcfmts was picked up
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_ds2md(work.test1)
|
@@ -48,11 +48,6 @@ create table cols as
|
|||||||
,msg=%str(syscc=&syscc afer &lib cols extraction)
|
,msg=%str(syscc=&syscc afer &lib cols extraction)
|
||||||
)
|
)
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
|
||||||
,mac=&_program
|
|
||||||
,msg=%str(syscc=&syscc afer &lib indexes extraction)
|
|
||||||
)
|
|
||||||
|
|
||||||
%if &engine=SQLSVR %then %do;
|
%if &engine=SQLSVR %then %do;
|
||||||
proc sql;
|
proc sql;
|
||||||
connect using &lib;
|
connect using &lib;
|
||||||
@@ -175,6 +170,11 @@ create table cols as
|
|||||||
|
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=&_program
|
||||||
|
,msg=%str(syscc=&syscc afer &lib indexes extraction)
|
||||||
|
)
|
||||||
|
|
||||||
/* load columns */
|
/* load columns */
|
||||||
%bitemporal_dataloader(base_lib=&mpelib
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
,base_dsn=mpe_datacatalog_vars
|
,base_dsn=mpe_datacatalog_vars
|
||||||
@@ -295,7 +295,7 @@ proc sql;
|
|||||||
%if &ds ne #ALL %then %do;
|
%if &ds ne #ALL %then %do;
|
||||||
and upcase(memname)="&ds"
|
and upcase(memname)="&ds"
|
||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%bitemporal_dataloader(base_lib=&mpelib
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
@@ -314,12 +314,32 @@ proc sql;
|
|||||||
|
|
||||||
%if &ds = #ALL %then %do;
|
%if &ds = #ALL %then %do;
|
||||||
proc sql;
|
proc sql;
|
||||||
create table statuslibs as select
|
create table work.sumcat as
|
||||||
|
select libname as libref,
|
||||||
|
count(distinct memname) as catalog_cnt
|
||||||
|
from dictionary.catalogs
|
||||||
|
where upcase(libname)="&lib"
|
||||||
|
group by 1;
|
||||||
|
create table work.sumdsn as select
|
||||||
libref
|
libref
|
||||||
,sum(filesize) as libsize
|
,sum(filesize) as libsize
|
||||||
,count(*) as table_cnt
|
,count(*) as table_cnt
|
||||||
from statustabs
|
from statustabs
|
||||||
group by 1;
|
group by 1;
|
||||||
|
create table work.libs as
|
||||||
|
select libref from work.sumcat
|
||||||
|
union
|
||||||
|
select libref from work.sumdsn;
|
||||||
|
create table work.statuslibs as
|
||||||
|
select a.libref,
|
||||||
|
b.libsize,
|
||||||
|
b.table_cnt,
|
||||||
|
c.catalog_cnt
|
||||||
|
from work.libs a
|
||||||
|
left join work.sumdsn b
|
||||||
|
on a.libref=b.libref
|
||||||
|
left join work.sumcat c
|
||||||
|
on a.libref=c.libref;
|
||||||
|
|
||||||
%bitemporal_dataloader(base_lib=&mpelib
|
%bitemporal_dataloader(base_lib=&mpelib
|
||||||
,base_dsn=mpe_datastatus_libs
|
,base_dsn=mpe_datastatus_libs
|
||||||
|
50
sas/sasjs/macros/mpe_refreshtables.test.sas
Normal file
50
sas/sasjs/macros/mpe_refreshtables.test.sas
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mpe_refreshtables macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mpe_refreshtables.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
|
4GL Apps Ltd.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%mpe_refreshtables(FMTONLY)
|
||||||
|
%mp_assertscope(COMPARE,
|
||||||
|
desc=Checking macro variables against previous snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
/* make sure that the process picks up a library that contains only a single
|
||||||
|
catalog */
|
||||||
|
proc sql;
|
||||||
|
create table work.libinfo as
|
||||||
|
select a.engine,
|
||||||
|
a.libname,
|
||||||
|
a.paths,
|
||||||
|
a.perms,
|
||||||
|
a.owners,
|
||||||
|
a.schemas,
|
||||||
|
a.libid,
|
||||||
|
b.libsize,
|
||||||
|
b.table_cnt,
|
||||||
|
b.catalog_cnt
|
||||||
|
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
||||||
|
left join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
||||||
|
on a.libref=b.libref
|
||||||
|
where a.libref="&libref";
|
||||||
|
|
||||||
|
%let test1=0;
|
||||||
|
data _null_;
|
||||||
|
set work.libinfo;
|
||||||
|
call symputx('test1',table_cnt);
|
||||||
|
run;
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test1>0),
|
||||||
|
desc=Checking fmtonly.dcfmts was picked up
|
||||||
|
)
|
@@ -94,38 +94,41 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "viya",
|
"name": "viya",
|
||||||
"serverUrl": "https://sas.4gl.io",
|
"serverUrl": "https://viya-f0g8ht62vq.engage.sas.com",
|
||||||
"serverType": "SASVIYA",
|
"serverType": "SASVIYA",
|
||||||
"httpsAgentOptions": {
|
"httpsAgentOptions": {
|
||||||
"allowInsecureRequests": false
|
"allowInsecureRequests": false
|
||||||
},
|
},
|
||||||
"appLoc": "/Public/app/dcplaceholder",
|
"appLoc": "/Public/app/dcplaceholder",
|
||||||
|
"deployConfig": {
|
||||||
|
"deployServicePack": true,
|
||||||
|
"deployScripts": [
|
||||||
|
"sasjs/utils/viyadeploy.sh"
|
||||||
|
]
|
||||||
|
},
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"sasjs/targets/viya/macros_viya"
|
"sasjs/targets/viya/macros_viya"
|
||||||
],
|
],
|
||||||
"programFolders": [
|
"programFolders": [
|
||||||
"sasjs/db/datactrl"
|
"sasjs/db/datactrl"
|
||||||
],
|
],
|
||||||
"binaryFolders": [],
|
|
||||||
"buildConfig": {
|
"buildConfig": {
|
||||||
"initProgram": "sasjs/utils/buildinitviya.sas",
|
"initProgram": "sasjs/utils/buildinitviya.sas",
|
||||||
"buildResultsFolder": "sasjsresults",
|
"buildResultsFolder": "sasjsresults",
|
||||||
"buildOutputFolder": "sasjsbuild",
|
"buildOutputFolder": "sasjsbuild",
|
||||||
"buildOutputFileName": "viya.sas"
|
"buildOutputFileName": "viya.sas",
|
||||||
|
"termProgram": "",
|
||||||
|
"macroVars": {}
|
||||||
},
|
},
|
||||||
"serviceConfig": {
|
"serviceConfig": {
|
||||||
"initProgram": "sasjs/utils/serviceinitviya.sas",
|
|
||||||
"serviceFolders": [
|
"serviceFolders": [
|
||||||
"sasjs/targets/viya/services_viya/viya_users",
|
"sasjs/targets/viya/services_viya/viya_users",
|
||||||
"sasjs/targets/viya/services_viya/admin",
|
"sasjs/targets/viya/services_viya/admin",
|
||||||
"sasjs/targets/viya/services_viya/public"
|
"sasjs/targets/viya/services_viya/public"
|
||||||
]
|
],
|
||||||
},
|
"initProgram": "sasjs/utils/serviceinitviya.sas",
|
||||||
"deployConfig": {
|
"termProgram": "",
|
||||||
"deployServicePack": true,
|
"macroVars": {}
|
||||||
"deployScripts": [
|
|
||||||
"sasjs/utils/viyadeploy.sh"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"streamConfig": {
|
"streamConfig": {
|
||||||
"streamWeb": true,
|
"streamWeb": true,
|
||||||
@@ -137,54 +140,6 @@
|
|||||||
},
|
},
|
||||||
"contextName": "SAS Job Execution compute context"
|
"contextName": "SAS Job Execution compute context"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "viyacloud",
|
|
||||||
"serverUrl": "https://4gl.viyacloud.sas.com",
|
|
||||||
"serverType": "SASVIYA",
|
|
||||||
"httpsAgentOptions": {
|
|
||||||
"rejectUnauthorized": false,
|
|
||||||
"allowInsecureRequests": true
|
|
||||||
},
|
|
||||||
"appLoc": "/30.SASApps/app/dc",
|
|
||||||
"macroFolders": [
|
|
||||||
"sasjs/targets/viya/macros_viya"
|
|
||||||
],
|
|
||||||
"programFolders": [
|
|
||||||
"sasjs/db/datactrl"
|
|
||||||
],
|
|
||||||
"buildConfig": {
|
|
||||||
"initProgram": "sasjs/utils/buildinitviya.sas",
|
|
||||||
"termProgram": "sasjs/utils/buildtermviya.sas",
|
|
||||||
"macroVars": {
|
|
||||||
"dcpath": "/opt/sas/viya/config/var/tmp/dc",
|
|
||||||
"adminGroup": "DataBuilders"
|
|
||||||
},
|
|
||||||
"buildResultsFolder": "sasjsresults",
|
|
||||||
"buildOutputFolder": "sasjsbuild",
|
|
||||||
"buildOutputFileName": "viya.sas"
|
|
||||||
},
|
|
||||||
"serviceConfig": {
|
|
||||||
"initProgram": "sasjs/utils/serviceinitviya.sas",
|
|
||||||
"serviceFolders": [
|
|
||||||
"sasjs/targets/viya/services_viya/viya_users",
|
|
||||||
"sasjs/targets/viya/services_viya/admin",
|
|
||||||
"sasjs/targets/viya/services_viya/public"
|
|
||||||
],
|
|
||||||
"termProgram": "",
|
|
||||||
"macroVars": {}
|
|
||||||
},
|
|
||||||
"streamConfig": {
|
|
||||||
"streamWeb": true,
|
|
||||||
"streamWebFolder": "webv",
|
|
||||||
"webSourcePath": "../client/dist",
|
|
||||||
"streamServiceName": "clickme",
|
|
||||||
"assetPaths": []
|
|
||||||
},
|
|
||||||
"deployConfig": {
|
|
||||||
"deployServicePack": true
|
|
||||||
},
|
|
||||||
"contextName": "Datacontroller compute context"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "vtest",
|
"name": "vtest",
|
||||||
"appLoc": "/30.SASApps/app/vtest",
|
"appLoc": "/30.SASApps/app/vtest",
|
||||||
|
@@ -675,7 +675,7 @@ data xl_rules;
|
|||||||
keep xl_column xl_rule;
|
keep xl_column xl_rule;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mpe_dsmeta(&libds, outds=dsmeta)
|
%mpe_dsmeta(&orig_libds, outds=dsmeta)
|
||||||
|
|
||||||
%mpe_getversions(&mpelib,
|
%mpe_getversions(&mpelib,
|
||||||
%scan(&orig_libds,1,.),
|
%scan(&orig_libds,1,.),
|
||||||
|
@@ -54,7 +54,10 @@ data _null_;
|
|||||||
/* if a TXTEMPORAL table then filter as such */
|
/* if a TXTEMPORAL table then filter as such */
|
||||||
call symputx('txfrom',var_txfrom);
|
call symputx('txfrom',var_txfrom);
|
||||||
call symputx('txto',var_txto);
|
call symputx('txto',var_txto);
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* if a format, extract relevant info */
|
||||||
|
data _null_;
|
||||||
ds=symget('ds');
|
ds=symget('ds');
|
||||||
is_fmt=0;
|
is_fmt=0;
|
||||||
if subpad(cats(reverse(ds)),1,3)=:'CF-' then do;
|
if subpad(cats(reverse(ds)),1,3)=:'CF-' then do;
|
||||||
|
@@ -76,3 +76,37 @@ run;
|
|||||||
test=EQUALS 3,
|
test=EQUALS 3,
|
||||||
outds=work.test_results
|
outds=work.test_results
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* test 3 - when there is a format catalog / nothing else in the library */
|
||||||
|
data work.params3;
|
||||||
|
length name $32 value $1000;
|
||||||
|
name='type';value='SAS';output;
|
||||||
|
name='table';value="FMTONLY.DCFMTS-FC";output;
|
||||||
|
name='filter';value='0';output;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mx_testservice(&_program,
|
||||||
|
viyacontext=&defaultcontext,
|
||||||
|
inputparams=work.params3,
|
||||||
|
outref=web3,
|
||||||
|
viyaresult=WEBOUT_TXT
|
||||||
|
)
|
||||||
|
|
||||||
|
data work.results3;
|
||||||
|
infile web3;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
if _infile_=:'datalines4;' then do;
|
||||||
|
output;
|
||||||
|
output;
|
||||||
|
output;
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
|
if _n_>100 then stop;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.results3,
|
||||||
|
desc=datalines file is successfully returned for LONE format catalog,
|
||||||
|
test=EQUALS 3,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
@@ -1,85 +1,86 @@
|
|||||||
/**
|
/**
|
||||||
@file refreshlibinfo.sas
|
@file refreshlibinfo.sas
|
||||||
@brief Refresh the Data Catalog for a particular library
|
@brief Refresh the Data Catalog for a particular library
|
||||||
@details When showing library info in the VIEW menu, the data is taken from
|
@details When showing library info in the VIEW menu, the data is taken from
|
||||||
the Data Catalog tables. These may be empty or outdated, and so this service
|
the Data Catalog tables. These may be empty or outdated, and so this service
|
||||||
allows end users to run a refresh of the data.
|
allows end users to run a refresh of the data.
|
||||||
|
|
||||||
<h4> Service Inputs </h4>
|
<h4> Service Inputs </h4>
|
||||||
<h5> lib2refresh </h5>
|
<h5> lib2refresh </h5>
|
||||||
Should contain the libref to be refreshed.
|
Should contain the libref to be refreshed.
|
||||||
|libref:$8.|
|
|libref:$8.|
|
||||||
|---|
|
|---|
|
||||||
|SOMELIB|
|
|SOMELIB|
|
||||||
|
|
||||||
<h4> Service Outputs </h4>
|
<h4> Service Outputs </h4>
|
||||||
|
|
||||||
<h5> libinfo </h5>
|
<h5> libinfo </h5>
|
||||||
|
|
||||||
|engine $|libname $|paths $|perms $|owners $|schemas $ |libid $|libsize $|table_cnt |
|
|engine $|libname $|paths $|perms $|owners $|schemas $ |libid $|libsize $|table_cnt |
|
||||||
|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|V9|SOMELIB|"some/path"|rwxrwxr-x|sassrv|` `|` `|636MB|33|
|
|V9|SOMELIB|"some/path"|rwxrwxr-x|sassrv|` `|` `|636MB|33|
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li dc_assignlib.sas
|
@li dc_assignlib.sas
|
||||||
@li dc_refreshcatalog.sas
|
@li dc_refreshcatalog.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author 4GL Apps Ltd
|
@author 4GL Apps Ltd
|
||||||
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
and may not be re-distributed or re-sold without the express permission of
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
4GL Apps Ltd.
|
4GL Apps Ltd.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
%mpeinit()
|
%mpeinit()
|
||||||
|
|
||||||
%webout(FETCH)
|
%webout(FETCH)
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,msg=%str(syscc=&syscc Problem on startup)
|
,msg=%str(syscc=&syscc Problem on startup)
|
||||||
)
|
)
|
||||||
|
|
||||||
%let libref=;
|
%let libref=;
|
||||||
data _null_;
|
data _null_;
|
||||||
set work.lib2refresh;
|
set work.lib2refresh;
|
||||||
call symputx('libref',libref);
|
call symputx('libref',libref);
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,msg=%str(syscc=&syscc Problem with inputs - was lib2refresh object sent?)
|
,msg=%str(syscc=&syscc Problem with inputs - was lib2refresh object sent?)
|
||||||
)
|
)
|
||||||
|
|
||||||
%dc_assignlib(WRITE,&libref)
|
%dc_assignlib(WRITE,&libref)
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,msg=%str(syscc=&syscc after lib assignment)
|
,msg=%str(syscc=&syscc after lib assignment)
|
||||||
)
|
)
|
||||||
|
|
||||||
%dc_refreshcatalog(&libref)
|
%dc_refreshcatalog(&libref)
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,msg=%str(syscc=&syscc Problem when running the catalog refresh)
|
,msg=%str(syscc=&syscc Problem when running the catalog refresh)
|
||||||
)
|
)
|
||||||
|
|
||||||
/* get libinfo */
|
/* get libinfo */
|
||||||
proc sql;
|
proc sql;
|
||||||
create table work.libinfo as
|
create table work.libinfo as
|
||||||
select a.engine,
|
select a.engine,
|
||||||
a.libname,
|
a.libname,
|
||||||
a.paths,
|
a.paths,
|
||||||
a.perms,
|
a.perms,
|
||||||
a.owners,
|
a.owners,
|
||||||
a.schemas,
|
a.schemas,
|
||||||
a.libid,
|
a.libid,
|
||||||
b.libsize,
|
b.libsize,
|
||||||
b.table_cnt
|
b.table_cnt,
|
||||||
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
b.catalog_cnt
|
||||||
inner join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
||||||
on a.libref=b.libref
|
inner join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
||||||
where a.libref="&libref";
|
on a.libref=b.libref
|
||||||
|
where a.libref="&libref";
|
||||||
%webout(OPEN)
|
|
||||||
%webout(OBJ,libinfo)
|
%webout(OPEN)
|
||||||
%webout(CLOSE)
|
%webout(OBJ,libinfo)
|
||||||
|
%webout(CLOSE)
|
||||||
|
@@ -355,7 +355,7 @@ run;
|
|||||||
|
|
||||||
%mp_getcols(&libds, outds=cols)
|
%mp_getcols(&libds, outds=cols)
|
||||||
|
|
||||||
%mpe_dsmeta(&libds, outds=dsmeta)
|
%mpe_dsmeta(&orig_libds, outds=dsmeta)
|
||||||
|
|
||||||
%mpe_getversions(&mpelib,
|
%mpe_getversions(&mpelib,
|
||||||
%scan(&orig_libds,1,.),
|
%scan(&orig_libds,1,.),
|
||||||
|
@@ -167,7 +167,8 @@ create table work.libinfo as
|
|||||||
a.schemas,
|
a.schemas,
|
||||||
a.libid,
|
a.libid,
|
||||||
coalesce(b.libsize,0) as libsize,
|
coalesce(b.libsize,0) as libsize,
|
||||||
coalesce(b.table_cnt,0) as table_cnt
|
coalesce(b.table_cnt,0) as table_cnt,
|
||||||
|
coalesce(b.catalog_cnt,0) as catalog_cnt
|
||||||
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
|
||||||
left join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
left join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
|
||||||
on a.libref=b.libref
|
on a.libref=b.libref
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mpe_refreshlibs.sas
|
@li mpe_refreshlibs.sas
|
||||||
@li dc_assignlib.sas
|
@li dc_assignlib.sas
|
||||||
|
@li mpe_refreshcatalogs.sas
|
||||||
@li mpe_refreshtables.sas
|
@li mpe_refreshtables.sas
|
||||||
@li mm_getrepos.sas
|
@li mm_getrepos.sas
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ run;
|
|||||||
%dc_assignlib(WRITE,&libref) /* write just in order to assign direct lib */
|
%dc_assignlib(WRITE,&libref) /* write just in order to assign direct lib */
|
||||||
%mpe_refreshlibs(lib=&libref)
|
%mpe_refreshlibs(lib=&libref)
|
||||||
%mpe_refreshtables(&libref)
|
%mpe_refreshtables(&libref)
|
||||||
|
%mpe_refreshcatalogs(&libref)
|
||||||
%end;
|
%end;
|
||||||
%else %do xx=1 %to &repocnt;
|
%else %do xx=1 %to &repocnt;
|
||||||
options metarepository=&&repo&xx;
|
options metarepository=&&repo&xx;
|
||||||
@@ -73,6 +75,7 @@ run;
|
|||||||
%do i=1 %to &libcnt;
|
%do i=1 %to &libcnt;
|
||||||
%dc_assignlib(WRITE,&&lib&i)
|
%dc_assignlib(WRITE,&&lib&i)
|
||||||
%mpe_refreshtables(&&lib&i)
|
%mpe_refreshtables(&&lib&i)
|
||||||
|
%mpe_refreshcatalogs(&&lib&i)
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mpe_refreshlibs.sas
|
@li mpe_refreshlibs.sas
|
||||||
@li dc_assignlib.sas
|
@li dc_assignlib.sas
|
||||||
|
@li mpe_refreshcatalogs.sas
|
||||||
@li mpe_refreshtables.sas
|
@li mpe_refreshtables.sas
|
||||||
|
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ data libraries;
|
|||||||
str=cats('%mpe_refreshtables(',libref,')');
|
str=cats('%mpe_refreshtables(',libref,')');
|
||||||
put str;
|
put str;
|
||||||
putlog str;
|
putlog str;
|
||||||
|
str=cats('%mpe_refreshcatalogs(',libref,')');
|
||||||
|
put str;
|
||||||
run;
|
run;
|
||||||
%inc executor/source2;
|
%inc executor/source2;
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
@li mpe_refreshlibs.sas
|
@li mpe_refreshlibs.sas
|
||||||
@li dc_assignlib.sas
|
@li dc_assignlib.sas
|
||||||
@li mpe_refreshtables.sas
|
@li mpe_refreshtables.sas
|
||||||
|
@li mpe_refreshcatalogs.sas
|
||||||
|
|
||||||
|
|
||||||
@version 3.4
|
@version 3.4
|
||||||
@@ -19,19 +20,29 @@
|
|||||||
4GL Apps Ltd.
|
4GL Apps Ltd.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro dc_refreshcatalog();
|
%macro dc_refreshcatalog(libref);
|
||||||
|
|
||||||
%mpe_refreshlibs()
|
%if #&libref# ne ## %then %do;
|
||||||
|
%put &sysmacroname: assigning specific libref, &libref;
|
||||||
filename executor catalog 'work.code.code.source';
|
%dc_assignlib(WRITE,&libref) /* write just in order to assign direct lib */
|
||||||
data libraries;
|
%mpe_refreshlibs(lib=&libref)
|
||||||
set &mpelib..mpe_datacatalog_libs;
|
%mpe_refreshtables(&libref)
|
||||||
where &dc_dttmtfmt. le TX_TO;
|
%mpe_refreshcatalogs(&libref)
|
||||||
file executor;
|
%end;
|
||||||
str=cats('%mpe_refreshtables(',libref,')');
|
%else %do;
|
||||||
put str;
|
%mpe_refreshlibs()
|
||||||
putlog str;
|
filename executor catalog 'work.code.code.source';
|
||||||
run;
|
data libraries;
|
||||||
%inc executor;
|
set &mpelib..mpe_datacatalog_libs;
|
||||||
|
where &dc_dttmtfmt. le TX_TO;
|
||||||
|
file executor;
|
||||||
|
str=cats('%mpe_refreshtables(',libref,')');
|
||||||
|
put str;
|
||||||
|
str=cats('%mpe_refreshcatalogs(',libref,')');
|
||||||
|
put str;
|
||||||
|
putlog str;
|
||||||
|
run;
|
||||||
|
%inc executor;
|
||||||
|
%end;
|
||||||
|
|
||||||
%mend dc_refreshcatalog;
|
%mend dc_refreshcatalog;
|
||||||
|
@@ -21,7 +21,7 @@ REMOVE THAT LAST MACRO
|
|||||||
%let syscc=0;
|
%let syscc=0;
|
||||||
%global apploc _program dclib defaultcontext _debug sasjs_mdebug dc_dttmtfmt;
|
%global apploc _program dclib defaultcontext _debug sasjs_mdebug dc_dttmtfmt;
|
||||||
|
|
||||||
%let defaultcontext=Datacontroller compute context;
|
%let defaultcontext=SAS Job Execution compute context;
|
||||||
%let sasjs_mdebug=0;
|
%let sasjs_mdebug=0;
|
||||||
|
|
||||||
options mprint mprintnest nobomfile lrecl=32767;
|
options mprint mprintnest nobomfile lrecl=32767;
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
%global apploc _program;
|
%global apploc _program;
|
||||||
|
|
||||||
%let defaultcontext=Datacontroller compute context;
|
%let defaultcontext=SAS Job Execution compute context;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
length _pgm $1000;
|
length _pgm $1000;
|
||||||
@@ -60,6 +60,9 @@ run;
|
|||||||
%let testloc=%sysfunc(pathname(&DC_LIBREF))/fmt%mf_getuniquefileref();
|
%let testloc=%sysfunc(pathname(&DC_LIBREF))/fmt%mf_getuniquefileref();
|
||||||
%mf_mkdir(&testloc)
|
%mf_mkdir(&testloc)
|
||||||
libname dctest "&testloc";
|
libname dctest "&testloc";
|
||||||
|
/* test library with only one format catalog */
|
||||||
|
%mf_mkdir(&testloc/fmtonly)
|
||||||
|
libname fmtonly "&testloc/fmtonly";
|
||||||
|
|
||||||
/* add formats */
|
/* add formats */
|
||||||
PROC FORMAT library=dctest.dcfmts;
|
PROC FORMAT library=dctest.dcfmts;
|
||||||
@@ -99,6 +102,10 @@ run;
|
|||||||
proc append base=&dc_libref..mpe_tables data=work.append;
|
proc append base=&dc_libref..mpe_tables data=work.append;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* lone format catalog */
|
||||||
|
proc format cntlin=work.fmts library=fmtonly.dcfmts;
|
||||||
|
run;
|
||||||
|
|
||||||
/* add some other tables */
|
/* add some other tables */
|
||||||
%mp_coretable(LOCKTABLE,libds=dctest.locktable)
|
%mp_coretable(LOCKTABLE,libds=dctest.locktable)
|
||||||
%mp_coretable(DIFFTABLE,libds=dctest.difftable)
|
%mp_coretable(DIFFTABLE,libds=dctest.difftable)
|
||||||
@@ -130,6 +137,7 @@ options mprint;
|
|||||||
put _infile_;
|
put _infile_;
|
||||||
if last then do;
|
if last then do;
|
||||||
put "libname dctest '&testloc';";
|
put "libname dctest '&testloc';";
|
||||||
|
put "libname fmtonly '&testloc/fmtonly';";
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -151,6 +159,7 @@ options mprint;
|
|||||||
put _infile_;
|
put _infile_;
|
||||||
if last then do;
|
if last then do;
|
||||||
put "libname dctest '&testloc';";
|
put "libname dctest '&testloc';";
|
||||||
|
put "libname fmtonly '&testloc/fmtonly';";
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
%ms_deletefile(&root/services/public/settings.sas)
|
%ms_deletefile(&root/services/public/settings.sas)
|
||||||
@@ -182,6 +191,7 @@ options mprint;
|
|||||||
put _infile_;
|
put _infile_;
|
||||||
if last then do;
|
if last then do;
|
||||||
put "libname dctest '&testloc';";
|
put "libname dctest '&testloc';";
|
||||||
|
put "libname fmtonly '&testloc/fmtonly';";
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
%mm_deletestp(target=&root/services/public/Data_Controller_Settings)
|
%mm_deletestp(target=&root/services/public/Data_Controller_Settings)
|
||||||
|
@@ -2,6 +2,11 @@
|
|||||||
@file buildinitviya.sas
|
@file buildinitviya.sas
|
||||||
@brief initialisation for viya build program
|
@brief initialisation for viya build program
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mfv_getfolderpath.sas
|
||||||
|
@li mfv_getpathuri.sas
|
||||||
|
@li mv_createfolder.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
options nonotes nomprint;
|
options nonotes nomprint;
|
||||||
@@ -9,4 +14,8 @@ options nonotes nomprint;
|
|||||||
/* update apploc to default to user home area if not set */
|
/* update apploc to default to user home area if not set */
|
||||||
%let apploc=%sysfunc(ifc("&apploc"="/Public/app/dcplaceholder"
|
%let apploc=%sysfunc(ifc("&apploc"="/Public/app/dcplaceholder"
|
||||||
,/Users/&sysuserid/My Folder/Data Controller
|
,/Users/&sysuserid/My Folder/Data Controller
|
||||||
,&apploc));
|
,&apploc));
|
||||||
|
|
||||||
|
/* ensure the correct casing of appLoc */
|
||||||
|
%mv_createfolder(path=&apploc)
|
||||||
|
%let apploc=%mfv_getfolderpath(%mfv_getpathuri(&apploc));
|
Reference in New Issue
Block a user