Compare commits

..

70 Commits

Author SHA1 Message Date
88679c0c9a Merge pull request 'fix: bumping CLI to 4.12.10' (#181) from clibump into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m35s
Release / Build-and-test-development (push) Failing after 3h10m30s
Release / release (push) Has been cancelled
Reviewed-on: #181
2025-07-23 15:04:30 +00:00
allan
a08a717ca8 fix: bumping CLI to 4.12.10
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m43s
Build / Build-and-test-development (pull_request) Successful in 8m11s
2025-07-23 16:03:57 +01:00
c8b6fdbfdb Merge pull request 'Improving accessibility score up to 100, hot update to v16.0.1' (#180) from accessibility-maxing into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m13s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #180
2025-07-23 13:21:40 +00:00
M
b495c41626 fix: adapter bump
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m39s
Build / Build-and-test-development (pull_request) Successful in 8m9s
2025-07-23 14:59:45 +02:00
M
7f4be474c6 chore(git): Merge branch 'main' into accessibility-maxing
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 8m12s
2025-07-23 13:23:53 +02:00
7f6f68fcbb Merge pull request 'fix: bumping sasjs/core and sasjs/cli' (#177) from issue157 into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m13s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #177
2025-07-23 11:14:37 +00:00
M
03fd7db033 chore: licence checker
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Successful in 8m13s
2025-07-23 12:22:04 +02:00
M
9dc5c66f7b chore: package-lock
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m18s
Build / Build-and-test-development (pull_request) Successful in 8m12s
2025-07-23 12:16:25 +02:00
M
aa1b08632e chore: package-lock
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m42s
Build / Build-and-test-development (pull_request) Successful in 7m59s
2025-07-23 11:52:56 +02:00
M
6bbe354c9e ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 46s
2025-07-23 11:51:40 +02:00
M
8ff429793b style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 46s
2025-07-23 11:50:35 +02:00
M
70d010127a style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 43s
Build / Build-and-test-development (pull_request) Failing after 44s
2025-07-23 11:49:34 +02:00
M
696717c509 ci: script
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Successful in 8m1s
2025-07-23 11:38:24 +02:00
M
71c308d052 feat: improving accessibility score up to 100, hot update to v16.0.1
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-07-23 11:17:57 +02:00
allan
bed5b320ad chore: lint fix
Some checks failed
Build / Build-and-ng-test (pull_request) Successful in 3m48s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-07-07 14:51:04 +01:00
b0e827412e Merge branch 'main' into issue157
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 45s
Build / Build-and-test-development (pull_request) Failing after 3h1m39s
2025-07-07 13:34:38 +00:00
allan
63e9af402e fix: bumping sasjs/core and sasjs/cli
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 41s
Build / Build-and-test-development (pull_request) Failing after 3h11m34s
2025-07-07 14:34:03 +01:00
fd55105f62 chore(git): adding size check in precommit hook
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 4m20s
Release / release (push) Has been cancelled
Release / Build-and-test-development (push) Has been cancelled
2025-06-27 16:42:13 +01:00
semantic-release-bot
c2e3b362e7 chore(release): 7.0.3 [skip ci]
## [7.0.3](https://git.datacontroller.io/dc/dc/compare/v7.0.2...v7.0.3) (2025-06-26)

### Bug Fixes

* makedata vars ([e7cb471](e7cb471c0b))
* viya deploy makedata missing params ([7a82316](7a8231615c))
2025-06-26 16:29:21 +00:00
Mihajlo Medjedovic
5d2d60d040 chore: npm audit fix for client folder, lint fix
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m5s
Release / Build-and-test-development (push) Successful in 9m9s
Release / release (push) Successful in 9m18s
2025-06-26 17:53:22 +02:00
0e59f5406f Merge pull request 'fix: viya deploy makedata missing params' (#176) from viya-deploy-params into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m18s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #176
2025-06-25 21:51:31 +00:00
Medjedovic
e7cb471c0b fix: makedata vars
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m5s
Build / Build-and-test-development (pull_request) Successful in 8m39s
2025-06-25 23:29:42 +02:00
Medjedovic
0465089207 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 8m36s
2025-06-25 23:17:48 +02:00
Medjedovic
4f2f59907c Merge branch 'viya-deploy-params' of ssh://git.datacontroller.io:29419/dc/dc into viya-deploy-params
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 47s
Build / Build-and-test-development (pull_request) Successful in 8m37s
2025-06-25 23:04:38 +02:00
Medjedovic
7d85328d41 style: lint 2025-06-25 23:04:19 +02:00
f2a9329196 chore: bumping node v
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m39s
2025-06-25 20:20:19 +01:00
Medjedovic
7a8231615c fix: viya deploy makedata missing params
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 9m5s
2025-06-25 15:41:43 +02:00
semantic-release-bot
0db6b25327 chore(release): 7.0.2 [skip ci]
## [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](0b4042af60))
2025-06-21 09:30:41 +00:00
e91f6f01a6 Merge pull request 'fix(viya deploy): run makedata in new window to ensure logs are available for the user' (#175) from issue154 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m4s
Release / Build-and-test-development (push) Successful in 8m40s
Release / release (push) Successful in 8m30s
Reviewed-on: #175
2025-06-21 09:14:11 +00:00
Mihajlo Medjedovic
0b4042af60 fix(viya deploy): run makedata in new window to ensure logs are available for the user
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m12s
Build / Build-and-test-development (pull_request) Successful in 8m42s
2025-06-17 15:33:11 +02:00
semantic-release-bot
519d8953b5 chore(release): 7.0.1 [skip ci]
## [7.0.1](https://git.datacontroller.io/dc/dc/compare/v7.0.0...v7.0.1) (2025-06-11)

### Bug Fixes

* refresh process ([4ecd186](4ecd186e5c))
2025-06-11 18:47:43 +00:00
14a616fc1b Merge pull request 'fix: refresh process' (#173) from issue157 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m9s
Release / Build-and-test-development (push) Successful in 8m45s
Release / release (push) Successful in 8m47s
Reviewed-on: #173
2025-06-11 18:05:06 +00:00
bfe5a8626f Merge branch 'main' into issue157
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m18s
Build / Build-and-test-development (pull_request) Successful in 8m43s
2025-06-11 18:04:48 +00:00
allan
4ecd186e5c fix: refresh process
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 8m44s
2025-06-11 19:04:21 +01:00
semantic-release-bot
8c60473c15 chore(release): 7.0.0 [skip ci]
# [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](e874143a95))
* commit git hooks checking lint ([69f687a](69f687a85f))
* ensuring apploc is not case sensitive. Closes [#171](#171) ([24545f2](24545f2acd))
* export unregistered formats ([f6d7d6f](f6d7d6f90c)), closes [#158](#158)
* reload startupservice after user approves the MPE_TABLES page ([e5f8e50](e5f8e500c1))
* showing catalog_cnt in libinfo ([e44a25d](e44a25dcc3)), closes [#160](#160)

### Features

* adding 4 new tables for catalogs ([e4dbab8](e4dbab8b16))
* capturing catalog specific information, closes [#159](#159) ([b4c586a](b4c586a859))
* viewer added catalog_cnt ([2aa19d1](2aa19d1dca))

### 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)
2025-06-11 13:46:10 +00:00
bb126eba5b Merge pull request 'Reload startupservice after user approves the MPE_TABLES changes' (#170) from issue157 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m8s
Release / Build-and-test-development (push) Successful in 8m47s
Release / release (push) Successful in 8m50s
Reviewed-on: #170
2025-06-11 13:16:22 +00:00
allan
d1998422d2 chore: fix for mpe_datastatus_libs
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m19s
Build / Build-and-test-development (pull_request) Successful in 8m45s
2025-06-11 14:15:50 +01:00
Mihajlo Medjedovic
69f687a85f fix: commit git hooks checking lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m11s
Build / Build-and-test-development (pull_request) Successful in 8m34s
2025-06-11 13:11:30 +02:00
Mihajlo Medjedovic
2aa19d1dca feat: viewer added catalog_cnt
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 48s
Build / Build-and-test-development (pull_request) Successful in 8m49s
2025-06-11 12:57:10 +02:00
allan
e44a25dcc3 fix: showing catalog_cnt in libinfo
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m19s
Build / Build-and-test-development (pull_request) Successful in 8m53s
Closes #160
2025-06-11 10:06:52 +01:00
allan
efb5ffa906 chore: reverting accidental change
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 8m33s
2025-06-10 22:45:39 +01:00
allan
b4c586a859 feat: capturing catalog specific information, closes #159
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m12s
Build / Build-and-test-development (pull_request) Successful in 8m35s
BREAKING CHANGE:  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)
2025-06-10 22:40:09 +01:00
allan
e874143a95 fix: bumping adapter to re-enable JES API method 2025-06-10 16:18:56 +01:00
allan
e4dbab8b16 feat: adding 4 new tables for catalogs 2025-06-10 16:18:30 +01:00
allan
f6d7d6f90c fix: export unregistered formats
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m14s
Build / Build-and-test-development (pull_request) Successful in 9m0s
Closes #158
2025-06-10 09:41:18 +01:00
allan
063c90caf4 chore(docs): readme fix
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 8m38s
2025-06-06 23:24:35 +01:00
allan
2011c2eee7 chore(docs): updating README with viya deploy details
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m0s
Build / Build-and-test-development (pull_request) Successful in 8m28s
2025-06-06 23:23:47 +01:00
allan
24545f2acd fix: ensuring apploc is not case sensitive. Closes #171
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 8m38s
2025-06-06 21:05:08 +01:00
a7c81245ff Merge branch 'main' into issue157
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 8m45s
2025-06-06 13:17:17 +00:00
Mihajlo Medjedovic
4f2c993b2d style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 8m35s
2025-06-06 15:03:51 +02:00
Mihajlo Medjedovic
e5f8e500c1 fix: reload startupservice after user approves the MPE_TABLES page 2025-06-06 15:03:36 +02:00
semantic-release-bot
a61e2de140 chore(release): 6.16.2 [skip ci]
## [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](4830c6d219))
2025-06-06 11:36:00 +00:00
881d2b060e Merge pull request 'fix: streaming viya deploy isStreaming function stability fix' (#169) from viya-deploy into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m53s
Release / Build-and-test-development (push) Successful in 8m36s
Release / release (push) Successful in 8m18s
Reviewed-on: #169
2025-06-06 11:08:03 +00:00
Mihajlo Medjedovic
4830c6d219 fix: streaming viya deploy isStreaming function stability fix
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Build / Build-and-test-development (pull_request) Successful in 8m30s
2025-06-06 13:06:55 +02:00
semantic-release-bot
4c3c9ac88c chore(release): 6.16.1 [skip ci]
## [6.16.1](https://git.datacontroller.io/dc/dc/compare/v6.16.0...v6.16.1) (2025-06-06)

### Bug Fixes

* viya deploy updating index html based on URL ([86134f4](86134f478a))
* viya streamed app deploy page flow fix ([89ab296](89ab296151))
2025-06-06 10:35:30 +00:00
7e1c610a4d Merge pull request 'Viya deploy, updating the compute context in index.html file, found based on URL' (#168) from viya-deploy into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Successful in 8m30s
Release / release (push) Successful in 8m19s
Reviewed-on: #168
2025-06-06 10:19:20 +00:00
Mihajlo Medjedovic
8139f495ce style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m4s
Build / Build-and-test-development (pull_request) Successful in 8m33s
2025-06-06 12:06:10 +02:00
Mihajlo Medjedovic
89ab296151 fix: viya streamed app deploy page flow fix 2025-06-06 12:05:52 +02:00
Mihajlo Medjedovic
a0dc92c403 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 8m34s
2025-06-06 10:58:01 +02:00
Mihajlo Medjedovic
86134f478a fix: viya deploy updating index html based on URL 2025-06-06 10:57:41 +02:00
semantic-release-bot
9a2addc18e chore(release): 6.16.0 [skip ci]
# [6.16.0](https://git.datacontroller.io/dc/dc/compare/v6.15.2...v6.16.0) (2025-06-05)

### Bug Fixes

* adapter bump ([ca7caa2](ca7caa25b6))
* automatic viya deploy timing issue ([037a97b](037a97b6ff))
* bump core to ensure ff works on viya streaming deploy ([cbd69df](cbd69df708)), closes [#156](#156)
* viya deploy load data timing ([abdbb67](abdbb67471))

### Features

* viya deploy, update the index.html contextname ([7223955](72239558af))
2025-06-05 13:53:03 +00:00
9264ce2a60 Merge pull request 'Adapter bump, including getFileContent and updateFileContent' (#167) from adapter-bump into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m4s
Release / Build-and-test-development (push) Successful in 9m9s
Release / release (push) Successful in 8m19s
Reviewed-on: #167
2025-06-05 13:17:06 +00:00
allan
cbd69df708 fix: bump core to ensure ff works on viya streaming deploy
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m3s
Build / Build-and-test-development (pull_request) Successful in 8m39s
closes #156 (along with previous releases)
2025-06-05 14:16:44 +01:00
Mihajlo Medjedovic
ca7caa25b6 fix: adapter bump
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m9s
Build / Build-and-test-development (pull_request) Successful in 8m45s
2025-06-05 14:49:21 +02:00
c10330627f Merge pull request 'Viya smooth deploy' (#166) from deploy-context into main
Some checks failed
Release / Build-production-and-ng-test (push) Has been cancelled
Release / Build-and-test-development (push) Has been cancelled
Release / release (push) Has been cancelled
Reviewed-on: #166
2025-06-05 12:30:29 +00:00
d80c59afce Merge branch 'main' into deploy-context
Some checks failed
Build / Build-and-ng-test (pull_request) Has been cancelled
Build / Build-and-test-development (pull_request) Has been cancelled
2025-06-05 12:30:19 +00:00
Mihajlo Medjedovic
abdbb67471 fix: viya deploy load data timing
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 4m0s
Build / Build-and-test-development (pull_request) Failing after 2h56m12s
2025-06-05 12:07:26 +02:00
Mihajlo Medjedovic
037a97b6ff fix: automatic viya deploy timing issue 2025-06-04 17:37:44 +02:00
Mihajlo Medjedovic
a0a529ad38 style: lint 2025-06-04 17:36:13 +02:00
Mihajlo Medjedovic
72239558af feat: viya deploy, update the index.html contextname 2025-06-04 17:35:15 +02:00
60 changed files with 2985 additions and 988 deletions

View File

@@ -1,11 +1,23 @@
#!/bin/sh
# Avoid commits to the master branch
BRANCH=`git rev-parse --abbrev-ref HEAD`
REGEX="^(master|development)$"
# Using `--silent` helps for showing any errs in the first line of the response
# The first line is picked up by the VS Code GIT UI popup when rc is not 0
if [[ "$BRANCH" =~ $REGEX ]]; then
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
echo "If so, commit with -n to bypass the pre-commit hook."
if npm run --silent lint:check:silent ; then
exit 0
else
npm run --silent lint:fix:silent
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
exit 1
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
)

View File

@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: 20.15.1
- name: Install Google Chrome
run: |
@@ -58,7 +58,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
node-version: 20.15.1
- name: Write .npmrc file
run: |
@@ -126,7 +126,7 @@ jobs:
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# 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
if: always()

View File

@@ -1,3 +1,79 @@
## [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)
### Bug Fixes
* viya deploy updating index html based on URL ([86134f4](https://git.datacontroller.io/dc/dc/commit/86134f478ae0b9426e01bfcc9ca4ee597ca733f7))
* viya streamed app deploy page flow fix ([89ab296](https://git.datacontroller.io/dc/dc/commit/89ab2961513b245eeea48d1867c6496d3261761e))
# [6.16.0](https://git.datacontroller.io/dc/dc/compare/v6.15.2...v6.16.0) (2025-06-05)
### Bug Fixes
* adapter bump ([ca7caa2](https://git.datacontroller.io/dc/dc/commit/ca7caa25b6eea1bd4579fb8b67ec9b211a893079))
* automatic viya deploy timing issue ([037a97b](https://git.datacontroller.io/dc/dc/commit/037a97b6ffa27b40891531ae6812ebe5b5e71e34))
* bump core to ensure ff works on viya streaming deploy ([cbd69df](https://git.datacontroller.io/dc/dc/commit/cbd69df708edf3a8446115ca7315fac3557dcf97)), closes [#156](https://git.datacontroller.io/dc/dc/issues/156)
* viya deploy load data timing ([abdbb67](https://git.datacontroller.io/dc/dc/commit/abdbb674713796e5308eb4272197a5c253868a85))
### Features
* viya deploy, update the index.html contextname ([7223955](https://git.datacontroller.io/dc/dc/commit/72239558af2ee50cdfc71b7e185e6661ab568ba1))
## [6.15.2](https://git.datacontroller.io/dc/dc/compare/v6.15.1...v6.15.2) (2025-06-04)

View File

@@ -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).
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
* Docs: https://docs.datacontroller.io

View File

@@ -10,7 +10,7 @@ const check = (cwd) => {
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;',
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) => {
if (error) {

1573
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -48,8 +48,8 @@
"@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
"@clr/icons": "^13.0.2",
"@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
"@handsontable/angular": "^15.3.0",
"@sasjs/adapter": "^4.11.0",
"@handsontable/angular": "^16.0.1",
"@sasjs/adapter": "^4.12.2",
"@sasjs/utils": "^3.4.0",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
"@types/d3-graphviz": "^2.6.7",
@@ -60,7 +60,7 @@
"crypto-js": "^4.2.0",
"d3-graphviz": "^5.0.2",
"fs-extra": "^7.0.1",
"handsontable": "^15.3.0",
"handsontable": "^16.0.1",
"https-browserify": "1.0.0",
"hyperformula": "^2.5.0",
"iconv-lite": "^0.5.0",

View File

@@ -8,6 +8,7 @@ import {
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { HelperService } from 'src/app/services'
import { DeployService } from 'src/app/services/deploy.service'
import { EventService } from 'src/app/services/event.service'
import { LoggerService } from 'src/app/services/logger.service'
@@ -77,28 +78,40 @@ export class AutomaticComponent implements OnInit {
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(
private eventService: EventService,
private deployService: DeployService,
private sasService: SasService,
private sasViyaService: SasViyaService,
private loggerService: LoggerService
private loggerService: LoggerService,
private helperService: HelperService
) {}
ngOnInit(): void {
const promiseGetAadminGroups = this.getAdminGroups()
const getCurrentUser = this.getCurrentUser()
const getComputeContexts = this.getComputeContexts()
this.loadData()
}
Promise.all([
promiseGetAadminGroups,
getCurrentUser,
getComputeContexts
]).then(() => {
public async loadData() {
await this.getAdminGroups()
await this.getComputeContexts()
await this.getCurrentUser()
setTimeout(() => {
if (this.selectedComputeContext) {
this.onComputeContextChange(this.selectedComputeContext)
}
})
}, 500)
}
public async getComputeContexts() {
@@ -144,6 +157,7 @@ export class AutomaticComponent implements OnInit {
resolve()
},
(err) => {
console.error('Error while getting current user', err)
reject(err)
}
)
@@ -153,8 +167,7 @@ export class AutomaticComponent implements OnInit {
public async getAdminGroups() {
return new Promise<void>((resolve, reject) => {
this.adminGroupsLoading = true
this.sasViyaService
;(this.sasViyaService
.getAdminGroups()
.subscribe((res: ViyaApiIdentities) => {
this.adminGroupsLoading = false
@@ -174,7 +187,7 @@ export class AutomaticComponent implements OnInit {
this.eventService.showAbortModal('admin groups', err)
reject(err)
}
})
})
}
@@ -190,6 +203,14 @@ export class AutomaticComponent implements OnInit {
})
}
public getComputeContextName(id: string): string | undefined {
return (
this.computeContexts.find(
(context: ComputeContextItem) => context.id === id
)?.name || undefined
)
}
/**
* Executes sas.json file to deploy the backend
* Method will first try to run the `auto deploy`
@@ -235,7 +256,7 @@ export class AutomaticComponent implements OnInit {
}
public async runAutoDeploy(executeJson: boolean = false) {
this.autodeploying = true
if (!this.deployInNewWindow) this.autodeploying = true
if (executeJson) {
this.executeJson()
@@ -244,7 +265,7 @@ export class AutomaticComponent implements OnInit {
if (this.recreateDatabase) {
this.createDatabase()
} else {
this.autodeployDone = true
if (!this.deployInNewWindow) this.autodeployDone = true
}
}
@@ -265,13 +286,12 @@ export class AutomaticComponent implements OnInit {
let selectedComputeContextName = this.sasJsConfig.contextName
if (this.selectedComputeContext.length && this.computeContexts.length) {
const computeContext = this.computeContexts.find(
(context: ComputeContextItem) =>
context.id === this.selectedComputeContext
const computeContextName = this.getComputeContextName(
this.selectedComputeContext
)
if (computeContext) {
selectedComputeContextName = computeContext.name
if (computeContextName) {
selectedComputeContextName = computeContextName
}
}
@@ -286,15 +306,17 @@ export class AutomaticComponent implements OnInit {
debug: true
}
if (this.deployInNewWindow) {
this.runMakedataInNewWindow({
contextName: selectedComputeContextName,
admin: this.selectedAdminGroup,
dcPath: this.dcPath
})
} else {
this.sasJs
.request(
`services/admin/makedata&_contextname=${selectedComputeContextName}`,
data,
overrideConfig,
() => {
.request(`services/admin/makedata`, data, overrideConfig, () => {
this.sasService.shouldLogin.next(true)
}
)
})
.then((res: any) => {
this.autodeployDone = true
@@ -321,6 +343,9 @@ export class AutomaticComponent implements OnInit {
MAC: macMsg
})
}
if (this.helperService.isStreamingViya())
this.updateIndexHtmlComputeContext()
})
.catch((err: any) => {
this.eventService.showAbortModal('makedata', JSON.stringify(err))
@@ -334,6 +359,85 @@ export class AutomaticComponent implements OnInit {
}
})
}
}
public runMakedataInNewWindow(params: {
contextName: string
admin: string
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`
let programUrl =
serverUrl +
execPath +
'/?_program=' +
appLoc +
'/services/admin/makedata' +
contextname +
admin +
dcPath +
debug
window.open(programUrl)
}
/**
* Only when on Viya streamed app, this method will update the `contextname` in the `index.html` on the SAS drive
* This is needed to ensure that the DC will use the same compute context `makedata` service used to run against.
*/
public async updateIndexHtmlComputeContext() {
const filenamePath = location.search.split('/').pop()
const filename = filenamePath?.includes('.') ? filenamePath : undefined
if (!filename) {
this.eventService.showAbortModal(
null,
'We could not figure out the file name of `index.html` based on the url.'
)
return
}
const indexHtmlContent = await this.sasService.getFileContent(
`${this.appLoc}/services`,
filename
)
if (!indexHtmlContent) {
this.loggerService.error(
`Failed to get ${filename} at ${this.appLoc}/services`
)
return
}
const computeContextName = this.getComputeContextName(
this.selectedComputeContext
)
if (!computeContextName) {
this.loggerService.error(
`Compute context name not found for ID: ${this.selectedComputeContext} | List: ${JSON.stringify(this.computeContexts)}`
)
return
}
const updatedContent = indexHtmlContent.replace(
/contextname="[^"]*"/g,
`contextname="${computeContextName}"`
)
await this.sasService
.updateFileContent(`${this.appLoc}/services`, filename, updatedContent)
.catch((err: any) => {
this.loggerService.error(`Failed to update DataController.html: ${err}`)
})
}
public downloadFile(
content: any,

View File

@@ -3,6 +3,7 @@ import {
ChangeDetectorRef,
Component,
ElementRef,
OnDestroy,
OnInit,
QueryList,
ViewChild,
@@ -71,7 +72,7 @@ import { ParseResult } from '../models/ParseResult.interface'
},
encapsulation: ViewEncapsulation.None
})
export class EditorComponent implements OnInit, AfterViewInit {
export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChildren('uploadStater')
uploadStaterCompList: QueryList<UploadStaterComponent> = new QueryList()
@ViewChildren('queryFilter')
@@ -124,7 +125,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
colHeaders: [],
hidden: true,
columns: [],
height: '100%',
height: 'calc(100vh - 160px)',
minSpareRows: 1,
licenseKey: undefined,
readOnly: true,
@@ -350,6 +351,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
public licenceState = this.licenceService.licenceState
private ariaObserver: MutationObserver | undefined
private ariaCheckInterval: any | undefined
constructor(
private licenceService: LicenceService,
private eventService: EventService,
@@ -896,6 +900,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
}
this.reSetCellValidationValues()
// Fix ARIA accessibility issues after table edit
setTimeout(() => {
this.fixAriaAccessibility()
}, 100)
}, 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) {
this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
@@ -2597,6 +2772,17 @@ export class EditorComponent implements OnInit, AfterViewInit {
hot.addHook('afterRender', (isForced: boolean) => {
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) => {
@@ -2660,5 +2846,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
}
hot.render()
// Fix ARIA accessibility issues after table initialization
setTimeout(() => {
this.fixAriaAccessibility()
}, 500)
}
}

View File

@@ -99,6 +99,11 @@ export class MetadataComponent implements OnInit {
}
this.pageSize = 5
// Initialize filters for accessibility
this.typeFilter = new TypeFilter()
this.nameFilter = new NameFilter()
this.valueFilter = new ValueFilter()
if (
globals.metadata.metaDataList &&
globals.metadata.metaRepositories &&

View File

@@ -8,4 +8,5 @@ export interface Libinfo {
LIBID: string
LIBSIZE: number
TABLE_CNT: number
CATALOG_CNT: number
}

View File

@@ -1,30 +1,14 @@
import { BaseSASResponse } from './common/BaseSASResponse'
export interface EditorsStageDataSASResponse extends BaseSASResponse {
SYSDATE: string
SYSTIME: string
sasparams: Sasparam[]
_DEBUG: string
_PROGRAM: string
AUTOEXEC: string
MF_GETUSER: string
SYSCC: string
SYSENCODING: string
SYSERRORTEXT: string
SYSHOSTINFOLONG: string
SYSHOSTNAME: string
SYSPROCESSID: string
SYSPROCESSMODE: string
SYSPROCESSNAME: string
SYSJOBID: string
SYSSCPL: string
SYSSITE: string
SYSTCPIPHOSTNAME: string
SYSUSERID: string
SYSVLONG: string
SYSWARNINGTEXT: string
END_DTTM: string
MEMSIZE: string
}
export interface Sasparam {

View 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
}

View File

@@ -13,6 +13,8 @@ import {
AuditorsPostdataSASResponse,
Param
} from '../../models/sas/auditors-postdata.model'
import { PublicGetChangeinfo } from 'src/app/models/sas/public-getchangeinfo.model'
import { SasService } from 'src/app/services'
interface ChangesObj {
ind: any
@@ -76,9 +78,11 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
public diffsLimit: boolean = false
public recordsLimit: number = 100
public refreshStartupserviceAfterApprove: boolean = false
constructor(
private sasStoreService: SasStoreService,
private sasService: SasService,
private eventService: EventService,
private router: ActivatedRoute,
private route: Router
@@ -162,6 +166,9 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
await this.sasStoreService
.approveTable(approveParams, 'SASControlTable', 'auditors/postdata')
.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')
})
.catch((err: any) => {
@@ -176,7 +183,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
public async callChangesInfo(tableId: any) {
await this.sasStoreService
.getChangeInfo(tableId)
.then((res: any) => {
.then((res: PublicGetChangeinfo) => {
this.tableDetails = res.jsparams[0]
this.jsParams = res.jsparams[0]
@@ -189,6 +196,11 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
}
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) => {
this.acceptLoading = false

View File

@@ -33,12 +33,34 @@
<div class="clr-col-md-12" ng-if="loaded">
<div *ngIf="approveList && remained !== 0">
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
<clr-dg-column [clrDgField]="'baseTable'">BASE TABLE</clr-dg-column>
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
<clr-dg-column [clrDgField]="'submitReason'"
>SUBMIT REASON</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]="'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>DOWNLOAD</clr-dg-column>
@@ -51,15 +73,19 @@
<clr-dg-cell>{{ approveItem.submitReason }}</clr-dg-cell>
<clr-dg-cell>
<div
class="clr-row"
role="tooltip"
class="d-flex justify-content-around"
class="clr-row d-flex justify-content-around"
role="toolbar"
aria-label="Table actions"
>
<a
class="column-center links tooltip tooltip-md tooltip-bottom-left color-green"
(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>
</a>
<a
@@ -70,10 +96,12 @@
*ngIf="!approveItem.rejectLoading"
shape="ban"
size="22"
aria-hidden="true"
></clr-icon>
<clr-spinner
*ngIf="approveItem.rejectLoading"
[clrSmall]="true"
aria-hidden="true"
></clr-spinner>
<span class="tooltip-content">Reject</span>
</a>
@@ -81,7 +109,11 @@
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
(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>
</a>
</div>

View File

@@ -8,6 +8,7 @@ import { SasStoreService } from '../../services/sas-store.service'
import { Router } from '@angular/router'
import { SasService } from '../../services/sas.service'
import { EventService } from '../../services/event.service'
import { ClrDatagridStringFilterInterface } from '@clr/angular'
interface ApproveData {
tableId: string
@@ -19,6 +20,32 @@ interface ApproveData {
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({
selector: 'app-approve',
templateUrl: './approve.component.html',
@@ -35,6 +62,12 @@ export class ApproveComponent implements OnInit {
public loaded: boolean = false
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(
private sasStoreService: SasStoreService,
private eventService: EventService,

View File

@@ -85,16 +85,48 @@
class="datagrid-history datagrid-custom-footer"
*ngIf="loaded"
>
<clr-dg-column [clrDgField]="'basetable'">BASE_TABLE</clr-dg-column>
<clr-dg-column [clrDgField]="'status'">STATUS</clr-dg-column>
<clr-dg-column [clrDgField]="'submitter'">SUBMITTER</clr-dg-column>
<clr-dg-column [clrDgField]="'submittedReason'"
>SUBMIT REASON</clr-dg-column
>
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
<clr-dg-column [clrDgField]="'reviewed'"
>APPROVED / REJECTED</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]="'status'">
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-row

View File

@@ -8,6 +8,55 @@ import {
EventService,
SasService
} 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({
selector: 'app-history',
@@ -29,6 +78,14 @@ export class HistoryComponent implements OnInit {
public approveData: any = {}
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 } = {
HIST: 0,
STARTROW: 1,

View File

@@ -44,10 +44,20 @@
<div *ngIf="submitterList && remained !== 0">
<clr-datagrid class="datagrid-compact datagrid-custom-footer">
<clr-dg-column>BASE TABLE</clr-dg-column>
<clr-dg-column [clrDgField]="'submitted'">SUBMITTED</clr-dg-column>
<clr-dg-column [clrDgField]="'submitReason'"
>SUBMIT REASON</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 class="d-flex justify-content-center"
>ACTION</clr-dg-column
>

View File

@@ -7,6 +7,7 @@ import {
import { Subscription } from 'rxjs'
import { ActivatedRoute, Router } from '@angular/router'
import { SasStoreService, EventService, SasService } from '../../services'
import { ClrDatagridStringFilterInterface } from '@clr/angular'
interface SubmitterData {
tableId: string
@@ -16,6 +17,22 @@ interface SubmitterData {
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({
selector: 'app-submitter',
templateUrl: './submitter.component.html',
@@ -37,6 +54,10 @@ export class SubmitterComponent implements OnInit, AfterViewInit {
private _readySub!: Subscription
private _backToSub!: Subscription
// Filter instances for datagrid accessibility
public submittedFilter = new SubmittedFilter()
public submitReasonFilter = new SubmitReasonFilter()
constructor(
private sasStoreService: SasStoreService,
private eventService: EventService,

View File

@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core'
import cloneDeep from 'lodash-es/cloneDeep'
import * as CryptoMD5 from 'crypto-js/md5'
import { SasService } from './sas.service'
const librariesToShow = 50
@@ -12,7 +13,7 @@ export class HelperService {
public loadMoreCount: number = librariesToShow
public isMicrosoft: boolean = false
constructor() {
constructor(private sasService: SasService) {
this.isMicrosoft = this.isIEorEDGE()
console.log('Is IE or Edge?', this.isMicrosoft)
}
@@ -314,6 +315,20 @@ export class HelperService {
}
}
public isStreamingViya(): boolean {
const serverType = this.sasService.getServerType()
if (serverType !== 'SASVIYA') return false
if (
location.search.toLowerCase().includes('?_file=') &&
location.pathname.toLowerCase().includes('/sasjobexecution')
)
return true
return false
}
// Required type is NodeJS.Timeout
// But NodeJS is not available in browser so we have to go with any
private debounceTimeout: any

View File

@@ -333,6 +333,10 @@ export class SasService {
})
}
public reloadStartupData() {
this.loadStartupServiceEmitter.emit()
}
public getLicenseSiteId(): string[] {
return this.license_site_id.value || []
}
@@ -657,4 +661,17 @@ export class SasService {
}
}
}
// Viya specific functions
public getFileContent(folderPath: string, fileName: string) {
return this.sasjsAdapter.getFileContent(folderPath, fileName)
}
public updateFileContent(
folderPath: string,
fileName: string,
content: string
) {
return this.sasjsAdapter.updateFileContent(folderPath, fileName, content)
}
}

View File

@@ -46,6 +46,10 @@
rejected: tableDetails?.REVIEW_STATUS_ID === 'REJECTED',
accepted: tableDetails?.REVIEW_STATUS_ID === 'APPROVED'
}"
[attr.aria-label]="
'Review status: ' + tableDetails?.REVIEW_STATUS_ID
"
role="status"
>
{{ tableDetails?.REVIEW_STATUS_ID }}
</span>
@@ -61,6 +65,7 @@
class="btn btn-sm btn-outline text-center mr-5i"
(click)="viewerTableScreen()"
[disabled]="revertingChanges"
aria-label="View base table"
>
View base table
</button>
@@ -74,6 +79,7 @@
"
(click)="approveTableScreen()"
[disabled]="revertingChanges"
aria-label="Approve table"
>
Approve
</button>
@@ -81,14 +87,16 @@
class="btn btn-sm btn-info-outline text-center mr-5i"
(click)="goBack()"
[disabled]="revertingChanges"
aria-label="Edit base table"
>
Edit base table
</button>
<button
class="btn btn-sm btn-success text-center mr-5i min-w-0"
(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>
<clr-tooltip>
@@ -98,6 +106,7 @@
clrTooltipTrigger
[clrLoading]="revertingChanges"
class="btn btn-sm btn-danger text-center mt-20"
aria-label="Revert this and all subsequent changes"
>
REVERT
@@ -128,6 +137,8 @@
[afterGetColHeader]="hotTable.afterGetColHeader"
stretchH="all"
[cells]="hotTable.cells"
[settings]="hotTable.settings"
aria-label="Staged data table"
>
<!--[licenseKey]=null-->
</hot-table>

View File

@@ -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 { Router } from '@angular/router'
import { ActivatedRoute } from '@angular/router'
@@ -19,7 +25,7 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
},
encapsulation: ViewEncapsulation.None
})
export class StageComponent implements OnInit {
export class StageComponent implements OnInit, AfterViewInit {
public table_id: any
public jsParams: any
public keysArray: any
@@ -32,12 +38,26 @@ export class StageComponent implements OnInit {
colHeaders: [],
columns: [],
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,
maxRows: this.licenceState.value.stage_rows_allowed || Infinity,
afterGetColHeader: (column, th, headerLevel) => {
// Dark mode
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() {
this.revertingChanges = true
@@ -204,4 +231,27 @@ export class StageComponent implements OnInit {
}
}, 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)
}
}

View File

@@ -598,6 +598,12 @@
{{ libinfo[0] ? libinfo[0].TABLE_CNT : '' }}
</td>
</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>
</ng-container>
</div>
@@ -616,6 +622,7 @@
<div *ngIf="!noData && !noDataReqErr && table" class="clr-flex-1">
<hot-table
#hotInstance
hotId="hotInstance"
id="hotTable"
className="htDark"

View File

@@ -3,9 +3,11 @@ import {
AfterContentInit,
ChangeDetectorRef,
AfterViewInit,
OnDestroy,
ViewChildren,
QueryList,
ViewEncapsulation
ViewEncapsulation,
ViewChild
} from '@angular/core'
import { SasStoreService } from '../services/sas-store.service'
import { Subscription } from 'rxjs'
@@ -49,10 +51,15 @@ import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapper
},
encapsulation: ViewEncapsulation.None
})
export class ViewerComponent implements AfterContentInit, AfterViewInit {
export class ViewerComponent
implements AfterContentInit, AfterViewInit, OnDestroy
{
@ViewChildren('queryFilter')
queryFilterCompList: QueryList<QueryComponent> = new QueryList()
@ViewChild('hotInstance', { static: false })
hotInstanceViewChild!: Handsontable
public libraries!: Array<any>
public librariesPaging: boolean = false
public librariesSearch: string = ''
@@ -107,11 +114,14 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
public licenceState = this.licenceService.licenceState
public Infinity = Infinity
private ariaObserver: MutationObserver | undefined
private ariaCheckInterval: any | undefined
public hotTable: HotTableInterface = {
data: [],
colHeaders: [],
columns: [],
height: '100%',
height: 'calc(100vh - 182px)',
maxRows: this.licenceState.value.viewer_rows_allowed || Infinity,
settings: {},
licenseKey: undefined,
@@ -530,7 +540,6 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
`#search_${library.LIBRARYREF}`
)
this.loggerService.log('[libTreeSearchInput]', libTreeSearchInput)
if (libTreeSearchInput) libTreeSearchInput.focus()
if (library && library.libinfo) this.libinfo = library.libinfo
@@ -819,6 +828,9 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
this.versions = res.versions || []
this.setDSNote()
this.queryText = res.sasparams[0].FILTER_TEXT
// Initialize columns only if we have data
if (res.viewdata && res.viewdata.length > 0) {
let columns: any[] = []
let colArr = []
@@ -832,14 +844,18 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
columns.push({ data: colArr[index] })
}
let cells = function () {
let cellProperties = { readOnly: true }
return cellProperties
}
this.hotTable.colHeaders = colArr
this.hotTable.columns = columns
this.hotTable.cells = cells
} else {
// Set empty arrays if no data
this.hotTable.colHeaders = []
this.hotTable.columns = []
}
// Set cells function
this.hotTable.cells = () => {
return { readOnly: true }
}
this.tableFlag = false
let ds = []
@@ -911,6 +927,11 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
//That is intorduced by HOT update
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.
* 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) {
this.hotInstance.updateSettings({
height: this.hotTable.height,
modifyColWidth: function (width: any, col: any) {
modifyColWidth: (width: any, col: any) => {
if (width > 500) return 500
else return width
},
@@ -1081,8 +1102,26 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
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)
}
@@ -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() {
if (this.hotTable.data.length > 0) {

View File

@@ -1042,12 +1042,12 @@ app-approve {
// HISTORY.COMPONENT
app-history {
.rejected {
color: #f83126;
color: #92201a;
font-weight: bold
}
.accepted {
color: #3fc424;
color: #105c26;
font-weight: bold
}
@@ -1206,6 +1206,8 @@ app-viewer {
}
hot-table {
height: calc(100vh - 200px);
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background-color: #306b00b0 !important;
@@ -3442,6 +3444,10 @@ app-approve-details {
width: 175px
}
#rejectBtn {
background-color: #a62f16 !important;
}
.formatted-values-toggle {
min-width: 75px
}
@@ -3630,12 +3636,12 @@ app-excel-password-modal {
// STAGE.COMPONENT
app-stage {
.rejected {
color: #f83126;
color: #92201a;
font-weight: bold
}
.accepted {
color: #3fc424;
color: #105c26;
font-weight: bold
}
@@ -3644,6 +3650,25 @@ app-stage {
margin-top:10px;
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"] {

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "dcfrontend",
"version": "6.14.7",
"version": "7.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dcfrontend",
"version": "6.14.7",
"version": "7.0.3",
"hasInstallScript": true,
"devDependencies": {
"@saithodev/semantic-release-gitea": "^2.1.0",
@@ -16,7 +16,7 @@
"@semantic-release/npm": "11.0.0",
"@semantic-release/release-notes-generator": "^11.0.4",
"commit-and-tag-version": "^11.2.2",
"prettier": "3.2.5"
"prettier": "3.6.2"
}
},
"node_modules/@babel/code-frame": {
@@ -6690,9 +6690,9 @@
}
},
"node_modules/prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"bin": {

View File

@@ -1,6 +1,6 @@
{
"name": "dcfrontend",
"version": "6.15.2",
"version": "7.0.3",
"description": "Data Controller",
"devDependencies": {
"@saithodev/semantic-release-gitea": "^2.1.0",
@@ -10,7 +10,7 @@
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^11.0.4",
"commit-and-tag-version": "^11.2.2",
"prettier": "3.2.5"
"prettier": "3.6.2"
},
"scripts": {
"install": "cd client && npm i && cd ../sas && npm i",
@@ -18,8 +18,11 @@
"release": "commit-and-tag-version",
"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: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\"",
"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": {
"type": "git",

213
sas/package-lock.json generated
View File

@@ -6,8 +6,8 @@
"": {
"name": "dc-sas",
"dependencies": {
"@sasjs/cli": "^4.12.7",
"@sasjs/core": "^4.58.1"
"@sasjs/cli": "^4.12.10",
"@sasjs/core": "^4.59.5"
}
},
"node_modules/@coolaj86/urequest": {
@@ -30,29 +30,29 @@
}
},
"node_modules/@sasjs/adapter": {
"version": "4.11.3",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.11.3.tgz",
"integrity": "sha512-KF6G4vzs4l4efjpCD02og3kB44uFfJ1u2UWu749VdHtLKNN9l+PO26/moR+YAmRmmz2I9sC3X09fZE1nlN6zgw==",
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.12.2.tgz",
"integrity": "sha512-OV5mx3N2Hywhp0M+SBLTuG42x/LDnMxrb2/pwG4RQbhfzvAwdHVEoXRouDJ49RMSY9s6TJcwUPh+Xzafl5sG/g==",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@sasjs/utils": "3.5.2",
"axios": "1.8.2",
"axios-cookiejar-support": "5.0.5",
"form-data": "4.0.0",
"form-data": "4.0.4",
"https": "1.0.0",
"tough-cookie": "4.1.3"
}
},
"node_modules/@sasjs/cli": {
"version": "4.12.7",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.7.tgz",
"integrity": "sha512-KcXSR+3dRgINOLiN+7oJbzWsNQu7qm1YQ7eaVqiHTZI429BSgZez9+7p1bq09R4otHN8IzMAgLP9se/r9p9yJA==",
"version": "4.12.10",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.12.10.tgz",
"integrity": "sha512-eWh7cTtIEH9PnKdooRWZ8yWVle4jPb0D7k+QKaPSBIW+ZbtyS87yNVU4MwkonSKCHneuhvrMMo8YV8QrqoUo7Q==",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@sasjs/adapter": "4.11.3",
"@sasjs/core": "4.58.1",
"@sasjs/adapter": "4.12.2",
"@sasjs/core": "4.59.1",
"@sasjs/lint": "2.4.3",
"@sasjs/utils": "3.5.2",
"adm-zip": "0.5.10",
@@ -76,10 +76,16 @@
"sasjs": "build/index.js"
}
},
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.1.tgz",
"integrity": "sha512-52GNI4nIll5YivI8uobWrucE6TkHcTjcbKTr/YPC9eTauC4sh0V0MptebfAJ5E6vE5P2WevNZGr42KdDpckLpg==",
"license": "MIT"
},
"node_modules/@sasjs/core": {
"version": "4.58.1",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.58.1.tgz",
"integrity": "sha512-Qp6KAtp1VZcmN5HLGSIUE9H41qpFuihWLbjNygOYp+NRs/Y8VagpHrYeyIQbh3cSgchiJEMXudLql8hoU06wpg==",
"version": "4.59.5",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.59.5.tgz",
"integrity": "sha512-PAPinHjuFi3P/MxkZHGSF0iLWoBFnpxon7/SshHPcBRmSQwmFOvh9X8xdE8ZClqY9XDOwFZ6sqeOQ7GYZliLfw==",
"license": "MIT"
},
"node_modules/@sasjs/lint": {
@@ -302,9 +308,10 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -341,6 +348,19 @@
"node": ">= 0.8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -555,6 +575,20 @@
"node": ">=12"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -571,6 +605,51 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
@@ -614,12 +693,15 @@
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -661,6 +743,43 @@
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -681,6 +800,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -695,6 +826,33 @@
"node": ">=8"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -742,9 +900,9 @@
}
},
"node_modules/http-cookie-agent/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
@@ -1048,6 +1206,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",

View File

@@ -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",
"viya": "npm run viyacbd && sasjs test",
"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",
"sasdocs": "sasjs doc && ./sasjs/utils/deploydocs.sh"
},
"private": true,
"dependencies": {
"@sasjs/cli": "^4.12.7",
"@sasjs/core": "^4.58.1"
"@sasjs/cli": "^4.12.10",
"@sasjs/core": "^4.59.5"
}
}

View 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));

View 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));

View 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));

View File

@@ -14,5 +14,6 @@ create table &curlib..mpe_datastatus_libs(
libref char(8) label='Library Name',
libsize num format=SIZEKMG. label='Size of file',
table_cnt num label='Number of Tables',
catalog_cnt num label='Number of Catalogs',
constraint pk_mpe_datastatus_libs
primary key(libref,tx_to));

View 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));

View File

@@ -14,8 +14,8 @@ create table &curlib..mpe_datastatus_tabs(
libref char(8) label='Library Name',
dsn char(64) label='Member Name',
filesize num format=SIZEKMG. label='Size of file',
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
crdate num format=DATETIME. label='Date Created',
modate num format=DATETIME. label='Date Modified',
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
constraint pk_mpe_datastatus_tabs
primary key(libref,dsn,tx_to));

View File

@@ -29,7 +29,7 @@ create table dc.mpe_datacatalog_TABS(
nvar num label='Number of Variables',
compress char(8) label='Compression Routine',
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
primary key(libref,dsn,tx_to));
@@ -67,8 +67,8 @@ create table dc.mpe_datacatalog_vars(
libref char(8) label='Library Name',
dsn char(64) label='Member Name',
filesize num format=SIZEKMG. label='Size of file',
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
crdate num format=DATETIME. label='Date Created',
modate num format=DATETIME. label='Date Modified',
nobs num label='Number of Physical (Actual, inc. deleted) Observations',
constraint pk
primary key(libref,dsn,tx_to));

View 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));

View File

@@ -14,14 +14,31 @@
**/
%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 libds=%upcase(&libds);
%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_;
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('ddld',dd_longdesc,'l');
run;

View File

@@ -34,4 +34,15 @@ run;
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
)

View File

@@ -106,6 +106,19 @@ proc datasets lib=&lib noprint;
/nomiss unique;
quit;
proc sql;
create table &lib..mpe_datacatalog_CATS(
TX_FROM float &notnull 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(
TX_FROM num &notnull format=datetime19.3,
TX_TO num &notnull format=datetime19.3,
@@ -125,6 +138,23 @@ proc datasets lib=&lib noprint;
/nomiss unique;
quit;
proc sql;
create table &lib..mpe_datacatalog_OBJS(
TX_FROM num &notnull format=datetime19.,
TX_TO num &notnull format=datetime19.,
libref char(8) &notnull label='Library Name',
memname char(64) &notnull label='Member Name',
objname char(32) &notnull label='Object Name',
objtype char(8) &notnull 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(
TX_FROM num &notnull format=datetime19.3,
TX_TO num &notnull format=datetime19.3,
@@ -137,7 +167,7 @@ create table &lib..mpe_datacatalog_TABS(
nvar num label='Number of Variables',
compress char(8) label='Compression Routine',
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;
proc datasets lib=&lib noprint;
modify mpe_datacatalog_TABS;
@@ -169,12 +199,29 @@ proc datasets lib=&lib noprint;
/nomiss unique;
quit;
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 &notnull label='Number of objects',
created num &notnull 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(
TX_FROM num &notnull format=datetime19.3,
TX_TO num &notnull format=datetime19.3,
libref char(8) label='Library Name',
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;
proc datasets lib=&lib noprint;
modify mpe_datastatus_libs;
@@ -183,14 +230,32 @@ proc datasets lib=&lib noprint;
/nomiss unique;
quit;
proc sql;
create table &lib..mpe_datastatus_OBJS(
TX_FROM float &notnull format=datetime19.,
TX_TO float &notnull 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 &notnull 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(
TX_FROM num &notnull format=datetime19.3,
TX_TO num &notnull format=datetime19.3,
libref char(8) label='Library Name',
dsn char(64) label='Member Name',
filesize num format=SIZEKMG. label='Size of file',
crdate num format=DATETIME. informat=DATETIME. label='Date Created',
modate num format=DATETIME. informat=DATETIME. label='Date Modified',
crdate num format=DATETIME. label='Date Created',
modate num format=DATETIME. label='Date Modified',
nobs num label='Number of Physical (Actual, inc. deleted) Observations'
);quit;
proc datasets lib=&lib noprint;

View 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
)

View 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;

View 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)

View File

@@ -48,11 +48,6 @@ create table cols as
,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;
proc sql;
connect using &lib;
@@ -175,6 +170,11 @@ create table cols as
%end;
%mp_abort(iftrue= (&syscc ne 0)
,mac=&_program
,msg=%str(syscc=&syscc afer &lib indexes extraction)
)
/* load columns */
%bitemporal_dataloader(base_lib=&mpelib
,base_dsn=mpe_datacatalog_vars
@@ -314,12 +314,32 @@ proc sql;
%if &ds = #ALL %then %do;
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
,sum(filesize) as libsize
,count(*) as table_cnt
from statustabs
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
,base_dsn=mpe_datastatus_libs

View 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
)

View File

@@ -94,38 +94,41 @@
},
{
"name": "viya",
"serverUrl": "https://sas.4gl.io",
"serverUrl": "https://viya-f0g8ht62vq.engage.sas.com",
"serverType": "SASVIYA",
"httpsAgentOptions": {
"allowInsecureRequests": false
},
"appLoc": "/Public/app/dcplaceholder",
"deployConfig": {
"deployServicePack": true,
"deployScripts": [
"sasjs/utils/viyadeploy.sh"
]
},
"macroFolders": [
"sasjs/targets/viya/macros_viya"
],
"programFolders": [
"sasjs/db/datactrl"
],
"binaryFolders": [],
"buildConfig": {
"initProgram": "sasjs/utils/buildinitviya.sas",
"buildResultsFolder": "sasjsresults",
"buildOutputFolder": "sasjsbuild",
"buildOutputFileName": "viya.sas"
"buildOutputFileName": "viya.sas",
"termProgram": "",
"macroVars": {}
},
"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"
]
},
"deployConfig": {
"deployServicePack": true,
"deployScripts": [
"sasjs/utils/viyadeploy.sh"
]
],
"initProgram": "sasjs/utils/serviceinitviya.sas",
"termProgram": "",
"macroVars": {}
},
"streamConfig": {
"streamWeb": true,
@@ -137,54 +140,6 @@
},
"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",
"appLoc": "/30.SASApps/app/vtest",

View File

@@ -675,7 +675,7 @@ data xl_rules;
keep xl_column xl_rule;
run;
%mpe_dsmeta(&libds, outds=dsmeta)
%mpe_dsmeta(&orig_libds, outds=dsmeta)
%mpe_getversions(&mpelib,
%scan(&orig_libds,1,.),

View File

@@ -54,7 +54,10 @@ data _null_;
/* if a TXTEMPORAL table then filter as such */
call symputx('txfrom',var_txfrom);
call symputx('txto',var_txto);
run;
/* if a format, extract relevant info */
data _null_;
ds=symget('ds');
is_fmt=0;
if subpad(cats(reverse(ds)),1,3)=:'CF-' then do;

View File

@@ -76,3 +76,37 @@ run;
test=EQUALS 3,
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
)

View File

@@ -74,7 +74,8 @@ create table work.libinfo as
a.schemas,
a.libid,
b.libsize,
b.table_cnt
b.table_cnt,
b.catalog_cnt
from &mpelib..mpe_datacatalog_libs(where=(&dc_dttmtfmt. lt tx_to)) a
inner join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
on a.libref=b.libref

View File

@@ -355,7 +355,7 @@ run;
%mp_getcols(&libds, outds=cols)
%mpe_dsmeta(&libds, outds=dsmeta)
%mpe_dsmeta(&orig_libds, outds=dsmeta)
%mpe_getversions(&mpelib,
%scan(&orig_libds,1,.),

View File

@@ -167,7 +167,8 @@ create table work.libinfo as
a.schemas,
a.libid,
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
left join &mpelib..mpe_datastatus_libs(where=(&dc_dttmtfmt. lt tx_to)) b
on a.libref=b.libref

View File

@@ -9,6 +9,7 @@
<h4> SAS Macros </h4>
@li mpe_refreshlibs.sas
@li dc_assignlib.sas
@li mpe_refreshcatalogs.sas
@li mpe_refreshtables.sas
@li mm_getrepos.sas
@@ -44,6 +45,7 @@ run;
%dc_assignlib(WRITE,&libref) /* write just in order to assign direct lib */
%mpe_refreshlibs(lib=&libref)
%mpe_refreshtables(&libref)
%mpe_refreshcatalogs(&libref)
%end;
%else %do xx=1 %to &repocnt;
options metarepository=&&repo&xx;
@@ -73,6 +75,7 @@ run;
%do i=1 %to &libcnt;
%dc_assignlib(WRITE,&&lib&i)
%mpe_refreshtables(&&lib&i)
%mpe_refreshcatalogs(&&lib&i)
%end;
%end;

View File

@@ -9,6 +9,7 @@
<h4> SAS Macros </h4>
@li mpe_refreshlibs.sas
@li dc_assignlib.sas
@li mpe_refreshcatalogs.sas
@li mpe_refreshtables.sas
@@ -34,6 +35,8 @@ data libraries;
str=cats('%mpe_refreshtables(',libref,')');
put str;
putlog str;
str=cats('%mpe_refreshcatalogs(',libref,')');
put str;
run;
%inc executor/source2;

View File

@@ -10,6 +10,7 @@
@li mpe_refreshlibs.sas
@li dc_assignlib.sas
@li mpe_refreshtables.sas
@li mpe_refreshcatalogs.sas
@version 3.4
@@ -19,19 +20,29 @@
4GL Apps Ltd.
**/
%macro dc_refreshcatalog();
%macro dc_refreshcatalog(libref);
%mpe_refreshlibs()
filename executor catalog 'work.code.code.source';
data libraries;
%if #&libref# ne ## %then %do;
%put &sysmacroname: assigning specific libref, &libref;
%dc_assignlib(WRITE,&libref) /* write just in order to assign direct lib */
%mpe_refreshlibs(lib=&libref)
%mpe_refreshtables(&libref)
%mpe_refreshcatalogs(&libref)
%end;
%else %do;
%mpe_refreshlibs()
filename executor catalog 'work.code.code.source';
data libraries;
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;
run;
%inc executor;
%end;
%mend dc_refreshcatalog;

View File

@@ -21,7 +21,7 @@ REMOVE THAT LAST MACRO
%let syscc=0;
%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;
options mprint mprintnest nobomfile lrecl=32767;

View File

@@ -28,7 +28,7 @@
%global apploc _program;
%let defaultcontext=Datacontroller compute context;
%let defaultcontext=SAS Job Execution compute context;
data _null_;
length _pgm $1000;
@@ -60,6 +60,9 @@ run;
%let testloc=%sysfunc(pathname(&DC_LIBREF))/fmt%mf_getuniquefileref();
%mf_mkdir(&testloc)
libname dctest "&testloc";
/* test library with only one format catalog */
%mf_mkdir(&testloc/fmtonly)
libname fmtonly "&testloc/fmtonly";
/* add formats */
PROC FORMAT library=dctest.dcfmts;
@@ -99,6 +102,10 @@ run;
proc append base=&dc_libref..mpe_tables data=work.append;
run;
/* lone format catalog */
proc format cntlin=work.fmts library=fmtonly.dcfmts;
run;
/* add some other tables */
%mp_coretable(LOCKTABLE,libds=dctest.locktable)
%mp_coretable(DIFFTABLE,libds=dctest.difftable)
@@ -130,6 +137,7 @@ options mprint;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
data _null_;
@@ -151,6 +159,7 @@ options mprint;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
%ms_deletefile(&root/services/public/settings.sas)
@@ -182,6 +191,7 @@ options mprint;
put _infile_;
if last then do;
put "libname dctest '&testloc';";
put "libname fmtonly '&testloc/fmtonly';";
end;
run;
%mm_deletestp(target=&root/services/public/Data_Controller_Settings)

View File

@@ -2,6 +2,11 @@
@file buildinitviya.sas
@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;
@@ -10,3 +15,7 @@ options nonotes nomprint;
%let apploc=%sysfunc(ifc("&apploc"="/Public/app/dcplaceholder"
,/Users/&sysuserid/My Folder/Data Controller
,&apploc));
/* ensure the correct casing of appLoc */
%mv_createfolder(path=&apploc)
%let apploc=%mfv_getfolderpath(%mfv_getpathuri(&apploc));