Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 322f904b4b | |||
| 982eeac58c | |||
| 0ab9717556 | |||
| 24a85de8e1 | |||
| 65f0b979a4 | |||
| 947f34a0ad | |||
| 0f60fd7181 | |||
| 251062e42e | |||
| 05a328976e | |||
| 503cb08b2f | |||
| f71be20476 | |||
| e6397cecc1 | |||
| 80ce80ece4 | |||
| 9546fcd631 | |||
| b79aaf4327 | |||
| 76f9198f73 | |||
| d60029deae | |||
| d26f7d2511 | |||
| 33efe09b50 | |||
| e0aef9bf00 | |||
| 02d1a2e0b1 | |||
| 4e3154e929 | |||
| 32c0713256 | |||
| defe15bcec | |||
| 6f8e471f16 | |||
| dc35abfd85 | |||
| 04a8c5d52a | |||
| 2cb370053d | |||
| 1707f3802a | |||
| c87ba660ca | |||
| ef8a2dbc38 | |||
| 40d04a53c4 | |||
| d5ebb01ce3 | |||
| ec66631a33 | |||
| d66eb5dfc2 | |||
| 731b589ed8 | |||
| fe92d5fc36 | |||
| a335b400f1 | |||
| f63e507ddf | |||
| 991cc0567d | |||
| 52d58036a4 | |||
| 26bff85792 | |||
| 2ccf0d1100 | |||
| 3be33186bc | |||
| 1a7f950ae2 | |||
| 8924dc8ab1 | |||
| 2c2901b537 | |||
| 2cae7ea638 | |||
| 66e98a96cb | |||
| 0b0db1c543 | |||
| 80039f4876 | |||
| 326c26fddf | |||
| e7b2ead0e2 | |||
| a89657b0b8 | |||
| 4ee15e1b6e | |||
| ed40df6295 | |||
| 6d590c050d | |||
| 47f9a54f97 | |||
| 17b0d72fbf | |||
| 0269c2421d | |||
| 5b260e4915 | |||
| 5290410a17 | |||
| dc9041aaec | |||
| b0dc441d68 | |||
| b0fc3eb5af | |||
| d9980e866d | |||
| 52ae3404ee | |||
| eecb4f4f53 | |||
| 744345af81 | |||
| 7694d1b0fb |
+49
-37
@@ -2,39 +2,53 @@ name: Build
|
||||
run-name: Running Lint Check and Licence checker on Pull Request
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.15.0'
|
||||
|
||||
jobs:
|
||||
Build-and-ng-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.5.0
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Google Chrome
|
||||
run: |
|
||||
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
|
||||
apt-get update
|
||||
apt-get install -y google-chrome-stable xvfb
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
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: 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
|
||||
@@ -52,26 +66,27 @@ jobs:
|
||||
|
||||
Build-and-test-development:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build-production-and-ng-test
|
||||
needs: Build-and-ng-test
|
||||
env:
|
||||
CHROME_BIN: /usr/bin/google-chrome
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.5.0
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
touch client/.npmrc
|
||||
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
||||
|
||||
- run: apt-get update
|
||||
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- run: apt install -y ./google-chrome*.deb;
|
||||
- run: export CHROME_BIN=/usr/bin/google-chrome
|
||||
- run: apt-get update -y
|
||||
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb
|
||||
- run: apt -y install jq
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
apt install -y ./google-chrome*.deb
|
||||
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip
|
||||
|
||||
- name: Write cypress credentials
|
||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
||||
@@ -86,17 +101,18 @@ jobs:
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm ci
|
||||
|
||||
# Install pm2 and prepare SASJS server
|
||||
- run: npm i -g pm2
|
||||
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
- run: unzip linux.zip
|
||||
- run: touch .env
|
||||
- run: echo RUN_TIMES=js >> .env
|
||||
- run: echo NODE_PATH=node >> .env
|
||||
- run: echo CORS=enable >> .env
|
||||
- run: echo WHITELIST=http://localhost:4200 >> .env
|
||||
- run: cat .env
|
||||
- run: pm2 start api-linux --wait-ready
|
||||
- name: Setup and start SASjs server
|
||||
run: |
|
||||
npm i -g pm2
|
||||
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
unzip linux.zip
|
||||
touch .env
|
||||
echo RUN_TIMES=js >> .env
|
||||
echo NODE_PATH=node >> .env
|
||||
echo CORS=enable >> .env
|
||||
echo WHITELIST=http://localhost:4200 >> .env
|
||||
cat .env
|
||||
pm2 start api-linux --wait-ready
|
||||
|
||||
- name: Deploy mocked services
|
||||
run: |
|
||||
@@ -106,11 +122,6 @@ jobs:
|
||||
sasjs cbd -t server-ci
|
||||
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
|
||||
|
||||
- name: Install ZIP
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install zip
|
||||
|
||||
- name: Prepare and run frontend and cypress
|
||||
run: |
|
||||
cd ./client
|
||||
@@ -126,11 +137,12 @@ jobs:
|
||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||
cat ./cypress.config.ts
|
||||
# Start frontend and run cypress
|
||||
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"
|
||||
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/csv-limited.cy.ts,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()
|
||||
run: |
|
||||
mkdir -p ./client/cypress/videos
|
||||
zip -r cypress-videos ./client/cypress/videos
|
||||
|
||||
- name: Add cypress videos artifacts
|
||||
|
||||
@@ -2,38 +2,31 @@ name: Lighthouse Checks
|
||||
run-name: Running Lighthouse Performance and Accessibility Checks on Pull Request
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.15.0'
|
||||
|
||||
jobs:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [24.5.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
- name: Use Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Google Chrome
|
||||
run: |
|
||||
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
|
||||
apt-get update
|
||||
apt-get install -y google-chrome-stable xvfb
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
apt install -y ./google-chrome*.deb
|
||||
|
||||
- name: Install pm2 for process management
|
||||
run: npm i -g pm2
|
||||
- name: Install global packages
|
||||
run: npm i -g pm2 @sasjs/cli wait-on
|
||||
|
||||
- name: Install @sasjs/cli
|
||||
run: npm i -g @sasjs/cli
|
||||
|
||||
- name: Install wait-on globally
|
||||
run: npm install -g wait-on
|
||||
|
||||
- name: Create .env file for sasjs/server
|
||||
- name: Setup and start SASjs server
|
||||
run: |
|
||||
touch .env
|
||||
echo RUN_TIMES=js >> .env
|
||||
@@ -41,15 +34,9 @@ jobs:
|
||||
echo CORS=enable >> .env
|
||||
echo WHITELIST=http://localhost:4200 >> .env
|
||||
cat .env
|
||||
|
||||
- name: Download sasjs/server package from github using curl
|
||||
run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
|
||||
- name: Unzip downloaded package
|
||||
run: unzip linux.zip
|
||||
|
||||
- name: Run sasjs server
|
||||
run: pm2 start api-linux --wait-ready
|
||||
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
unzip linux.zip
|
||||
pm2 start api-linux --wait-ready
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: echo "$NPMRC" > client/.npmrc
|
||||
@@ -61,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
|
||||
|
||||
|
||||
@@ -5,15 +5,20 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.5.0'
|
||||
|
||||
jobs:
|
||||
Build-production-and-ng-test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CHROME_BIN: /usr/bin/google-chrome
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.5.0
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
@@ -24,8 +29,7 @@ jobs:
|
||||
run: |
|
||||
apt-get update
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
apt install -y ./google-chrome*.deb;
|
||||
export CHROME_BIN=/usr/bin/google-chrome
|
||||
apt install -y ./google-chrome*.deb
|
||||
|
||||
- name: Write cypress credentials
|
||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
||||
@@ -43,9 +47,9 @@ jobs:
|
||||
- name: Check audit
|
||||
# Audit should fail and stop the CI if critical vulnerability found
|
||||
run: |
|
||||
npm audit --audit-level=critical --omit=dev
|
||||
npm audit --omit=dev
|
||||
cd ./sas
|
||||
npm audit --audit-level=critical --omit=dev
|
||||
npm audit --omit=dev
|
||||
cd ../client
|
||||
npm audit --audit-level=critical --omit=dev
|
||||
|
||||
@@ -63,25 +67,26 @@ jobs:
|
||||
Build-and-test-development:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build-production-and-ng-test
|
||||
env:
|
||||
CHROME_BIN: /usr/bin/google-chrome
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.5.0
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
touch client/.npmrc
|
||||
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
||||
|
||||
- run: apt-get update
|
||||
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- run: apt install -y ./google-chrome*.deb;
|
||||
- run: export CHROME_BIN=/usr/bin/google-chrome
|
||||
- run: apt-get update -y
|
||||
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb
|
||||
- run: apt -y install jq
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
apt install -y ./google-chrome*.deb
|
||||
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip
|
||||
|
||||
- name: Write cypress credentials
|
||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
||||
@@ -96,17 +101,18 @@ jobs:
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm ci
|
||||
|
||||
# Install pm2 and prepare SASJS server
|
||||
- run: npm i -g pm2
|
||||
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
- run: unzip linux.zip
|
||||
- run: touch .env
|
||||
- run: echo RUN_TIMES=js >> .env
|
||||
- run: echo NODE_PATH=node >> .env
|
||||
- run: echo CORS=enable >> .env
|
||||
- run: echo WHITELIST=http://localhost:4200 >> .env
|
||||
- run: cat .env
|
||||
- run: pm2 start api-linux --wait-ready
|
||||
- name: Setup and start SASjs server
|
||||
run: |
|
||||
npm i -g pm2
|
||||
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
unzip linux.zip
|
||||
touch .env
|
||||
echo RUN_TIMES=js >> .env
|
||||
echo NODE_PATH=node >> .env
|
||||
echo CORS=enable >> .env
|
||||
echo WHITELIST=http://localhost:4200 >> .env
|
||||
cat .env
|
||||
pm2 start api-linux --wait-ready
|
||||
|
||||
- name: Deploy mocked services
|
||||
run: |
|
||||
@@ -116,11 +122,6 @@ jobs:
|
||||
sasjs cbd -t server-ci
|
||||
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
|
||||
|
||||
- name: Install ZIP
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install zip
|
||||
|
||||
- name: Prepare and run frontend and cypress
|
||||
run: |
|
||||
cd ./client
|
||||
@@ -136,11 +137,12 @@ jobs:
|
||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||
cat ./cypress.config.ts
|
||||
# Start frontend and run cypress
|
||||
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"
|
||||
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/csv-limited.cy.ts,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()
|
||||
run: |
|
||||
mkdir -p ./client/cypress/videos
|
||||
zip -r cypress-videos ./client/cypress/videos
|
||||
|
||||
- name: Add cypress videos artifacts
|
||||
@@ -155,10 +157,10 @@ jobs:
|
||||
needs: [Build-production-and-ng-test, Build-and-test-development]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.5.0
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
@@ -168,17 +170,11 @@ jobs:
|
||||
env:
|
||||
NPMRC: ${{ secrets.NPMRC}}
|
||||
|
||||
- name: Install packages
|
||||
- name: Install system packages
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install zip -y
|
||||
# sasjs cli is used to compile & build the SAS services
|
||||
apt-get install -y zip jq doxygen
|
||||
npm i -g @sasjs/cli
|
||||
# jq is used to parse the release JSON
|
||||
apt-get install jq -y
|
||||
# doxygen is used for the SASJS docs
|
||||
apt-get update
|
||||
apt-get install doxygen -y
|
||||
|
||||
- name: Frontend Preliminary Build
|
||||
description: We want to prevent creating empty release if frontend fails
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
legacy-peer-deps=true
|
||||
ignore-scripts=true
|
||||
save-exact=true
|
||||
fund=false
|
||||
@@ -1,3 +1,84 @@
|
||||
## [7.7.3](https://git.datacontroller.io/dc/dc/compare/v7.7.2...v7.7.3) (2026-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move cas session assign to settings.sas and abort when lib is unassigned ([65f0b97](https://git.datacontroller.io/dc/dc/commit/65f0b979a401277b3e070d409659ae3fae2ff8c0))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* support for SASIOSNF engine (SNOW alias) plus meta assignment ([7694d1b](https://git.datacontroller.io/dc/dc/commit/7694d1b0fb2bd0407c8598147fbae87a00d889a8))
|
||||
|
||||
# [7.4.0](https://git.datacontroller.io/dc/dc/compare/v7.3.0...v7.4.0) (2026-02-20)
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/images",
|
||||
"output": "images"
|
||||
"output": "images",
|
||||
"ignore": ["spinner.svg", "caret.svg"]
|
||||
}
|
||||
],
|
||||
"styles": ["src/styles.scss"],
|
||||
|
||||
+18
-17
@@ -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
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
const username = Cypress.env('username')
|
||||
const password = Cypress.env('password')
|
||||
const hostUrl = Cypress.env('hosturl')
|
||||
const appLocation = Cypress.env('appLocation')
|
||||
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
|
||||
const serverType = Cypress.env('serverType')
|
||||
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
|
||||
const fixturePath = 'csvs/'
|
||||
|
||||
context('csv file upload restriction (free tier): ', function () {
|
||||
this.beforeEach(() => {
|
||||
cy.visit(hostUrl + appLocation)
|
||||
|
||||
cy.get('body').then(($body) => {
|
||||
const usernameInput = $body.find('input.username')[0]
|
||||
|
||||
if (usernameInput && !Cypress.dom.isHidden(usernameInput)) {
|
||||
cy.get('input.username').type(username)
|
||||
cy.get('input.password').type(password)
|
||||
cy.get('.login-group button').click()
|
||||
}
|
||||
})
|
||||
|
||||
cy.get('.app-loading', { timeout: longerCommandTimeout }).should(
|
||||
'not.exist'
|
||||
)
|
||||
|
||||
// Skip licensing page if presented - continue with free tier
|
||||
cy.url().then((url) => {
|
||||
if (url.includes('licensing')) {
|
||||
cy.get('button').contains('Continue with free tier').click()
|
||||
}
|
||||
})
|
||||
|
||||
visitPage('home')
|
||||
})
|
||||
|
||||
it('1 | File upload is restricted on free tier', () => {
|
||||
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
|
||||
|
||||
// Click upload button - should show feature locked modal
|
||||
cy.get('.buttonBar button:last-child').should('exist').click()
|
||||
|
||||
cy.get('.modal-title').should('contain', 'Locked Feature (File Upload)')
|
||||
})
|
||||
})
|
||||
|
||||
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
|
||||
cy.get('.app-loading', { timeout: longerCommandTimeout })
|
||||
.should('not.exist')
|
||||
.then(() => {
|
||||
cy.get('.nav-tree clr-tree > clr-tree-node', {
|
||||
timeout: longerCommandTimeout
|
||||
}).then((treeNodes: any) => {
|
||||
let targetLib
|
||||
|
||||
for (let node of treeNodes) {
|
||||
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
|
||||
targetLib = node
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cy.get(targetLib).within(() => {
|
||||
cy.get('.clr-tree-node-content-container > button').click()
|
||||
|
||||
cy.get('.clr-treenode-link').then((innerNodes: any) => {
|
||||
for (let innerNode of innerNodes) {
|
||||
if (innerNode.innerText.toLowerCase().includes(tablename)) {
|
||||
innerNode.click()
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const attachFile = (filename: string, callback?: any) => {
|
||||
cy.get('.buttonBar button:last-child')
|
||||
.should('exist')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.get('input[type="file"]#file-upload')
|
||||
.attachFile(`/${fixturePath}/${filename}`)
|
||||
.then(() => {
|
||||
if (callback) callback()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const visitPage = (url: string) => {
|
||||
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
|
||||
}
|
||||
@@ -4,7 +4,11 @@ PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,SOME_NUM,SOME_DATE,SOME_DATETIME,SOME_
|
||||
2,even more dummy data,Option 3,42,12FEB1960,01JAN1960:00:00:42,0:02:22,3,44
|
||||
3,"It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:",Option 2,1613.001,27FEB1961,01JAN1960:00:07:03,0:00:44,3,44
|
||||
4,if you can fill the unforgiving minute,Option 1,1613.0011235,02AUG1971,29MAY1973:06:12:03,0:06:52,3,44
|
||||
1010,10 bottles of beer on the wall,Option 1,0.9153696885,04MAR1962,01JAN1960:12:47:55,0:01:40,92,76
|
||||
1010,"10 bottles of beer
|
||||
|
||||
|
||||
|
||||
on the wall",Option 1,0.9153696885,04MAR1962,01JAN1960:12:47:55,0:01:40,92,76
|
||||
1011,11 bottles of beer on the wall,Option 1,0.3531217558,29MAR1960,01JAN1960:03:33:24,0:01:03,80,29
|
||||
1012,12 bottles of beer on the wall,Option 1,0.6743748717,02AUG1962,01JAN1960:07:25:59,0:00:10,16,98
|
||||
1013,13 bottles of beer on the wall,Option 1,0.1305445992,11SEP1960,01JAN1960:13:51:32,0:00:35,73,15
|
||||
|
||||
|
@@ -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) {
|
||||
|
||||
Generated
+5344
-6472
File diff suppressed because it is too large
Load Diff
+23
-20
@@ -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",
|
||||
@@ -67,7 +67,7 @@
|
||||
"hyperformula": "^2.5.0",
|
||||
"iconv-lite": "^0.5.0",
|
||||
"jquery-datetimepicker": "^2.5.21",
|
||||
"jsrsasign": "^11.1.0",
|
||||
"jsrsasign": "11.1.1",
|
||||
"marked": "^5.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"ngx-clipboard": "^16.0.0",
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)`)
|
||||
}
|
||||
@@ -55,6 +55,7 @@ export interface HandsontableStaticConfig {
|
||||
* Cached viyaApi collections, search and selected endpoint
|
||||
*/
|
||||
export const globals: {
|
||||
embed: boolean
|
||||
rootParam: string
|
||||
dcLib: string
|
||||
xlmaps: XLMapListItem[]
|
||||
@@ -69,6 +70,7 @@ export const globals: {
|
||||
handsontable: HandsontableStaticConfig
|
||||
[key: string]: any
|
||||
} = {
|
||||
embed: false,
|
||||
rootParam: <string>'',
|
||||
dcLib: '',
|
||||
xlmaps: [],
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<header class="app-header">
|
||||
<header class="app-header" *ngIf="!embed">
|
||||
<!-- <button
|
||||
*ngIf="
|
||||
isMainRoute('view') ||
|
||||
@@ -213,9 +213,10 @@
|
||||
</header>
|
||||
<nav
|
||||
*ngIf="
|
||||
router.url.includes('submitted') ||
|
||||
router.url.includes('approve') ||
|
||||
router.url.includes('history')
|
||||
!embed &&
|
||||
(router.url.includes('submitted') ||
|
||||
router.url.includes('approve') ||
|
||||
router.url.includes('history'))
|
||||
"
|
||||
class="subnav"
|
||||
>
|
||||
|
||||
@@ -70,6 +70,7 @@ export class AppComponent {
|
||||
|
||||
public syssite = this.appService.syssite
|
||||
public licenceState = this.licenceService.licenceState
|
||||
public embed = globals.embed
|
||||
|
||||
constructor(
|
||||
private appService: AppService,
|
||||
@@ -143,6 +144,16 @@ export class AppComponent {
|
||||
}
|
||||
})
|
||||
|
||||
const hashQuery = window.location.hash.split('?')[1]
|
||||
if (hashQuery) {
|
||||
const embedParam = new URLSearchParams(hashQuery).get('embed')
|
||||
if (embedParam !== null) {
|
||||
const isEmbed = embedParam !== 'false'
|
||||
globals.embed = isEmbed
|
||||
this.embed = isEmbed
|
||||
}
|
||||
}
|
||||
|
||||
this.subscribeToShowAbortModal()
|
||||
this.subscribeToRequestsModal()
|
||||
this.subscribeToStartupData()
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
class="card-header clr-row buttonBar headerBar clr-flex-md-row clr-justify-content-center clr-justify-content-lg-end"
|
||||
>
|
||||
<div
|
||||
*ngIf="tableTrue"
|
||||
*ngIf="tableTrue && !embed"
|
||||
class="clr-col-12 clr-col-md-3 clr-col-lg-4 backBtn"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -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[][]
|
||||
@@ -264,6 +265,9 @@ export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
public badEdit = false
|
||||
public badEditCause: string | undefined
|
||||
public badEditTitle: string | undefined
|
||||
get embed() {
|
||||
return globals.embed
|
||||
}
|
||||
public tableTrue: boolean | undefined
|
||||
public saveLoading = false
|
||||
public approvers: string[] = []
|
||||
@@ -1666,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
|
||||
|
||||
@@ -30,7 +30,7 @@ export const freeTierConfig: LicenceState = {
|
||||
lineage_daily_limit: 3,
|
||||
tables_in_library_limit: 35,
|
||||
viewbox: true,
|
||||
fileUpload: true,
|
||||
fileUpload: false,
|
||||
editRecord: true,
|
||||
addRecord: true
|
||||
}
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -375,38 +375,30 @@ export class SpreadsheetUtil {
|
||||
fileType: string
|
||||
): Promise<ParseResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.licenceState.value.submit_rows_limit !== Infinity) {
|
||||
if (!this.licenceState.value.fileUpload) {
|
||||
uploader.queue.pop()
|
||||
return reject(
|
||||
'Excel files only. To unlock CSV uploads, please contact support@datacontroller.io'
|
||||
'File uploads are not enabled for this licence. Please contact support@datacontroller.io'
|
||||
)
|
||||
}
|
||||
|
||||
if (parseParams.encoding === 'WLATIN1') {
|
||||
let reader = new FileReader()
|
||||
const self = this
|
||||
// Closure to capture the file information.
|
||||
reader.onload = (theFile: any) => {
|
||||
let encoded = iconv.decode(
|
||||
Buffer.from(theFile.target.result),
|
||||
'CP-1252'
|
||||
)
|
||||
let blob = new Blob([encoded], { type: fileType })
|
||||
let encodedFile: File = blobToFile(blob, parseParams.file.name)
|
||||
uploader.queue.pop()
|
||||
uploader.addToQueue([encodedFile])
|
||||
if (parseParams.encoding !== 'WLATIN1') return resolve({ uploader })
|
||||
|
||||
return resolve({
|
||||
uploader
|
||||
})
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = (theFile) => {
|
||||
if (!theFile.target?.result) return resolve({ uploader })
|
||||
|
||||
reader.readAsArrayBuffer(parseParams.file)
|
||||
} else {
|
||||
return resolve({
|
||||
uploader
|
||||
})
|
||||
const text = theFile.target.result as string
|
||||
const encoded = iconv.encode(text, 'CP-1252')
|
||||
const blob = new Blob([encoded], { type: fileType })
|
||||
const encodedFile: File = blobToFile(blob, parseParams.file.name)
|
||||
uploader.queue.pop()
|
||||
uploader.addToQueue([encodedFile])
|
||||
|
||||
return resolve({ uploader })
|
||||
}
|
||||
|
||||
reader.readAsText(parseParams.file)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Strips characters that could cause SAS macro injection (& % ;).
|
||||
*/
|
||||
export function sanitiseForSas(input: string): string {
|
||||
return input.replace(/[%&;]/g, '')
|
||||
}
|
||||
@@ -236,7 +236,36 @@
|
||||
<div class="admin-action">
|
||||
Download Configuration
|
||||
|
||||
<button (click)="downloadConfiguration()" class="btn btn-info btn-sm">
|
||||
<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)"
|
||||
class="btn btn-info btn-sm"
|
||||
>
|
||||
DOWNLOAD
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +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;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { EnvironmentInfo } from './models/environment-info.model'
|
||||
import { AppSettingsService } from '../services/app-settings.service'
|
||||
import { AppSettings } from '../models/AppSettings'
|
||||
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
|
||||
import { globals } from '../_globals'
|
||||
|
||||
@Component({
|
||||
selector: 'app-system',
|
||||
@@ -39,6 +40,8 @@ export class SystemComponent implements OnInit {
|
||||
responseModal: boolean = false
|
||||
|
||||
Infinity = Infinity
|
||||
dcLib: string = globals.dcLib
|
||||
targetLibref: string = globals.dcLib
|
||||
|
||||
licenceState = this.licenceService.licenceState
|
||||
settings: AppSettings
|
||||
@@ -71,13 +74,21 @@ export class SystemComponent implements OnInit {
|
||||
this.appSettingsService.setAppSettings(this.settings)
|
||||
}
|
||||
|
||||
isValidLibref(value: string): boolean {
|
||||
return /^[A-Za-z_]\w{0,7}$/.test(value.trim())
|
||||
}
|
||||
|
||||
downloadConfiguration() {
|
||||
let sasjsConfig = this.sasService.getSasjsConfig()
|
||||
let storage = sasjsConfig.serverUrl
|
||||
let metaData = sasjsConfig.appLoc
|
||||
let path = this.sasService.getExecutionPath()
|
||||
let lib = this.targetLibref.toUpperCase().trim()
|
||||
let downUrl =
|
||||
storage + path + '/?_program=' + metaData + '/services/admin/exportconfig'
|
||||
if (lib && lib !== this.dcLib && this.isValidLibref(lib)) {
|
||||
downUrl += '&dclib=' + encodeURIComponent(lib)
|
||||
}
|
||||
window.open(downUrl)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
-14
@@ -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;
|
||||
|
||||
Generated
+1890
-2359
File diff suppressed because it is too large
Load Diff
+8
-5
@@ -1,17 +1,20 @@
|
||||
{
|
||||
"name": "dcfrontend",
|
||||
"version": "7.4.0",
|
||||
"version": "7.7.3",
|
||||
"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",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ignore-scripts=true
|
||||
save-exact=true
|
||||
Generated
+318
-179
@@ -6,8 +6,32 @@
|
||||
"": {
|
||||
"name": "dc-sas",
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.14.0",
|
||||
"@sasjs/core": "^4.61.0"
|
||||
"@sasjs/cli": "4.16.2",
|
||||
"@sasjs/core": "4.65.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
|
||||
"integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/css-calc": "^2.1.3",
|
||||
"@csstools/css-color-parser": "^3.0.9",
|
||||
"@csstools/css-parser-algorithms": "^3.0.4",
|
||||
"@csstools/css-tokenizer": "^3.0.3",
|
||||
"lru-cache": "^10.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/dom-selector": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz",
|
||||
"integrity": "sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.0.3",
|
||||
"css-tree": "^2.3.1",
|
||||
"is-potential-custom-element-name": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@coolaj86/urequest": {
|
||||
@@ -16,6 +40,116 @@
|
||||
"integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA==",
|
||||
"license": "(MIT OR Apache-2.0)"
|
||||
},
|
||||
"node_modules/@csstools/color-helpers": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
|
||||
"integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
|
||||
"integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^3.0.5",
|
||||
"@csstools/css-tokenizer": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-color-parser": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
|
||||
"integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/color-helpers": "^5.1.0",
|
||||
"@csstools/css-calc": "^2.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^3.0.5",
|
||||
"@csstools/css-tokenizer": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-parser-algorithms": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
|
||||
"integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-tokenizer": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-tokenizer": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
|
||||
"integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@fast-csv/format": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
|
||||
@@ -66,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",
|
||||
@@ -80,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",
|
||||
@@ -115,21 +249,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.14.0.tgz",
|
||||
"integrity": "sha512-WpZFLxPuh0xBPfX4Vy5kkhvz2QVRLmOwmw70rdHd5DpSw3U4CGY3EbCLVSd0K0CLEBWJLR5EX2gITV8hUcCP4w==",
|
||||
"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.61.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",
|
||||
"find": "0.3.0",
|
||||
"js-base64": "3.7.5",
|
||||
"jsdom": "22.1.0",
|
||||
"jsdom": "23.2.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"lodash.groupby": "4.6.0",
|
||||
"lodash.uniqby": "4.7.0",
|
||||
@@ -145,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",
|
||||
@@ -181,9 +321,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.61.0.tgz",
|
||||
"integrity": "sha512-gpewnAUBDqPOSR1PsRPCB6vba+kY5NL6UyYuSUZFVh1j9Mz5Wkli3eZZjStZABflqKQVQbPNsgIlqw/SpzwGxg==",
|
||||
"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": {
|
||||
@@ -233,15 +373,6 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
|
||||
@@ -276,13 +407,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
|
||||
"deprecated": "Use your platform's native atob() and btoa() methods instead",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/accumulate-stream": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accumulate-stream/-/accumulate-stream-5.0.0.tgz",
|
||||
@@ -342,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": {
|
||||
@@ -407,6 +531,15 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
@@ -624,30 +757,49 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/cssstyle": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
|
||||
"integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rrweb-cssom": "^0.6.0"
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
|
||||
"integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
|
||||
"node_modules/cssstyle": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
|
||||
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abab": "^2.0.6",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"whatwg-url": "^12.0.0"
|
||||
"@asamuzakjp/css-color": "^3.2.0",
|
||||
"rrweb-cssom": "^0.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cssstyle/node_modules/rrweb-cssom": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
|
||||
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
|
||||
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"whatwg-url": "^14.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
@@ -694,19 +846,6 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/domexception": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
|
||||
"integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
|
||||
"deprecated": "Use your platform's native DOMException instead",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||
@@ -878,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",
|
||||
@@ -1073,15 +1212,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
|
||||
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-encoding": "^2.0.0"
|
||||
"whatwg-encoding": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/http-cookie-agent": {
|
||||
@@ -1109,29 +1248,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tootallnate/once": "2",
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent/node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/https": {
|
||||
@@ -1141,28 +1267,16 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent/node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
@@ -1339,40 +1453,38 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
"version": "22.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
|
||||
"integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
|
||||
"version": "23.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz",
|
||||
"integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abab": "^2.0.6",
|
||||
"cssstyle": "^3.0.0",
|
||||
"data-urls": "^4.0.0",
|
||||
"@asamuzakjp/dom-selector": "^2.0.1",
|
||||
"cssstyle": "^4.0.1",
|
||||
"data-urls": "^5.0.0",
|
||||
"decimal.js": "^10.4.3",
|
||||
"domexception": "^4.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"html-encoding-sniffer": "^4.0.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"nwsapi": "^2.2.4",
|
||||
"parse5": "^7.1.2",
|
||||
"rrweb-cssom": "^0.6.0",
|
||||
"saxes": "^6.0.0",
|
||||
"symbol-tree": "^3.2.4",
|
||||
"tough-cookie": "^4.1.2",
|
||||
"w3c-xmlserializer": "^4.0.0",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"w3c-xmlserializer": "^5.0.0",
|
||||
"webidl-conversions": "^7.0.0",
|
||||
"whatwg-encoding": "^2.0.0",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"whatwg-url": "^12.0.1",
|
||||
"ws": "^8.13.0",
|
||||
"xml-name-validator": "^4.0.0"
|
||||
"whatwg-encoding": "^3.1.1",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"whatwg-url": "^14.0.0",
|
||||
"ws": "^8.16.0",
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^2.5.0"
|
||||
"canvas": "^2.11.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
@@ -1475,6 +1587,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -1484,6 +1602,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
@@ -1594,12 +1718,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nwsapi": {
|
||||
"version": "2.2.23",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
|
||||
"integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
@@ -1706,9 +1824,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
@@ -1731,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",
|
||||
@@ -1806,6 +1927,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -1948,6 +2078,15 @@
|
||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ssl-root-cas": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ssl-root-cas/-/ssl-root-cas-1.3.1.tgz",
|
||||
@@ -2036,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",
|
||||
@@ -2057,15 +2195,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
|
||||
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.0"
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/traverse-chain": {
|
||||
@@ -2111,15 +2249,15 @@
|
||||
"integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
||||
"integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xml-name-validator": "^4.0.0"
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/wcwidth": {
|
||||
@@ -2141,37 +2279,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-encoding": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
||||
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
||||
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iconv-lite": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
|
||||
"integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "^4.1.1",
|
||||
"tr46": "^5.1.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
@@ -2207,9 +2346,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -2234,12 +2373,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlchars": {
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.14.0",
|
||||
"@sasjs/core": "^4.61.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';
|
||||
@@ -26,8 +26,7 @@ NOTES:
|
||||
|
||||
One cannot use BETWEEN
|
||||
One cannot use &xx_from LE [tstamp] LE &xx_from (equivalent to above).
|
||||
Background:
|
||||
http://stackoverflow.com/questions/20005950/best-practice-for-scd-date-pairs-closing-opening-timestamps
|
||||
Background: https://stackoverflow.com/questions/20005950
|
||||
|
||||
Areas for optimisation
|
||||
- loading temporal history (currently experimental)
|
||||
@@ -220,8 +219,8 @@ Areas for optimisation
|
||||
|
||||
%local engine_type;
|
||||
%let engine_type=%mf_getengine(&base_lib);
|
||||
%if (&engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW)
|
||||
and %length(&CLOSE_VARS)>0
|
||||
%if %length(&CLOSE_VARS)>0 and (&engine_type=REDSHIFT or &engine_type=POSTGRES
|
||||
or &engine_type=SNOW or &engine_type=SASIOSNF)
|
||||
%then %do;
|
||||
%put NOTE:; %put NOTE-;%put NOTE-;%put NOTE-;
|
||||
%put NOTE- CLOSE_VARS functionality not yet supported in &engine_type;
|
||||
@@ -638,6 +637,7 @@ data work.bitemp0_append &keepvars &outds_del(drop=&md5_col )
|
||||
create table work.bitemp0_base as select * from connection to myAlias(
|
||||
%end;
|
||||
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
||||
or &engine_type=SASIOSNF
|
||||
%then %do;
|
||||
/* grab schema */
|
||||
%let baselib_schema=%mf_getschema(&base_lib);
|
||||
@@ -661,7 +661,7 @@ data work.bitemp0_append &keepvars &outds_del(drop=&md5_col )
|
||||
%else %let base_table=&baselib_schema.&base_dsn;
|
||||
/* make in-db empty table with PK + MD5 only */
|
||||
%dc_assignlib(WRITE,&base_lib,passthru=myAlias)
|
||||
%if &engine_type=SNOW %then %do;
|
||||
%if &engine_type=SNOW or &engine_type=SASIOSNF %then %do;
|
||||
exec (create transient table &baselib_schema.&temp_table
|
||||
like &baselib_schema.&base_dsn
|
||||
) by myAlias;
|
||||
@@ -686,9 +686,12 @@ data work.bitemp0_append &keepvars &outds_del(drop=&md5_col )
|
||||
exec (alter table &temp_table add column &md5_col varchar(32);) by myAlias;
|
||||
/* create view to strip formats and avoid warns in log */
|
||||
data work.vw_bitemp0/view=work.vw_bitemp0;
|
||||
/* inherit remote length to handle byte expansion */
|
||||
if 0 then set &base_lib..&temp_table(keep=&md5_col);
|
||||
set work.bitemp0_append(keep=&pk &md5_col);
|
||||
format _all_;
|
||||
run;
|
||||
|
||||
proc append base=&base_lib..&temp_table
|
||||
%if &engine_type=REDSHIFT %then %do;
|
||||
(
|
||||
@@ -741,7 +744,7 @@ data work.bitemp0_append &keepvars &outds_del(drop=&md5_col )
|
||||
|
||||
|
||||
%if &engine_type=OLEDB or &engine_type=REDSHIFT or &engine_type=POSTGRES
|
||||
or &engine_type=SNOW
|
||||
or &engine_type=SNOW or &engine_type=SASIOSNF
|
||||
%then %do;
|
||||
); proc sql; drop table &base_lib.."&temp_table"n;
|
||||
%end;
|
||||
@@ -1196,7 +1199,7 @@ run;
|
||||
%else %if (&loadtype=BITEMPORAL or &loadtype=TXTEMPORAL or &loadtype=UPDATE)
|
||||
%then %do;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: &loadtype operation using &engine_type engine";
|
||||
putlog "&sysmacroname: &loadtype operation using *&engine_type* engine";
|
||||
run;
|
||||
%local flexinow;
|
||||
proc sql;
|
||||
@@ -1213,6 +1216,7 @@ run;
|
||||
execute(
|
||||
%end;
|
||||
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
||||
or &engine_type=SASIOSNF
|
||||
%then %do;
|
||||
%let innertable=%upcase(%mf_getuniquename(prefix=XDCTEMP));
|
||||
%let top_table=&baselib_schema.&base_dsn;
|
||||
@@ -1220,7 +1224,7 @@ run;
|
||||
/* make empty table first - must clone & drop extra cols
|
||||
as autoload is bad */
|
||||
%dc_assignlib(WRITE,&base_lib,passthru=myAlias)
|
||||
%if &engine_type=SNOW %then %do;
|
||||
%if &engine_type=SNOW or &engine_type=SASIOSNF %then %do;
|
||||
exec (create transient table &baselib_schema.&innertable
|
||||
like &baselib_schema.&base_dsn
|
||||
) by myAlias;
|
||||
@@ -1259,6 +1263,7 @@ run;
|
||||
execute(
|
||||
%end;
|
||||
%else %do;
|
||||
%put Not using passthrough for *&engine_type* engine;
|
||||
%let innertable=bitemp5d_subquery;
|
||||
%let top_table=&base_lib..&base_dsn;
|
||||
%let flexinow=&now;
|
||||
@@ -1311,7 +1316,7 @@ run;
|
||||
1=1);
|
||||
|
||||
%if &engine_type=OLEDB or &engine_type=REDSHIFT or &engine_type=POSTGRES
|
||||
or &engine_type=SNOW
|
||||
or &engine_type=SNOW or &engine_type=SASIOSNF
|
||||
%then %do;
|
||||
) by myAlias;
|
||||
execute (drop table &baselib_schema.&innertable) by myAlias;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
@li mf_existfeature.sas
|
||||
@li dc_assignlib.sas
|
||||
@li mp_ds2cards.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_cntlout.sas
|
||||
@@ -117,10 +118,8 @@ options validvarname=upcase;
|
||||
/* cannot proc export excel if PC Files is not licensed */
|
||||
%then %do;
|
||||
%let outfile=%sysfunc(pathname(work))/&table..csv;
|
||||
PROC EXPORT DATA= staged
|
||||
OUTFILE= "&outfile"
|
||||
DBMS=csv REPLACE;
|
||||
RUN;
|
||||
/* cannot use PROC EXPORT as we need to wrap all char values in quotes */
|
||||
%mp_ds2csv(work.staged,outfile="&outfile",headerformat=NAME)
|
||||
%let ext=csv;
|
||||
%let mimetype=csv;
|
||||
%end;
|
||||
|
||||
@@ -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,39 @@
|
||||
/**
|
||||
@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 mp_abort.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);
|
||||
%local eng;
|
||||
%let eng=%mf_getengine(&libds);
|
||||
%mp_abort(iftrue= (X&eng.X=XX)
|
||||
,mac=&_program
|
||||
,msg=%str(Library %scan(&libds,1,.) is not assigned)
|
||||
)
|
||||
|
||||
%if &eng=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;
|
||||
|
||||
@@ -124,6 +124,9 @@ data _null_;
|
||||
put '/* This physical location is used for staging data and audit history */';
|
||||
put '%let dc_staging_area=' "&dcpath/dc_staging;";
|
||||
put ' ';
|
||||
put 'cas dcsession sessopts=(caslib=casuser);';
|
||||
put 'caslib _all_ assign;';
|
||||
put ' ';
|
||||
if &syssite in (70221618,70253615) then do;
|
||||
put "libname dcdemo '&dcpath/dc_demo';";
|
||||
end;
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
|
||||
options noquotelenmax ps=max;
|
||||
|
||||
cas dcsession sessopts=(caslib=casuser);
|
||||
caslib _all_ assign;
|
||||
|
||||
libname casuser cas caslib=casuser;
|
||||
|
||||
|
||||
/*caslib casmusic path='/opt/sas/viya/cascache/tracks' libref=casmusic ;*/
|
||||
|
||||
%let syscc=0;
|
||||
|
||||
Reference in New Issue
Block a user