Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -2,32 +2,31 @@ name: Build
|
|||||||
run-name: Running Lint Check and Licence checker on Pull Request
|
run-name: Running Lint Check and Licence checker on Pull Request
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: '24.5.0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-and-ng-test:
|
Build-and-ng-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24.5.0
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Install Google Chrome
|
- name: Install Google Chrome
|
||||||
run: |
|
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 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
|
- name: Write .npmrc file
|
||||||
run: echo "$NPMRC" > client/.npmrc
|
run: echo "$NPMRC" >> client/.npmrc
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
NPMRC: ${{ secrets.NPMRC}}
|
NPMRC: ${{ secrets.NPMRC}}
|
||||||
|
|
||||||
- name: Lint check
|
|
||||||
run: npm run lint:check
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
cd client
|
cd client
|
||||||
@@ -35,6 +34,18 @@ jobs:
|
|||||||
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 ./libraries/sheet-crypto.tgz.gpg
|
||||||
npm ci
|
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
|
- name: Licence checker
|
||||||
run: |
|
run: |
|
||||||
cd client
|
cd client
|
||||||
@@ -52,26 +63,27 @@ jobs:
|
|||||||
|
|
||||||
Build-and-test-development:
|
Build-and-test-development:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: Build-production-and-ng-test
|
needs: Build-and-ng-test
|
||||||
|
env:
|
||||||
|
CHROME_BIN: /usr/bin/google-chrome
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24.5.0
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: |
|
run: |
|
||||||
touch client/.npmrc
|
touch client/.npmrc
|
||||||
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
||||||
|
|
||||||
- run: apt-get update
|
- name: Install system dependencies
|
||||||
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
run: |
|
||||||
- run: apt install -y ./google-chrome*.deb;
|
apt-get update
|
||||||
- run: export CHROME_BIN=/usr/bin/google-chrome
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
- run: apt-get update -y
|
apt install -y ./google-chrome*.deb
|
||||||
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb
|
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip
|
||||||
- run: apt -y install jq
|
|
||||||
|
|
||||||
- name: Write cypress credentials
|
- name: Write cypress credentials
|
||||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
||||||
@@ -86,17 +98,18 @@ jobs:
|
|||||||
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 ./libraries/sheet-crypto.tgz.gpg
|
||||||
npm ci
|
npm ci
|
||||||
|
|
||||||
# Install pm2 and prepare SASJS server
|
- name: Setup and start SASjs server
|
||||||
- run: npm i -g pm2
|
run: |
|
||||||
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
npm i -g pm2
|
||||||
- run: unzip linux.zip
|
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||||
- run: touch .env
|
unzip linux.zip
|
||||||
- run: echo RUN_TIMES=js >> .env
|
touch .env
|
||||||
- run: echo NODE_PATH=node >> .env
|
echo RUN_TIMES=js >> .env
|
||||||
- run: echo CORS=enable >> .env
|
echo NODE_PATH=node >> .env
|
||||||
- run: echo WHITELIST=http://localhost:4200 >> .env
|
echo CORS=enable >> .env
|
||||||
- run: cat .env
|
echo WHITELIST=http://localhost:4200 >> .env
|
||||||
- run: pm2 start api-linux --wait-ready
|
cat .env
|
||||||
|
pm2 start api-linux --wait-ready
|
||||||
|
|
||||||
- name: Deploy mocked services
|
- name: Deploy mocked services
|
||||||
run: |
|
run: |
|
||||||
@@ -106,11 +119,6 @@ jobs:
|
|||||||
sasjs cbd -t server-ci
|
sasjs cbd -t server-ci
|
||||||
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
|
# 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
|
- name: Prepare and run frontend and cypress
|
||||||
run: |
|
run: |
|
||||||
cd ./client
|
cd ./client
|
||||||
@@ -126,7 +134,7 @@ jobs:
|
|||||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||||
cat ./cypress.config.ts
|
cat ./cypress.config.ts
|
||||||
# Start frontend and run cypress
|
# Start frontend and run cypress
|
||||||
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
|
- name: Zip Cypress videos
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -2,38 +2,31 @@ name: Lighthouse Checks
|
|||||||
run-name: Running Lighthouse Performance and Accessibility Checks on Pull Request
|
run-name: Running Lighthouse Performance and Accessibility Checks on Pull Request
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: '24.5.0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lighthouse:
|
lighthouse:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [24.5.0]
|
|
||||||
|
|
||||||
steps:
|
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
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Install Google Chrome
|
- name: Install Google Chrome
|
||||||
run: |
|
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 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
|
- name: Install global packages
|
||||||
run: npm i -g pm2
|
run: npm i -g pm2 @sasjs/cli wait-on
|
||||||
|
|
||||||
- name: Install @sasjs/cli
|
- name: Setup and start SASjs server
|
||||||
run: npm i -g @sasjs/cli
|
|
||||||
|
|
||||||
- name: Install wait-on globally
|
|
||||||
run: npm install -g wait-on
|
|
||||||
|
|
||||||
- name: Create .env file for sasjs/server
|
|
||||||
run: |
|
run: |
|
||||||
touch .env
|
touch .env
|
||||||
echo RUN_TIMES=js >> .env
|
echo RUN_TIMES=js >> .env
|
||||||
@@ -41,15 +34,9 @@ jobs:
|
|||||||
echo CORS=enable >> .env
|
echo CORS=enable >> .env
|
||||||
echo WHITELIST=http://localhost:4200 >> .env
|
echo WHITELIST=http://localhost:4200 >> .env
|
||||||
cat .env
|
cat .env
|
||||||
|
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||||
- name: Download sasjs/server package from github using curl
|
unzip linux.zip
|
||||||
run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
pm2 start api-linux --wait-ready
|
||||||
|
|
||||||
- name: Unzip downloaded package
|
|
||||||
run: unzip linux.zip
|
|
||||||
|
|
||||||
- name: Run sasjs server
|
|
||||||
run: pm2 start api-linux --wait-ready
|
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: echo "$NPMRC" > client/.npmrc
|
run: echo "$NPMRC" > client/.npmrc
|
||||||
|
|||||||
@@ -5,15 +5,20 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: '24.5.0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-production-and-ng-test:
|
Build-production-and-ng-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
CHROME_BIN: /usr/bin/google-chrome
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24.5.0
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: |
|
run: |
|
||||||
@@ -24,8 +29,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
apt install -y ./google-chrome*.deb;
|
apt install -y ./google-chrome*.deb
|
||||||
export CHROME_BIN=/usr/bin/google-chrome
|
|
||||||
|
|
||||||
- name: Write cypress credentials
|
- name: Write cypress credentials
|
||||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
||||||
@@ -43,9 +47,9 @@ jobs:
|
|||||||
- name: Check audit
|
- name: Check audit
|
||||||
# Audit should fail and stop the CI if critical vulnerability found
|
# Audit should fail and stop the CI if critical vulnerability found
|
||||||
run: |
|
run: |
|
||||||
npm audit --audit-level=critical --omit=dev
|
npm audit --omit=dev
|
||||||
cd ./sas
|
cd ./sas
|
||||||
npm audit --audit-level=critical --omit=dev
|
npm audit --omit=dev
|
||||||
cd ../client
|
cd ../client
|
||||||
npm audit --audit-level=critical --omit=dev
|
npm audit --audit-level=critical --omit=dev
|
||||||
|
|
||||||
@@ -63,25 +67,26 @@ jobs:
|
|||||||
Build-and-test-development:
|
Build-and-test-development:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: Build-production-and-ng-test
|
needs: Build-production-and-ng-test
|
||||||
|
env:
|
||||||
|
CHROME_BIN: /usr/bin/google-chrome
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24.5.0
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: |
|
run: |
|
||||||
touch client/.npmrc
|
touch client/.npmrc
|
||||||
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
||||||
|
|
||||||
- run: apt-get update
|
- name: Install system dependencies
|
||||||
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
run: |
|
||||||
- run: apt install -y ./google-chrome*.deb;
|
apt-get update
|
||||||
- run: export CHROME_BIN=/usr/bin/google-chrome
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
- run: apt-get update -y
|
apt install -y ./google-chrome*.deb
|
||||||
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb
|
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip
|
||||||
- run: apt -y install jq
|
|
||||||
|
|
||||||
- name: Write cypress credentials
|
- name: Write cypress credentials
|
||||||
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
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
|
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||||
npm ci
|
npm ci
|
||||||
|
|
||||||
# Install pm2 and prepare SASJS server
|
- name: Setup and start SASjs server
|
||||||
- run: npm i -g pm2
|
run: |
|
||||||
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
npm i -g pm2
|
||||||
- run: unzip linux.zip
|
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||||
- run: touch .env
|
unzip linux.zip
|
||||||
- run: echo RUN_TIMES=js >> .env
|
touch .env
|
||||||
- run: echo NODE_PATH=node >> .env
|
echo RUN_TIMES=js >> .env
|
||||||
- run: echo CORS=enable >> .env
|
echo NODE_PATH=node >> .env
|
||||||
- run: echo WHITELIST=http://localhost:4200 >> .env
|
echo CORS=enable >> .env
|
||||||
- run: cat .env
|
echo WHITELIST=http://localhost:4200 >> .env
|
||||||
- run: pm2 start api-linux --wait-ready
|
cat .env
|
||||||
|
pm2 start api-linux --wait-ready
|
||||||
|
|
||||||
- name: Deploy mocked services
|
- name: Deploy mocked services
|
||||||
run: |
|
run: |
|
||||||
@@ -116,11 +122,6 @@ jobs:
|
|||||||
sasjs cbd -t server-ci
|
sasjs cbd -t server-ci
|
||||||
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
|
# 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
|
- name: Prepare and run frontend and cypress
|
||||||
run: |
|
run: |
|
||||||
cd ./client
|
cd ./client
|
||||||
@@ -136,7 +137,7 @@ jobs:
|
|||||||
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
||||||
cat ./cypress.config.ts
|
cat ./cypress.config.ts
|
||||||
# Start frontend and run cypress
|
# Start frontend and run cypress
|
||||||
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
|
- name: Zip Cypress videos
|
||||||
if: always()
|
if: always()
|
||||||
@@ -155,10 +156,10 @@ jobs:
|
|||||||
needs: [Build-production-and-ng-test, Build-and-test-development]
|
needs: [Build-production-and-ng-test, Build-and-test-development]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24.5.0
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
- name: Write .npmrc file
|
- name: Write .npmrc file
|
||||||
run: |
|
run: |
|
||||||
@@ -168,17 +169,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NPMRC: ${{ secrets.NPMRC}}
|
NPMRC: ${{ secrets.NPMRC}}
|
||||||
|
|
||||||
- name: Install packages
|
- name: Install system packages
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install zip -y
|
apt-get install -y zip jq doxygen
|
||||||
# sasjs cli is used to compile & build the SAS services
|
|
||||||
npm i -g @sasjs/cli
|
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
|
- name: Frontend Preliminary Build
|
||||||
description: We want to prevent creating empty release if frontend fails
|
description: We want to prevent creating empty release if frontend fails
|
||||||
|
|||||||
2
.npmrc
2
.npmrc
@@ -1 +1,3 @@
|
|||||||
legacy-peer-deps=true
|
legacy-peer-deps=true
|
||||||
|
ignore-scripts=true
|
||||||
|
save-exact=true
|
||||||
|
|||||||
40
CHANGELOG.md
40
CHANGELOG.md
@@ -1,3 +1,43 @@
|
|||||||
|
# [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)
|
# [7.4.0](https://git.datacontroller.io/dc/dc/compare/v7.3.0...v7.4.0) (2026-02-20)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
95
client/cypress/e2e/csv-limited.cy.ts
Normal file
95
client/cypress/e2e/csv-limited.cy.ts
Normal file
@@ -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
|
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
|
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
|
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
|
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
|
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
|
1013,13 bottles of beer on the wall,Option 1,0.1305445992,11SEP1960,01JAN1960:13:51:32,0:00:35,73,15
|
||||||
|
|||||||
|
14
client/package-lock.json
generated
14
client/package-lock.json
generated
@@ -37,7 +37,7 @@
|
|||||||
"hyperformula": "^2.5.0",
|
"hyperformula": "^2.5.0",
|
||||||
"iconv-lite": "^0.5.0",
|
"iconv-lite": "^0.5.0",
|
||||||
"jquery-datetimepicker": "^2.5.21",
|
"jquery-datetimepicker": "^2.5.21",
|
||||||
"jsrsasign": "^11.1.0",
|
"jsrsasign": "11.1.1",
|
||||||
"marked": "^5.0.0",
|
"marked": "^5.0.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"ngx-clipboard": "^16.0.0",
|
"ngx-clipboard": "^16.0.0",
|
||||||
@@ -18234,9 +18234,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsrsasign": {
|
"node_modules/jsrsasign": {
|
||||||
"version": "11.1.0",
|
"version": "11.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.1.1.tgz",
|
||||||
"integrity": "sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==",
|
"integrity": "sha512-6w95OOXH8DNeGxakqLndBEqqwQ6A70zGaky1oxfg8WVLWOnghTfJsc5Tknx+Z88MHSb1bGLcqQHImOF8Lk22XA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/kjur/jsrsasign#donations"
|
"url": "https://github.com/kjur/jsrsasign#donations"
|
||||||
@@ -25444,9 +25444,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.16.0",
|
"version": "7.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
"hyperformula": "^2.5.0",
|
"hyperformula": "^2.5.0",
|
||||||
"iconv-lite": "^0.5.0",
|
"iconv-lite": "^0.5.0",
|
||||||
"jquery-datetimepicker": "^2.5.21",
|
"jquery-datetimepicker": "^2.5.21",
|
||||||
"jsrsasign": "^11.1.0",
|
"jsrsasign": "11.1.1",
|
||||||
"marked": "^5.0.0",
|
"marked": "^5.0.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"ngx-clipboard": "^16.0.0",
|
"ngx-clipboard": "^16.0.0",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export interface HandsontableStaticConfig {
|
|||||||
* Cached viyaApi collections, search and selected endpoint
|
* Cached viyaApi collections, search and selected endpoint
|
||||||
*/
|
*/
|
||||||
export const globals: {
|
export const globals: {
|
||||||
|
embed: boolean
|
||||||
rootParam: string
|
rootParam: string
|
||||||
dcLib: string
|
dcLib: string
|
||||||
xlmaps: XLMapListItem[]
|
xlmaps: XLMapListItem[]
|
||||||
@@ -69,6 +70,7 @@ export const globals: {
|
|||||||
handsontable: HandsontableStaticConfig
|
handsontable: HandsontableStaticConfig
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
} = {
|
} = {
|
||||||
|
embed: false,
|
||||||
rootParam: <string>'',
|
rootParam: <string>'',
|
||||||
dcLib: '',
|
dcLib: '',
|
||||||
xlmaps: [],
|
xlmaps: [],
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<header class="app-header">
|
<header class="app-header" *ngIf="!embed">
|
||||||
<!-- <button
|
<!-- <button
|
||||||
*ngIf="
|
*ngIf="
|
||||||
isMainRoute('view') ||
|
isMainRoute('view') ||
|
||||||
@@ -213,9 +213,10 @@
|
|||||||
</header>
|
</header>
|
||||||
<nav
|
<nav
|
||||||
*ngIf="
|
*ngIf="
|
||||||
router.url.includes('submitted') ||
|
!embed &&
|
||||||
|
(router.url.includes('submitted') ||
|
||||||
router.url.includes('approve') ||
|
router.url.includes('approve') ||
|
||||||
router.url.includes('history')
|
router.url.includes('history'))
|
||||||
"
|
"
|
||||||
class="subnav"
|
class="subnav"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export class AppComponent {
|
|||||||
|
|
||||||
public syssite = this.appService.syssite
|
public syssite = this.appService.syssite
|
||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
|
public embed = globals.embed
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appService: AppService,
|
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.subscribeToShowAbortModal()
|
||||||
this.subscribeToRequestsModal()
|
this.subscribeToRequestsModal()
|
||||||
this.subscribeToStartupData()
|
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"
|
class="card-header clr-row buttonBar headerBar clr-flex-md-row clr-justify-content-center clr-justify-content-lg-end"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
*ngIf="tableTrue"
|
*ngIf="tableTrue && !embed"
|
||||||
class="clr-col-12 clr-col-md-3 clr-col-lg-4 backBtn"
|
class="clr-col-12 clr-col-md-3 clr-col-lg-4 backBtn"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import Handsontable from 'handsontable'
|
import Handsontable from 'handsontable'
|
||||||
import { Subject, Subscription } from 'rxjs'
|
import { Subject, Subscription } from 'rxjs'
|
||||||
|
import { sanitiseForSas } from '../shared/utils/sanitise'
|
||||||
import { SasStoreService } from '../services/sas-store.service'
|
import { SasStoreService } from '../services/sas-store.service'
|
||||||
|
|
||||||
type AOA = any[][]
|
type AOA = any[][]
|
||||||
@@ -264,6 +265,9 @@ export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
public badEdit = false
|
public badEdit = false
|
||||||
public badEditCause: string | undefined
|
public badEditCause: string | undefined
|
||||||
public badEditTitle: string | undefined
|
public badEditTitle: string | undefined
|
||||||
|
get embed() {
|
||||||
|
return globals.embed
|
||||||
|
}
|
||||||
public tableTrue: boolean | undefined
|
public tableTrue: boolean | undefined
|
||||||
public saveLoading = false
|
public saveLoading = false
|
||||||
public approvers: string[] = []
|
public approvers: string[] = []
|
||||||
@@ -1666,7 +1670,7 @@ export class EditorComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.submit = true
|
this.submit = true
|
||||||
const updateParams: any = {}
|
const updateParams: any = {}
|
||||||
updateParams.ACTION = 'LOAD'
|
updateParams.ACTION = 'LOAD'
|
||||||
this.message = this.message.replace(/\n/g, '. ')
|
this.message = sanitiseForSas(this.message.replace(/\n/g, '. '))
|
||||||
updateParams.MESSAGE = this.message
|
updateParams.MESSAGE = this.message
|
||||||
// updateParams.APPROVER = this.approver;
|
// updateParams.APPROVER = this.approver;
|
||||||
updateParams.LIBDS = this.libds
|
updateParams.LIBDS = this.libds
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const freeTierConfig: LicenceState = {
|
|||||||
lineage_daily_limit: 3,
|
lineage_daily_limit: 3,
|
||||||
tables_in_library_limit: 35,
|
tables_in_library_limit: 35,
|
||||||
viewbox: true,
|
viewbox: true,
|
||||||
fileUpload: true,
|
fileUpload: false,
|
||||||
editRecord: true,
|
editRecord: true,
|
||||||
addRecord: true
|
addRecord: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
|
import { sanitiseForSas } from '../../shared/utils/sanitise'
|
||||||
import { SasStoreService } from '../../services/sas-store.service'
|
import { SasStoreService } from '../../services/sas-store.service'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
@@ -136,7 +137,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
public async rejecting() {
|
public async rejecting() {
|
||||||
this.rejectLoading = true
|
this.rejectLoading = true
|
||||||
this.submitReason = this.submitReason.replace(/\n/g, '. ')
|
this.submitReason = sanitiseForSas(this.submitReason.replace(/\n/g, '. '))
|
||||||
|
|
||||||
let rejParams = {
|
let rejParams = {
|
||||||
STP_ACTION: 'REJECT_TABLE',
|
STP_ACTION: 'REJECT_TABLE',
|
||||||
|
|||||||
@@ -375,38 +375,30 @@ export class SpreadsheetUtil {
|
|||||||
fileType: string
|
fileType: string
|
||||||
): Promise<ParseResult> {
|
): Promise<ParseResult> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (this.licenceState.value.submit_rows_limit !== Infinity) {
|
if (!this.licenceState.value.fileUpload) {
|
||||||
uploader.queue.pop()
|
uploader.queue.pop()
|
||||||
return reject(
|
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') {
|
if (parseParams.encoding !== 'WLATIN1') return resolve({ uploader })
|
||||||
let reader = new FileReader()
|
|
||||||
const self = this
|
const reader = new FileReader()
|
||||||
// Closure to capture the file information.
|
reader.onload = (theFile) => {
|
||||||
reader.onload = (theFile: any) => {
|
if (!theFile.target?.result) return resolve({ uploader })
|
||||||
let encoded = iconv.decode(
|
|
||||||
Buffer.from(theFile.target.result),
|
const text = theFile.target.result as string
|
||||||
'CP-1252'
|
const encoded = iconv.encode(text, 'CP-1252')
|
||||||
)
|
const blob = new Blob([encoded], { type: fileType })
|
||||||
let blob = new Blob([encoded], { type: fileType })
|
const encodedFile: File = blobToFile(blob, parseParams.file.name)
|
||||||
let encodedFile: File = blobToFile(blob, parseParams.file.name)
|
|
||||||
uploader.queue.pop()
|
uploader.queue.pop()
|
||||||
uploader.addToQueue([encodedFile])
|
uploader.addToQueue([encodedFile])
|
||||||
|
|
||||||
return resolve({
|
return resolve({ uploader })
|
||||||
uploader
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.readAsArrayBuffer(parseParams.file)
|
reader.readAsText(parseParams.file)
|
||||||
} else {
|
|
||||||
return resolve({
|
|
||||||
uploader
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
client/src/app/shared/utils/sanitise.ts
Normal file
6
client/src/app/shared/utils/sanitise.ts
Normal 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">
|
<div class="admin-action">
|
||||||
Download Configuration
|
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
|
DOWNLOAD
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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 { AppSettingsService } from '../services/app-settings.service'
|
||||||
import { AppSettings } from '../models/AppSettings'
|
import { AppSettings } from '../models/AppSettings'
|
||||||
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
|
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
|
||||||
|
import { globals } from '../_globals'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-system',
|
selector: 'app-system',
|
||||||
@@ -39,6 +40,8 @@ export class SystemComponent implements OnInit {
|
|||||||
responseModal: boolean = false
|
responseModal: boolean = false
|
||||||
|
|
||||||
Infinity = Infinity
|
Infinity = Infinity
|
||||||
|
dcLib: string = globals.dcLib
|
||||||
|
targetLibref: string = globals.dcLib
|
||||||
|
|
||||||
licenceState = this.licenceService.licenceState
|
licenceState = this.licenceService.licenceState
|
||||||
settings: AppSettings
|
settings: AppSettings
|
||||||
@@ -71,13 +74,21 @@ export class SystemComponent implements OnInit {
|
|||||||
this.appSettingsService.setAppSettings(this.settings)
|
this.appSettingsService.setAppSettings(this.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValidLibref(value: string): boolean {
|
||||||
|
return /^[A-Za-z_]\w{0,7}$/.test(value.trim())
|
||||||
|
}
|
||||||
|
|
||||||
downloadConfiguration() {
|
downloadConfiguration() {
|
||||||
let sasjsConfig = this.sasService.getSasjsConfig()
|
let sasjsConfig = this.sasService.getSasjsConfig()
|
||||||
let storage = sasjsConfig.serverUrl
|
let storage = sasjsConfig.serverUrl
|
||||||
let metaData = sasjsConfig.appLoc
|
let metaData = sasjsConfig.appLoc
|
||||||
let path = this.sasService.getExecutionPath()
|
let path = this.sasService.getExecutionPath()
|
||||||
|
let lib = this.targetLibref.toUpperCase().trim()
|
||||||
let downUrl =
|
let downUrl =
|
||||||
storage + path + '/?_program=' + metaData + '/services/admin/exportconfig'
|
storage + path + '/?_program=' + metaData + '/services/admin/exportconfig'
|
||||||
|
if (lib && lib !== this.dcLib && this.isValidLibref(lib)) {
|
||||||
|
downUrl += '&dclib=' + encodeURIComponent(lib)
|
||||||
|
}
|
||||||
window.open(downUrl)
|
window.open(downUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "7.4.0",
|
"version": "7.6.0",
|
||||||
"description": "Data Controller",
|
"description": "Data Controller",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
|
|||||||
2
sas/.npmrc
Normal file
2
sas/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ignore-scripts=true
|
||||||
|
save-exact=true
|
||||||
439
sas/package-lock.json
generated
439
sas/package-lock.json
generated
@@ -6,8 +6,32 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "dc-sas",
|
"name": "dc-sas",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.14.0",
|
"@sasjs/cli": "4.15.2",
|
||||||
"@sasjs/core": "^4.61.0"
|
"@sasjs/core": "4.63.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": {
|
"node_modules/@coolaj86/urequest": {
|
||||||
@@ -16,6 +40,118 @@
|
|||||||
"integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA==",
|
"integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA==",
|
||||||
"license": "(MIT OR Apache-2.0)"
|
"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",
|
||||||
|
"peer": true,
|
||||||
|
"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",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@fast-csv/format": {
|
"node_modules/@fast-csv/format": {
|
||||||
"version": "4.3.5",
|
"version": "4.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
|
||||||
@@ -115,13 +251,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/cli": {
|
"node_modules/@sasjs/cli": {
|
||||||
"version": "4.14.0",
|
"version": "4.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-4.15.2.tgz",
|
||||||
"integrity": "sha512-WpZFLxPuh0xBPfX4Vy5kkhvz2QVRLmOwmw70rdHd5DpSw3U4CGY3EbCLVSd0K0CLEBWJLR5EX2gITV8hUcCP4w==",
|
"integrity": "sha512-lY9H+HIquLAPXuhk6ov/xyBooERvefT6oiwNRaQ6DHMMFE4cgPvrUH5s3RRkLI2+lET0M0hPPbuaZ4w9yFIDuA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "4.16.3",
|
"@sasjs/adapter": "4.16.3",
|
||||||
"@sasjs/core": "4.61.0",
|
"@sasjs/core": "4.63.0",
|
||||||
"@sasjs/lint": "2.4.3",
|
"@sasjs/lint": "2.4.3",
|
||||||
"@sasjs/utils": "3.5.6",
|
"@sasjs/utils": "3.5.6",
|
||||||
"adm-zip": "0.5.10",
|
"adm-zip": "0.5.10",
|
||||||
@@ -129,7 +265,7 @@
|
|||||||
"dotenv": "16.0.3",
|
"dotenv": "16.0.3",
|
||||||
"find": "0.3.0",
|
"find": "0.3.0",
|
||||||
"js-base64": "3.7.5",
|
"js-base64": "3.7.5",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "23.2.0",
|
||||||
"jwt-decode": "3.1.2",
|
"jwt-decode": "3.1.2",
|
||||||
"lodash.groupby": "4.6.0",
|
"lodash.groupby": "4.6.0",
|
||||||
"lodash.uniqby": "4.7.0",
|
"lodash.uniqby": "4.7.0",
|
||||||
@@ -181,9 +317,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/core": {
|
"node_modules/@sasjs/core": {
|
||||||
"version": "4.61.0",
|
"version": "4.63.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.61.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.63.0.tgz",
|
||||||
"integrity": "sha512-gpewnAUBDqPOSR1PsRPCB6vba+kY5NL6UyYuSUZFVh1j9Mz5Wkli3eZZjStZABflqKQVQbPNsgIlqw/SpzwGxg==",
|
"integrity": "sha512-NlIihA4BbP+mveAbb7A/hgnrZEpJKKIkq0v4SSDdYXg8YYdKAdyTK8K+6FNPwp+U6hixQCKVX8oCA1DIUppLqA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/lint": {
|
"node_modules/@sasjs/lint": {
|
||||||
@@ -233,15 +369,6 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"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": {
|
"node_modules/@types/fs-extra": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
|
||||||
@@ -276,13 +403,6 @@
|
|||||||
"@types/node": "*"
|
"@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": {
|
"node_modules/accumulate-stream": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accumulate-stream/-/accumulate-stream-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accumulate-stream/-/accumulate-stream-5.0.0.tgz",
|
||||||
@@ -407,6 +527,15 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/bl": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||||
@@ -624,30 +753,49 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cssstyle": {
|
"node_modules/css-tree": {
|
||||||
"version": "3.0.0",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||||
"integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
|
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rrweb-cssom": "^0.6.0"
|
"mdn-data": "2.0.30",
|
||||||
|
"source-map-js": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/data-urls": {
|
"node_modules/cssstyle": {
|
||||||
"version": "4.0.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
|
||||||
"integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
|
"integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"abab": "^2.0.6",
|
"@asamuzakjp/css-color": "^3.2.0",
|
||||||
"whatwg-mimetype": "^3.0.0",
|
"rrweb-cssom": "^0.8.0"
|
||||||
"whatwg-url": "^12.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"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": {
|
"node_modules/debug": {
|
||||||
@@ -694,19 +842,6 @@
|
|||||||
"node": ">=0.4.0"
|
"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": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.0.3",
|
"version": "16.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||||
@@ -1073,15 +1208,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/html-encoding-sniffer": {
|
"node_modules/html-encoding-sniffer": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
|
||||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"whatwg-encoding": "^2.0.0"
|
"whatwg-encoding": "^3.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-cookie-agent": {
|
"node_modules/http-cookie-agent": {
|
||||||
@@ -1109,29 +1244,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-proxy-agent": {
|
"node_modules/http-proxy-agent": {
|
||||||
"version": "5.0.0",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tootallnate/once": "2",
|
"agent-base": "^7.1.0",
|
||||||
"agent-base": "6",
|
"debug": "^4.3.4"
|
||||||
"debug": "4"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 14"
|
||||||
}
|
|
||||||
},
|
|
||||||
"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_modules/https": {
|
"node_modules/https": {
|
||||||
@@ -1141,28 +1263,16 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
"node_modules/https-proxy-agent": {
|
||||||
"version": "5.0.1",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "6",
|
"agent-base": "^7.1.2",
|
||||||
"debug": "4"
|
"debug": "4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 14"
|
||||||
}
|
|
||||||
},
|
|
||||||
"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_modules/human-signals": {
|
"node_modules/human-signals": {
|
||||||
@@ -1339,40 +1449,38 @@
|
|||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/jsdom": {
|
"node_modules/jsdom": {
|
||||||
"version": "22.1.0",
|
"version": "23.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz",
|
||||||
"integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
|
"integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"abab": "^2.0.6",
|
"@asamuzakjp/dom-selector": "^2.0.1",
|
||||||
"cssstyle": "^3.0.0",
|
"cssstyle": "^4.0.1",
|
||||||
"data-urls": "^4.0.0",
|
"data-urls": "^5.0.0",
|
||||||
"decimal.js": "^10.4.3",
|
"decimal.js": "^10.4.3",
|
||||||
"domexception": "^4.0.0",
|
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"html-encoding-sniffer": "^3.0.0",
|
"html-encoding-sniffer": "^4.0.0",
|
||||||
"http-proxy-agent": "^5.0.0",
|
"http-proxy-agent": "^7.0.0",
|
||||||
"https-proxy-agent": "^5.0.1",
|
"https-proxy-agent": "^7.0.2",
|
||||||
"is-potential-custom-element-name": "^1.0.1",
|
"is-potential-custom-element-name": "^1.0.1",
|
||||||
"nwsapi": "^2.2.4",
|
|
||||||
"parse5": "^7.1.2",
|
"parse5": "^7.1.2",
|
||||||
"rrweb-cssom": "^0.6.0",
|
"rrweb-cssom": "^0.6.0",
|
||||||
"saxes": "^6.0.0",
|
"saxes": "^6.0.0",
|
||||||
"symbol-tree": "^3.2.4",
|
"symbol-tree": "^3.2.4",
|
||||||
"tough-cookie": "^4.1.2",
|
"tough-cookie": "^4.1.3",
|
||||||
"w3c-xmlserializer": "^4.0.0",
|
"w3c-xmlserializer": "^5.0.0",
|
||||||
"webidl-conversions": "^7.0.0",
|
"webidl-conversions": "^7.0.0",
|
||||||
"whatwg-encoding": "^2.0.0",
|
"whatwg-encoding": "^3.1.1",
|
||||||
"whatwg-mimetype": "^3.0.0",
|
"whatwg-mimetype": "^4.0.0",
|
||||||
"whatwg-url": "^12.0.1",
|
"whatwg-url": "^14.0.0",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.16.0",
|
||||||
"xml-name-validator": "^4.0.0"
|
"xml-name-validator": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"canvas": "^2.5.0"
|
"canvas": "^2.11.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"canvas": {
|
"canvas": {
|
||||||
@@ -1475,6 +1583,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -1484,6 +1598,12 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@@ -1594,12 +1714,6 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/onetime": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
@@ -1706,9 +1820,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
@@ -1806,6 +1920,15 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
@@ -1948,6 +2071,15 @@
|
|||||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/ssl-root-cas": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ssl-root-cas/-/ssl-root-cas-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ssl-root-cas/-/ssl-root-cas-1.3.1.tgz",
|
||||||
@@ -2057,15 +2189,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
"version": "4.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||||
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"punycode": "^2.3.0"
|
"punycode": "^2.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/traverse-chain": {
|
"node_modules/traverse-chain": {
|
||||||
@@ -2111,15 +2243,15 @@
|
|||||||
"integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="
|
"integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="
|
||||||
},
|
},
|
||||||
"node_modules/w3c-xmlserializer": {
|
"node_modules/w3c-xmlserializer": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||||
"integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
|
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"xml-name-validator": "^4.0.0"
|
"xml-name-validator": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wcwidth": {
|
"node_modules/wcwidth": {
|
||||||
@@ -2141,37 +2273,38 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-encoding": {
|
"node_modules/whatwg-encoding": {
|
||||||
"version": "2.0.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
||||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
||||||
|
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iconv-lite": "0.6.3"
|
"iconv-lite": "0.6.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-mimetype": {
|
"node_modules/whatwg-mimetype": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-url": {
|
"node_modules/whatwg-url": {
|
||||||
"version": "12.0.1",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||||
"integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tr46": "^4.1.1",
|
"tr46": "^5.1.0",
|
||||||
"webidl-conversions": "^7.0.0"
|
"webidl-conversions": "^7.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
@@ -2207,9 +2340,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.18.3",
|
"version": "8.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
@@ -2234,12 +2367,12 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/xml-name-validator": {
|
"node_modules/xml-name-validator": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||||
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
|
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xmlchars": {
|
"node_modules/xmlchars": {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/cli": "^4.14.0",
|
"@sasjs/cli": "4.15.2",
|
||||||
"@sasjs/core": "^4.61.0"
|
"@sasjs/core": "4.63.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
sas/sasjs/db/migrations/20260403_v7.6_release.sas
Normal file
52
sas/sasjs/db/migrations/20260403_v7.6_release.sas
Normal file
@@ -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 BETWEEN
|
||||||
One cannot use &xx_from LE [tstamp] LE &xx_from (equivalent to above).
|
One cannot use &xx_from LE [tstamp] LE &xx_from (equivalent to above).
|
||||||
Background:
|
Background: https://stackoverflow.com/questions/20005950
|
||||||
http://stackoverflow.com/questions/20005950/best-practice-for-scd-date-pairs-closing-opening-timestamps
|
|
||||||
|
|
||||||
Areas for optimisation
|
Areas for optimisation
|
||||||
- loading temporal history (currently experimental)
|
- loading temporal history (currently experimental)
|
||||||
@@ -220,8 +219,8 @@ Areas for optimisation
|
|||||||
|
|
||||||
%local engine_type;
|
%local engine_type;
|
||||||
%let engine_type=%mf_getengine(&base_lib);
|
%let engine_type=%mf_getengine(&base_lib);
|
||||||
%if (&engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW)
|
%if %length(&CLOSE_VARS)>0 and (&engine_type=REDSHIFT or &engine_type=POSTGRES
|
||||||
and %length(&CLOSE_VARS)>0
|
or &engine_type=SNOW or &engine_type=SASIOSNF)
|
||||||
%then %do;
|
%then %do;
|
||||||
%put NOTE:; %put NOTE-;%put NOTE-;%put NOTE-;
|
%put NOTE:; %put NOTE-;%put NOTE-;%put NOTE-;
|
||||||
%put NOTE- CLOSE_VARS functionality not yet supported in &engine_type;
|
%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(
|
create table work.bitemp0_base as select * from connection to myAlias(
|
||||||
%end;
|
%end;
|
||||||
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
||||||
|
or &engine_type=SASIOSNF
|
||||||
%then %do;
|
%then %do;
|
||||||
/* grab schema */
|
/* grab schema */
|
||||||
%let baselib_schema=%mf_getschema(&base_lib);
|
%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;
|
%else %let base_table=&baselib_schema.&base_dsn;
|
||||||
/* make in-db empty table with PK + MD5 only */
|
/* make in-db empty table with PK + MD5 only */
|
||||||
%dc_assignlib(WRITE,&base_lib,passthru=myAlias)
|
%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
|
exec (create transient table &baselib_schema.&temp_table
|
||||||
like &baselib_schema.&base_dsn
|
like &baselib_schema.&base_dsn
|
||||||
) by myAlias;
|
) 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;
|
exec (alter table &temp_table add column &md5_col varchar(32);) by myAlias;
|
||||||
/* create view to strip formats and avoid warns in log */
|
/* create view to strip formats and avoid warns in log */
|
||||||
data work.vw_bitemp0/view=work.vw_bitemp0;
|
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);
|
set work.bitemp0_append(keep=&pk &md5_col);
|
||||||
format _all_;
|
format _all_;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
proc append base=&base_lib..&temp_table
|
proc append base=&base_lib..&temp_table
|
||||||
%if &engine_type=REDSHIFT %then %do;
|
%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
|
%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;
|
%then %do;
|
||||||
); proc sql; drop table &base_lib.."&temp_table"n;
|
); proc sql; drop table &base_lib.."&temp_table"n;
|
||||||
%end;
|
%end;
|
||||||
@@ -1196,7 +1199,7 @@ run;
|
|||||||
%else %if (&loadtype=BITEMPORAL or &loadtype=TXTEMPORAL or &loadtype=UPDATE)
|
%else %if (&loadtype=BITEMPORAL or &loadtype=TXTEMPORAL or &loadtype=UPDATE)
|
||||||
%then %do;
|
%then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
putlog "&sysmacroname: &loadtype operation using &engine_type engine";
|
putlog "&sysmacroname: &loadtype operation using *&engine_type* engine";
|
||||||
run;
|
run;
|
||||||
%local flexinow;
|
%local flexinow;
|
||||||
proc sql;
|
proc sql;
|
||||||
@@ -1213,6 +1216,7 @@ run;
|
|||||||
execute(
|
execute(
|
||||||
%end;
|
%end;
|
||||||
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
%else %if &engine_type=REDSHIFT or &engine_type=POSTGRES or &engine_type=SNOW
|
||||||
|
or &engine_type=SASIOSNF
|
||||||
%then %do;
|
%then %do;
|
||||||
%let innertable=%upcase(%mf_getuniquename(prefix=XDCTEMP));
|
%let innertable=%upcase(%mf_getuniquename(prefix=XDCTEMP));
|
||||||
%let top_table=&baselib_schema.&base_dsn;
|
%let top_table=&baselib_schema.&base_dsn;
|
||||||
@@ -1220,7 +1224,7 @@ run;
|
|||||||
/* make empty table first - must clone & drop extra cols
|
/* make empty table first - must clone & drop extra cols
|
||||||
as autoload is bad */
|
as autoload is bad */
|
||||||
%dc_assignlib(WRITE,&base_lib,passthru=myAlias)
|
%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
|
exec (create transient table &baselib_schema.&innertable
|
||||||
like &baselib_schema.&base_dsn
|
like &baselib_schema.&base_dsn
|
||||||
) by myAlias;
|
) by myAlias;
|
||||||
@@ -1259,6 +1263,7 @@ run;
|
|||||||
execute(
|
execute(
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
|
%put Not using passthrough for *&engine_type* engine;
|
||||||
%let innertable=bitemp5d_subquery;
|
%let innertable=bitemp5d_subquery;
|
||||||
%let top_table=&base_lib..&base_dsn;
|
%let top_table=&base_lib..&base_dsn;
|
||||||
%let flexinow=&now;
|
%let flexinow=&now;
|
||||||
@@ -1311,7 +1316,7 @@ run;
|
|||||||
1=1);
|
1=1);
|
||||||
|
|
||||||
%if &engine_type=OLEDB or &engine_type=REDSHIFT or &engine_type=POSTGRES
|
%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;
|
%then %do;
|
||||||
) by myAlias;
|
) by myAlias;
|
||||||
execute (drop table &baselib_schema.&innertable) by myAlias;
|
execute (drop table &baselib_schema.&innertable) by myAlias;
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ run;
|
|||||||
filename __out email (&emails)
|
filename __out email (&emails)
|
||||||
subject="Table &alert_lib..&alert_ds has been &alert_event";
|
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;
|
%local SUBMITTED_TXT;
|
||||||
%if &alert_event=SUBMITTED %then %do;
|
%if &alert_event=SUBMITTED %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -136,30 +141,54 @@ filename __out email (&emails)
|
|||||||
run;
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
File __out lrecl=32000;
|
File __out lrecl=32000;
|
||||||
|
length txt $2048;
|
||||||
|
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
|
||||||
put 'Dear user,';
|
put 'Dear user,';
|
||||||
put ' ';
|
put ' ';
|
||||||
put "Please be advised that a change to table &alert_lib..&alert_ds has "
|
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 " ";
|
put " ";
|
||||||
length txt $2048;
|
|
||||||
txt=symget('SUBMITTED_TXT');
|
txt=symget('SUBMITTED_TXT');
|
||||||
put "Reason provided: " txt;
|
put "Reason provided: " txt;
|
||||||
put " ";
|
put " ";
|
||||||
put "This is an automated email by Data Controller for SAS. For "
|
put "This is an automated email by Data Controller for SAS. For "
|
||||||
"documentation, please visit https://docs.datacontroller.io";
|
"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;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &alert_event=APPROVED %then %do;
|
%else %if &alert_event=APPROVED %then %do;
|
||||||
/* there is no approval message */
|
/* there is no approval message */
|
||||||
data _null_;
|
data _null_;
|
||||||
File __out lrecl=32000;
|
File __out lrecl=32000;
|
||||||
|
length txt $2048;
|
||||||
|
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
|
||||||
|
/* fallback message */
|
||||||
put 'Dear user,';
|
put 'Dear user,';
|
||||||
put ' ';
|
put ' ';
|
||||||
put "Please be advised that a change to table &alert_lib..&alert_ds has "
|
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 " ";
|
||||||
put "This is an automated email by Data Controller for SAS. For "
|
put "This is an automated email by Data Controller for SAS. For "
|
||||||
"documentation, please visit https://docs.datacontroller.io";
|
"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;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &alert_event=REJECTED %then %do;
|
%else %if &alert_event=REJECTED %then %do;
|
||||||
@@ -170,17 +199,29 @@ filename __out email (&emails)
|
|||||||
run;
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
File __out lrecl=32000;
|
File __out lrecl=32000;
|
||||||
|
length txt $2048;
|
||||||
|
%if %mf_getattrn(alertmessage,NLOBS)=0 %then %do;
|
||||||
|
/* fallback message */
|
||||||
put 'Dear user,';
|
put 'Dear user,';
|
||||||
put ' ';
|
put ' ';
|
||||||
put "Please be advised that a change to table &alert_lib..&alert_ds has "
|
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 " ";
|
put " ";
|
||||||
length txt $2048;
|
|
||||||
txt=symget('REVIEW_REASON_TXT');
|
txt=symget('REVIEW_REASON_TXT');
|
||||||
put "Reason provided: " txt;
|
put "Reason provided: " txt;
|
||||||
put " ";
|
put " ";
|
||||||
put "This is an automated email by Data Controller for SAS. For "
|
put "This is an automated email by Data Controller for SAS. For "
|
||||||
"documentation, please visit https://docs.datacontroller.io";
|
"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;
|
run;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -201,6 +201,44 @@ insert into &lib..mpe_config set
|
|||||||
,var_value=' '
|
,var_value=' '
|
||||||
,var_active=1
|
,var_active=1
|
||||||
,var_desc='Activation Key';
|
,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
|
insert into &lib..mpe_datadictionary set
|
||||||
@@ -213,7 +251,6 @@ insert into &lib..mpe_datadictionary set
|
|||||||
,DD_RESPONSIBLE="&sysuserid"
|
,DD_RESPONSIBLE="&sysuserid"
|
||||||
,DD_SENSITIVITY="Low"
|
,DD_SENSITIVITY="Low"
|
||||||
,tx_to='31DEC5999:23:59:59'dt;
|
,tx_to='31DEC5999:23:59:59'dt;
|
||||||
|
|
||||||
insert into &lib..mpe_datadictionary set
|
insert into &lib..mpe_datadictionary set
|
||||||
tx_from=0
|
tx_from=0
|
||||||
,DD_TYPE='TABLE'
|
,DD_TYPE='TABLE'
|
||||||
@@ -224,7 +261,6 @@ insert into &lib..mpe_datadictionary set
|
|||||||
,DD_RESPONSIBLE="&sysuserid"
|
,DD_RESPONSIBLE="&sysuserid"
|
||||||
,DD_SENSITIVITY="Low"
|
,DD_SENSITIVITY="Low"
|
||||||
,tx_to='31DEC5999:23:59:59'dt;
|
,tx_to='31DEC5999:23:59:59'dt;
|
||||||
|
|
||||||
insert into &lib..mpe_datadictionary set
|
insert into &lib..mpe_datadictionary set
|
||||||
tx_from=0
|
tx_from=0
|
||||||
,DD_TYPE='COLUMN'
|
,DD_TYPE='COLUMN'
|
||||||
@@ -235,7 +271,6 @@ insert into &lib..mpe_datadictionary set
|
|||||||
,DD_RESPONSIBLE="&sysuserid"
|
,DD_RESPONSIBLE="&sysuserid"
|
||||||
,DD_SENSITIVITY="Low"
|
,DD_SENSITIVITY="Low"
|
||||||
,tx_to='31DEC5999:23:59:59'dt;
|
,tx_to='31DEC5999:23:59:59'dt;
|
||||||
|
|
||||||
insert into &lib..mpe_datadictionary set
|
insert into &lib..mpe_datadictionary set
|
||||||
tx_from=0
|
tx_from=0
|
||||||
,DD_TYPE='DIRECTORY'
|
,DD_TYPE='DIRECTORY'
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuser.sas
|
@li mf_getuser.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mp_ds2cards.sas
|
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mp_binarycopy.sas
|
@li mp_binarycopy.sas
|
||||||
|
@li mp_ds2cards.sas
|
||||||
|
@li mp_ds2csv.sas
|
||||||
@li mp_streamfile.sas
|
@li mp_streamfile.sas
|
||||||
|
@li mp_validatecol.sas
|
||||||
|
|
||||||
@author 4GL Apps Ltd
|
@author 4GL Apps Ltd
|
||||||
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
@@ -21,23 +23,33 @@
|
|||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
%global dclib islib newlib;
|
||||||
%mpeinit()
|
%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));
|
%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 mime=application/csv;
|
||||||
%let dbms=CSV;
|
%let dbms=CSV;
|
||||||
%let ext=csv;
|
%let ext=csv;
|
||||||
|
|
||||||
%macro conditional_export(ds);
|
%macro conditional_export(ds);
|
||||||
%if %mf_nobs(&ds)>0 %then %do;
|
%if %mf_nobs(&ds)>0 %then %do;
|
||||||
PROC EXPORT DATA= &ds OUTFILE= "&work/&ds..&ext"
|
/* cannot use PROC EXPORT as we need to wrap all csv char values in quotes */
|
||||||
DBMS=&dbms REPLACE;
|
/* cannot use excel as it does not work consistently in all SAS envs */
|
||||||
RUN;
|
%mp_ds2csv(&ds,outfile="&work/&newlib..&ds..csv",headerformat=NAME)
|
||||||
ods package(ProdOutput) add file="&work/&ds..&ext" mimetype="&mime";
|
ods package(ProdOutput) add file="&work/&newlib..&ds..&ext" mimetype="&mime";
|
||||||
%end;
|
%end;
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,mac=&_program
|
,mac=&_program
|
||||||
@@ -52,6 +64,7 @@ data MPE_ALERTS;
|
|||||||
set &mpelib..MPE_ALERTS;
|
set &mpelib..MPE_ALERTS;
|
||||||
where &dc_dttmtfmt. le tx_to;
|
where &dc_dttmtfmt. le tx_to;
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
if alert_lib="&mpelib" then alert_lib="&newlib";
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_ALERTS)
|
%conditional_export(MPE_ALERTS)
|
||||||
|
|
||||||
@@ -61,6 +74,7 @@ data MPE_COLUMN_LEVEL_SECURITY;
|
|||||||
where &dc_dttmtfmt. le tx_to;
|
where &dc_dttmtfmt. le tx_to;
|
||||||
where also CLS_LIBREF ne "&mpelib";
|
where also CLS_LIBREF ne "&mpelib";
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
CLS_LIBREF="&newlib";
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_COLUMN_LEVEL_SECURITY)
|
%conditional_export(MPE_COLUMN_LEVEL_SECURITY)
|
||||||
|
|
||||||
@@ -68,6 +82,7 @@ data MPE_CONFIG;
|
|||||||
set &mpelib..MPE_CONFIG;
|
set &mpelib..MPE_CONFIG;
|
||||||
where &dc_dttmtfmt. le tx_to;
|
where &dc_dttmtfmt. le tx_to;
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
if var_name='DC_MACROS' then var_value=tranwrd(var_value,"&mpelib","&newlib");
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_CONFIG)
|
%conditional_export(MPE_CONFIG)
|
||||||
|
|
||||||
@@ -93,6 +108,7 @@ data MPE_EXCEL_CONFIG;
|
|||||||
set &mpelib..MPE_EXCEL_CONFIG;
|
set &mpelib..MPE_EXCEL_CONFIG;
|
||||||
where &dc_dttmtfmt. le tx_to;
|
where &dc_dttmtfmt. le tx_to;
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
if xl_libref="&mpelib" then xl_libref="&newlib";
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_EXCEL_CONFIG)
|
%conditional_export(MPE_EXCEL_CONFIG)
|
||||||
|
|
||||||
@@ -107,6 +123,7 @@ data MPE_ROW_LEVEL_SECURITY;
|
|||||||
set &mpelib..MPE_ROW_LEVEL_SECURITY;
|
set &mpelib..MPE_ROW_LEVEL_SECURITY;
|
||||||
where &dc_dttmtfmt. le tx_to;
|
where &dc_dttmtfmt. le tx_to;
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
if rls_libref="&mpelib" then rls_libref="&newlib";
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_ROW_LEVEL_SECURITY)
|
%conditional_export(MPE_ROW_LEVEL_SECURITY)
|
||||||
|
|
||||||
@@ -115,6 +132,7 @@ data MPE_SECURITY;
|
|||||||
set &mpelib..MPE_SECURITY;
|
set &mpelib..MPE_SECURITY;
|
||||||
where &dc_dttmtfmt. le TX_TO;
|
where &dc_dttmtfmt. le TX_TO;
|
||||||
drop tx_: ;
|
drop tx_: ;
|
||||||
|
if libref="&mpelib" then libref="&newlib";
|
||||||
run;
|
run;
|
||||||
%conditional_export(MPE_SECURITY)
|
%conditional_export(MPE_SECURITY)
|
||||||
|
|
||||||
@@ -142,6 +160,23 @@ data MPE_VALIDATIONS;
|
|||||||
run;
|
run;
|
||||||
%conditional_export(MPE_VALIDATIONS)
|
%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 */
|
/* finish up zip file */
|
||||||
ods package(ProdOutput) publish archive properties
|
ods package(ProdOutput) publish archive properties
|
||||||
(archive_name="DCBACKUP.zip" archive_path="&work");
|
(archive_name="DCBACKUP.zip" archive_path="&work");
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ data work.reject;
|
|||||||
REVIEW_STATUS_ID="REJECTED";
|
REVIEW_STATUS_ID="REJECTED";
|
||||||
REVIEWED_BY_NM="&user";
|
REVIEWED_BY_NM="&user";
|
||||||
REVIEWED_ON_DTTM=&now;
|
REVIEWED_ON_DTTM=&now;
|
||||||
REVIEW_REASON_TXT=symget('STP_REASON');
|
/* sanitise message to prevent code injection */
|
||||||
|
REVIEW_REASON_TXT=compress(symget('STP_REASON'), '&%;');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mp_lockanytable(LOCK,
|
%mp_lockanytable(LOCK,
|
||||||
|
|||||||
@@ -51,6 +51,8 @@
|
|||||||
data _null_;
|
data _null_;
|
||||||
set work.sascontroltable;
|
set work.sascontroltable;
|
||||||
call symputx('action',action);
|
call symputx('action',action);
|
||||||
|
/* sanitise message to prevent code injection */
|
||||||
|
message=compress(message, '&%;');
|
||||||
call symputx('message',message);
|
call symputx('message',message);
|
||||||
libds=upcase(libds);
|
libds=upcase(libds);
|
||||||
call symputx('orig_libds',libds);
|
call symputx('orig_libds',libds);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
@li mf_existfeature.sas
|
@li mf_existfeature.sas
|
||||||
@li dc_assignlib.sas
|
@li dc_assignlib.sas
|
||||||
@li mp_ds2cards.sas
|
@li mp_ds2cards.sas
|
||||||
|
@li mp_ds2csv.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mp_binarycopy.sas
|
@li mp_binarycopy.sas
|
||||||
@li mp_cntlout.sas
|
@li mp_cntlout.sas
|
||||||
@@ -117,10 +118,8 @@ options validvarname=upcase;
|
|||||||
/* cannot proc export excel if PC Files is not licensed */
|
/* cannot proc export excel if PC Files is not licensed */
|
||||||
%then %do;
|
%then %do;
|
||||||
%let outfile=%sysfunc(pathname(work))/&table..csv;
|
%let outfile=%sysfunc(pathname(work))/&table..csv;
|
||||||
PROC EXPORT DATA= staged
|
/* cannot use PROC EXPORT as we need to wrap all char values in quotes */
|
||||||
OUTFILE= "&outfile"
|
%mp_ds2csv(work.staged,outfile="&outfile",headerformat=NAME)
|
||||||
DBMS=csv REPLACE;
|
|
||||||
RUN;
|
|
||||||
%let ext=csv;
|
%let ext=csv;
|
||||||
%let mimetype=csv;
|
%let mimetype=csv;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
Reference in New Issue
Block a user