Compare commits

...

43 Commits

Author SHA1 Message Date
semantic-release-bot 0f60fd7181 chore(release): 7.7.2 [skip ci]
## [7.7.2](https://git.datacontroller.io/dc/dc/compare/v7.7.1...v7.7.2) (2026-05-07)

### Bug Fixes

* **client:** bundle Metropolis font locally to satisfy CSP ([9546fcd](9546fcd631))
* **client:** clear angular build cache on font strip to avoid stale dist ([503cb08](503cb08b2f))
* **client:** postinstall removal of Metropolis [@font-face](https://git.datacontroller.io/font-face) from @clr/ui ([e6397ce](e6397cecc1))
* **client:** serve text-security-disc font locally ([80ce80e](80ce80ece4))
* **editor:** preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT ([05a3289](05a328976e))
2026-05-07 15:18:44 +00:00
allan 251062e42e Merge pull request 'Multiple frontend client issues' (#228) from 227-csp-issues-20260507 into main
Release / Build-production-and-ng-test (push) Successful in 3m38s
Release / Build-and-test-development (push) Successful in 8m50s
Release / release (push) Successful in 7m35s
Reviewed-on: #228
2026-05-07 15:03:08 +00:00
sead 05a328976e fix(editor): preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT
Build / Build-and-ng-test (pull_request) Successful in 3m43s
Build / Build-and-test-development (pull_request) Successful in 9m21s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m25s
2026-05-07 15:02:06 +02:00
sead 503cb08b2f fix(client): clear angular build cache on font strip to avoid stale dist 2026-05-07 13:43:57 +02:00
sead f71be20476 build(client): drop duplicate asset emits for fonts and CSS-referenced svgs 2026-05-07 13:43:49 +02:00
sead e6397cecc1 fix(client): postinstall removal of Metropolis @font-face from @clr/ui 2026-05-07 13:43:37 +02:00
sead 80ce80ece4 fix(client): serve text-security-disc font locally 2026-05-07 13:43:21 +02:00
sead 9546fcd631 fix(client): bundle Metropolis font locally to satisfy CSP 2026-05-07 13:43:05 +02:00
semantic-release-bot b79aaf4327 chore(release): 7.7.1 [skip ci]
## [7.7.1](https://git.datacontroller.io/dc/dc/compare/v7.7.0...v7.7.1) (2026-05-05)

### Bug Fixes

* **client:** bump adapter ([d26f7d2](d26f7d2511))
* **sas:** bump cli ([d60029d](d60029deae))
2026-05-05 20:04:33 +00:00
allan 76f9198f73 Merge pull request 'fix(client): bump adapter' (#226) from fix/adapter-20260505 into main
Release / Build-production-and-ng-test (push) Successful in 3m28s
Release / Build-and-test-development (push) Successful in 8m44s
Release / release (push) Successful in 7m33s
Reviewed-on: #226
2026-05-05 19:49:17 +00:00
4gl d60029deae fix(sas): bump cli
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 9m9s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m10s
2026-05-05 18:48:15 +01:00
sead d26f7d2511 fix(client): bump adapter
Build / Build-and-ng-test (pull_request) Successful in 4m21s
Build / Build-and-test-development (pull_request) Successful in 10m34s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m37s
2026-05-05 17:16:55 +02:00
semantic-release-bot 33efe09b50 chore(release): 7.7.0 [skip ci]
# [7.7.0](https://git.datacontroller.io/dc/dc/compare/v7.6.0...v7.7.0) (2026-05-04)

### Bug Fixes

* bump adapter to 4.16.6 ([1707f38](1707f3802a))
* remove data:image/svg+xml CSP violation, use class instead changing style directly ([d66eb5d](d66eb5dfc2))
* remove WORK, SASUSER and CASUSER as library options.  [#224](#224) ([ec66631](ec66631a33))

### Features

* auto-save CAS tables [#224](#224) ([40d04a5](40d04a53c4))
* autoload CAS tables. [#224](#224) ([d5ebb01](d5ebb01ce3))
2026-05-04 23:24:36 +00:00
4gl e0aef9bf00 chore: pin got lib to enable release flow
Release / Build-production-and-ng-test (push) Successful in 11m47s
Release / Build-and-test-development (push) Successful in 17m37s
Release / release (push) Successful in 8m18s
2026-05-04 23:51:19 +01:00
allan 02d1a2e0b1 Merge pull request 'fix: resolve CSP violation and update dependancies' (#223) from fix/audit-20260413 into main
Release / Build-production-and-ng-test (push) Successful in 8m48s
Release / Build-and-test-development (push) Successful in 17m50s
Release / release (push) Failing after 5m52s
Reviewed-on: #223
Reviewed-by: allan <allan@4gl.io>
2026-05-04 16:50:52 +00:00
sead 4e3154e929 chore(cypress): enable e2e video, folder guards
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 9m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m21s
2026-05-04 12:57:31 +02:00
sead 32c0713256 chore: add hyperformula license exception
Build / Build-and-ng-test (pull_request) Successful in 3m44s
Build / Build-and-test-development (pull_request) Failing after 8m45s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m29s
2026-05-04 11:05:00 +02:00
sead defe15bcec chore: bump client eslint and sas sasjs packages
Build / Build-and-ng-test (pull_request) Failing after 1m34s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m47s
2026-05-04 10:42:31 +02:00
4gl 6f8e471f16 chore: dep man
Build / Build-and-ng-test (pull_request) Failing after 40s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 55s
2026-05-01 12:53:07 +01:00
4gl dc35abfd85 chore: dependency bumps
Build / Build-and-ng-test (pull_request) Failing after 42s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m0s
2026-05-01 12:36:39 +01:00
4gl 04a8c5d52a chore: bumping node
Build / Build-and-ng-test (pull_request) Failing after 50s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 57s
2026-05-01 11:46:10 +01:00
4gl 2cb370053d chore: rebuilt package lock
Build / Build-and-ng-test (pull_request) Failing after 42s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Failing after 1m3s
2026-05-01 11:34:30 +01:00
4gl 1707f3802a fix: bump adapter to 4.16.6
Lighthouse Checks / lighthouse (pull_request) Failing after 1m7s
Build / Build-and-ng-test (pull_request) Failing after 1m44s
Build / Build-and-test-development (pull_request) Has been skipped
2026-05-01 10:59:59 +01:00
allan c87ba660ca Merge branch 'main' into fix/audit-20260413
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 9m56s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m30s
2026-04-30 16:44:46 +00:00
allan ef8a2dbc38 Merge pull request 'fix: remove WORK, SASUSER and CASUSER as library options, plus auto CAS table load' (#225) from issue224 into main
Release / Build-production-and-ng-test (push) Failing after 1m28s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #225
2026-04-30 15:28:50 +00:00
4gl 40d04a53c4 feat: auto-save CAS tables #224
Build / Build-and-ng-test (pull_request) Successful in 4m2s
Build / Build-and-test-development (pull_request) Successful in 10m6s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m5s
2026-04-30 16:04:31 +01:00
4gl d5ebb01ce3 feat: autoload CAS tables. #224
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m54s
2026-04-30 15:41:20 +01:00
allan ec66631a33 fix: remove WORK, SASUSER and CASUSER as library options. #224
Build / Build-and-ng-test (pull_request) Successful in 4m15s
Build / Build-and-test-development (pull_request) Successful in 10m28s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m47s
2026-04-17 14:21:24 +01:00
sead d66eb5dfc2 fix: remove data:image/svg+xml CSP violation, use class instead changing style directly
Build / Build-and-ng-test (pull_request) Failing after 1m16s
Build / Build-and-test-development (pull_request) Has been skipped
Lighthouse Checks / lighthouse (pull_request) Successful in 18m7s
2026-04-13 10:29:54 +02:00
sead 731b589ed8 chore: override ajv and regenrate lock file 2026-04-13 09:23:13 +02:00
sead fe92d5fc36 chore: bump angular to latest 19 2026-04-13 08:58:54 +02:00
sead a335b400f1 chore: bump @sasjs/adapter 2026-04-13 08:55:48 +02:00
semantic-release-bot f63e507ddf chore(release): 7.6.0 [skip ci]
# [7.6.0](https://git.datacontroller.io/dc/dc/compare/v7.5.0...v7.6.0) (2026-04-03)

### Bug Fixes

* add label and tooltip for libref download, sanitise input ([52d5803](52d58036a4))

### Features

* configurable email alerts.  Closes [#217](#217) ([2ccf0d1](2ccf0d1100))
2026-04-03 22:17:14 +00:00
allan 991cc0567d Merge pull request 'feat: configurable email alerts. Closes #217' (#222) from issue217 into main
Release / Build-production-and-ng-test (push) Successful in 3m42s
Release / Build-and-test-development (push) Successful in 10m10s
Release / release (push) Successful in 7m48s
Reviewed-on: #222
2026-04-03 21:09:11 +00:00
sead 52d58036a4 fix: add label and tooltip for libref download, sanitise input
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Build / Build-and-test-development (pull_request) Successful in 10m13s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m37s
2026-04-03 19:55:42 +02:00
allan 26bff85792 chore: fix debug line
Build / Build-and-ng-test (pull_request) Successful in 4m47s
Build / Build-and-test-development (pull_request) Successful in 10m16s
Lighthouse Checks / lighthouse (pull_request) Successful in 19m41s
2026-04-03 18:35:48 +01:00
allan 2ccf0d1100 feat: configurable email alerts. Closes #217
Build / Build-and-ng-test (pull_request) Successful in 4m42s
Build / Build-and-test-development (pull_request) Has been cancelled
Lighthouse Checks / lighthouse (pull_request) Has been cancelled
2026-04-03 18:34:23 +01:00
semantic-release-bot 3be33186bc chore(release): 7.5.0 [skip ci]
# [7.5.0](https://git.datacontroller.io/dc/dc/compare/v7.4.1...v7.5.0) (2026-04-03)

### Bug Fixes

* add workflow audits, update deps ([66e98a9](66e98a96cb))
* allow CSV uploads with licence row limit ([5b260e4](5b260e4915)), closes [#213](#213)
* bumping cli and pinning versions in .npmrc ([80039f4](80039f4876))
* guard CSV upload with fileUpload licence flag ([ed40df6](ed40df6295))
* parse embed param from window.location.hash for hash router compatibility ([0269c24](0269c2421d))
* quote CSV char values.  Closes [#215](#215) ([d9980e8](d9980e866d))
* resolve outer promise in parseCsvFile for non-WLATIN1 path ([4ee15e1](4ee15e1b6e))
* use XLSX for CSV row truncation to handle new lines in values ([6d590c0](6d590c050d))

### Features

* add embed URL parameter to hide header and back button ([b0dc441](b0dc441d68)), closes [#214](#214)
* add target libref input to config download ([a89657b](a89657b0b8)), closes [#212](#212)
* export config service to allow dclib swapping.  Closes [#212](#212) ([326c26f](326c26fddf))
2026-04-03 11:06:36 +00:00
allan 1a7f950ae2 Merge pull request 'feat: enabling dclib switching when exporting config' (#220) from issue212 into main
Release / Build-production-and-ng-test (push) Successful in 3m39s
Release / Build-and-test-development (push) Successful in 9m55s
Release / release (push) Successful in 7m46s
Reviewed-on: #220
2026-04-03 10:49:43 +00:00
allan 8924dc8ab1 chore: merge buid.yaml
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 10m3s
Lighthouse Checks / lighthouse (pull_request) Successful in 18m46s
2026-04-03 10:30:05 +00:00
allan 0b0db1c543 chore: run audit check in build.yaml as well as release.yaml
Build / Build-and-ng-test (pull_request) Failing after 1m31s
Build / Build-and-test-development (pull_request) Successful in 10m23s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m32s
2026-04-03 01:18:54 +00:00
allan 80039f4876 fix: bumping cli and pinning versions in .npmrc
Build / Build-and-ng-test (pull_request) Successful in 3m51s
Build / Build-and-test-development (pull_request) Successful in 10m9s
Lighthouse Checks / lighthouse (24.5.0) (pull_request) Successful in 19m11s
2026-04-03 02:02:05 +01:00
allan 326c26fddf feat: export config service to allow dclib swapping. Closes #212 2026-04-03 02:01:44 +01:00
50 changed files with 7949 additions and 9003 deletions
+19 -15
View File
@@ -3,7 +3,7 @@ run-name: Running Lint Check and Licence checker on Pull Request
on: [pull_request]
env:
NODE_VERSION: '24.5.0'
NODE_VERSION: '24.15.0'
jobs:
Build-and-ng-test:
@@ -22,30 +22,33 @@ jobs:
apt install -y ./google-chrome*.deb
- name: Write .npmrc file
run: echo "$NPMRC" > client/.npmrc
run: echo "$NPMRC" >> client/.npmrc
shell: bash
env:
NPMRC: ${{ secrets.NPMRC}}
- name: Check audit
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --omit=dev
cd ./sas
npm audit --omit=dev
cd ../client
npm audit --audit-level=critical --omit=dev
- name: Lint check
run: npm run lint:check
- name: Install dependencies
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
echo "${{ secrets.SHEET_PWD }}" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
npm ci
- name: Check audit
# Audit should fail and stop the CI if critical vulnerability found
run: |
npm audit --audit-level=critical --omit=dev
cd ./sas
npm audit --audit-level=critical --omit=dev
cd ../client
npm audit --audit-level=critical --omit=dev
- name: Lint check
run: npm run lint:check
- name: Licence checker
run: |
cd client
@@ -139,6 +142,7 @@ jobs:
- name: Zip Cypress videos
if: always()
run: |
mkdir -p ./client/cypress/videos
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
+5 -2
View File
@@ -3,7 +3,7 @@ run-name: Running Lighthouse Performance and Accessibility Checks on Pull Reques
on: [pull_request]
env:
NODE_VERSION: '24.5.0'
NODE_VERSION: '24.15.0'
jobs:
lighthouse:
@@ -48,7 +48,10 @@ jobs:
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
echo "${{ secrets.SHEET_PWD }}" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
npm ci
npm install -g replace-in-files-cli
+1
View File
@@ -142,6 +142,7 @@ jobs:
- name: Zip Cypress videos
if: always()
run: |
mkdir -p ./client/cypress/videos
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
+3
View File
@@ -1 +1,4 @@
legacy-peer-deps=true
ignore-scripts=true
save-exact=true
fund=false
+67
View File
@@ -1,3 +1,70 @@
## [7.7.2](https://git.datacontroller.io/dc/dc/compare/v7.7.1...v7.7.2) (2026-05-07)
### Bug Fixes
* **client:** bundle Metropolis font locally to satisfy CSP ([9546fcd](https://git.datacontroller.io/dc/dc/commit/9546fcd6312f3e81f746ef6e32ef398810ed434a))
* **client:** clear angular build cache on font strip to avoid stale dist ([503cb08](https://git.datacontroller.io/dc/dc/commit/503cb08b2fa40397434189f9c20eff3358eb7010))
* **client:** postinstall removal of Metropolis [@font-face](https://git.datacontroller.io/font-face) from @clr/ui ([e6397ce](https://git.datacontroller.io/dc/dc/commit/e6397cecc13afe2a9238bdfb2b4b9b81f38d055c))
* **client:** serve text-security-disc font locally ([80ce80e](https://git.datacontroller.io/dc/dc/commit/80ce80ece40012e59c7cd0340b4aa9a9aca46443))
* **editor:** preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT ([05a3289](https://git.datacontroller.io/dc/dc/commit/05a328976ea3d1d6ef7559850369aa580f0d067f))
## [7.7.1](https://git.datacontroller.io/dc/dc/compare/v7.7.0...v7.7.1) (2026-05-05)
### Bug Fixes
* **client:** bump adapter ([d26f7d2](https://git.datacontroller.io/dc/dc/commit/d26f7d2511008634124c7d6fde115abb43db9c43))
* **sas:** bump cli ([d60029d](https://git.datacontroller.io/dc/dc/commit/d60029deae0ec21f3b8570461e2a4ca041d58f72))
# [7.7.0](https://git.datacontroller.io/dc/dc/compare/v7.6.0...v7.7.0) (2026-05-04)
### Bug Fixes
* bump adapter to 4.16.6 ([1707f38](https://git.datacontroller.io/dc/dc/commit/1707f3802a97de8c659f1a88c92fc917e8a30615))
* remove data:image/svg+xml CSP violation, use class instead changing style directly ([d66eb5d](https://git.datacontroller.io/dc/dc/commit/d66eb5dfc2dbb01f1e6c0c7d15fc2ad2a39dd829))
* remove WORK, SASUSER and CASUSER as library options. [#224](https://git.datacontroller.io/dc/dc/issues/224) ([ec66631](https://git.datacontroller.io/dc/dc/commit/ec66631a33aabb8ab2f92fe22c15440127085782))
### Features
* auto-save CAS tables [#224](https://git.datacontroller.io/dc/dc/issues/224) ([40d04a5](https://git.datacontroller.io/dc/dc/commit/40d04a53c4c00183116bdbd08397e0f2ffb1f578))
* autoload CAS tables. [#224](https://git.datacontroller.io/dc/dc/issues/224) ([d5ebb01](https://git.datacontroller.io/dc/dc/commit/d5ebb01ce381f5f4ec06de041f3ab9e632c02e43))
# [7.6.0](https://git.datacontroller.io/dc/dc/compare/v7.5.0...v7.6.0) (2026-04-03)
### Bug Fixes
* add label and tooltip for libref download, sanitise input ([52d5803](https://git.datacontroller.io/dc/dc/commit/52d58036a40e25847e900f9b04a77dbcc409c12b))
### Features
* configurable email alerts. Closes [#217](https://git.datacontroller.io/dc/dc/issues/217) ([2ccf0d1](https://git.datacontroller.io/dc/dc/commit/2ccf0d11000129629a0665421135b7530af9892f))
# [7.5.0](https://git.datacontroller.io/dc/dc/compare/v7.4.1...v7.5.0) (2026-04-03)
### Bug Fixes
* add workflow audits, update deps ([66e98a9](https://git.datacontroller.io/dc/dc/commit/66e98a96cbd092e762b94a04660f8e17ca003ceb))
* allow CSV uploads with licence row limit ([5b260e4](https://git.datacontroller.io/dc/dc/commit/5b260e49153dd85bc0023ad94d8a5f57b8ffa6dc)), closes [#213](https://git.datacontroller.io/dc/dc/issues/213)
* bumping cli and pinning versions in .npmrc ([80039f4](https://git.datacontroller.io/dc/dc/commit/80039f4876c8e09dc477678e1eff58329094c9e9))
* guard CSV upload with fileUpload licence flag ([ed40df6](https://git.datacontroller.io/dc/dc/commit/ed40df62953c3055770b5cbf50738f4a48b943cd))
* parse embed param from window.location.hash for hash router compatibility ([0269c24](https://git.datacontroller.io/dc/dc/commit/0269c2421db245f7f5405678605cb4d4587e2a67))
* quote CSV char values. Closes [#215](https://git.datacontroller.io/dc/dc/issues/215) ([d9980e8](https://git.datacontroller.io/dc/dc/commit/d9980e866d1a2fe7a731ff279d73accd35003e67))
* resolve outer promise in parseCsvFile for non-WLATIN1 path ([4ee15e1](https://git.datacontroller.io/dc/dc/commit/4ee15e1b6e83f27f279fc345e6998452a8f64d7e))
* use XLSX for CSV row truncation to handle new lines in values ([6d590c0](https://git.datacontroller.io/dc/dc/commit/6d590c050dcd593a73464fae5604f774f016b10d))
### Features
* add embed URL parameter to hide header and back button ([b0dc441](https://git.datacontroller.io/dc/dc/commit/b0dc441d681369e06eee58288dbdbb236f930bdc)), closes [#214](https://git.datacontroller.io/dc/dc/issues/214)
* add target libref input to config download ([a89657b](https://git.datacontroller.io/dc/dc/commit/a89657b0b81b9c531f64c0dda2714b4eb16c4bc9)), closes [#212](https://git.datacontroller.io/dc/dc/issues/212)
* export config service to allow dclib swapping. Closes [#212](https://git.datacontroller.io/dc/dc/issues/212) ([326c26f](https://git.datacontroller.io/dc/dc/commit/326c26fddfa88a0dc4ca79d3bd0c77c4d807f37c))
## [7.4.1](https://git.datacontroller.io/dc/dc/compare/v7.4.0...v7.4.1) (2026-03-12)
+2 -1
View File
@@ -62,7 +62,8 @@
{
"glob": "**/*",
"input": "src/images",
"output": "images"
"output": "images",
"ignore": ["spinner.svg", "caret.svg"]
}
],
"styles": ["src/styles.scss"],
+18 -17
View File
@@ -1,13 +1,13 @@
import { defineConfig } from "cypress";
import { defineConfig } from 'cypress'
export default defineConfig({
reporter: "mochawesome",
reporter: 'mochawesome',
reporterOptions: {
reportDir: "cypress/results",
reportDir: 'cypress/results',
overwrite: false,
html: true,
json: false,
json: false
},
viewportHeight: 900,
viewportWidth: 1600,
@@ -16,24 +16,25 @@ export default defineConfig({
defaultCommandTimeout: 30000,
env: {
hosturl: "http://localhost:4200",
appLocation: "",
site_id_SAS9: "70221618",
site_id_SASVIYA: "70253615",
site_id_SASJS: "123",
serverType: "SASJS",
libraryToOpenIncludes_SASVIYA: "viya",
libraryToOpenIncludes_SAS9: "dc",
libraryToOpenIncludes_SASJS: "dc",
hosturl: 'http://localhost:4200',
appLocation: '',
site_id_SAS9: '70221618',
site_id_SASVIYA: '70253615',
site_id_SASJS: '123',
serverType: 'SASJS',
libraryToOpenIncludes_SASVIYA: 'viya',
libraryToOpenIncludes_SAS9: 'dc',
libraryToOpenIncludes_SASJS: 'dc',
debug: false,
screenshotOnRunFailure: false,
longerCommandTimeout: 50000,
testLicenceUserLimits: false,
testLicenceUserLimits: false
},
e2e: {
video: true,
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
}
}
})
+1 -1
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-wrapper@16.0.1;handsontable@^16.0.1;handsontable@16.2.0;hyperformula@2.7.1;hyperformula@3.0.0;hyperformula@3.1.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
'@cds/city@1.1.0;@handsontable/angular-wrapper@16.0.1;handsontable@^16.0.1;handsontable@16.2.0;hyperformula@2.7.1;hyperformula@3.0.0;hyperformula@3.1.0;hyperformula@3.2.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
},
(error, json) => {
if (error) {
+5340 -6468
View File
File diff suppressed because it is too large Load Diff
+22 -19
View File
@@ -23,7 +23,7 @@
"watch": "ng test watch=true",
"pree2e": "webdriver-manager update",
"e2e": "protractor protractor.config.js",
"postinstall": "node ./src/version.ts && npm run add-githook",
"postinstall": "node ./src/version.ts && npm run add-githook && node ./scripts/strip-clr-base64-fonts.mjs",
"add-githook": "[ -d ../.git ] && git config core.hooksPath ./.git-hooks || true",
"cypress": "cypress open",
"cy:run": "cypress run",
@@ -37,21 +37,21 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^19.2.18",
"@angular/animations": "^19.2.20",
"@angular/cdk": "^19.2.19",
"@angular/common": "^19.2.18",
"@angular/compiler": "^19.2.18",
"@angular/core": "^19.2.18",
"@angular/forms": "^19.2.18",
"@angular/platform-browser": "^19.2.18",
"@angular/platform-browser-dynamic": "^19.2.18",
"@angular/router": "^19.2.18",
"@angular/common": "^19.2.20",
"@angular/compiler": "^19.2.20",
"@angular/core": "^19.2.20",
"@angular/forms": "^19.2.20",
"@angular/platform-browser": "^19.2.20",
"@angular/platform-browser-dynamic": "^19.2.20",
"@angular/router": "^19.2.20",
"@cds/core": "^6.15.1",
"@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-wrapper": "16.0.1",
"@sasjs/adapter": "^4.16.3",
"@sasjs/adapter": "^4.16.7",
"@sasjs/utils": "^3.5.3",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz",
"@types/d3-graphviz": "^2.6.7",
@@ -86,18 +86,18 @@
"zone.js": "~0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.2.19",
"@angular-devkit/build-angular": "^19.2.24",
"@angular-eslint/builder": "19.8.1",
"@angular-eslint/eslint-plugin": "19.8.1",
"@angular-eslint/eslint-plugin-template": "19.8.1",
"@angular-eslint/schematics": "19.8.1",
"@angular-eslint/template-parser": "19.8.1",
"@angular/cli": "^19.2.19",
"@angular/compiler-cli": "^19.2.18",
"@angular/cli": "^19.2.24",
"@angular/compiler-cli": "^19.2.20",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@compodoc/compodoc": "^1.1.21",
"@compodoc/compodoc": "^1.2.1",
"@cypress/webpack-preprocessor": "^5.17.1",
"@lhci/cli": "^0.12.0",
"@lhci/cli": "^0.15.1",
"@types/core-js": "^2.5.5",
"@types/crypto-js": "^4.2.1",
"@types/es6-shim": "^0.31.39",
@@ -105,15 +105,15 @@
"@types/lodash-es": "^4.17.3",
"@types/marked": "^4.3.0",
"@types/node": "12.20.50",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"@typescript-eslint/eslint-plugin": "8.31.1",
"@typescript-eslint/parser": "8.31.1",
"core-js": "^2.5.4",
"cypress": "12.17.1",
"cypress": "^15.14.2",
"cypress-file-upload": "^5.0.8",
"cypress-plugin-tab": "^1.0.5",
"cypress-real-events": "^1.8.1",
"es6-shim": "^0.35.5",
"eslint": "^8.33.0",
"eslint": "8.57.1",
"git-describe": "^4.0.4",
"jasmine-core": "~5.1.2",
"karma": "~6.4.3",
@@ -132,5 +132,8 @@
"typescript": "~5.8.3",
"wait-on": "^6.0.1",
"watch": "^1.0.2"
},
"overrides": {
"ajv": "8.18.0"
}
}
+59
View File
@@ -0,0 +1,59 @@
import { readFileSync, writeFileSync, statSync, rmSync, existsSync } from 'fs'
import { resolve } from 'path'
/**
* Remove Clarity's Metropolis @font-face blocks from clr-ui.min.css.
*
* Why: Clarity ships Metropolis as base64 data: URLs. The deployed app
* runs under CSP `default-src 'self'` (no data: font-src), so every page
* logs a font-load failure for each weight. Firefox preemptively
* validates every parsed src against CSP even when a later @font-face
* supersedes the rule at render time, so the only way to silence the
* console is to remove the offending blocks from the parsed CSS.
*
* Our styles.scss declares the same family/weight/style with same-origin
* .woff files, so removing Clarity's blocks entirely is safe and leaves
* Metropolis fully functional.
*
* Idempotent: matches by font-family, so works on a fresh install or a
* file that's already been stripped on a previous run.
*/
const target = resolve('node_modules/@clr/ui/clr-ui.min.css')
let css
try {
css = readFileSync(target, 'utf8')
} catch (err) {
if (err.code === 'ENOENT') {
console.log(`skip: ${target} not found (likely pre-install run)`)
process.exit(0)
}
throw err
}
const sizeBefore = statSync(target).size
const blockRe = /@font-face\{[^}]*Metropolis[^}]*\}/g
const matches = css.match(blockRe) ?? []
if (matches.length === 0) {
console.log(`already stripped: ${target}`)
process.exit(0)
}
const stripped = css.replace(blockRe, '')
writeFileSync(target, stripped)
const sizeAfter = Buffer.byteLength(stripped)
console.log(
`removed ${matches.length} Metropolis @font-face block(s) from clr-ui.min.css ` +
`(${sizeBefore} -> ${sizeAfter} bytes, saved ${sizeBefore - sizeAfter})`
)
// Webpack 5's persistent cache treats node_modules as immutable
// (snapshot.module.managedPaths default), so in-place edits don't
// invalidate cached entries. Drop the Angular build cache so the next
// build re-reads our stripped clr-ui.min.css.
const cacheDir = resolve('.angular/cache')
if (existsSync(cacheDir)) {
rmSync(cacheDir, { recursive: true, force: true })
console.log(`cleared ${cacheDir} (webpack persistent cache)`)
}
+2 -1
View File
@@ -13,6 +13,7 @@ import {
import { ActivatedRoute, Router } from '@angular/router'
import Handsontable from 'handsontable'
import { Subject, Subscription } from 'rxjs'
import { sanitiseForSas } from '../shared/utils/sanitise'
import { SasStoreService } from '../services/sas-store.service'
type AOA = any[][]
@@ -1669,7 +1670,7 @@ export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
this.submit = true
const updateParams: any = {}
updateParams.ACTION = 'LOAD'
this.message = this.message.replace(/\n/g, '. ')
this.message = sanitiseForSas(this.message.replace(/\n/g, '. '))
updateParams.MESSAGE = this.message
// updateParams.APPROVER = this.approver;
updateParams.LIBDS = this.libds
@@ -1,4 +1,5 @@
import { ActivatedRoute } from '@angular/router'
import { sanitiseForSas } from '../../shared/utils/sanitise'
import { SasStoreService } from '../../services/sas-store.service'
import {
Component,
@@ -136,7 +137,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
public async rejecting() {
this.rejectLoading = true
this.submitReason = this.submitReason.replace(/\n/g, '. ')
this.submitReason = sanitiseForSas(this.submitReason.replace(/\n/g, '. '))
let rejParams = {
STP_ACTION: 'REJECT_TABLE',
@@ -284,10 +284,18 @@ export class DcValidator {
)
if (source.length > 0) {
// For SAS num cols keep type='numeric' so HOT's numericEditor /
// numericRenderer + numbro coercion stay alive — same per-column
// model as the per-cell pattern in
// editor.component.ts:reSetCellValidationValues().
this.rules[i].source = source
this.rules[i].type = 'autocomplete'
this.rules[i].editor = 'autocomplete.custom'
this.rules[i].renderer = 'autocomplete'
this.rules[i].filter = false
if (this.rules[i].sasType !== 'num') {
this.rules[i].type = 'autocomplete'
}
}
if (this.hasDqRules(ruleColName, ['SOFTSELECT'])) {
@@ -10,6 +10,9 @@ export interface DcColumnSettings {
valid?: boolean
desc?: string
clsRule?: string
// SAS-side column type from $dataFormats (e.g. 'num', 'char') — distinct
// from Handsontable's `type` which drives renderer/editor selection
sasType?: string
}
export interface DcValidation extends HotColumnSettings, DcColumnSettings {}
@@ -38,11 +38,52 @@ describe('DC Validator - merge spec rules', () => {
data: 'test_col',
desc: 'test_desc',
clsRule: 'cls_rule',
length: 8
length: 8,
sasType: 'test_type'
}
]
expect(mergeColsRules(cols, rules, $dataFormats)).toEqual(expected)
expect(cols[0].TYPE).toEqual('test_type')
})
it('should populate sasType for num and char cols', () => {
const rules: DcValidation[] = [{ data: 'num_col' }, { data: 'char_col' }]
const cols: Col[] = [
{
NAME: 'num_col',
MEMLABEL: '',
DESC: '',
LONGDESC: '',
TYPE: '',
CLS_RULE: '',
VARNUM: 0,
LABEL: '',
FMTNAME: '',
DDTYPE: ''
},
{
NAME: 'char_col',
MEMLABEL: '',
DESC: '',
LONGDESC: '',
TYPE: '',
CLS_RULE: '',
VARNUM: 0,
LABEL: '',
FMTNAME: '',
DDTYPE: ''
}
]
const $dataFormats: any = {
vars: {
num_col: { format: 'best.', label: '', length: '8', type: 'num' },
char_col: { format: '$32.', label: '', length: '32', type: 'char' }
}
}
const merged = mergeColsRules(cols, rules, $dataFormats)
expect(merged.find((r) => r.data === 'num_col')?.sasType).toEqual('num')
expect(merged.find((r) => r.data === 'char_col')?.sasType).toEqual('char')
})
})
@@ -29,6 +29,7 @@ export const mergeColsRules = (
if (rule && col.DESC) rule.desc = col.DESC
if (rule && colFormats.length) rule.length = parseInt(colFormats.length)
if (rule && col.CLS_RULE) rule.clsRule = col.CLS_RULE
if (rule && colFormats?.type) rule.sasType = colFormats.type
}
return rules
@@ -80,15 +80,13 @@ export class SidebarComponent implements OnInit {
public resizeStart() {
this.resizing = true
let body = document.getElementsByTagName('body')[0]
body.style.cssText = 'user-select: none'
document.body.classList.add('select-none')
}
public resizeEnd() {
this.resizing = false
let body = document.getElementsByTagName('body')[0]
body.style.cssText = ''
document.body.classList.remove('select-none')
}
@HostListener('document:mousemove', ['$event'])
+6
View File
@@ -0,0 +1,6 @@
/**
* Strips characters that could cause SAS macro injection (& % ;).
*/
export function sanitiseForSas(input: string): string {
return input.replace(/[%&;]/g, '')
}
+25 -8
View File
@@ -236,14 +236,31 @@
<div class="admin-action">
Download Configuration
<input
type="text"
class="clr-input libref-input"
maxlength="8"
[ngModel]="dcLib"
(ngModelChange)="targetLibref = $event.toUpperCase()"
placeholder="Target Libref"
/>
<div class="libref-group">
<clr-tooltip class="libref-tooltip">
<label clrTooltipTrigger class="libref-label">
Target DC Library
<cds-icon shape="info-circle" size="16"></cds-icon>
</label>
<clr-tooltip-content
clrPosition="bottom-left"
clrSize="md"
*clrIfOpen
>
Enter the target DC library and the downloaded files will
contain this, instead of the original.
</clr-tooltip-content>
</clr-tooltip>
<input
type="text"
class="clr-input libref-input"
maxlength="8"
[ngModel]="dcLib"
(ngModelChange)="targetLibref = $event.toUpperCase()"
placeholder="e.g. MYLIB"
/>
</div>
<button
(click)="downloadConfiguration()"
[disabled]="targetLibref !== dcLib && !isValidLibref(targetLibref)"
+17 -1
View File
@@ -1,5 +1,21 @@
.libref-group {
display: inline-flex;
align-items: center;
gap: 4px;
margin: 0 8px;
}
.libref-label {
cursor: pointer;
font-size: 0.55rem;
font-weight: 600;
color: var(--clr-p4-color, #565656);
display: inline-flex;
align-items: center;
gap: 4px;
}
.libref-input {
width: 100px;
margin: 0 8px;
text-transform: uppercase;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31 -14
View File
@@ -9,9 +9,39 @@
@import './colors.scss';
/* CSP: replace Clarity's base64 Metropolis @font-face srcs with same-origin files. */
@font-face {
font-family: 'Metropolis';
src: url('./assets/fonts/Metropolis-200.woff') format('woff');
font-weight: 200;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Metropolis';
src: url('./assets/fonts/Metropolis-400.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Metropolis';
src: url('./assets/fonts/Metropolis-500.woff') format('woff');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Metropolis';
src: url('./assets/fonts/Metropolis-600.woff') format('woff');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: text-security-disc;
src: url('https://raw.githubusercontent.com/noppa/text-security/master/dist/text-security-disc.woff');
src: url('./assets/fonts/text-security-disc.woff') format('woff');
}
// TODO: IMPORTANT CSP WOKRAROUND
@@ -1882,19 +1912,6 @@ app-query {
}
}
.clause-row:after {
position: relative;
content: "";
height: .41667rem;
width: .41667rem;
top: .29167rem;
right: .25rem;
background-image: url(data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org…%2C9.84%2C3.24a0.68%2C0.68%2C0%2C1%2C1%2C1%2C1Z%22%2F%3E%0A%3C%2Fsvg%3E%0A);
background-repeat: no-repeat;
background-size: contain;
vertical-align: middle;
margin: 0;
}
pre[class*="language-"] {
padding: 8px;
+1875 -2381
View File
File diff suppressed because it is too large Load Diff
+8 -5
View File
@@ -1,17 +1,20 @@
{
"name": "dcfrontend",
"version": "7.4.1",
"version": "7.7.2",
"description": "Data Controller",
"devDependencies": {
"@saithodev/semantic-release-gitea": "^2.1.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^10.0.1",
"@semantic-release/commit-analyzer": "13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "11.0.0",
"@semantic-release/release-notes-generator": "^11.0.4",
"commit-and-tag-version": "^11.2.2",
"@semantic-release/npm": "13.1.5",
"@semantic-release/release-notes-generator": "14.1.0",
"commit-and-tag-version": "12.7.1",
"prettier": "^3.7.4"
},
"overrides": {
"got": "11.8.6"
},
"scripts": {
"install": "cd client && npm i && cd ../sas && npm i",
"build-frontend": "cd client && npm run build",
+2
View File
@@ -0,0 +1,2 @@
ignore-scripts=true
save-exact=true
+43 -37
View File
@@ -6,8 +6,8 @@
"": {
"name": "dc-sas",
"dependencies": {
"@sasjs/cli": "^4.15.0",
"@sasjs/core": "^4.62.0"
"@sasjs/cli": "4.16.2",
"@sasjs/core": "4.65.5"
}
},
"node_modules/@asamuzakjp/css-color": {
@@ -124,7 +124,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -147,7 +146,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -202,13 +200,13 @@
}
},
"node_modules/@sasjs/adapter": {
"version": "4.16.3",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.16.3.tgz",
"integrity": "sha512-xcoZT9qZhF6pXvXx4bHxbmauLdEHng8pSlTK4F6asUkHNR5uzeSvY6znA1yJqK+8FFtsVILyvMQyGyhWw6WsOA==",
"version": "4.16.7",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-4.16.7.tgz",
"integrity": "sha512-pOKAhOPijr663PFWGx+JEcWaMOGwICMA0w8LeXHPHQwvbHVGPCJKV5QroGqrjTO62HlhsHzow4Vb+DjIui1jgQ==",
"license": "ISC",
"dependencies": {
"@sasjs/utils": "3.5.6",
"axios": "^1.13.5",
"@sasjs/utils": "^3.5.6",
"axios": "1.16.0",
"axios-cookiejar-support": "5.0.5",
"form-data": "4.0.4",
"https": "1.0.0",
@@ -216,9 +214,9 @@
}
},
"node_modules/@sasjs/adapter/node_modules/@sasjs/utils": {
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-3.5.6.tgz",
"integrity": "sha512-jx8zWSOysDD66vTjA0BWiZ8bcFqmqh8F+56fUCgLmJhm89eDbKrGF3mDKMQx3UE7d2+gxp9xYhJCdaBWz0Dlxw==",
"version": "3.5.8",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-3.5.8.tgz",
"integrity": "sha512-rR2nCJG5AsuLj+nonpVO4PvA5OW8pUhDivY/25E4PCrQYLAmOaWQyIRne1gvSLL5ELzsOwRZWz6Zf6a02uNayA==",
"license": "ISC",
"dependencies": {
"@fast-csv/format": "4.3.5",
@@ -251,15 +249,15 @@
}
},
"node_modules/@sasjs/cli": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.15.0.tgz",
"integrity": "sha512-lVKzm8+4b9VSqbchfLnzyNm53cKDZfqSM8KU7izD/JDBsuATSZtjLo61iNenZaPg9d3WXglb1jf2ASbVKbXxUQ==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.16.2.tgz",
"integrity": "sha512-hIyIK8reud8zS6A40R4kgE7Uh0X2Ppaqn3jWt5JuNoZLqfooVBv/2kuqJX+CNkOW5QAaGhoPHCvj99Ujvk4ExQ==",
"license": "ISC",
"dependencies": {
"@sasjs/adapter": "4.16.3",
"@sasjs/core": "4.62.0",
"@sasjs/adapter": "4.16.7",
"@sasjs/core": "4.64.1",
"@sasjs/lint": "2.4.3",
"@sasjs/utils": "3.5.6",
"@sasjs/utils": "3.5.8",
"adm-zip": "0.5.10",
"chalk": "4.1.2",
"dotenv": "16.0.3",
@@ -281,10 +279,16 @@
"sasjs": "build/index.js"
}
},
"node_modules/@sasjs/cli/node_modules/@sasjs/core": {
"version": "4.64.1",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.64.1.tgz",
"integrity": "sha512-gdVzSM3+FYvd9XZ26ftLv6yDLndA6L/14nQGLUqjfL/jTPwuhJiojrkaLBAsPDlnRbHSMZSqTF94cKLvS09NBg==",
"license": "MIT"
},
"node_modules/@sasjs/cli/node_modules/@sasjs/utils": {
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-3.5.6.tgz",
"integrity": "sha512-jx8zWSOysDD66vTjA0BWiZ8bcFqmqh8F+56fUCgLmJhm89eDbKrGF3mDKMQx3UE7d2+gxp9xYhJCdaBWz0Dlxw==",
"version": "3.5.8",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-3.5.8.tgz",
"integrity": "sha512-rR2nCJG5AsuLj+nonpVO4PvA5OW8pUhDivY/25E4PCrQYLAmOaWQyIRne1gvSLL5ELzsOwRZWz6Zf6a02uNayA==",
"license": "ISC",
"dependencies": {
"@fast-csv/format": "4.3.5",
@@ -317,9 +321,9 @@
}
},
"node_modules/@sasjs/core": {
"version": "4.62.0",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.62.0.tgz",
"integrity": "sha512-xMWeZbxlvuCP0B9fnSTgSFbSiA0hiKDpTua8wb0ghMUOl+dnh/XF+BYgrHhWhPL9j0+k5d8mJejLmf/l/txpzg==",
"version": "4.65.5",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.65.5.tgz",
"integrity": "sha512-7oQVOpUN48/Qx7php3NTN0br84Tbm5tnyXRASt/pSJAwgK7C6N2gy7hsyPueOoKRmZ6OU86YxeYopSIoetrnBQ==",
"license": "MIT"
},
"node_modules/@sasjs/lint": {
@@ -462,14 +466,14 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios-cookiejar-support": {
@@ -1013,9 +1017,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
@@ -1845,10 +1849,13 @@
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/psl": {
"version": "1.15.0",
@@ -2168,7 +2175,6 @@
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
+2 -2
View File
@@ -28,7 +28,7 @@
},
"private": true,
"dependencies": {
"@sasjs/cli": "^4.15.0",
"@sasjs/core": "^4.62.0"
"@sasjs/cli": "4.16.2",
"@sasjs/core": "4.65.5"
}
}
@@ -0,0 +1,52 @@
/**
@file
@brief migration script to move from v7.0 to v7.6 of data controller
OPTIONAL CHANGE - upload additional data as placeholders for modifying the
default email message
**/
%let dclib=YOURDCLIB;
libname &dclib "/YOUR/DATACONTROLLER/LIBRARY/PATH";
proc sql;
insert into &dclib..mpe_config set
tx_from=%sysfunc(datetime())
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="SUBMITTED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been proposed by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'Reason provided: '
!!'%superq(SUBMITTED_TXT)'
!!'0A'x!!'This is an automated email by Data Controller for SAS. For '
!!'documentation, please visit https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after submitting a change';
insert into &dclib..mpe_config set
tx_from=%sysfunc(datetime())
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="APPROVED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been approved by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'This is an automated email by Data'
!!' Controller for SAS. For documentation, please visit '
!!'https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after approving a change';
insert into &dclib..mpe_config set
tx_from=%sysfunc(datetime())
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="REJECTED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been rejected by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'Reason provided: '
!!'%superq(REVIEW_REASON_TXT)'
!!'0A'x!!'This is an automated email by Data Controller for SAS. For '
!!'documentation, please visit https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after rejecting a change';
+46 -5
View File
@@ -127,6 +127,11 @@ run;
filename __out email (&emails)
subject="Table &alert_lib..&alert_ds has been &alert_event";
data work.alertmessage;
set &mpelib..mpe_config;
where &dc_dttmtfmt. lt tx_to;
where also var_scope='DC_EMAIL' and var_name="&alert_event._TEMPLATE";
run;
%local SUBMITTED_TXT;
%if &alert_event=SUBMITTED %then %do;
data _null_;
@@ -136,30 +141,54 @@ filename __out email (&emails)
run;
data _null_;
File __out lrecl=32000;
length txt $2048;
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
put 'Dear user,';
put ' ';
put "Please be advised that a change to table &alert_lib..&alert_ds has "
"been proposed by &from_user on the '&syshostname' SAS server.";
"been proposed by &from_user on the &syshostname SAS server.";
put " ";
length txt $2048;
txt=symget('SUBMITTED_TXT');
put "Reason provided: " txt;
put " ";
put "This is an automated email by Data Controller for SAS. For "
"documentation, please visit https://docs.datacontroller.io";
%end;
%else %do;
/* take template from config table */
set work.alertmessage;
cnt=countw(var_value,'0A'x);
do i=1 to cnt;
txt=resolve(scan(var_value,i,'0A'x));
put txt /;
end;
%end;
run;
%end;
%else %if &alert_event=APPROVED %then %do;
/* there is no approval message */
data _null_;
File __out lrecl=32000;
length txt $2048;
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
/* fallback message */
put 'Dear user,';
put ' ';
put "Please be advised that a change to table &alert_lib..&alert_ds has "
"been approved by &from_user on the '&syshostname' SAS server.";
"been approved by &from_user on the &syshostname SAS server.";
put " ";
put "This is an automated email by Data Controller for SAS. For "
"documentation, please visit https://docs.datacontroller.io";
%end;
%else %do;
/* take template from config table */
set work.alertmessage;
cnt=countw(var_value,'0A'x);
do i=1 to cnt;
txt=resolve(scan(var_value,i,'0A'x));
put txt /;
end;
%end;
run;
%end;
%else %if &alert_event=REJECTED %then %do;
@@ -170,17 +199,29 @@ filename __out email (&emails)
run;
data _null_;
File __out lrecl=32000;
length txt $2048;
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
/* fallback message */
put 'Dear user,';
put ' ';
put "Please be advised that a change to table &alert_lib..&alert_ds has "
"been rejected by &from_user on the '&syshostname' SAS server.";
"been rejected by &from_user on the &syshostname SAS server.";
put " ";
length txt $2048;
txt=symget('REVIEW_REASON_TXT');
put "Reason provided: " txt;
put " ";
put "This is an automated email by Data Controller for SAS. For "
"documentation, please visit https://docs.datacontroller.io";
%end;
%else %do;
/* take template from config table */
set work.alertmessage;
cnt=countw(var_value,'0A'x);
do i=1 to cnt;
txt=resolve(scan(var_value,i,'0A'x));
put txt /;
end;
%end;
run;
%end;
+38 -3
View File
@@ -201,6 +201,44 @@ insert into &lib..mpe_config set
,var_value=' '
,var_active=1
,var_desc='Activation Key';
insert into &lib..mpe_config set
tx_from=0
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="SUBMITTED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been proposed by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'Reason provided: '
!!'%superq(SUBMITTED_TXT)'
!!'0A'x!!'This is an automated email by Data Controller for SAS. For '
!!'documentation, please visit https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after submitting a change';
insert into &lib..mpe_config set
tx_from=0
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="APPROVED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been approved by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'This is an automated email by Data'
!!' Controller for SAS. For documentation, please visit '
!!'https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after approving a change';
insert into &lib..mpe_config set
tx_from=0
,tx_to='31DEC9999:23:59:59'dt
,var_scope="DC_EMAIL"
,var_name="REJECTED_TEMPLATE"
,var_value='Dear user,'!!'0A'x!!'Please be advised that a change to table'
!!' &alert_lib..&alert_ds has been rejected by &from_user on the '
!!'&syshostname SAS server.'!!'0A'x!!'Reason provided: '
!!'%superq(REVIEW_REASON_TXT)'
!!'0A'x!!'This is an automated email by Data Controller for SAS. For '
!!'documentation, please visit https://docs.datacontroller.io'
,var_active=1
,var_desc='Template email, sent after rejecting a change';
insert into &lib..mpe_datadictionary set
@@ -213,7 +251,6 @@ insert into &lib..mpe_datadictionary set
,DD_RESPONSIBLE="&sysuserid"
,DD_SENSITIVITY="Low"
,tx_to='31DEC5999:23:59:59'dt;
insert into &lib..mpe_datadictionary set
tx_from=0
,DD_TYPE='TABLE'
@@ -224,7 +261,6 @@ insert into &lib..mpe_datadictionary set
,DD_RESPONSIBLE="&sysuserid"
,DD_SENSITIVITY="Low"
,tx_to='31DEC5999:23:59:59'dt;
insert into &lib..mpe_datadictionary set
tx_from=0
,DD_TYPE='COLUMN'
@@ -235,7 +271,6 @@ insert into &lib..mpe_datadictionary set
,DD_RESPONSIBLE="&sysuserid"
,DD_SENSITIVITY="Low"
,tx_to='31DEC5999:23:59:59'dt;
insert into &lib..mpe_datadictionary set
tx_from=0
,DD_TYPE='DIRECTORY'
+45 -10
View File
@@ -9,10 +9,12 @@
<h4> SAS Macros </h4>
@li mf_getuser.sas
@li mf_nobs.sas
@li mp_ds2cards.sas
@li mp_abort.sas
@li mp_binarycopy.sas
@li mp_ds2cards.sas
@li mp_ds2csv.sas
@li mp_streamfile.sas
@li mp_validatecol.sas
@author 4GL Apps Ltd
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
@@ -21,23 +23,33 @@
**/
%global dclib islib newlib;
%mpeinit()
data _null_;
newlib=coalescec(symget('dclib'),"&mpelib");
%mp_validatecol(newlib,ISLIB,islib)
call symputx('islib',islib);
call symputx('newlib',upcase(newlib));
putlog (_all_)(=);
run;
%mp_abort(iftrue= (&islib ne 1)
,mac=&_program
,msg=%nrstr(&newlib is not a valid libref)
)
%let work=%sysfunc(pathname(work));
/* excel does not work in all envs */
%let mime=application/vnd.ms-excel;
%let dbms=EXCEL;
%let mime=application/csv;
%let dbms=CSV;
%let ext=csv;
%macro conditional_export(ds);
%if %mf_nobs(&ds)>0 %then %do;
PROC EXPORT DATA= &ds OUTFILE= "&work/&ds..&ext"
DBMS=&dbms REPLACE;
RUN;
ods package(ProdOutput) add file="&work/&ds..&ext" mimetype="&mime";
/* cannot use PROC EXPORT as we need to wrap all csv char values in quotes */
/* cannot use excel as it does not work consistently in all SAS envs */
%mp_ds2csv(&ds,outfile="&work/&newlib..&ds..csv",headerformat=NAME)
ods package(ProdOutput) add file="&work/&newlib..&ds..&ext" mimetype="&mime";
%end;
%mp_abort(iftrue= (&syscc ne 0)
,mac=&_program
@@ -52,6 +64,7 @@ data MPE_ALERTS;
set &mpelib..MPE_ALERTS;
where &dc_dttmtfmt. le tx_to;
drop tx_: ;
if alert_lib="&mpelib" then alert_lib="&newlib";
run;
%conditional_export(MPE_ALERTS)
@@ -61,6 +74,7 @@ data MPE_COLUMN_LEVEL_SECURITY;
where &dc_dttmtfmt. le tx_to;
where also CLS_LIBREF ne "&mpelib";
drop tx_: ;
CLS_LIBREF="&newlib";
run;
%conditional_export(MPE_COLUMN_LEVEL_SECURITY)
@@ -68,6 +82,7 @@ data MPE_CONFIG;
set &mpelib..MPE_CONFIG;
where &dc_dttmtfmt. le tx_to;
drop tx_: ;
if var_name='DC_MACROS' then var_value=tranwrd(var_value,"&mpelib","&newlib");
run;
%conditional_export(MPE_CONFIG)
@@ -93,6 +108,7 @@ data MPE_EXCEL_CONFIG;
set &mpelib..MPE_EXCEL_CONFIG;
where &dc_dttmtfmt. le tx_to;
drop tx_: ;
if xl_libref="&mpelib" then xl_libref="&newlib";
run;
%conditional_export(MPE_EXCEL_CONFIG)
@@ -107,6 +123,7 @@ data MPE_ROW_LEVEL_SECURITY;
set &mpelib..MPE_ROW_LEVEL_SECURITY;
where &dc_dttmtfmt. le tx_to;
drop tx_: ;
if rls_libref="&mpelib" then rls_libref="&newlib";
run;
%conditional_export(MPE_ROW_LEVEL_SECURITY)
@@ -115,6 +132,7 @@ data MPE_SECURITY;
set &mpelib..MPE_SECURITY;
where &dc_dttmtfmt. le TX_TO;
drop tx_: ;
if libref="&mpelib" then libref="&newlib";
run;
%conditional_export(MPE_SECURITY)
@@ -142,6 +160,23 @@ data MPE_VALIDATIONS;
run;
%conditional_export(MPE_VALIDATIONS)
data MPE_XLMAP_INFO;
set &mpelib..MPE_XLMAP_INFO;
where &dc_dttmtfmt. le TX_TO;
drop tx_: ;
if XLMAP_TARGETLIBDS=:"&mpelib.." then
XLMAP_TARGETLIBDS=tranwrd(XLMAP_TARGETLIBDS,"&mpelib..","&newlib..");
run;
%conditional_export(MPE_XLMAP_INFO)
data MPE_XLMAP_RULES;
set &mpelib..MPE_XLMAP_RULES;
where &dc_dttmtfmt. le TX_TO;
drop tx_: ;
run;
%conditional_export(MPE_XLMAP_RULES)
/* finish up zip file */
ods package(ProdOutput) publish archive properties
(archive_name="DCBACKUP.zip" archive_path="&work");
+2 -1
View File
@@ -84,7 +84,8 @@ data work.reject;
REVIEW_STATUS_ID="REJECTED";
REVIEWED_BY_NM="&user";
REVIEWED_ON_DTTM=&now;
REVIEW_REASON_TXT=symget('STP_REASON');
/* sanitise message to prevent code injection */
REVIEW_REASON_TXT=compress(symget('STP_REASON'), '&%;');
run;
%mp_lockanytable(LOCK,
+4
View File
@@ -19,6 +19,7 @@
<h4> SAS Macros </h4>
@li bitemporal_dataloader.sas
@li dc_assignlib.sas
@li dc_cassave.sas
@li mf_existds.sas
@li mf_existvar.sas
@li mf_getattrn.sas
@@ -669,6 +670,9 @@ run;
ctl_ds=&mpelib..mpe_lockanytable
)
/* save table to disk (if viya + cas) */
%dc_cassave(&libds)
/* run post-approve hook */
%mpe_runhook(POST_APPROVE_HOOK)
+6 -4
View File
@@ -24,10 +24,10 @@
<h5> cols </h5>
Contains column level attributes.
@li NAME - column name
@li VARNUM - variable position. Source: https://core.sasjs.io/mp__getcols_8sas.html
@li LABEL - variable label. Source: https://core.sasjs.io/mp__getcols_8sas.html
@li FMTNAME - derived format name. Source: https://core.sasjs.io/mp__getcols_8sas.html
@li DDTYPE - derived dropdown type. Source: https://core.sasjs.io/mp__getcols_8sas.html
@li VARNUM - var position. https://core.sasjs.io/mp__getcols_8sas.html
@li LABEL - var label. https://core.sasjs.io/mp__getcols_8sas.html
@li FMTNAME - derived format. https://core.sasjs.io/mp__getcols_8sas.html
@li DDTYPE - derived dropdown. https://core.sasjs.io/mp__getcols_8sas.html
@li CLS_RULE - values include:
- EDIT - the column is editable
- READ - the column should be readonly
@@ -47,6 +47,7 @@
<h4> SAS Macros </h4>
@li dc_assignlib.sas
@li dc_casload.sas
@li dc_getgroupmembers.sas
@li mf_existvar.sas
@li mf_getattrn.sas
@@ -142,6 +143,7 @@ run;
%let libref=%upcase(%scan(&libds,1,.));
%let dsn=%upcase(%scan(&libds,2,.));
%dc_assignlib(WRITE,&libref)
%dc_casload(&libds)
/**
* First check user has access permission to edit the table
+2
View File
@@ -51,6 +51,8 @@
data _null_;
set work.sascontroltable;
call symputx('action',action);
/* sanitise message to prevent code injection */
message=compress(message, '&%;');
call symputx('message',message);
libds=upcase(libds);
call symputx('orig_libds',libds);
@@ -19,6 +19,11 @@
data work.staging_ds;
set work.staging_ds;
LIBREF=upcase(LIBREF);
if LIBREF in ('WORK','CASUSER','SASUSER') then do;
putlog "ERR" +(-1) "OR: invalid LIBREF - " LIBREF;
call symputx('errval',1);
call symputx('errmsg',"Invalid LIBREF: "!!LIBREF);
end;
DSN=upcase(DSN);
ACCESS_LEVEL=upcase(ACCESS_LEVEL);
if ACCESS_LEVEL not in ('EDIT','APPROVE','VIEW','SIGNOFF','AUDIT') then do;
@@ -39,6 +39,13 @@ data work.staging_ds;
audit_libds=upcase(audit_libds);
rk_underlying=upcase(rk_underlying);
/* do not accept certain librefs */
if LIBREF in ('WORK','CASUSER','SASUSER')
then do;
call symputx('errmsg',"Invalid LIBREF: "!!LIBREF);
call symputx('errflag',1);
end;
/* check for valid loadtype */
if LOADTYPE not in ('UPDATE','TXTEMPORAL','FORMAT_CAT','BITEMPORAL','REPLACE')
then do;
+2
View File
@@ -39,6 +39,7 @@
<h4> SAS Macros </h4>
@li dc_assignlib.sas
@li dc_casload.sas
@li dc_createdataset.sas
@li dc_gettableid.sas
@li mf_existds.sas
@@ -144,6 +145,7 @@ run;
* assign the Library
*/
%dc_assignlib(READ,%scan(&LIBDS,1,.))
%dc_casload(&libds)
/* abort if looking for a format and the catalog doesn't exist */
%mp_abort(iftrue= (&fmt_ind=1 and %sysfunc(exist(&libds,CATALOG))=0)
@@ -0,0 +1,18 @@
/**
@file
@brief Loads a CAS table into memory
@details There are three versions of this macro, one per build
target. The interface is the same. This version is META and
is a no-op as CAS is not available on this platform.
@param [in] libds library.dataset of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging
@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.
**/
%macro dc_casload(libds, mdebug=0);
%mend dc_casload;
@@ -0,0 +1,18 @@
/**
@file
@brief Saves an in-memory CAS table back to persistent storage
@details There are three versions of this macro, one per build
target. The interface is the same. This version is META and
is a no-op as CAS is not available on this platform.
@param [in] libds library.dataset of the CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging
@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.
**/
%macro dc_cassave(libds, mdebug=0);
%mend dc_cassave;
@@ -0,0 +1,18 @@
/**
@file
@brief Loads a CAS table into memory
@details There are three versions of this macro, one per build
target. The interface is the same. This version is SERVER and
is a no-op as CAS is not available on this platform.
@param [in] libds library.dataset of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging
@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.
**/
%macro dc_casload(libds, mdebug=0);
%mend dc_casload;
@@ -0,0 +1,18 @@
/**
@file
@brief Saves an in-memory CAS table back to persistent storage
@details There are three versions of this macro, one per build
target. The interface is the same. This version is SERVER and
is a no-op as CAS is not available on this platform.
@param [in] libds library.dataset of the CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging
@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.
**/
%macro dc_cassave(libds, mdebug=0);
%mend dc_cassave;
@@ -0,0 +1,30 @@
/**
@file
@brief Loads a CAS table into memory
@details There are three versions of this macro, one per build
target. The interface is the same. This version is VIYA and
delegates to mv_castabload to ensure the named table is promoted
and available in memory before use.
@param [in] libds library.dataset of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging
<h4> SAS Macros </h4>
@li mf_getengine.sas
@li mv_castabload.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.
**/
%macro dc_casload(libds, mdebug=0);
%if %mf_getengine(&libds)=CAS %then %do;
%mv_castabload(
lib=%scan(&libds,1,.),
table=%scan(&libds,2,.),
mdebug=&mdebug
)
%end;
%mend dc_casload;
@@ -0,0 +1,30 @@
/**
@file
@brief Saves an in-memory CAS table back to persistent storage
@details There are three versions of this macro, one per build
target. The interface is the same. This version is VIYA and
delegates to mv_castabsave to save the named table back to its
original source file.
@param [in] libds library.dataset of the CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging
<h4> SAS Macros </h4>
@li mf_getengine.sas
@li mv_castabsave.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.
**/
%macro dc_cassave(libds, mdebug=0);
%if %mf_getengine(&libds)=CAS %then %do;
%mv_castabsave(
lib=%scan(&libds,1,.),
table=%scan(&libds,2,.),
mdebug=&mdebug
)
%end;
%mend dc_cassave;
@@ -21,7 +21,7 @@ create table &outds as
,engine
,'' as libraryid length=17
from dictionary.libnames
where libname not in ('WORK','SASUSER');
where libname not in ('WORK','SASUSER','CASUSER');
insert into &syslast values ("&DC_LIBREF", "&DC_LIBNAME",'','V9');
%mend dc_getlibs;