Compare commits
98 Commits
Author | SHA1 | Date |
---|---|---|
semantic-release-bot | 39c3e5411f | |
allan | aad419c55d | |
^ | ee58fd5b4b | |
allan | aedd2c451b | |
Mihajlo Medjedovic | 6d597611b6 | |
Mihajlo Medjedovic | 9ffe5efe5d | |
semantic-release-bot | 2715950d86 | |
Mihajlo Medjedovic | 77a7190f4d | |
allan | cc65890fea | |
^ | 93758efb27 | |
Mihajlo Medjedovic | c0dc9191e3 | |
semantic-release-bot | 485783a782 | |
mihajlo | ee07bef2b8 | |
Mihajlo Medjedovic | 0de8481314 | |
Mihajlo Medjedovic | ec0f539a33 | |
semantic-release-bot | 173ee2daff | |
Mihajlo Medjedovic | 5c0091b5e8 | |
allan | 84dce7f6e8 | |
Mihajlo Medjedovic | e8a943a35a | |
Mihajlo Medjedovic | b3171a8125 | |
Mihajlo Medjedovic | d77f2eb674 | |
Mihajlo Medjedovic | 5474fad9cc | |
Mihajlo Medjedovic | 3dd85cc60b | |
Mihajlo Medjedovic | 510e412ff2 | |
Mihajlo Medjedovic | 3dfdccbc6b | |
Mihajlo Medjedovic | a55661548a | |
Mihajlo Medjedovic | 69363b37e9 | |
Mihajlo Medjedovic | 2d6a753921 | |
Mihajlo Medjedovic | dc989e5668 | |
Mihajlo Medjedovic | 227ac480d5 | |
Mihajlo Medjedovic | c5e4650327 | |
allan | 56cf271e77 | |
^ | fa8396f039 | |
^ | 904ca30f91 | |
Mihajlo Medjedovic | 549f35766b | |
Mihajlo Medjedovic | 1589c799ec | |
Mihajlo Medjedovic | 604c2e70bd | |
^ | 297a84d3a4 | |
^ | aaad9f7207 | |
^ | a4028562ce | |
Mihajlo Medjedovic | 5ab3f98855 | |
^ | eba21e96b4 | |
^ | 9ad7ae47b5 | |
allan | cf54e4c8f3 | |
Mihajlo Medjedovic | 57aa6fa0fc | |
Mihajlo Medjedovic | 4a01f3d490 | |
Mihajlo Medjedovic | 80a0db951d | |
Mihajlo Medjedovic | 3202cb8e08 | |
Mihajlo Medjedovic | ddf36230bf | |
Mihajlo Medjedovic | b67c2be968 | |
Mihajlo Medjedovic | dc2c8da92b | |
Mihajlo Medjedovic | 5d6c3701d0 | |
Mihajlo Medjedovic | b024e263b4 | |
allan | b706864e40 | |
Mihajlo Medjedovic | 60cc666b67 | |
Mihajlo Medjedovic | efff4dd553 | |
Mihajlo Medjedovic | 2b1dad8e48 | |
Mihajlo Medjedovic | 8c7de5aad7 | |
Mihajlo Medjedovic | 1db8bc2573 | |
Mihajlo Medjedovic | c60dd65a16 | |
allan | f7f59a4b0a | |
^ | ec7615e7e3 | |
Mihajlo Medjedovic | f411c33754 | |
Mihajlo Medjedovic | 79121168e4 | |
Mihajlo Medjedovic | ef81e33f70 | |
semantic-release-bot | 96066c66cb | |
Mihajlo Medjedovic | b1819b776d | |
Mihajlo Medjedovic | bd7a392ffc | |
mihajlo | 7997b77158 | |
Mihajlo Medjedovic | d1966bcdc5 | |
semantic-release-bot | 7b5bbe024d | |
mihajlo | 4d84f15aca | |
Mihajlo Medjedovic | 928937daab | |
Mihajlo Medjedovic | 3bd8d247e5 | |
Mihajlo Medjedovic | cf6c9dd5f2 | |
Mihajlo Medjedovic | ff55cbbaad | |
Mihajlo Medjedovic | 3eda4e2c58 | |
Mihajlo Medjedovic | 02a8a1c565 | |
^ | 8769841f08 | |
Mihajlo Medjedovic | 7208fe1c3b | |
Mihajlo Medjedovic | 801c8c6a9f | |
^ | 51ebd25aa3 | |
Mihajlo Medjedovic | c6595c1f61 | |
Mihajlo Medjedovic | a267666e99 | |
Mihajlo Medjedovic | b27fea5b91 | |
^ | f8a14d4bde | |
^ | 633e35338d | |
^ | 8003da94e6 | |
allan | c3af97ef57 | |
Allan | 31d4e5c727 | |
Allan | fbbcf90956 | |
Allan | f522038b8d | |
Mihajlo Medjedovic | ace599b39f | |
Mihajlo Medjedovic | 963562621d | |
semantic-release-bot | 5171d07441 | |
allan | 9a0b9573d5 | |
Mihajlo Medjedovic | 4733311ef3 | |
Mihajlo Medjedovic | 432450a15b |
|
@ -10,7 +10,14 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install Google Chrome
|
||||
run: |
|
||||
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
|
||||
apt-get update
|
||||
apt-get install -y google-chrome-stable xvfb
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: echo "$NPMRC" > client/.npmrc
|
||||
|
@ -21,8 +28,26 @@ jobs:
|
|||
- name: Lint check
|
||||
run: npm run lint:check
|
||||
|
||||
- name: Licence checker
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
npm run license-checker
|
||||
# Decrypt and Install sheet
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm i ./libraries/sheet-crypto.tgz
|
||||
# End
|
||||
|
||||
- name: Licence checker
|
||||
run: |
|
||||
cd client
|
||||
npm run license-checker
|
||||
|
||||
- name: Angular Tests
|
||||
run: |
|
||||
cd client
|
||||
npm run test:headless
|
||||
|
||||
- name: Production Build
|
||||
run: |
|
||||
cd client
|
||||
npm run build
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
|
@ -34,7 +34,13 @@ jobs:
|
|||
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
# Decrypt and Install sheet
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm i ./libraries/sheet-crypto.tgz
|
||||
# End
|
||||
|
||||
- name: Check audit
|
||||
# Audit should fail and stop the CI if critical vulnerability found
|
||||
|
@ -48,7 +54,7 @@ jobs:
|
|||
- name: Angular Tests
|
||||
run: |
|
||||
cd client
|
||||
npm test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
|
||||
npm run test:headless
|
||||
|
||||
- name: Angular Production Build
|
||||
run: |
|
||||
|
@ -64,7 +70,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
|
@ -86,7 +92,13 @@ jobs:
|
|||
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
# Decrypt and Install sheet
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm i ./libraries/sheet-crypto.tgz
|
||||
# End
|
||||
|
||||
# Install pm2 and prepare SASJS server
|
||||
- run: npm i -g pm2
|
||||
|
@ -172,6 +184,18 @@ jobs:
|
|||
apt-get update
|
||||
apt-get install doxygen -y
|
||||
|
||||
- name: Frontend Preliminary Build
|
||||
description: We want to prevent creating empty release if frontend fails
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
# Decrypt and Install sheet
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm i ./libraries/sheet-crypto.tgz
|
||||
# End
|
||||
npm i webpack
|
||||
npm run build
|
||||
|
||||
- name: Create Empty Release (assets are posted later)
|
||||
run: |
|
||||
npm i
|
||||
|
@ -184,7 +208,6 @@ jobs:
|
|||
description: Must be created AFTER the release as the version (git tag) is used in the interface
|
||||
run: |
|
||||
cd client
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Build SAS9 EBI Release
|
||||
|
@ -228,6 +251,7 @@ jobs:
|
|||
rm sasjsbuild/services/clickme.html
|
||||
sasjs b -t viya
|
||||
cp sasjsbuild/viya.sas ./viya.sas
|
||||
cp sasjsbuild/viya.json ./viya.json
|
||||
|
||||
- name: Zip Frontend (including viya.json for full viya deploy)
|
||||
run: |
|
||||
|
@ -263,3 +287,4 @@ jobs:
|
|||
curl -k $URL -F attachment=@sas/sasjs_server.json.zip
|
||||
curl -k $URL -F attachment=@sas/sas9.sas
|
||||
curl -k $URL -F attachment=@sas/viya.sas
|
||||
curl -k $URL -F attachment=@sas/viya.json
|
||||
|
|
|
@ -11,6 +11,8 @@ client/cypress/screenshots
|
|||
client/cypress/results
|
||||
client/cypress/videos
|
||||
client/documentation
|
||||
client/sheet-crypto*
|
||||
client/.nx
|
||||
cypress.env.json
|
||||
sasjsbuild
|
||||
sasjsresults
|
||||
|
|
77
CHANGELOG.md
77
CHANGELOG.md
|
@ -1,3 +1,80 @@
|
|||
## [6.8.3](https://git.datacontroller.io/dc/dc/compare/v6.8.2...v6.8.3) (2024-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* updating core to increase filename length, closes [#103](https://git.datacontroller.io/dc/dc/issues/103) ([ee58fd5](https://git.datacontroller.io/dc/dc/commit/ee58fd5b4bc0dd3e3f232c4f26bb85b2e7fe2b54))
|
||||
|
||||
## [6.8.2](https://git.datacontroller.io/dc/dc/compare/v6.8.1...v6.8.2) (2024-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dc_request_logs option feature ([93758ef](https://git.datacontroller.io/dc/dc/commit/93758efb275966c181f1ee8b6c752010909a0282))
|
||||
* release process ([c0dc919](https://git.datacontroller.io/dc/dc/commit/c0dc9191e3b95ea6f7e5021fc0bdbcab0af4cc64))
|
||||
|
||||
## [6.8.1](https://git.datacontroller.io/dc/dc/compare/v6.8.0...v6.8.1) (2024-05-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* hide approve button when table revertable ([ec0f539](https://git.datacontroller.io/dc/dc/commit/ec0f539a337b176c83a661ff520a6892d47efa02))
|
||||
|
||||
# [6.8.0](https://git.datacontroller.io/dc/dc/compare/v6.7.0...v6.8.0) (2024-05-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ci sheet lib, submit message auto focus ([c5e4650](https://git.datacontroller.io/dc/dc/commit/c5e46503272f3f3d9cd83ac04225babf79d4de44))
|
||||
* **clarity:** new version style issues ([8c7de5a](https://git.datacontroller.io/dc/dc/commit/8c7de5aad7e7e32a64769696af9b93eb9a6225d3))
|
||||
* cypress tests ([3dd85cc](https://git.datacontroller.io/dc/dc/commit/3dd85cc60bd5ac99bc930b6b9c89a8e707e4d51d))
|
||||
* ensuring that only restorable versions are restorable ([a402856](https://git.datacontroller.io/dc/dc/commit/a4028562ce91b32ff971ab9821328b97cd23f381))
|
||||
* ensuring version history only includes loaded versions ([51ebd25](https://git.datacontroller.io/dc/dc/commit/51ebd25aa362aa8e66c83b29b2c64aa0f206f5bd))
|
||||
* final testing on restore feature ([297a84d](https://git.datacontroller.io/dc/dc/commit/297a84d3a4ebb47bef7f3ca9758978d727afed8d))
|
||||
* issue with multiple adds/deletes, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([904ca30](https://git.datacontroller.io/dc/dc/commit/904ca30f918da085fa05dae066367b512933d1a9))
|
||||
* load_ref var ([aaad9f7](https://git.datacontroller.io/dc/dc/commit/aaad9f7207115599a006980fff099d59738dd2cd))
|
||||
* removing alerts dummy data, closes [#93](https://git.datacontroller.io/dc/dc/issues/93) ([eba21e9](https://git.datacontroller.io/dc/dc/commit/eba21e96b4fa34e63b4477281f47d9a01d621f2e))
|
||||
* restore table version improvement ([549f357](https://git.datacontroller.io/dc/dc/commit/549f35766ba7b5bbe55694845e85bfefc4193375))
|
||||
* **sas:** viewer versions fix ([c6595c1](https://git.datacontroller.io/dc/dc/commit/c6595c1f618803d9202cba1a1fe76986449cf2e2))
|
||||
* stage and approve buttons renaming ([ef81e33](https://git.datacontroller.io/dc/dc/commit/ef81e33f704d0b4f99405d96498e1d29ac817982))
|
||||
* supporting SCD2 data reversions ([fa8396f](https://git.datacontroller.io/dc/dc/commit/fa8396f0394cbddb6dbacb4b355de078fad49980))
|
||||
* table info modal, versions - column names ([801c8c6](https://git.datacontroller.io/dc/dc/commit/801c8c6a9fb95388a06a6c6284fec4dc25bb77c5))
|
||||
* **updates:** angular, clarity, resolved legacy-peer-deps ([c60dd65](https://git.datacontroller.io/dc/dc/commit/c60dd65a1637333f11a0c39ef697c7292a6ede07))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* backend to show in getchangeinfo whether a user is allowed to restore ([8769841](https://git.datacontroller.io/dc/dc/commit/8769841f08694f672ef7ae1a17beacd0dbedda52))
|
||||
* list versions of target tables (backend) ([f8a14d4](https://git.datacontroller.io/dc/dc/commit/f8a14d4bdef055b99930491d1f6fabe55a50a497))
|
||||
* restore ([604c2e7](https://git.datacontroller.io/dc/dc/commit/604c2e70bdeeeb1aa5bb18b94f525ebd049397fa))
|
||||
* SAS services & tests for RESTORE, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([9ad7ae4](https://git.datacontroller.io/dc/dc/commit/9ad7ae47b5e793ce68ab21c9eeb8dee6cb85e496))
|
||||
* staging page, restore buttons ([02a8a1c](https://git.datacontroller.io/dc/dc/commit/02a8a1c5654350cafc53b749cceb686fc6848c33))
|
||||
* table metadata modal, versions tab (and link) ([b27fea5](https://git.datacontroller.io/dc/dc/commit/b27fea5b91e33b4673a3a991aedae558e366ca29))
|
||||
* **versions:** getting list of versions (plus test) ([8003da9](https://git.datacontroller.io/dc/dc/commit/8003da94e615463ed3ddfd60b0cbf2e58615eab1))
|
||||
|
||||
# [6.7.0](https://git.datacontroller.io/dc/dc/compare/v6.6.4...v6.7.0) (2024-04-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* numeric values in hot dropdown aligned right ([9635626](https://git.datacontroller.io/dc/dc/commit/963562621ddf0e8d24a29a8481c5e6da1b040708))
|
||||
|
||||
## [6.6.4](https://git.datacontroller.io/dc/dc/compare/v6.6.3...v6.6.4) (2024-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ordering SOFTSELECT numerically in dropdown ([f522038](https://git.datacontroller.io/dc/dc/commit/f522038b8ddb1da14b8adbf8346d0a4539a94cc8)), closes [#85](https://git.datacontroller.io/dc/dc/issues/85)
|
||||
* reverting col ([fbbcf90](https://git.datacontroller.io/dc/dc/commit/fbbcf90956bf538b032b0107c07b8576d20353b9))
|
||||
* typo ([31d4e5c](https://git.datacontroller.io/dc/dc/commit/31d4e5c727f790d428fb2ea8da60dca929561805))
|
||||
|
||||
## [6.6.3](https://git.datacontroller.io/dc/dc/compare/v6.6.2...v6.6.3) (2024-02-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow empty clause value when NE or CONTAINS ([432450a](https://git.datacontroller.io/dc/dc/commit/432450a15b51a269821ba1d430854f5d1dd04703))
|
||||
|
||||
## [6.6.2](https://git.datacontroller.io/dc/dc/compare/v6.6.1...v6.6.2) (2024-02-22)
|
||||
|
||||
|
||||
|
|
|
@ -27,4 +27,6 @@ For more information:
|
|||
|
||||
* Main site: https://datacontroller.io
|
||||
* Docs: https://docs.datacontroller.io
|
||||
* Code: https://code.datacontroller.io
|
||||
* Code: https://code.datacontroller.io
|
||||
|
||||
For support, contact support@4gl.io or reach out on [Matrix](https://matrix.to/#/#dc:4gl.io)!
|
|
@ -45,6 +45,7 @@
|
|||
"numbro",
|
||||
"@clr/icons",
|
||||
"@sasjs/adapter",
|
||||
"@sasjs/utils/types/serverType",
|
||||
"@sasjs/utils/input/validators",
|
||||
"@sasjs/utils/utils/bytesToSize",
|
||||
"base64-arraybuffer",
|
||||
|
@ -67,7 +68,6 @@
|
|||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/@clr/icons/clr-icons.min.js",
|
||||
"node_modules/marked/marked.min.js"
|
||||
]
|
||||
},
|
||||
|
@ -116,10 +116,10 @@
|
|||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "datacontroller:build:production"
|
||||
"buildTarget": "datacontroller:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "datacontroller:build:development"
|
||||
"buildTarget": "datacontroller:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
|
@ -127,30 +127,27 @@
|
|||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "datacontroller:build"
|
||||
"buildTarget": "datacontroller:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"codeCoverage": true,
|
||||
"polyfills": [
|
||||
"src/polyfills.ts",
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"karmaConfig": "karma.conf.js"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -9,6 +9,8 @@ export default defineConfig({
|
|||
html: true,
|
||||
json: false,
|
||||
},
|
||||
viewportHeight: 900,
|
||||
viewportWidth: 1600,
|
||||
|
||||
chromeWebSecurity: false,
|
||||
defaultCommandTimeout: 30000,
|
||||
|
|
|
@ -221,13 +221,13 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
.includes('approve')
|
||||
) {
|
||||
approvalButton.click()
|
||||
break
|
||||
|
|
|
@ -405,13 +405,13 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
.includes('approve')
|
||||
) {
|
||||
approvalButton.click()
|
||||
break
|
||||
|
@ -438,13 +438,13 @@ const rejectExcel = (callback?: any) => {
|
|||
|
||||
const acceptExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
.includes('approve')
|
||||
) {
|
||||
approvalButton.click()
|
||||
break
|
||||
|
|
|
@ -159,20 +159,21 @@ context('filtering tests: ', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('7 | filter bestnum field BETWEEN', (done) => {
|
||||
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
|
||||
// TODO: fix
|
||||
// it('7 | filter bestnum field BETWEEN', (done) => {
|
||||
// openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
|
||||
|
||||
openFilterPopup(() => {
|
||||
setFilterWithValue('SOME_BESTNUM', '0-10', 'between', () => {
|
||||
checkInfoBarIncludes(
|
||||
`AND,AND,0,SOME_BESTNUM,BETWEEN,0 AND 10`,
|
||||
(includes: boolean) => {
|
||||
if (includes) done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
// openFilterPopup(() => {
|
||||
// setFilterWithValue('SOME_BESTNUM', '0-10', 'between', () => {
|
||||
// checkInfoBarIncludes(
|
||||
// `AND,AND,0,SOME_BESTNUM,BETWEEN,0 AND 10`,
|
||||
// (includes: boolean) => {
|
||||
// if (includes) done()
|
||||
// }
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
this.afterEach(() => {
|
||||
// cy.visit(`${hostUrl}/SASLogon/logout`)
|
||||
|
|
|
@ -699,13 +699,13 @@ const submitTable = (callback?: any) => {
|
|||
|
||||
const approveTable = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
.includes('approve')
|
||||
) {
|
||||
approvalButton.click()
|
||||
break
|
||||
|
|
|
@ -125,13 +125,13 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
.includes('approve')
|
||||
) {
|
||||
approvalButton.click()
|
||||
break
|
||||
|
|
|
@ -221,14 +221,10 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
) {
|
||||
if (approvalButton.innerText.toLowerCase().includes('approve')) {
|
||||
approvalButton.click()
|
||||
break
|
||||
}
|
||||
|
|
|
@ -407,14 +407,10 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
) {
|
||||
if (approvalButton.innerText.toLowerCase().includes('approve')) {
|
||||
approvalButton.click()
|
||||
break
|
||||
}
|
||||
|
@ -440,14 +436,10 @@ const rejectExcel = (callback?: any) => {
|
|||
|
||||
const acceptExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
) {
|
||||
if (approvalButton.innerText.toLowerCase().includes('approve')) {
|
||||
approvalButton.click()
|
||||
break
|
||||
}
|
||||
|
|
|
@ -699,14 +699,10 @@ const submitTable = (callback?: any) => {
|
|||
|
||||
const approveTable = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
) {
|
||||
if (approvalButton.innerText.toLowerCase().includes('approve')) {
|
||||
approvalButton.click()
|
||||
break
|
||||
}
|
||||
|
|
|
@ -125,14 +125,10 @@ const submitExcel = (callback?: any) => {
|
|||
|
||||
const rejectExcel = (callback?: any) => {
|
||||
cy.get('button', { timeout: longerCommandTimeout })
|
||||
.should('contain', 'Go to approvals screen')
|
||||
.should('contain', 'Approve')
|
||||
.then((allButtons: any) => {
|
||||
for (let approvalButton of allButtons) {
|
||||
if (
|
||||
approvalButton.innerText
|
||||
.toLowerCase()
|
||||
.includes('go to approvals screen')
|
||||
) {
|
||||
if (approvalButton.innerText.toLowerCase().includes('approve')) {
|
||||
approvalButton.click()
|
||||
break
|
||||
}
|
||||
|
|
|
@ -42,4 +42,4 @@ module.exports = function (config) {
|
|||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
Binary file not shown.
|
@ -10,7 +10,7 @@ const check = (cwd) => {
|
|||
onlyAllow:
|
||||
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
|
||||
excludePackages:
|
||||
'@cds/city@1.1.0;@handsontable/angular@13.1.0;handsontable@13.1.0;hyperformula@2.5.0;jackspeak@2.2.0;path-scurry@1.7.0'
|
||||
'@cds/city@1.1.0;@handsontable/angular@13.1.0;handsontable@13.1.0;hyperformula@2.7.0;jackspeak@2.2.0;path-scurry@1.7.0'
|
||||
},
|
||||
(error, json) => {
|
||||
if (error) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,8 +18,8 @@
|
|||
"deploy_sasjs": "rsync -avhe ssh ./dist/* --delete root@${npm_config_account}.4gl.io:/var/www/html/dc/dev",
|
||||
"viyabuild": "cd build; ./viyabuild.sh",
|
||||
"lint": "cd .. && npm run lint",
|
||||
"test": "ng test",
|
||||
"test:headless": "ng test --browsers ChromeHeadless",
|
||||
"test": "npx ng test",
|
||||
"test:headless": "npx ng test --no-watch --no-progress --browsers ChromeHeadlessCI",
|
||||
"watch": "ng test watch=true",
|
||||
"pree2e": "webdriver-manager update",
|
||||
"e2e": "protractor protractor.config.js",
|
||||
|
@ -35,23 +35,22 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.1.2",
|
||||
"@angular/cdk": "^15.2.0",
|
||||
"@angular/common": "^16.1.2",
|
||||
"@angular/compiler": "^16.1.2",
|
||||
"@angular/core": "^16.1.2",
|
||||
"@angular/forms": "^16.1.2",
|
||||
"@angular/platform-browser": "^16.1.2",
|
||||
"@angular/platform-browser-dynamic": "^16.1.2",
|
||||
"@angular/router": "^16.1.2",
|
||||
"@cds/core": "^6.4.2",
|
||||
"@clr/angular": "^13.17.0",
|
||||
"@angular/animations": "^17.3.3",
|
||||
"@angular/cdk": "^17.3.3",
|
||||
"@angular/common": "^17.3.3",
|
||||
"@angular/compiler": "^17.3.3",
|
||||
"@angular/core": "^17.3.3",
|
||||
"@angular/forms": "^17.3.3",
|
||||
"@angular/platform-browser": "^17.3.3",
|
||||
"@angular/platform-browser-dynamic": "^17.3.3",
|
||||
"@angular/router": "^17.3.3",
|
||||
"@cds/core": "^6.10.0",
|
||||
"@clr/angular": "^17.0.1",
|
||||
"@clr/icons": "^13.0.2",
|
||||
"@clr/ui": "^13.17.0",
|
||||
"@clr/ui": "^17.0.1",
|
||||
"@handsontable/angular": "^13.1.0",
|
||||
"@sasjs/adapter": "4.10.2",
|
||||
"@sasjs/utils": "^3.4.0",
|
||||
"@sheet/crypto": "1.20211122.1",
|
||||
"@types/d3-graphviz": "^2.6.7",
|
||||
"@types/text-encoding": "0.0.35",
|
||||
"base64-arraybuffer": "^0.2.0",
|
||||
|
@ -78,24 +77,25 @@
|
|||
"stream-http": "3.2.0",
|
||||
"text-encoding": "^0.7.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.0"
|
||||
"vm": "^0.1.0",
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.1.0",
|
||||
"@angular-eslint/builder": "16.0.3",
|
||||
"@angular-eslint/eslint-plugin": "16.0.3",
|
||||
"@angular-eslint/eslint-plugin-template": "16.0.3",
|
||||
"@angular-eslint/schematics": "16.0.3",
|
||||
"@angular-eslint/template-parser": "16.0.3",
|
||||
"@angular/cli": "^16.1.0",
|
||||
"@angular/compiler-cli": "^16.1.2",
|
||||
"@angular-devkit/build-angular": "^17.3.3",
|
||||
"@angular-eslint/builder": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.3.0",
|
||||
"@angular-eslint/schematics": "17.3.0",
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "^17.3.3",
|
||||
"@angular/compiler-cli": "^17.3.3",
|
||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||
"@compodoc/compodoc": "^1.1.21",
|
||||
"@cypress/webpack-preprocessor": "^5.17.1",
|
||||
"@types/core-js": "^2.5.5",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@types/es6-shim": "^0.31.39",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasmine": "~5.1.4",
|
||||
"@types/lodash-es": "^4.17.3",
|
||||
"@types/marked": "^4.3.0",
|
||||
"@types/node": "12.20.50",
|
||||
|
@ -109,12 +109,12 @@
|
|||
"es6-shim": "^0.35.5",
|
||||
"eslint": "^8.33.0",
|
||||
"git-describe": "^4.0.4",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.1.0",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"jasmine-core": "~5.1.2",
|
||||
"karma": "~6.4.3",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.1",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"license-checker": "25.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mochawesome": "^7.1.3",
|
||||
|
@ -123,9 +123,7 @@
|
|||
"rimraf": "3.0.2",
|
||||
"ts-loader": "^9.2.8",
|
||||
"ts-node": "^3.3.0",
|
||||
"typedoc": "^0.24.8",
|
||||
"typedoc-plugin-external-module-name": "^4.0.6",
|
||||
"typescript": "~4.9.4",
|
||||
"typescript": "~5.4.4",
|
||||
"wait-on": "^6.0.1",
|
||||
"watch": "^1.0.2"
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="alert-items">
|
||||
<div class="alert-item static">
|
||||
<div class="alert-icon-wrapper">
|
||||
<clr-icon class="mt-2" shape="warning-standard"></clr-icon>
|
||||
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
|
||||
</div>
|
||||
<div class="alert-text">
|
||||
Data Controller (FREE Tier) - to upgrade contact
|
||||
|
@ -30,7 +30,7 @@
|
|||
<div class="alert-items">
|
||||
<div class="alert-item static">
|
||||
<div class="alert-icon-wrapper">
|
||||
<clr-icon class="mt-2" shape="warning-standard"></clr-icon>
|
||||
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
|
||||
</div>
|
||||
<div class="alert-text">
|
||||
Data Controller (FREE Tier) - Problem with licence
|
||||
|
@ -55,7 +55,7 @@
|
|||
<div class="alert-items">
|
||||
<div class="alert-item static">
|
||||
<div class="alert-icon-wrapper">
|
||||
<clr-icon class="mt-2" shape="warning-standard"></clr-icon>
|
||||
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
|
||||
</div>
|
||||
|
||||
<div class="alert-text">
|
||||
|
@ -85,7 +85,7 @@
|
|||
<div class="alert-items">
|
||||
<div class="alert-item static">
|
||||
<div class="alert-icon-wrapper">
|
||||
<clr-icon class="mt-2" shape="warning-standard"></clr-icon>
|
||||
<cds-icon class="alert-icon" shape="warning-standard"></cds-icon>
|
||||
</div>
|
||||
|
||||
<div class="alert-text">
|
||||
|
@ -204,14 +204,7 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="header-actions">
|
||||
<div class="nav-text">
|
||||
<app-loading-indicator></app-loading-indicator>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<app-user-nav-dropdown></app-user-nav-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<app-header-actions></app-header-actions>
|
||||
</header>
|
||||
<nav
|
||||
*ngIf="
|
||||
|
|
|
@ -91,33 +91,12 @@ header {
|
|||
}
|
||||
}
|
||||
|
||||
.nav
|
||||
.nav-link {
|
||||
color: #fafafa;
|
||||
opacity: .9;
|
||||
line-height: 1.45rem;
|
||||
}
|
||||
|
||||
.nav .nav-link:hover {
|
||||
box-shadow: inset 0 -3px 0 transparent;
|
||||
transition: box-shadow .2s ease-in;
|
||||
}
|
||||
|
||||
.nav
|
||||
.nav-link:hover {
|
||||
.nav-link:hover {
|
||||
color: #fafafa;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav .nav-link.active {
|
||||
background: #61717D;
|
||||
opacity: 1;
|
||||
box-shadow: inset 0 -3px transparent;
|
||||
// padding: 0 1rem 0 1rem;
|
||||
}
|
||||
|
||||
.nav .nav-item {
|
||||
margin-right: 1rem;
|
||||
.nav-link.active {
|
||||
background: #61717D;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer'
|
|||
SharedModule,
|
||||
ClarityModule,
|
||||
AppSharedModule,
|
||||
HomeModule,
|
||||
PipesModule,
|
||||
DirectivesModule,
|
||||
NgxJsonViewerModule
|
||||
|
|
|
@ -45,7 +45,10 @@ export const ROUTES: Routes = [
|
|||
path: 'licensing',
|
||||
loadChildren: () => LicensingModule
|
||||
},
|
||||
{ path: 'home', loadChildren: () => HomeModule },
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () => HomeModule
|
||||
},
|
||||
{
|
||||
/**
|
||||
* Load editor module with subroutes
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
|
||||
<div
|
||||
*ngIf="
|
||||
['autocomplete'].includes(
|
||||
['autocomplete', 'autocomplete.custom'].includes(
|
||||
$any(currentRecordValidator?.getRule(col.key)?.editor)
|
||||
)
|
||||
"
|
||||
|
@ -163,7 +163,7 @@
|
|||
|
||||
<div
|
||||
*ngIf="
|
||||
['autocomplete'].includes(
|
||||
['autocomplete', 'autocomplete.custom'].includes(
|
||||
$any(currentRecordValidator?.getRule(col.key)?.editor)
|
||||
)
|
||||
"
|
||||
|
|
|
@ -203,18 +203,21 @@
|
|||
|
||||
<span clrTooltipTrigger>
|
||||
{{ libdsParsed.libName }}.<a
|
||||
class="mr-10"
|
||||
class="mr-10 view-table"
|
||||
[routerLink]="'/view/data/' + libds!"
|
||||
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
|
||||
>
|
||||
</span>
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-left"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
{{ this.dsNote }}
|
||||
</clr-tooltip-content>
|
||||
|
||||
<ng-container *ngIf="this.dsNote && this.dsNote.length > 0">
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-left"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
{{ this.dsNote }}
|
||||
</clr-tooltip-content>
|
||||
</ng-container>
|
||||
</clr-tooltip>
|
||||
|
||||
<ng-container *ngIf="dataSource">
|
||||
|
@ -843,6 +846,12 @@
|
|||
</div>
|
||||
</clr-modal>
|
||||
|
||||
<app-dataset-info [(open)]="datasetInfo" [dsmeta]="dsmeta"></app-dataset-info>
|
||||
<app-dataset-info
|
||||
[(open)]="datasetInfo"
|
||||
[dsmeta]="dsmeta"
|
||||
[versions]="versions"
|
||||
(rowClicked)="datasetInfoModalRowClicked($event)"
|
||||
>
|
||||
</app-dataset-info>
|
||||
|
||||
<app-viewboxes [(viewboxModal)]="viewboxes"></app-viewboxes>
|
||||
|
|
|
@ -214,6 +214,10 @@ hot-table {
|
|||
width: 150px;
|
||||
}
|
||||
|
||||
.view-table {
|
||||
font-size: inherit !important;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// Let's leave it here for a reference if there
|
||||
// is an issue with viewboxes/filter modal overlaying
|
||||
|
|
|
@ -38,7 +38,8 @@ import { HotTableInterface } from '../models/HotTable.interface'
|
|||
import {
|
||||
$DataFormats,
|
||||
DSMeta,
|
||||
EditorsGetDataServiceResponse
|
||||
EditorsGetDataServiceResponse,
|
||||
Version
|
||||
} from '../models/sas/editors-getdata.model'
|
||||
import { DataFormat } from '../models/sas/common/DateFormat'
|
||||
import SheetInfo from '../models/SheetInfo'
|
||||
|
@ -121,6 +122,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
datasetInfo: boolean = false
|
||||
dsmeta: DSMeta[] = []
|
||||
versions: Version[] = []
|
||||
dsNote = ''
|
||||
|
||||
viewboxes: boolean = false
|
||||
|
@ -980,15 +982,18 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
// Apply licence rows limitation if exists, it is only affecting data
|
||||
// which will be send to SAS
|
||||
const strippedCsvArrayData = csvArrayData.slice(0, this.licenceState.value.submit_rows_limit)
|
||||
const strippedCsvArrayData = csvArrayData.slice(
|
||||
0,
|
||||
this.licenceState.value.submit_rows_limit
|
||||
)
|
||||
// To submit to sas service, we need clean version of CSV of file
|
||||
// attached. XLSX will do the parsing and heavy lifting
|
||||
// First we create worksheet of json (data we extracted)
|
||||
let ws = XLSX.utils.json_to_sheet(strippedCsvArrayData, {
|
||||
skipHeader: true
|
||||
});
|
||||
})
|
||||
// create CSV to be uploaded from worksheet
|
||||
let csvContentClean = XLSX.utils.sheet_to_csv(ws);
|
||||
let csvContentClean = XLSX.utils.sheet_to_csv(ws)
|
||||
// Prepend headers
|
||||
csvContentClean = csvArrayHeaders.join(',') + '\n' + csvContentClean
|
||||
|
||||
|
@ -1953,13 +1958,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
if (entry.values.length > 0) {
|
||||
hot.setCellMeta(entry.row, entry.col, 'renderer', 'autocomplete')
|
||||
hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete')
|
||||
hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete.custom')
|
||||
hot.setCellMeta(entry.row, entry.col, 'strict', entry.strict)
|
||||
hot.setCellMeta(entry.row, entry.col, 'filter', false)
|
||||
|
||||
this.currentEditRecordValidator?.updateRule(entry.col, {
|
||||
renderer: 'autocomplete',
|
||||
editor: 'autocomplete',
|
||||
editor: 'autocomplete.custom',
|
||||
strict: entry.strict,
|
||||
filter: false
|
||||
})
|
||||
|
@ -2054,13 +2059,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
|
||||
hot.setCellMeta(row, cellCol, 'renderer', 'autocomplete')
|
||||
hot.setCellMeta(row, cellCol, 'editor', 'autocomplete')
|
||||
hot.setCellMeta(row, cellCol, 'editor', 'autocomplete.custom')
|
||||
hot.setCellMeta(row, cellCol, 'strict', cellValidationEntry.strict)
|
||||
hot.setCellMeta(row, cellCol, 'filter', false)
|
||||
|
||||
this.currentEditRecordValidator?.updateRule(cellCol, {
|
||||
renderer: 'autocomplete',
|
||||
editor: 'autocomplete',
|
||||
editor: 'autocomplete.custom',
|
||||
strict: cellValidationEntry.strict,
|
||||
filter: false
|
||||
})
|
||||
|
@ -2258,8 +2263,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
setTimeout(() => {
|
||||
let txt: any = document.getElementById('formFields_8')
|
||||
txt.focus()
|
||||
})
|
||||
if (txt) txt.focus()
|
||||
}, 200)
|
||||
})
|
||||
|
||||
// let cnt = 0;
|
||||
|
@ -2703,13 +2708,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
const strict = this.cellValidationSource[validationSourceIndex].strict
|
||||
|
||||
hot.setCellMeta(row, column, 'renderer', 'autocomplete')
|
||||
hot.setCellMeta(row, column, 'editor', 'autocomplete')
|
||||
hot.setCellMeta(row, column, 'editor', 'autocomplete.custom')
|
||||
hot.setCellMeta(row, column, 'strict', strict)
|
||||
hot.setCellMeta(row, column, 'filter', false)
|
||||
|
||||
this.currentEditRecordValidator?.updateRule(column, {
|
||||
renderer: 'autocomplete',
|
||||
editor: 'autocomplete',
|
||||
editor: 'autocomplete.custom',
|
||||
strict: strict,
|
||||
filter: false
|
||||
})
|
||||
|
@ -2931,6 +2936,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
}
|
||||
|
||||
datasetInfoModalRowClicked(value: Version | DSMeta) {
|
||||
if ((<Version>value).LOAD_REF !== undefined) {
|
||||
// Type is Version
|
||||
const row = value as Version
|
||||
const url = `/stage/${row.LOAD_REF}`
|
||||
|
||||
this.router.navigate([url])
|
||||
}
|
||||
}
|
||||
|
||||
viewboxManager() {
|
||||
this.viewboxes = true
|
||||
}
|
||||
|
@ -2943,6 +2958,37 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Function checks if selected hot cell is solo cell selected
|
||||
* and if it is, set the `filter` property based on filter param.
|
||||
*
|
||||
* @param filter
|
||||
*/
|
||||
private setCellFilter(filter: boolean) {
|
||||
const hotSelected = this.hotInstance.getSelected()
|
||||
const selection = hotSelected ? hotSelected[0] : hotSelected
|
||||
|
||||
// When we open a dropdown we want filter disabled so value in cell
|
||||
// don't filter out items, since we want to see them all.
|
||||
// But when we start typing we want to be able to start filtering values
|
||||
// again
|
||||
if (selection) {
|
||||
const startRow = selection[0]
|
||||
const endRow = selection[2]
|
||||
const startCell = selection[1]
|
||||
const endCell = selection[3]
|
||||
|
||||
if (startRow === endRow && startCell === endCell) {
|
||||
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
|
||||
|
||||
// If filter is not already set at the value in the param, set it
|
||||
if (cellMeta && cellMeta.filter === !filter) {
|
||||
this.hotInstance.setCellMeta(startRow, startCell, 'filter', filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.licenceService.hot_license_key.subscribe(
|
||||
(hot_license_key: string | undefined) => {
|
||||
|
@ -3010,6 +3056,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
this.cols = response.data.cols
|
||||
this.dsmeta = response.data.dsmeta
|
||||
this.versions = response.data.versions || []
|
||||
|
||||
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
|
||||
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
|
||||
|
@ -3298,28 +3345,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
)
|
||||
|
||||
hot.addHook('beforeKeyDown', (e: any) => {
|
||||
const hotSelected = this.hotInstance.getSelected()
|
||||
const selection = hotSelected ? hotSelected[0] : hotSelected
|
||||
|
||||
hot.addHook('afterBeginEditing', () => {
|
||||
// When we open a dropdown we want filter disabled so value in cell
|
||||
// don't filter out items, since we want to see them all.
|
||||
this.setCellFilter(false)
|
||||
})
|
||||
|
||||
hot.addHook('beforeKeyDown', () => {
|
||||
// When we start typing, we are enabling the filter since we want to find
|
||||
// values faster.
|
||||
if (selection) {
|
||||
const startRow = selection[0]
|
||||
const endRow = selection[2]
|
||||
const startCell = selection[1]
|
||||
const endCell = selection[3]
|
||||
|
||||
if (startRow === endRow && startCell === endCell) {
|
||||
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
|
||||
|
||||
if (cellMeta && cellMeta.filter === false) {
|
||||
this.hotInstance.setCellMeta(startRow, startCell, 'filter', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setCellFilter(true)
|
||||
})
|
||||
|
||||
hot.addHook('afterChange', (source: any, change: any) => {
|
||||
|
|
|
@ -12,7 +12,6 @@ import { EditRecordComponent } from './components/edit-record/edit-record.compon
|
|||
import { UploadStaterComponent } from './components/upload-stater/upload-stater.component'
|
||||
import { EditorRoutingModule } from './editor-routing.module'
|
||||
import { EditorComponent } from './editor.component'
|
||||
import { HomeModule } from '../home/home.module'
|
||||
import { DcTreeModule } from '../shared/dc-tree/dc-tree.module'
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { ViewboxesModule } from '../shared/viewboxes/viewboxes.module'
|
||||
|
@ -33,7 +32,6 @@ registerAllModules()
|
|||
AppSharedModule,
|
||||
DirectivesModule,
|
||||
SharedModule,
|
||||
HomeModule,
|
||||
PipesModule,
|
||||
DcTreeModule,
|
||||
DragDropModule,
|
||||
|
|
|
@ -94,15 +94,17 @@
|
|||
{{ libTable.replace('-FC', '') }}
|
||||
</button>
|
||||
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
<span *ngIf="tableLocked">
|
||||
To unlock all tables, contact support@datacontroller.io
|
||||
</span>
|
||||
</clr-tooltip-content>
|
||||
<ng-container *ngIf="tableLocked">
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
<span>
|
||||
To unlock all tables, contact support@datacontroller.io
|
||||
</span>
|
||||
</clr-tooltip-content>
|
||||
</ng-container>
|
||||
</clr-tooltip>
|
||||
</clr-tree-node>
|
||||
</clr-tree-node>
|
||||
|
|
|
@ -2,147 +2,152 @@
|
|||
<div class="card-header">Licencing</div>
|
||||
|
||||
<div [ngSwitch]="action" class="card-block">
|
||||
<ng-container *ngSwitchCase="'key'">
|
||||
<p class="key-error" *ngIf="!keyError">
|
||||
Licence key is invalid. We can't provide you more details at the moment
|
||||
<div class="card-text">
|
||||
<ng-container *ngSwitchCase="'key'">
|
||||
<p class="key-error" *ngIf="!keyError">
|
||||
Licence key is invalid. We can't provide you more details at the
|
||||
moment
|
||||
</p>
|
||||
|
||||
<p
|
||||
class="key-error"
|
||||
*ngIf="keyError"
|
||||
[innerHTML]="licenseErrors[keyError]"
|
||||
></p>
|
||||
|
||||
<p *ngIf="errorDetails"><strong>Details:</strong> {{ errorDetails }}</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'limit'">
|
||||
<p class="key-error">
|
||||
The registered number of users reached the limit specified for your
|
||||
licence. Please contact
|
||||
<contact-link classes="color-green" />
|
||||
or your reseller to arrange additional licences for this product.
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'update'">
|
||||
<p class="key-error">
|
||||
Update the license key by uploading the licence file or by pasting a
|
||||
license key and activation key in the inputs below.
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<p>
|
||||
<strong>SYSSITE:</strong>
|
||||
<span
|
||||
*ngFor="let id of syssite.value; let i = index"
|
||||
[class.misskey]="missmatchedKey && missmatchedKey === id"
|
||||
>
|
||||
{{ id }}{{ i === syssite.value?.length! - 1 ? '' : ',' }}
|
||||
</span>
|
||||
|
||||
<a
|
||||
class="tooltip tooltip-md tooltip-top-right"
|
||||
(click)="copySyssite(copyIcon, copyTooltip, syssite.value || [])"
|
||||
>
|
||||
<clr-icon
|
||||
#copyIcon
|
||||
class="cursor-pointer"
|
||||
shape="copy"
|
||||
size="15"
|
||||
></clr-icon>
|
||||
<span #copyTooltip class="tooltip-content">Copy to clipboard</span>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p
|
||||
class="key-error"
|
||||
*ngIf="keyError"
|
||||
[innerHTML]="licenseErrors[keyError]"
|
||||
></p>
|
||||
|
||||
<p *ngIf="errorDetails"><strong>Details:</strong> {{ errorDetails }}</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'limit'">
|
||||
<p class="key-error">
|
||||
The registered number of users reached the limit specified for your
|
||||
licence. Please contact
|
||||
<contact-link classes="color-green" />
|
||||
or your reseller to arrange additional licences for this product.
|
||||
<p *ngIf="licenseKeyData && userCountLimitation" class="m-0">
|
||||
<strong>Allowed users:</strong>
|
||||
{{ licenseKeyData.users_allowed }}
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'update'">
|
||||
<p class="key-error">
|
||||
Update the license key by uploading the licence file or by pasting a
|
||||
license key and activation key in the inputs below.
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<p>
|
||||
<strong>SYSSITE:</strong>
|
||||
<span
|
||||
*ngFor="let id of syssite.value; let i = index"
|
||||
[class.misskey]="missmatchedKey && missmatchedKey === id"
|
||||
>
|
||||
{{ id }}{{ i === syssite.value?.length! - 1 ? '' : ',' }}
|
||||
</span>
|
||||
|
||||
<a
|
||||
class="tooltip tooltip-md tooltip-top-right"
|
||||
(click)="copySyssite(copyIcon, copyTooltip, syssite.value || [])"
|
||||
>
|
||||
<clr-icon
|
||||
#copyIcon
|
||||
class="cursor-pointer"
|
||||
shape="copy"
|
||||
size="15"
|
||||
></clr-icon>
|
||||
<span #copyTooltip class="tooltip-content">Copy to clipboard</span>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p *ngIf="licenseKeyData && userCountLimitation" class="m-0">
|
||||
<strong>Allowed users:</strong>
|
||||
{{ licenseKeyData.users_allowed }}
|
||||
</p>
|
||||
|
||||
<clr-tabs>
|
||||
<clr-tab>
|
||||
<button clrTabLink>Upload licence</button>
|
||||
<clr-tab-content>
|
||||
<input
|
||||
#licenceFile
|
||||
(change)="onFileCapture($event)"
|
||||
type="file"
|
||||
hidden
|
||||
/>
|
||||
<div
|
||||
(click)="licenceFile.click()"
|
||||
appFileDrop
|
||||
(fileDrop)="onFileCapture($event, true)"
|
||||
class="drop-area"
|
||||
>
|
||||
<clr-spinner
|
||||
class="spinner-sm"
|
||||
*ngIf="licenceFileLoading"
|
||||
></clr-spinner>
|
||||
<ng-container *ngIf="!licenceFileLoading">
|
||||
<div *ngIf="licencefile.filename === ''">
|
||||
Drop / Browse licence file
|
||||
</div>
|
||||
<div *ngIf="licencefile.filename !== ''">
|
||||
Selected file: <strong>{{ licencefile.filename }}</strong>
|
||||
</div>
|
||||
<div *ngIf="licenceFileError">
|
||||
<strong>{{ licenceFileError }}</strong>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
|
||||
<clr-tab>
|
||||
<button clrTabLink>Paste licence</button>
|
||||
<clr-tab-content>
|
||||
<form class="clr-form license-key-form">
|
||||
<p>Licence key:</p>
|
||||
<div class="clr-control-container">
|
||||
<textarea
|
||||
[(ngModel)]="licenceKeyValue"
|
||||
(mouseleave)="trimKeys()"
|
||||
name="license-key-area"
|
||||
placeholder="Paste licence key here"
|
||||
class="clr-textarea"
|
||||
></textarea>
|
||||
<clr-tabs>
|
||||
<clr-tab>
|
||||
<button clrTabLink>Upload licence</button>
|
||||
<clr-tab-content>
|
||||
<input
|
||||
#licenceFile
|
||||
(change)="onFileCapture($event)"
|
||||
type="file"
|
||||
hidden
|
||||
/>
|
||||
<div
|
||||
(click)="licenceFile.click()"
|
||||
appFileDrop
|
||||
(fileDrop)="onFileCapture($event, true)"
|
||||
class="drop-area"
|
||||
>
|
||||
<clr-spinner
|
||||
class="spinner-sm"
|
||||
*ngIf="licenceFileLoading"
|
||||
></clr-spinner>
|
||||
<ng-container *ngIf="!licenceFileLoading">
|
||||
<div *ngIf="licencefile.filename === ''">
|
||||
Drop / Browse licence file
|
||||
</div>
|
||||
<div *ngIf="licencefile.filename !== ''">
|
||||
Selected file: <strong>{{ licencefile.filename }}</strong>
|
||||
</div>
|
||||
<div *ngIf="licenceFileError">
|
||||
<strong>{{ licenceFileError }}</strong>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</form>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
|
||||
<form class="clr-form activation-key-form">
|
||||
<p>Activation key:</p>
|
||||
<div class="clr-control-container">
|
||||
<textarea
|
||||
[(ngModel)]="activationKeyValue"
|
||||
(mouseleave)="trimKeys()"
|
||||
name="activation-key-area"
|
||||
placeholder="Paste activation key here"
|
||||
class="clr-textarea"
|
||||
></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
</clr-tabs>
|
||||
<clr-tab>
|
||||
<button clrTabLink>Paste licence</button>
|
||||
<clr-tab-content>
|
||||
<form class="clr-form license-key-form">
|
||||
<p>Licence key:</p>
|
||||
<div class="clr-control-container">
|
||||
<textarea
|
||||
[(ngModel)]="licenceKeyValue"
|
||||
(mouseleave)="trimKeys()"
|
||||
name="license-key-area"
|
||||
placeholder="Paste licence key here"
|
||||
class="clr-textarea"
|
||||
></textarea>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button
|
||||
(click)="applyKeys()"
|
||||
class="btn btn-primary apply-keys"
|
||||
[clrLoading]="applyingKeys"
|
||||
[disabled]="disableApplyButton"
|
||||
>
|
||||
Apply licence keys
|
||||
</button>
|
||||
<form class="clr-form activation-key-form">
|
||||
<p>Activation key:</p>
|
||||
<div class="clr-control-container">
|
||||
<textarea
|
||||
[(ngModel)]="activationKeyValue"
|
||||
(mouseleave)="trimKeys()"
|
||||
name="activation-key-area"
|
||||
placeholder="Paste activation key here"
|
||||
class="clr-textarea"
|
||||
></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
</clr-tabs>
|
||||
</div>
|
||||
|
||||
<button
|
||||
*ngIf="isAppFreeTier.value"
|
||||
routerLink="/"
|
||||
class="btn btn-sm btn-link"
|
||||
>
|
||||
Continue with free tier
|
||||
</button>
|
||||
<div class="card-footer d-flex clr-align-items-center">
|
||||
<button
|
||||
(click)="applyKeys()"
|
||||
class="btn btn-primary apply-keys"
|
||||
[clrLoading]="applyingKeys"
|
||||
[disabled]="disableApplyButton"
|
||||
>
|
||||
Apply licence keys
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="isAppFreeTier.value"
|
||||
routerLink="/"
|
||||
class="btn btn-sm btn-link"
|
||||
>
|
||||
Continue with free tier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
.apply-keys {
|
||||
height: 40px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.drop-area {
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface EditorsGetDataSASResponse extends BaseSASResponse {
|
|||
dqrules: DQRule[]
|
||||
dsmeta: DSMeta[]
|
||||
dqdata: DQData[]
|
||||
versions: Version[]
|
||||
cols: Col[]
|
||||
maxvarlengths: Maxvarlength[]
|
||||
xl_rules: any[]
|
||||
|
@ -27,6 +28,18 @@ export interface DSMeta {
|
|||
ODS_TABLE: string
|
||||
NAME: string
|
||||
VALUE: string
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
LOAD_REF: string
|
||||
USER_NM: string
|
||||
VERSION_DTTM: string
|
||||
VERSION_DESC: string
|
||||
CHANGED_RECORDS: number
|
||||
NEW_RECORDS: number
|
||||
DELETED_RECORDS: number
|
||||
[key: string]: string | number
|
||||
}
|
||||
|
||||
export interface Sasdata {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { BaseSASResponse } from './common/BaseSASResponse'
|
||||
|
||||
export interface EditorsRestoreServiceResponse extends BaseSASResponse {
|
||||
restore_out: RestoreOut[]
|
||||
}
|
||||
|
||||
export interface RestoreOut {
|
||||
LOADREF: string
|
||||
}
|
|
@ -143,7 +143,6 @@
|
|||
(ngModelChange)="
|
||||
setVariableOperator(queryIndex, query.operator, clauseIndex)
|
||||
"
|
||||
class="mt-2"
|
||||
clrSelect
|
||||
>
|
||||
<option *ngFor="let opr of query.operators">{{ opr }}</option>
|
||||
|
|
|
@ -878,17 +878,25 @@ export class QueryComponent
|
|||
*/
|
||||
public hasInvalidCluase(clauses: any): boolean {
|
||||
for (let clause of clauses) {
|
||||
clause['invalidClause'] = false
|
||||
|
||||
if (
|
||||
clause.variable === null ||
|
||||
clause.operator === null ||
|
||||
clause.value === null ||
|
||||
clause.value === ''
|
||||
clause.value === '' &&
|
||||
!(clause.operator === 'NE' || clause.operator === 'CONTAINS')
|
||||
) {
|
||||
clause['invalidClause'] = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
clause.variable === null ||
|
||||
clause.operator === null ||
|
||||
clause.value === null
|
||||
) {
|
||||
clause['invalidClause'] = true
|
||||
|
||||
return true
|
||||
} else {
|
||||
clause['invalidClause'] = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -139,10 +139,7 @@
|
|||
<div class="card-header p-0">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-md-4 approvalBack">
|
||||
<span
|
||||
class="btn btn-sm btn-outline m-0"
|
||||
(click)="goToApprovalsList()"
|
||||
>
|
||||
<span class="btn btn-outline m-0" (click)="goToApprovalsList()">
|
||||
<clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to
|
||||
approvals list
|
||||
</span>
|
||||
|
@ -209,22 +206,22 @@
|
|||
<div class="d-flex justify-content-center mt-0">
|
||||
<div class="clr-row clr-gap-5 clr-gap-sm-0">
|
||||
<button
|
||||
class="btn btn-sm btn-outline text-center mt-5"
|
||||
class="btn btn-sm btn-outline text-center mt-5 mr-5i"
|
||||
(click)="goToBase(jsParams?.TABLE_NM)"
|
||||
>
|
||||
Go to base table screen
|
||||
View base table
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success-outline text-center mt-5"
|
||||
class="btn btn-sm btn-success-outline text-center mt-5 mr-5i"
|
||||
(click)="getTable(tableId)"
|
||||
>
|
||||
Go to edited screen
|
||||
View staged data
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-info-outline text-center mt-5"
|
||||
(click)="goBack(jsParams?.TABLE_NM)"
|
||||
>
|
||||
Go back to editor
|
||||
Edit base table
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -236,7 +233,7 @@
|
|||
id="acceptBtn"
|
||||
[clrLoading]="acceptLoading"
|
||||
type="submit"
|
||||
class="btn btn-sm btn-success"
|
||||
class="btn btn-sm btn-success mr-5i"
|
||||
(click)="approveTable()"
|
||||
[disabled]="
|
||||
!loadingTable || params?.ISAPPROVER === 'NO' || noChanges
|
||||
|
@ -246,7 +243,7 @@
|
|||
</button>
|
||||
<button
|
||||
id="rejectBtn"
|
||||
class="btn btn-sm btn btn-danger mr-0"
|
||||
class="btn btn-sm btn btn-danger mr-5i"
|
||||
(click)="rejectOpen = true"
|
||||
[disabled]="
|
||||
!loadingTable || params?.ISAPPROVER === 'NO' || noChanges
|
||||
|
@ -394,9 +391,9 @@
|
|||
<div class="card-header">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-md-4 approvalBack">
|
||||
<span class="btn btn-sm btn-outline" (click)="goToSubmitList()">
|
||||
<clr-icon shape="caret" dir="left" size="20"></clr-icon>Back to
|
||||
submitted list
|
||||
<span class="btn btn-outline" (click)="goToSubmitList()">
|
||||
<cds-icon shape="angle" direction="left" size="20"></cds-icon
|
||||
>Back to submitted list
|
||||
</span>
|
||||
</div>
|
||||
<div class="clr-col-md-4">
|
||||
|
@ -443,22 +440,22 @@
|
|||
<div class="d-flex justify-content-center mt-0">
|
||||
<div class="clr-row clr-gap-5 clr-gap-sm-0">
|
||||
<button
|
||||
class="btn btn-sm btn-outline text-center mt-5"
|
||||
class="btn btn-sm btn-outline text-center mt-5 mr-5i"
|
||||
(click)="goToBase(subObj.base)"
|
||||
>
|
||||
Go to base table screen
|
||||
View base table
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success-outline text-center mt-5"
|
||||
class="btn btn-sm btn-success-outline text-center mt-5 mr-5i"
|
||||
(click)="getTable(subObj.tableId)"
|
||||
>
|
||||
Go to edited screen
|
||||
View staged data
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-info-outline text-center mt-5"
|
||||
(click)="goBack(subObj.base)"
|
||||
>
|
||||
Go back to editor
|
||||
Edit base table
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,25 +6,31 @@
|
|||
>
|
||||
<h3 class="modal-title center text-center color-darker-gray">Dataset Meta</h3>
|
||||
<div class="modal-body">
|
||||
<p *ngIf="dsmetaGroupped.length < 1" class="text-center">
|
||||
<p *ngIf="dsmetaTabs.length < 1" class="text-center">
|
||||
No dataset meta to show.
|
||||
</p>
|
||||
|
||||
<clr-tabs clrLayout="vertical">
|
||||
<clr-tab *ngFor="let dsmeta of dsmetaGroupped; let index = index">
|
||||
<button clrTabLink id="link1">{{ dsmeta.group }}</button>
|
||||
<clr-tab *ngFor="let tab of tabs; let index = index">
|
||||
<button clrTabLink id="link1">{{ tab.name }}</button>
|
||||
<clr-tab-content
|
||||
id="content1"
|
||||
*clrIfActive="index === 0"
|
||||
class="d-flex clr-justify-content-center w-100"
|
||||
>
|
||||
<clr-datagrid>
|
||||
<clr-dg-column>Name</clr-dg-column>
|
||||
<clr-dg-column>Value</clr-dg-column>
|
||||
<ng-container *ngFor="let col of tab.colsToDisplay">
|
||||
<clr-dg-column>{{ col.colName || col.colKey }}</clr-dg-column>
|
||||
</ng-container>
|
||||
|
||||
<clr-dg-row *ngFor="let info of dsmeta.dsmeta">
|
||||
<clr-dg-cell>{{ info.NAME }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ info.VALUE }}</clr-dg-cell>
|
||||
<clr-dg-row
|
||||
(click)="tab.onRowClick ? tab.onRowClick(info) : ''"
|
||||
class="clickable-row"
|
||||
*ngFor="let info of tab.meta"
|
||||
>
|
||||
<ng-container *ngFor="let col of tab.colsToDisplay">
|
||||
<clr-dg-cell>{{ info[col.colKey] }}</clr-dg-cell>
|
||||
</ng-container>
|
||||
</clr-dg-row>
|
||||
</clr-datagrid>
|
||||
</clr-tab-content>
|
||||
|
|
|
@ -13,4 +13,23 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clr-modal {
|
||||
::ng-deep {
|
||||
.modal-dialog {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clickable-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
.datagrid-table .datagrid-cell:focus {
|
||||
outline: none;
|
||||
outline-offset: 0;
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@ import {
|
|||
Output,
|
||||
SimpleChanges
|
||||
} from '@angular/core'
|
||||
import { DSMeta } from 'src/app/models/sas/editors-getdata.model'
|
||||
import { DSMetaGroupped } from './models/dsmeta-groupped.model'
|
||||
import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model'
|
||||
import { Tab } from './models/dsmeta-groupped.model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-dataset-info',
|
||||
|
@ -18,10 +18,15 @@ import { DSMetaGroupped } from './models/dsmeta-groupped.model'
|
|||
export class DatasetInfoComponent implements OnInit, OnChanges {
|
||||
@Input() open: boolean = false
|
||||
@Input() dsmeta: DSMeta[] = []
|
||||
@Input() versions: Version[] = []
|
||||
|
||||
@Output() openChange = new EventEmitter<boolean>()
|
||||
@Output() rowClicked = new EventEmitter<Version | DSMeta>()
|
||||
|
||||
dsmetaGroupped: DSMetaGroupped[] = []
|
||||
dsmetaTabs: Tab<DSMeta>[] = []
|
||||
versionsTabs: Tab<Version>[] = []
|
||||
|
||||
tabs: Tab<DSMeta | Version>[] = []
|
||||
|
||||
constructor() {}
|
||||
|
||||
|
@ -30,28 +35,58 @@ export class DatasetInfoComponent implements OnInit, OnChanges {
|
|||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.dsmeta?.currentValue?.length > 0) {
|
||||
this.parseDSMeta()
|
||||
this.parseVersions()
|
||||
|
||||
this.tabs = [...[...this.dsmetaTabs], ...[...this.versionsTabs]]
|
||||
}
|
||||
}
|
||||
|
||||
parseDSMeta() {
|
||||
this.dsmetaGroupped = []
|
||||
this.dsmetaTabs = []
|
||||
|
||||
for (let info of this.dsmeta) {
|
||||
let groupIndex = this.dsmetaGroupped.findIndex(
|
||||
(x) => x.group === info.ODS_TABLE
|
||||
let groupIndex = this.dsmetaTabs.findIndex(
|
||||
(x) => x.name === info.ODS_TABLE
|
||||
)
|
||||
|
||||
if (groupIndex < 0)
|
||||
groupIndex =
|
||||
this.dsmetaGroupped.push({
|
||||
group: info.ODS_TABLE,
|
||||
dsmeta: []
|
||||
this.dsmetaTabs.push({
|
||||
name: info.ODS_TABLE,
|
||||
title: 'Dataset Meta',
|
||||
colsToDisplay: [{ colKey: 'NAME' }, { colKey: 'VALUE' }],
|
||||
meta: [],
|
||||
onRowClick: (value: DSMeta) => {
|
||||
this.rowClicked.emit(value)
|
||||
}
|
||||
}) - 1
|
||||
|
||||
this.dsmetaGroupped[groupIndex].dsmeta.push(info)
|
||||
this.dsmetaTabs[groupIndex].meta.push(info)
|
||||
}
|
||||
}
|
||||
|
||||
parseVersions() {
|
||||
this.versionsTabs = [
|
||||
{
|
||||
name: 'VERSIONS',
|
||||
title: 'Dataset Meta',
|
||||
colsToDisplay: [
|
||||
{ colKey: 'LOAD_REF' },
|
||||
{ colKey: 'USER_NM' },
|
||||
{ colKey: 'VERSION_DTTM' },
|
||||
{ colKey: 'NEW_RECORDS', colName: 'ADD' },
|
||||
{ colKey: 'CHANGED_RECORDS', colName: 'MOD' },
|
||||
{ colKey: 'DELETED_RECORDS', colName: 'DEL' },
|
||||
{ colKey: 'VERSION_DESC' }
|
||||
],
|
||||
meta: this.versions,
|
||||
onRowClick: (value: Version) => {
|
||||
this.rowClicked.emit(value)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
onOpenChange(open: boolean) {
|
||||
this.open = open
|
||||
this.openChange.emit(open)
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { DSMeta } from 'src/app/models/sas/editors-getdata.model'
|
||||
|
||||
export interface DSMetaGroupped {
|
||||
group: string
|
||||
dsmeta: DSMeta[]
|
||||
export interface Tab<T> {
|
||||
name: string
|
||||
title: string
|
||||
/**
|
||||
* Columns to be displayed in the the grid
|
||||
* If empty, all columns will be displayed
|
||||
*/
|
||||
colsToDisplay: {
|
||||
colKey: string
|
||||
colName?: string
|
||||
}[]
|
||||
meta: T[]
|
||||
onRowClick?: (value: any) => void
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { parseColType } from './utils/parseColType'
|
|||
import { dqValidate } from './validations/dq-validation'
|
||||
import { specialMissingNumericValidator } from './validations/hot-custom-validators'
|
||||
import { applyNumericFormats } from './utils/applyNumericFormats'
|
||||
import { CustomAutocompleteEditor } from './editors/numericAutocomplete'
|
||||
|
||||
export class DcValidator {
|
||||
private rules: DcValidation[] = []
|
||||
|
@ -38,6 +39,8 @@ export class DcValidator {
|
|||
dqData: DQData[],
|
||||
hotInstance?: Handsontable
|
||||
) {
|
||||
this.registerCustomEditors()
|
||||
|
||||
this.sasparams = sasparams
|
||||
this.hotInstance = hotInstance
|
||||
this.rules = parseColType(sasparams.COLTYPE)
|
||||
|
@ -51,6 +54,13 @@ export class DcValidator {
|
|||
this.setupValidations()
|
||||
}
|
||||
|
||||
registerCustomEditors() {
|
||||
Handsontable.editors.registerEditor(
|
||||
'autocomplete.custom',
|
||||
CustomAutocompleteEditor
|
||||
)
|
||||
}
|
||||
|
||||
getRules(): DcValidation[] {
|
||||
return this.rules
|
||||
}
|
||||
|
@ -262,6 +272,7 @@ export class DcValidator {
|
|||
if (source.length > 0) {
|
||||
this.rules[i].source = source
|
||||
this.rules[i].type = 'autocomplete'
|
||||
this.rules[i].editor = 'autocomplete.custom'
|
||||
this.rules[i].filter = false
|
||||
}
|
||||
|
||||
|
@ -315,7 +326,10 @@ export class DcValidator {
|
|||
|
||||
// Because of dynamic cell validation, that will change the type of cell to dropdown
|
||||
// `rules[i].colType` could be different type (eg. numeric). So we check if current cell is dropdown, to call HOT native dropdown validator
|
||||
if (this.editor === 'autocomplete') {
|
||||
if (
|
||||
this.editor === 'autocomplete' ||
|
||||
this.editor === 'autocomplete.custom'
|
||||
) {
|
||||
self
|
||||
.getHandsontableValidator('autocomplete')
|
||||
.call(this, value, (valid: boolean) => {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import Handsontable from 'handsontable'
|
||||
import Core from 'handsontable/core'
|
||||
|
||||
export class CustomAutocompleteEditor extends Handsontable.editors
|
||||
.AutocompleteEditor {
|
||||
constructor(instance: Core) {
|
||||
super(instance)
|
||||
}
|
||||
|
||||
createElements() {
|
||||
super.createElements()
|
||||
}
|
||||
|
||||
// Listbox open
|
||||
open(event?: Event | undefined): void {
|
||||
super.open(event)
|
||||
|
||||
if (this.isCellNumeric()) {
|
||||
this.htContainer.classList.add('numericListbox')
|
||||
} else {
|
||||
this.htContainer.classList.remove('numericListbox')
|
||||
}
|
||||
}
|
||||
|
||||
isCellNumeric() {
|
||||
return this.cellProperties?.className?.includes('htNumeric')
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ $clr-green: #60b515;
|
|||
height: $clr-header-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-right: 10px;
|
||||
|
||||
.spinner {
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
*ngFor="let programLog of sasjsRequests; let i = index"
|
||||
[id]="'request_' + i"
|
||||
[clrStackViewLevel]="1"
|
||||
[clrStackViewSetsize]="3"
|
||||
[clrStackViewPosinset]="3"
|
||||
>
|
||||
<clr-stack-label>
|
||||
{{ programLog.serviceLink }}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { LoadingIndicatorComponent } from './loading-indicator/loading-indicator
|
|||
import { LoginComponent } from './login/login.component'
|
||||
import { UserService } from './user.service'
|
||||
import { AlertsService } from './alerts/alerts.service'
|
||||
import { UserNavDropdownComponent } from './user-nav-dropdown/user-nav-dropdown.component'
|
||||
import { HeaderActions } from './user-nav-dropdown/header-actions.component'
|
||||
import { AlertsComponent } from './alerts/alerts.component'
|
||||
import { TermsComponent } from './terms/terms.component'
|
||||
import { DirectivesModule } from '../directives/directives.module'
|
||||
|
@ -26,7 +26,7 @@ import { ContactLinkComponent } from './contact-link/contact-link.component'
|
|||
declarations: [
|
||||
LoadingIndicatorComponent,
|
||||
LoginComponent,
|
||||
UserNavDropdownComponent,
|
||||
HeaderActions,
|
||||
AlertsComponent,
|
||||
TermsComponent,
|
||||
DatasetInfoComponent,
|
||||
|
@ -35,7 +35,7 @@ import { ContactLinkComponent } from './contact-link/contact-link.component'
|
|||
exports: [
|
||||
LoadingIndicatorComponent,
|
||||
LoginComponent,
|
||||
UserNavDropdownComponent,
|
||||
HeaderActions,
|
||||
AlertsComponent,
|
||||
TermsComponent,
|
||||
DatasetInfoComponent,
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<div class="header-actions">
|
||||
<app-loading-indicator></app-loading-indicator>
|
||||
|
||||
<clr-dropdown class="app-nav-dropdown">
|
||||
<button class="nav-text color-white" clrDropdownToggle>
|
||||
<span>{{ userName }}</span>
|
||||
<span *ngIf="userName !== 'Not logged in' && isViya"
|
||||
><img class="avatar-img" src="{{ getPictureUrl() }}" alt=""
|
||||
/></span>
|
||||
<span
|
||||
class="badge badge-danger"
|
||||
*ngIf="!sasjsConfig.debug"
|
||||
[class.hidden]="failedReqs.length === 0"
|
||||
>{{ failedReqs.length }}</span
|
||||
>
|
||||
<span
|
||||
class="badge badge-info"
|
||||
*ngIf="sasjsConfig.debug"
|
||||
[class.hidden]="debugLogs.length === 0"
|
||||
>{{ debugLogs.length }}</span
|
||||
>
|
||||
<clr-icon *ngIf="!isViya" shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
|
||||
<div #dropdownItemDebug class="debug-switch-item" clrDropdownItem>
|
||||
<clr-toggle-container
|
||||
class="toggle-switch"
|
||||
(click)="onDebugRowClick($event, dropdownItemDebug)"
|
||||
>
|
||||
<clr-toggle-wrapper>
|
||||
<input
|
||||
id="debug-toggle1"
|
||||
type="checkbox"
|
||||
[(ngModel)]="sasjsConfig.debug"
|
||||
(ngModelChange)="onDebugModeChange()"
|
||||
clrToggle
|
||||
/>
|
||||
<label>Debug Mode</label>
|
||||
</clr-toggle-wrapper>
|
||||
</clr-toggle-container>
|
||||
</div>
|
||||
<a (click)="openRequestsModal()" clrDropdownItem>
|
||||
<span>SAS Requests</span>
|
||||
</a>
|
||||
|
||||
<ng-container *ngIf="!isDeployPage">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://docs.datacontroller.io"
|
||||
clrDropdownItem
|
||||
>
|
||||
<span class="dropdown-text">Documentation</span>
|
||||
</a>
|
||||
</ng-container>
|
||||
|
||||
<div class="separator"></div>
|
||||
<a href="..." routerLink="/system" clrDropdownItem>
|
||||
<span>System</span>
|
||||
</a>
|
||||
<a href="..." (click)="logout($event)" clrDropdownItem>
|
||||
<span>Log Out</span>
|
||||
<clr-icon class="clr-logout" shape="logout"></clr-icon>
|
||||
</a>
|
||||
<div class="copyRight">
|
||||
<span>v{{ commitVer }}</span>
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
</div>
|
|
@ -1,17 +1,12 @@
|
|||
// it must be a better way to read clarity variables...
|
||||
//@import '../../../../node_modules/@clr/ui/src/utils/helpers.clarity';
|
||||
|
||||
//@import '../../../../node_modules/@clr/ui/src/color/utils/colors.clarity';
|
||||
//@import '../../../../node_modules/@clr/ui/src/color/utils/contrast-cache.clarity';
|
||||
//@import '../../../../node_modules/@clr/ui/src/color/utils/helpers.clarity';
|
||||
|
||||
//@import '../../../../node_modules/@clr/ui/src/utils/variables.clarity';
|
||||
|
||||
$clr-header-height: 3rem;
|
||||
$clr-near-white: #fafafa;
|
||||
$clr-dark-gray: #565656;
|
||||
$clr-light-gray: #eee;
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.copyRight {
|
||||
margin-top: 10px;
|
||||
|
|
@ -8,11 +8,11 @@ import { EventService } from '../../services/event.service'
|
|||
import { Router } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-nav-dropdown',
|
||||
templateUrl: './user-nav-dropdown.component.html',
|
||||
styleUrls: ['./user-nav-dropdown.component.scss']
|
||||
selector: 'app-header-actions',
|
||||
templateUrl: './header-actions.component.html',
|
||||
styleUrls: ['./header-actions.component.scss']
|
||||
})
|
||||
export class UserNavDropdownComponent implements OnInit, OnDestroy {
|
||||
export class HeaderActions implements OnInit, OnDestroy {
|
||||
public userName: string = 'Not logged in'
|
||||
private reqSub: Subscription = new Subscription()
|
||||
private userSub: Subscription = new Subscription()
|
|
@ -1,121 +0,0 @@
|
|||
<clr-dropdown class="app-nav-dropdown d-md-block">
|
||||
<button class="nav-text color-white" clrDropdownToggle>
|
||||
<span>{{ userName }}</span>
|
||||
<span *ngIf="userName !== 'Not logged in' && isViya"
|
||||
><img class="avatar-img" src="{{ getPictureUrl() }}" alt=""
|
||||
/></span>
|
||||
<span
|
||||
class="badge badge-danger"
|
||||
*ngIf="!sasjsConfig.debug"
|
||||
[class.hidden]="failedReqs.length === 0"
|
||||
>{{ failedReqs.length }}</span
|
||||
>
|
||||
<span
|
||||
class="badge badge-info"
|
||||
*ngIf="sasjsConfig.debug"
|
||||
[class.hidden]="debugLogs.length === 0"
|
||||
>{{ debugLogs.length }}</span
|
||||
>
|
||||
<clr-icon *ngIf="!isViya" shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
|
||||
<div #dropdownItemDebug class="debug-switch-item" clrDropdownItem>
|
||||
<clr-toggle-container
|
||||
class="toggle-switch"
|
||||
(click)="onDebugRowClick($event, dropdownItemDebug)"
|
||||
>
|
||||
<clr-toggle-wrapper>
|
||||
<input
|
||||
id="debug-toggle1"
|
||||
type="checkbox"
|
||||
[(ngModel)]="sasjsConfig.debug"
|
||||
(ngModelChange)="onDebugModeChange()"
|
||||
clrToggle
|
||||
/>
|
||||
<label>Debug Mode</label>
|
||||
</clr-toggle-wrapper>
|
||||
</clr-toggle-container>
|
||||
</div>
|
||||
<a (click)="openRequestsModal()" clrDropdownItem>
|
||||
<span>SAS Requests</span>
|
||||
</a>
|
||||
|
||||
<ng-container *ngIf="!isDeployPage">
|
||||
<a target="_blank" href="https://docs.datacontroller.io" clrDropdownItem>
|
||||
<span class="dropdown-text">Documentation</span>
|
||||
</a>
|
||||
</ng-container>
|
||||
|
||||
<div class="separator"></div>
|
||||
<a href="..." routerLink="/system" clrDropdownItem>
|
||||
<span>System</span>
|
||||
</a>
|
||||
<a href="..." (click)="logout($event)" clrDropdownItem>
|
||||
<span>Log Out</span>
|
||||
<clr-icon class="clr-logout" shape="logout"></clr-icon>
|
||||
</a>
|
||||
<div class="copyRight">
|
||||
<span>v{{ commitVer }}</span>
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
<div class="content-container h-auto">
|
||||
<nav class="sidenav d-block d-md-none" [clr-nav-level]="2">
|
||||
<section class="sidenav-content">
|
||||
<a href="..." class="nav-link active">
|
||||
{{ userName }}
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<form>
|
||||
<div class="toggle-switch">
|
||||
<input
|
||||
id="debug-toggle2"
|
||||
type="checkbox"
|
||||
[(ngModel)]="sasjsConfig.debug"
|
||||
(ngModelChange)="onDebugModeChange()"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
<label
|
||||
for="debug-toggle2"
|
||||
class="debug-toggle-label color-dark-gray"
|
||||
>Debug Mode</label
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- <a href="..." class="nav-link d-block" [routerLink]="['/application-logs']">
|
||||
<span>Application Logs</span>
|
||||
<span class="badge" *ngIf="appLogs.length > 0">{{appLogs.length}}</span>
|
||||
</a>
|
||||
<a *ngIf="debugMode" class="nav-link d-block" href="..." [routerLink]="['/debug-logs']">
|
||||
<span>Debug Logs</span>
|
||||
<span class="badge badge-info" *ngIf="debugLogs.length > 0">{{debugLogs.length}}</span>
|
||||
</a>
|
||||
<a *ngIf="!debugMode" class="nav-link d-block" href="..." [routerLink]="['/failed-requests']">
|
||||
<span>Failed Requests</span>
|
||||
<span class="badge badge-danger" *ngIf="failedReqs.length > 0">{{failedReqs.length}}</span>
|
||||
</a>
|
||||
<a href="..." class="nav-link d-block" [routerLink]="['/errors']">
|
||||
<span>Errors</span>
|
||||
<span class="badge badge-warning" *ngIf="sasErrors.length > 0">{{sasErrors.length}}</span>
|
||||
</a> -->
|
||||
<a
|
||||
class="nav-link d-block"
|
||||
target="_blank"
|
||||
href="https://docs.datacontroller.io"
|
||||
>
|
||||
<span>Documentation</span>
|
||||
</a>
|
||||
<div class="separator"></div>
|
||||
<a routerLink="/system" class="nav-link d-block">
|
||||
<span>System</span>
|
||||
<clr-icon shape="logout"></clr-icon>
|
||||
</a>
|
||||
<a href="..." class="nav-link d-block" (click)="logout($event)">
|
||||
<span>Log Out</span>
|
||||
<clr-icon shape="logout"></clr-icon>
|
||||
</a>
|
||||
</section>
|
||||
</nav>
|
||||
</div>
|
|
@ -58,34 +58,58 @@
|
|||
<div class="mt-20">
|
||||
<div class="row">
|
||||
<button
|
||||
class="btn btn-sm btn-outline text-center mt-20"
|
||||
class="btn btn-sm btn-outline text-center mr-5i"
|
||||
(click)="viewerTableScreen()"
|
||||
[disabled]="revertingChanges"
|
||||
>
|
||||
Go to base table screen
|
||||
View base table
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!(tableDetails?.['ALLOW_RESTORE'] === 'YES')"
|
||||
id="approval-btn"
|
||||
class="btn btn-sm btn-success-outline text-center mt-20"
|
||||
class="btn btn-sm btn-success-outline text-center mr-5i"
|
||||
[disabled]="
|
||||
tableDetails?.REVIEW_STATUS_ID === 'APPROVED' ||
|
||||
tableDetails?.REVIEW_STATUS_ID === 'REJECTED'
|
||||
"
|
||||
(click)="approveTableScreen()"
|
||||
[disabled]="revertingChanges"
|
||||
>
|
||||
Go to approvals screen
|
||||
Approve
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-info-outline text-center mt-20"
|
||||
class="btn btn-sm btn-info-outline text-center mr-5i"
|
||||
(click)="goBack()"
|
||||
[disabled]="revertingChanges"
|
||||
>
|
||||
Go back to editor
|
||||
Edit base table
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success text-center mt-20 min-w-0"
|
||||
class="btn btn-sm btn-success text-center mr-5i min-w-0"
|
||||
(click)="download(tableDetails?.TABLE_ID)"
|
||||
>
|
||||
<clr-icon shape="download"></clr-icon>
|
||||
</button>
|
||||
|
||||
<clr-tooltip>
|
||||
<button
|
||||
*ngIf="tableDetails?.['ALLOW_RESTORE'] === 'YES'"
|
||||
(click)="revertChanges()"
|
||||
clrTooltipTrigger
|
||||
[clrLoading]="revertingChanges"
|
||||
class="btn btn-sm btn-danger text-center mt-20"
|
||||
>
|
||||
REVERT
|
||||
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-left"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
<span> Revert this and all subsequent changes </span>
|
||||
</clr-tooltip-content>
|
||||
</button>
|
||||
</clr-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,10 +4,10 @@ import { Router } from '@angular/router'
|
|||
import { ActivatedRoute } from '@angular/router'
|
||||
import { SasService } from '../services/sas.service'
|
||||
import { EventService } from '../services/event.service'
|
||||
import { AppService } from '../services/app.service'
|
||||
import { HotTableInterface } from '../models/HotTable.interface'
|
||||
import { LicenceService } from '../services/licence.service'
|
||||
import { globals } from '../_globals'
|
||||
import { EditorsRestoreServiceResponse } from '../models/sas/editors-restore.model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-stage',
|
||||
|
@ -23,6 +23,7 @@ export class StageComponent implements OnInit {
|
|||
public keysArray: any
|
||||
public tableDetails: any
|
||||
public loaded: boolean = false
|
||||
public revertingChanges: boolean = false
|
||||
public licenceState = this.licenceService.licenceState
|
||||
public hotTable: HotTableInterface = {
|
||||
data: [],
|
||||
|
@ -162,6 +163,31 @@ export class StageComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
revertChanges() {
|
||||
this.revertingChanges = true
|
||||
|
||||
const data = {
|
||||
restore_in: [
|
||||
{
|
||||
load_ref: this.table_id
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.sasService
|
||||
.request('editors/restore', data)
|
||||
.then((res: EditorsRestoreServiceResponse) => {
|
||||
if (res.restore_out) {
|
||||
this.route.navigate([`/stage`]).then(() => {
|
||||
this.route.navigate([`/stage/${res.restore_out[0].LOADREF}`])
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.revertingChanges = false
|
||||
})
|
||||
}
|
||||
|
||||
private setFocus() {
|
||||
setTimeout(() => {
|
||||
let approvalBtn: any = window.document.getElementById('approval-btn')
|
||||
|
|
|
@ -9,43 +9,45 @@
|
|||
class="sys-info d-flex clr-justify-content-center clr-flex-column clr-flex-lg-row"
|
||||
>
|
||||
<div>
|
||||
<h6 class="m-0">Environment Details <span class="dark"></span></h6>
|
||||
<p class="m-0">
|
||||
<h6 cds-text="subsection" class="mb-10">
|
||||
Environment Details <span class="dark"></span>
|
||||
</h6>
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSSITE: <span class="dark">{{ environmentInfo?.SYSSITE }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSSCPL: <span class="dark">{{ environmentInfo?.SYSSCPL }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSTCPIPHOSTNAME:
|
||||
<span class="dark">{{ environmentInfo?.SYSTCPIPHOSTNAME }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSVLONG: <span class="dark">{{ environmentInfo?.SYSVLONG }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
MEMSIZE: <span class="dark">{{ environmentInfo?.MEMSIZE }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSPROCESSMODE:
|
||||
<span class="dark">{{ environmentInfo?.SYSPROCESSMODE }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSHOSTNAME:
|
||||
<span class="dark">{{ environmentInfo?.SYSHOSTNAME }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSHOSTINFOLONG:
|
||||
<span class="dark">{{ environmentInfo?.SYSHOSTINFOLONG }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
SYSENCODING:
|
||||
<span class="dark">{{ environmentInfo?.SYSENCODING }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
AUTOEXEC: <span class="dark">{{ environmentInfo?.AUTOEXEC }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
DC ADMIN GROUP:
|
||||
<span class="dark">{{ environmentInfo?.DC_ADMIN_GROUP }}</span>
|
||||
</p>
|
||||
|
@ -53,42 +55,44 @@
|
|||
|
||||
<div class="d-flex clr-justify-content-lg-center">
|
||||
<div>
|
||||
<h6 class="m-0">
|
||||
<h6 cds-text="subsection" class="mb-10">
|
||||
Data Controller Details <span class="dark"></span>
|
||||
</h6>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Application version:
|
||||
<span class="dark">{{ appInfo.appVersion }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Build timestamp:
|
||||
<span class="dark">{{ appInfo.buildTimestamp }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Adapter version:
|
||||
<span class="dark">{{ appInfo.adapterVersion }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
HTTP: <span class="dark">{{ http ? 'YES' : 'NO' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 class="m-0">Licence details <span class="dark"></span></h6>
|
||||
<p class="m-0">
|
||||
<h6 cds-text="subsection" class="mb-10">
|
||||
Licence details <span class="dark"></span>
|
||||
</h6>
|
||||
<p cds-text="label" class="m-0">
|
||||
Valid until:
|
||||
<span class="dark">{{ licenceInfo?.valid_until }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Users allowed:
|
||||
<span class="dark">{{ licenceInfo?.users_allowed }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Site IDs:
|
||||
<span class="dark">{{ licenceInfo?.site_id_multiple }}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Free Tier:
|
||||
<span class="dark">{{ licenceInfo?.demo ? 'YES' : 'NO' }}</span>
|
||||
</p>
|
||||
|
@ -157,25 +161,25 @@
|
|||
licenceState.value.lineage_daily_limit
|
||||
}}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Viewboxes:
|
||||
<span class="dark">{{
|
||||
licenceState.value.viewbox ? 'YES' : 'NO'
|
||||
}}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
File Upload:
|
||||
<span class="dark">{{
|
||||
licenceState.value.fileUpload ? 'YES' : 'NO'
|
||||
}}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Edit record:
|
||||
<span class="dark">{{
|
||||
licenceState.value.editRecord ? 'YES' : 'NO'
|
||||
}}</span>
|
||||
</p>
|
||||
<p class="m-0">
|
||||
<p cds-text="label" class="m-0">
|
||||
Add record:
|
||||
<span class="dark">{{
|
||||
licenceState.value.addRecord ? 'YES' : 'NO'
|
||||
|
|
|
@ -99,15 +99,18 @@
|
|||
</ng-container>
|
||||
{{ libTable.replace('-FC', '') }}
|
||||
</button>
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
<span *ngIf="tableLocked">
|
||||
To unlock all tables, contact support@datacontroller.io
|
||||
</span>
|
||||
</clr-tooltip-content>
|
||||
|
||||
<ng-container *ngIf="tableLocked">
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
<span>
|
||||
To unlock all tables, contact support@datacontroller.io
|
||||
</span>
|
||||
</clr-tooltip-content>
|
||||
</ng-container>
|
||||
</clr-tooltip>
|
||||
</clr-tree-node>
|
||||
</clr-tree-node>
|
||||
|
@ -361,7 +364,7 @@
|
|||
<h3
|
||||
class="viewerTitle clr-flex-column d-flex clr-flex-sm-row clr-align-items-center clr-justify-content-center"
|
||||
>
|
||||
<clr-tooltip class="d-flex">
|
||||
<clr-tooltip class="d-flex clr-align-items-center">
|
||||
<clr-icon
|
||||
clrTooltipTrigger
|
||||
(click)="datasetInfo = true"
|
||||
|
@ -373,19 +376,22 @@
|
|||
<clr-icon
|
||||
*ngIf="tableTitle?.includes('-FC')"
|
||||
shape="bolt"
|
||||
class="color-yellow mt-5 mr-5"
|
||||
class="color-yellow mr-5"
|
||||
></clr-icon>
|
||||
|
||||
<span clrTooltipTrigger *ngIf="tableTitle && tableTitle.length > 0">
|
||||
{{ tableTitle?.replace('-FC', '') }}
|
||||
</span>
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-left"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
{{ this.dsNote }}
|
||||
</clr-tooltip-content>
|
||||
|
||||
<ng-container *ngIf="this.dsNote && this.dsNote.length > 0">
|
||||
<clr-tooltip-content
|
||||
clrPosition="bottom-left"
|
||||
clrSize="lg"
|
||||
*clrIfOpen
|
||||
>
|
||||
{{ this.dsNote }}
|
||||
</clr-tooltip-content>
|
||||
</ng-container>
|
||||
</clr-tooltip>
|
||||
|
||||
<ng-container *ngIf="tableTitle && tableTitle.length > 0">
|
||||
|
@ -419,62 +425,34 @@
|
|||
options
|
||||
</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success-outline"
|
||||
(click)="newViewbox()"
|
||||
clrDropdownItem
|
||||
>
|
||||
<div (click)="newViewbox()" clrDropdownItem>
|
||||
<clr-icon shape="view-cards"></clr-icon>
|
||||
<span>Viewboxes</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success-outline"
|
||||
</div>
|
||||
<div
|
||||
*ngIf="tableEditExists()"
|
||||
(click)="editTable()"
|
||||
clrDropdownItem
|
||||
>
|
||||
<clr-icon shape="pencil"></clr-icon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success-outline"
|
||||
*ngIf="tableuri"
|
||||
(click)="goToLineage()"
|
||||
clrDropdownItem
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="tableuri" (click)="goToLineage()" clrDropdownItem>
|
||||
<clr-icon shape="switch"></clr-icon>
|
||||
<span>Lineage</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline btn-block"
|
||||
(click)="openQb()"
|
||||
clrDropdownItem
|
||||
>
|
||||
</div>
|
||||
<div (click)="openQb()" clrDropdownItem>
|
||||
<clr-icon shape="filter"></clr-icon>
|
||||
<span>Filter</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success-outline"
|
||||
(click)="openDownload = true"
|
||||
clrDropdownItem
|
||||
>
|
||||
</div>
|
||||
<div (click)="openDownload = true" clrDropdownItem>
|
||||
<clr-icon shape="download"></clr-icon>
|
||||
<span>Download</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success-outline"
|
||||
(click)="showWebQuery()"
|
||||
clrDropdownItem
|
||||
>
|
||||
</div>
|
||||
<div (click)="showWebQuery()" clrDropdownItem>
|
||||
<clr-icon shape="download-cloud"></clr-icon>
|
||||
<span>Web Query URL</span>
|
||||
</button>
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
</div>
|
||||
|
@ -667,6 +645,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<app-dataset-info [(open)]="datasetInfo" [dsmeta]="dsmeta"></app-dataset-info>
|
||||
<app-dataset-info
|
||||
[(open)]="datasetInfo"
|
||||
[dsmeta]="dsmeta"
|
||||
[versions]="versions"
|
||||
(rowClicked)="datasetInfoModalRowClicked($event)"
|
||||
>
|
||||
</app-dataset-info>
|
||||
|
||||
<app-viewboxes [(viewboxModal)]="viewboxOpen"></app-viewboxes>
|
||||
|
|
|
@ -57,6 +57,7 @@ clr-tree-node button {
|
|||
.viewerTitle {
|
||||
text-align:center;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
|
|
@ -25,7 +25,11 @@ import { FilterGroup, FilterQuery } from '../models/FilterQuery'
|
|||
import { HotTableInterface } from '../models/HotTable.interface'
|
||||
import { LoggerService } from '../services/logger.service'
|
||||
import Handsontable from 'handsontable'
|
||||
import { $DataFormats, DSMeta } from '../models/sas/editors-getdata.model'
|
||||
import {
|
||||
$DataFormats,
|
||||
DSMeta,
|
||||
Version
|
||||
} from '../models/sas/editors-getdata.model'
|
||||
import { mergeColsRules } from '../shared/dc-validator/utils/mergeColsRules'
|
||||
import { PublicViewtablesServiceResponse } from '../models/sas/public-viewtables.model'
|
||||
import { PublicViewlibsServiceResponse } from '../models/sas/public-viewlibs.model'
|
||||
|
@ -95,6 +99,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||
public $dataFormats: $DataFormats | null = null
|
||||
public datasetInfo: boolean = false
|
||||
public dsmeta: DSMeta[] = []
|
||||
public versions: Version[] = []
|
||||
public dsNote = ''
|
||||
|
||||
public licenceState = this.licenceService.licenceState
|
||||
|
@ -247,6 +252,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||
this.hotTable.data = res.viewdata
|
||||
this.$dataFormats = res.$viewdata
|
||||
this.dsmeta = res.dsmeta
|
||||
this.versions = res.versions || []
|
||||
this.setDSNote()
|
||||
this.numberOfRows = res.sasparams[0].NOBS
|
||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||
|
@ -805,6 +811,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||
this.hotTable.data = res.viewdata
|
||||
this.$dataFormats = res.$viewdata
|
||||
this.dsmeta = res.dsmeta
|
||||
this.versions = res.versions || []
|
||||
this.setDSNote()
|
||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||
let columns: any[] = []
|
||||
|
@ -1019,6 +1026,16 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||
this.sasStoreService.removeClause()
|
||||
}
|
||||
|
||||
public datasetInfoModalRowClicked(value: Version | DSMeta) {
|
||||
if ((<Version>value).LOAD_REF !== undefined) {
|
||||
// Type is Version
|
||||
const row = value as Version
|
||||
const url = `/stage/${row.LOAD_REF}`
|
||||
|
||||
this.router.navigate([url])
|
||||
}
|
||||
}
|
||||
|
||||
private setDSNote() {
|
||||
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
|
||||
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
|
||||
|
|
|
@ -94,13 +94,21 @@
|
|||
}}</i>
|
||||
<h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i">
|
||||
Rules Source:
|
||||
<a class="ml-10" [routerLink]="'/view/data/' + rulesSource">
|
||||
<a
|
||||
cds-text="labelLink"
|
||||
class="ml-10"
|
||||
[routerLink]="'/view/data/' + rulesSource"
|
||||
>
|
||||
{{ rulesSource }}
|
||||
</a>
|
||||
</h5>
|
||||
<h5 class="d-flex clr-col-12 clr-justify-content-center mt-5-i">
|
||||
Target dataset:
|
||||
<a class="ml-10" [routerLink]="'/view/data/' + selectedXLMap.targetDS">
|
||||
<a
|
||||
cds-text="labelLink"
|
||||
class="ml-10"
|
||||
[routerLink]="'/view/data/' + selectedXLMap.targetDS"
|
||||
>
|
||||
{{ selectedXLMap.targetDS }}
|
||||
</a>
|
||||
</h5>
|
||||
|
|
|
@ -18,4 +18,4 @@ In any case, you must not make any such use of this software as to develop softw
|
|||
UNLESS EXPRESSLY AGREED OTHERWISE, 4GL APPS PROVIDES THIS SOFTWARE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, AND IN NO EVENT AND UNDER NO LEGAL THEORY, SHALL 4GL APPS BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING FROM USE OR INABILITY TO USE THIS SOFTWARE.
|
||||
|
||||
|
||||
`
|
||||
`
|
||||
|
|
|
@ -15,7 +15,7 @@ try {
|
|||
|
||||
writeFileSync(
|
||||
file,
|
||||
`//IMPORTANT: THIS FILE IS AUTO GENERATED BASED ON LICENCE.MD FILE!\nexport const EULA = \`\n${licence}\n\``,
|
||||
`//IMPORTANT: THIS FILE IS AUTO GENERATED BASED ON LICENCE.MD FILE!\nexport const EULA = \`\n${licence}\n\`\n`,
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
>
|
||||
</sasjs>
|
||||
|
||||
<body class="m-0">
|
||||
<body cds-theme="light" class="m-0">
|
||||
<my-app></my-app>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
@import '~handsontable/dist/handsontable.full.css';
|
||||
|
||||
@import '~@clr/ui/clr-ui.min.css';
|
||||
@import '~@clr/icons/clr-icons.min.css';
|
||||
|
||||
@import '@cds/core/global.min.css';
|
||||
@import '@cds/core/styles/theme.dark.min.css';
|
||||
@import '@clr/ui/clr-ui.min.css';
|
||||
|
||||
@font-face {
|
||||
font-family: text-security-disc;
|
||||
src: url('https://raw.githubusercontent.com/noppa/text-security/master/dist/text-security-disc.woff');
|
||||
|
@ -29,6 +32,14 @@ button {
|
|||
}
|
||||
}
|
||||
|
||||
[cds-text=label] {
|
||||
color: var(--cds-global-typography-color-200);
|
||||
}
|
||||
|
||||
[cds-text=labelLink] {
|
||||
line-height: 1.8 !important;
|
||||
}
|
||||
|
||||
// Custom loading spinner
|
||||
.slider {
|
||||
position: absolute;
|
||||
|
@ -261,6 +272,10 @@ button {
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mr-5i {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
@ -660,6 +675,10 @@ clr-icon.is-info {
|
|||
-webkit-box-direction: normal;
|
||||
}
|
||||
|
||||
.btn .clr-loading-btn-content {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn.btn-danger,
|
||||
.btn.btn-warning {
|
||||
border-color: #ef4f2e;
|
||||
|
@ -667,6 +686,11 @@ clr-icon.is-info {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
// Vertical align fix for small buttons with icons
|
||||
.btn.btn-sm:has(clr-icon) {
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -713,6 +737,11 @@ clr-icon.is-info {
|
|||
border: 1px solid red !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.handsontable .numericListbox {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.margin-top-20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dcfrontend",
|
||||
"version": "6.6.2",
|
||||
"version": "6.8.3",
|
||||
"description": "Data Controller",
|
||||
"devDependencies": {
|
||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||
|
|
|
@ -14,6 +14,10 @@ _webout = `{"SYSDATE" : "26SEP22"
|
|||
"APPROVER": "sasdemo"
|
||||
}
|
||||
]
|
||||
, "histparams":
|
||||
[
|
||||
{"HIST":100 ,"STARTROW":1 ,"NOBS":3 }
|
||||
]
|
||||
,"_DEBUG" : ""
|
||||
,"_METAUSER": "sasdemo@SAS"
|
||||
,"_METAPERSON": "sasdemo"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"name": "dc-sas",
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.11.1",
|
||||
"@sasjs/core": "^4.49.0"
|
||||
"@sasjs/core": "^4.52.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@coolaj86/urequest": {
|
||||
|
@ -116,9 +116,9 @@
|
|||
"integrity": "sha512-Grwydm5GxBsYk238PZw41XPjXVVQ9vWcvfZ06L2P0bQbvK0sGn7l69JA7H5MGr3QcaLpiD4Kg70cAh7PgE+JOw=="
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.49.0.tgz",
|
||||
"integrity": "sha512-hp3Hb4DkT6FmowyNHTOvSlgmSObW9WeuTJj+TQlwPgnBo59mAB4XFUnUaYSA+7ghvsHqUZf1OP2eSYqmnN5swQ=="
|
||||
"version": "4.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz",
|
||||
"integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg=="
|
||||
},
|
||||
"node_modules/@sasjs/lint": {
|
||||
"version": "2.3.1",
|
||||
|
@ -1834,9 +1834,9 @@
|
|||
}
|
||||
},
|
||||
"@sasjs/core": {
|
||||
"version": "4.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.49.0.tgz",
|
||||
"integrity": "sha512-hp3Hb4DkT6FmowyNHTOvSlgmSObW9WeuTJj+TQlwPgnBo59mAB4XFUnUaYSA+7ghvsHqUZf1OP2eSYqmnN5swQ=="
|
||||
"version": "4.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz",
|
||||
"integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg=="
|
||||
},
|
||||
"@sasjs/lint": {
|
||||
"version": "2.3.1",
|
||||
|
|
|
@ -29,6 +29,6 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.11.1",
|
||||
"@sasjs/core": "^4.49.0"
|
||||
"@sasjs/core": "^4.52.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
@file
|
||||
@brief migration script to move from v6.5 to 6.8.2 of data controller
|
||||
|
||||
**/
|
||||
|
||||
%let dclib=YOURDCLIB;
|
||||
|
||||
libname &dclib "/your/dc/path";
|
||||
|
||||
/**
|
||||
* Change 1
|
||||
* New MPE_SUBMIT table
|
||||
*/
|
||||
proc sql;
|
||||
insert into &dclib..mpe_config set
|
||||
tx_from=0
|
||||
,tx_to='31DEC9999:23:59:59'dt
|
||||
,var_scope="DC"
|
||||
,var_name="DC_REQUEST_LOGS"
|
||||
,var_value="YES"
|
||||
,var_active=1
|
||||
,var_desc='Setting to NO will prevent each request being logged to the'
|
||||
!!' MPE_REQUESTS table Default=YES.';
|
|
@ -75,7 +75,9 @@ data basetable;
|
|||
run;
|
||||
/* create results table */
|
||||
data results;
|
||||
format test $7. result $4. reason $50.; stop;
|
||||
format test $7. result $4. reason $50.;
|
||||
call missing(of _all_);
|
||||
stop;
|
||||
run;
|
||||
|
||||
%put assigning lib..;
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
@file
|
||||
@brief Checks if a user is able to restore a LOAD_REF
|
||||
@details Not all LOAD_REFs can be restored - maybe the user does not have
|
||||
permission, maybe the load was never loaded, or maybe the load was not
|
||||
tracked.
|
||||
|
||||
The macro creates two output (global) macro variables.
|
||||
|
||||
@param [in] LOAD_REF The Load Reference to check
|
||||
@param [out] outresult= (ALLOW_RESTORE) Output macro variable NAME. Will be
|
||||
given the value of YES or NO depending on whether the user is allowed to
|
||||
restore the load ref.
|
||||
@param [out] outreason= (REASON) Output macro variable NAME.
|
||||
Will be populated with the reason for which the restore decision was made.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mf_getuser.sas
|
||||
@li mpe_accesscheck.sas
|
||||
@li mpe_getgroups.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mpe_checkrestore.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author 4GL Apps Ltd
|
||||
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||
and may not be re-distributed or re-sold without the express permission of
|
||||
4GL Apps Ltd.
|
||||
**/
|
||||
|
||||
%macro mpe_checkrestore(load_ref,
|
||||
outresult=ALLOW_RESTORE,
|
||||
outreason=REASON
|
||||
);
|
||||
|
||||
%global &outresult &outreason;
|
||||
%let &outresult=NO;
|
||||
%let &outreason=NOTFOUND;
|
||||
|
||||
/* check if there is actually a version to restore */
|
||||
%local chk;
|
||||
%let chk=0;
|
||||
proc sql noprint;
|
||||
select count(*) into: chk from &dc_libref..mpe_audit
|
||||
where load_ref="&load_ref";
|
||||
%if &chk=0 %then %do;
|
||||
%let allow_restore=NO;
|
||||
%let reason=No entry for &load_ref in MPE_AUDIT;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* grab user groups */
|
||||
%local user;
|
||||
%let user=%mf_getuser();
|
||||
%mpe_getgroups(user=&user,outds=work.groups)
|
||||
|
||||
/* check if user is admin */
|
||||
%local is_admin;
|
||||
%let is_admin=0;
|
||||
proc sql;
|
||||
select count(*) into: is_admin from work.groups
|
||||
where groupname="&dc_admin_group";
|
||||
|
||||
%if &is_admin>0 %then %do;
|
||||
%let allow_restore=YES;
|
||||
%let reason=IS ADMIN;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* check if user has basic access */
|
||||
%local libds;
|
||||
proc sql noprint;
|
||||
select cats(base_lib,'.',base_ds) into: libds
|
||||
from &mpelib..mpe_submit
|
||||
where TABLE_ID="&load_ref";
|
||||
%mpe_accesscheck(&libds,outds=work.access_check
|
||||
,user=&user
|
||||
,access_level=EDIT
|
||||
)
|
||||
%if %mf_nobs(access_check)=0 %then %do;
|
||||
%let allow_restore=NO;
|
||||
%let reason=No access in MPE_TABLES;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* check if user has column level security rules */
|
||||
proc sql;
|
||||
create table work.cls_rules as
|
||||
select *
|
||||
from &mpelib..mpe_column_level_security
|
||||
where &dc_dttmtfmt. lt tx_to
|
||||
and CLS_SCOPE in ("EDIT",'ALL')
|
||||
and CLS_ACTIVE=1
|
||||
and upcase(CLS_GROUP) in (select upcase(groupname) from work.groups)
|
||||
and CLS_LIBREF="%upcase(&base_lib)"
|
||||
and CLS_TABLE="%upcase(&base_ds)";
|
||||
%if %mf_nobs(work.cls_rules)>0 %then %do;
|
||||
%let allow_restore=NO;
|
||||
%let reason=User has restrictions in MPE_COLUMN_LEVEL_SECURITY;
|
||||
data _null_;
|
||||
set work.cls_rules;
|
||||
putlog (_all_)(=);
|
||||
if _n_>5 then stop;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* check if user has row level security rules */
|
||||
proc sql;
|
||||
create table work.rls_rules as
|
||||
select *
|
||||
from &mpelib..mpe_row_level_security
|
||||
where &dc_dttmtfmt. lt tx_to
|
||||
and rls_scope in ("EDIT",'ALL')
|
||||
and upcase(rls_group) in (select upcase(groupname) from work.groups)
|
||||
and rls_libref="&base_lib"
|
||||
and rls_table="&base_ds"
|
||||
and rls_active=1;
|
||||
%if %mf_nobs(work.rls_rules)>0 %then %do;
|
||||
%let allow_restore=NO;
|
||||
%let reason=User has restrictions in MPE_ROW_LEVEL_SECURITY;
|
||||
data _null_;
|
||||
set work.rls_rules;
|
||||
putlog (_all_)(=);
|
||||
if _n_>5 then stop;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%else %do;
|
||||
%let allow_restore=YES;
|
||||
%let reason=CHECKS PASSED;
|
||||
%end;
|
||||
%mend mpe_checkrestore;
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
@file
|
||||
@brief Testing mpe_checkrestore macro
|
||||
@details Checking functionality of mpe_checkrestore.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li dc_getsettings.sas
|
||||
@li mpe_checkrestore.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_testservice.sas
|
||||
|
||||
|
||||
@author 4GL Apps Ltd
|
||||
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||
and may not be re-distributed or re-sold without the express permission of
|
||||
4GL Apps Ltd.
|
||||
|
||||
**/
|
||||
|
||||
/* first, run a data update */
|
||||
%mp_testservice(&appLoc/tests/services/auditors/postdata.test.1,
|
||||
viyacontext=&defaultcontext
|
||||
)
|
||||
|
||||
/* now grab the latest update */
|
||||
%let loadref=0;
|
||||
data APPROVE1;
|
||||
set &mpelib..mpe_submit end=last;
|
||||
if last then call symputx('loadref',table_id);
|
||||
run;
|
||||
|
||||
%global dc_repo_users dc_macros dc_libref dc_staging_area dc_admin_group
|
||||
mpelib dc_dttmtfmt;
|
||||
%dc_getsettings()
|
||||
|
||||
%put checking it is restorable;
|
||||
%global allow_restore reason;
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mpe_checkrestore(&loadref,outresult=ALLOW_RESTORE,outreason=REASON)
|
||||
%mp_assertscope(COMPARE,
|
||||
desc=Checking macro variables against previous snapshot,
|
||||
ignorelist=ALLOW_RESTORE REASON
|
||||
MCLIB0_JADP1LEN MCLIB0_JADP2LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
|
||||
MCLIB2_JADP1LEN MCLIB2_JADP2LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking successful submission
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&ALLOW_RESTORE=YES),
|
||||
desc=Checking restoring is possible
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&REASON=IS ADMIN),
|
||||
desc=Checking reason code returned
|
||||
)
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
@file
|
||||
@brief Get previous versions of a table
|
||||
@details Used to fetch version data for a particular table
|
||||
Delivered as part of this issue: https://git.datacontroller.io/dc/dc/issues/84
|
||||
|
||||
@param [in] dclib The DC libref
|
||||
@param [in] lib The library of the dataset for which to fetch versions
|
||||
@param [in] ds The dataset to fetch versions for
|
||||
@param [out] outds= (work.mpe_getversions) the DS to create
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mpe_getversions(dclib,libref,ds,outds=work.mpe_getversions);
|
||||
|
||||
proc sql;
|
||||
create table &outds as
|
||||
select a.table_id as LOAD_REF
|
||||
,a.reviewed_by_nm as user_nm
|
||||
,a.reviewed_on_dttm as version_dttm_num
|
||||
,put(a.reviewed_on_dttm,datetime19.) as VERSION_DTTM
|
||||
,a.submitted_reason_txt as VERSION_DESC
|
||||
,b.changed_records
|
||||
,b.new_records
|
||||
,b.deleted_records
|
||||
from &dclib..mpe_submit a
|
||||
left join &dclib..MPE_DATALOADS(where=(libref="&libref" & dsn="&ds")) b
|
||||
on a.table_id=b.etlsource
|
||||
where a.base_lib="&libref" and a.base_ds="&ds"
|
||||
and a.submit_status_cd='APPROVED' and a.num_of_approvals_remaining=0
|
||||
order by a.reviewed_on_dttm desc;
|
||||
|
||||
%mend mpe_getversions;
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
@file
|
||||
@brief Testing mpe_getversions macro
|
||||
@details Checking functionality of mpe_getversions.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mpe_getversions.sas
|
||||
@li mpe_targetloader.sas
|
||||
|
||||
**/
|
||||
|
||||
/* run the macro*/
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mpe_getversions(&mpelib,&mpelib,MPE_DATADICTIONARY, outds=ds0)
|
||||
%mp_assertscope(COMPARE,
|
||||
desc=Checking macro variables against previous snapshot
|
||||
)
|
||||
|
||||
|
||||
/* now stage some data */
|
||||
|
||||
%let f1=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &f1 termstr=crlf;
|
||||
put 'ACTION:$char4. MESSAGE:$char40. LIBDS:$char38.';
|
||||
put "LOAD,staging some data,&dclib..MPE_DATADICTIONARY";
|
||||
run;
|
||||
data work.jsdata;
|
||||
set &mpelib..MPE_DATADICTIONARY;
|
||||
_____DELETE__THIS__RECORD_____='No';
|
||||
dd_source=cats(ranuni(0));
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%mx_testservice(&appLoc/services/editors/stagedata,
|
||||
viyacontext=&defaultcontext,
|
||||
inputfiles=&f1:sascontroltable,
|
||||
inputdatasets=jsdata,
|
||||
outlib=web1,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
%let status=0;
|
||||
data work.sasparams;
|
||||
set web1.sasparams;
|
||||
putlog (_all_)(=);
|
||||
if status='SUCCESS' then call symputx('status',1);
|
||||
call symputx('dsid',dsid);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&status=1),
|
||||
desc=Checking staged data component,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* now approve the data so the change is applied */
|
||||
data work.sascontroltable;
|
||||
ACTION='APPROVE_TABLE';
|
||||
TABLE="&dsid";
|
||||
DIFFTIME="%sysfunc(datetime(),datetime19.)";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%mx_testservice(&appLoc/services/auditors/postdata,
|
||||
viyacontext=&defaultcontext,
|
||||
inputdatasets=work.sascontroltable,
|
||||
outlib=web2,
|
||||
outref=wbout,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
|
||||
/* finally - check that we have an extra version! */
|
||||
|
||||
%mpe_getversions(&mpelib,&mpelib,MPE_DATADICTIONARY, outds=ds1)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(ds0) = %mf_nobs(ds1)-1),
|
||||
desc=Checking one extra version was created
|
||||
)
|
||||
|
|
@ -121,7 +121,8 @@ insert into &mpelib..mpe_loads
|
|||
set USER_NM="&user"
|
||||
,STATUS='IN PROGRESS'
|
||||
,CSV_dir="&mperef"
|
||||
,PROCESSED_DTTM=&now;
|
||||
,PROCESSED_DTTM=&now
|
||||
,reason_txt = symget('submitted_reason_txt');
|
||||
|
||||
|
||||
/* import CSV */
|
||||
|
@ -300,28 +301,6 @@ create table vars_csv2 as
|
|||
on a.name=b.name
|
||||
order by a.varnum;
|
||||
|
||||
/* make sure that the variables we are importing, actually
|
||||
exist on the target table */
|
||||
|
||||
/** edit - extra variables are now simply ignored
|
||||
%local very_bad_vars;
|
||||
select name into: very_bad_vars separated by ' '
|
||||
from vars_csv1
|
||||
where name not in (select name from vars)
|
||||
and name ne "_____DELETE__THIS__RECORD_____";
|
||||
%if %length(&very_bad_vars) > 0 %then %do;
|
||||
%let msg=%str(WARNING: The following vars are not defined in %trim(
|
||||
)&libref..&ds, yet they exist in &csv_dir/&ds..csv: &very_bad_vars);
|
||||
%mpe_loadfail(
|
||||
status=FAILED
|
||||
,now=&now
|
||||
,mperef=&mperef
|
||||
,reason_txt=%quote(&msg)
|
||||
,dc_dttmtfmt=&dc_dttmtfmt.
|
||||
)
|
||||
%return;
|
||||
%end;
|
||||
**/
|
||||
|
||||
/* now build input statement */
|
||||
data final_check;
|
||||
|
@ -408,7 +387,7 @@ run;
|
|||
/* get log back */
|
||||
proc printto log=&logloc;run;
|
||||
data _null_; infile tmp; input; putlog _infile_;run;
|
||||
/* scan log for invalid data warning */
|
||||
/* scan log for invalid data warnings */
|
||||
data _null_;
|
||||
infile tmp;
|
||||
input;
|
||||
|
|
|
@ -26,14 +26,6 @@
|
|||
%end;
|
||||
|
||||
proc sql;
|
||||
insert into &lib..mpe_alerts set
|
||||
tx_from=0
|
||||
,tx_to='31DEC9999:23:59:59'dt
|
||||
,alert_event='*ALL*'
|
||||
,alert_lib='*ALL*'
|
||||
,alert_ds='*ALL*'
|
||||
,alert_user="&sysuserid";
|
||||
|
||||
insert into &lib..mpe_column_level_security set
|
||||
tx_from=0
|
||||
,tx_to='31DEC9999:23:59:59'dt
|
||||
|
@ -86,6 +78,15 @@ insert into &lib..mpe_config set
|
|||
!!' into the browser for editing in the EDIT screen. A higher number'
|
||||
!!' will require a decent browser (ie, not IE) and more memory on the'
|
||||
!!' client side.';
|
||||
insert into &lib..mpe_config set
|
||||
tx_from=0
|
||||
,tx_to='31DEC9999:23:59:59'dt
|
||||
,var_scope="DC"
|
||||
,var_name="DC_REQUEST_LOGS"
|
||||
,var_value="YES"
|
||||
,var_active=1
|
||||
,var_desc='Setting to NO will prevent each request being logged to the'
|
||||
!!' MPE_REQUESTS table Default=YES.';
|
||||
insert into &lib..mpe_config set
|
||||
tx_from=0
|
||||
,tx_to='31DEC9999:23:59:59'dt
|
||||
|
@ -1874,6 +1875,16 @@ insert into &lib..MPE_VALIDATIONS set
|
|||
,rule_value="services/validations/columns_in_libds"
|
||||
,rule_active=1
|
||||
,tx_to='31DEC5999:23:59:59'dt;
|
||||
/* test softselect on numeric var (should be ordered numerically) */
|
||||
insert into &lib..MPE_VALIDATIONS set
|
||||
tx_from=0
|
||||
,base_lib="&lib"
|
||||
,base_ds="MPE_X_TEST"
|
||||
,base_col="SOME_BESTNUM"
|
||||
,rule_type='SOFTSELECT'
|
||||
,rule_value="&lib..MPE_X_TEST.SOME_BESTNUM"
|
||||
,rule_active=1
|
||||
,tx_to='31DEC5999:23:59:59'dt;
|
||||
insert into &lib..MPE_VALIDATIONS set
|
||||
tx_from=0
|
||||
,base_lib="&lib"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
,CLOSE_VARS= /* provide close vars to override defaults */
|
||||
,dclib=NOTPROVIDED
|
||||
,mdebug=0
|
||||
,dc_dttmtfmt=E8601DT26.6
|
||||
,dc_dttmtfmt=%sysfunc(datetime())
|
||||
);
|
||||
%local lib ds nobs;
|
||||
|
||||
|
@ -219,7 +219,7 @@ run;
|
|||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%put WARNING: LOADTYPE &LOADTYPE not supported;
|
||||
%put %str(WARN)ING: LOADTYPE &LOADTYPE not supported;
|
||||
%let syscc=4;
|
||||
%mp_abort(msg=LOADTYPE &LOADTYPE not supported,mac=mpe_targetloader.sas)
|
||||
%end;
|
||||
|
|
|
@ -29,11 +29,15 @@
|
|||
mpelocapprovals /* location for landing and staging files */
|
||||
mpelib /* location of configuration tables for DC */
|
||||
dc_repo_users /* location of user / group metadata */
|
||||
dc_licence_key /* extracted in dc_getsettings */
|
||||
dc_activation_key /* extracted in dc_getsettings */
|
||||
dc_locale /* extracted in dc_getsettings */
|
||||
/* extracted in dc_getsettings */
|
||||
dc_activation_key
|
||||
dc_licence_key
|
||||
dc_locale
|
||||
dc_request_logs
|
||||
dc_restrict_viewer
|
||||
dc_dttmtfmt /* can be overridden in dc_getsettings */
|
||||
_debug
|
||||
_debug /* automatic variable when provided in URL */
|
||||
sasjs_mdebug /* used to show extra info when _debug is enabled */
|
||||
;
|
||||
|
||||
%if &mpeinit=1 %then %return;
|
||||
|
@ -110,4 +114,9 @@ run;
|
|||
,msg=%str(Problem during compilation or with STP precode (&syswarningtext))
|
||||
)
|
||||
|
||||
%if "&_debug"="2477" or "&_debug"="fields,log,trace" or "&_debug"="131"
|
||||
%then %do;
|
||||
%let sasjs_mdebug=1;
|
||||
%end;
|
||||
|
||||
%mend mpeinit;
|
||||
|
|
|
@ -35,6 +35,7 @@ run;
|
|||
run;
|
||||
%end;
|
||||
%if %sysfunc(exist(&dc_libref..mpe_requests)) and %mf_getplatform() ne SASVIYA
|
||||
and &DC_REQUEST_LOGS ne NO
|
||||
%then %do;
|
||||
data ;
|
||||
if 0 then set &dc_libref..mpe_requests;
|
||||
|
@ -42,6 +43,8 @@ run;
|
|||
request_user="%mf_getuser()";
|
||||
request_service="%scan(&_program,-2,/)/%scan(&_program,-1,/)";
|
||||
request_params='';
|
||||
/* sleep random amount to avoid parallel update attempts */
|
||||
call sleep(ranuni(0)*0.1,1);
|
||||
output;stop;
|
||||
proc append base=&dc_libref..mpe_requests data=&syslast force nowarn;
|
||||
run;
|
||||
|
|
|
@ -233,7 +233,7 @@
|
|||
"streamWeb": true,
|
||||
"streamWebFolder": "web",
|
||||
"webSourcePath": "../client/dist",
|
||||
"streamServiceName": "DataController",
|
||||
"streamServiceName": "DataController2",
|
||||
"streamLogo": "favicon.ico",
|
||||
"assetPaths": []
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ data _null_;
|
|||
call symputx('LOAD_REF',TABLE);
|
||||
/* DIFFTIME is when the DIFF was generated on the frontend */
|
||||
call symputx('DIFFTIME',DIFFTIME);
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%global action is_err err_msg msg;
|
||||
|
@ -190,7 +191,7 @@ run;
|
|||
and REVIEW_STATUS_ID ne "SUBMITTED";
|
||||
%let authcheck=%mf_getattrn(work.authAPP,NLOBS);
|
||||
%if &authcheck=0 or &prev_upload_check=1 %then %do;
|
||||
%put WARNING: authcheck=&authcheck prev_upload_check=&prev_upload_check;
|
||||
%put %str(WARN)ING: &=authcheck &=prev_upload_check;
|
||||
data apPARAMS;
|
||||
AUTHORISED=&authcheck;
|
||||
PREV_UPLOAD_CHECK=&prev_upload_check;
|
||||
|
|
|
@ -34,12 +34,17 @@ data work.jsdata;
|
|||
SOME_DATETIME=put(dttm2,datetime19.);
|
||||
some_time=put(tm2,time.);
|
||||
drop dt2 dttm2 tm2;
|
||||
_____DELETE__THIS__RECORD_____='No';
|
||||
if _n_=1 then do;
|
||||
primary_key_field=sum(&maxpk,1);
|
||||
some_char=' leadingblanks';
|
||||
some_num=._;
|
||||
_____DELETE__THIS__RECORD_____='Yes';
|
||||
output;
|
||||
_____DELETE__THIS__RECORD_____='No';
|
||||
some_char=' leadingblanks';
|
||||
some_bestnum=._;
|
||||
/* modify 1 record and add 4 more */
|
||||
do primary_key_field=&maxpk to (&maxpk+5);
|
||||
some_num=ranuni(0);
|
||||
output;
|
||||
end;
|
||||
end;
|
||||
else stop;
|
||||
run;
|
||||
|
@ -52,7 +57,7 @@ run;
|
|||
)
|
||||
|
||||
%let status=0;
|
||||
data work.sasparams;
|
||||
data _null_;
|
||||
set web1.sasparams;
|
||||
putlog (_all_)(=);
|
||||
if status='SUCCESS' then call symputx('status',1);
|
||||
|
@ -91,7 +96,7 @@ data _null_;
|
|||
/* the JSON libname engine removes leading blanks!!!! */
|
||||
if index(_infile_,' leadingblanks') then call symputx('leadcheck',1);
|
||||
/* there is no clean way to send a special missing - so is sent as string */
|
||||
if index(_infile_,',"SOME_NUM":"_"') then call symputx('speshcheck',1);
|
||||
if index(_infile_,',"SOME_BESTNUM":"_"') then call symputx('speshcheck',1);
|
||||
run;
|
||||
|
||||
|
||||
|
@ -103,3 +108,32 @@ run;
|
|||
iftrue=(&leadcheck=1),
|
||||
desc=Checking special characters were applied
|
||||
)
|
||||
|
||||
/* Now actually approve the table */
|
||||
data work.sascontroltable;
|
||||
ACTION='APPROVE_TABLE';
|
||||
TABLE="&dsid";
|
||||
/* difftime is numeric for approve action */
|
||||
DIFFTIME="%sysfunc(datetime())";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%mx_testservice(&appLoc/services/auditors/postdata,
|
||||
viyacontext=&defaultcontext,
|
||||
inputdatasets=work.sascontroltable,
|
||||
outlib=web3,
|
||||
outref=wb3,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
%let status=0;
|
||||
data _null_;
|
||||
set web3.apparams;
|
||||
putlog (_all_)(=);
|
||||
if response='SUCCESS!' then call symputx('status',1);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&status=1 and &syscc=0),
|
||||
desc=Checking successful submission
|
||||
)
|
|
@ -41,6 +41,9 @@
|
|||
<h5> xl_rules </h5>
|
||||
<h5> query </h5>
|
||||
|
||||
<h5> versions </h5>
|
||||
history of DC versions for this particular table
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li dc_assignlib.sas
|
||||
|
@ -63,6 +66,7 @@
|
|||
@li mpe_columnlevelsecurity.sas
|
||||
@li mpe_dsmeta.sas
|
||||
@li mpe_getlabels.sas
|
||||
@li mpe_getversions.sas
|
||||
@li mpe_filtermaster.sas
|
||||
@li mpe_runhook.sas
|
||||
|
||||
|
@ -631,9 +635,14 @@ create table dqdata as
|
|||
select distinct "&&base_col&x" as base_col length=32
|
||||
,"&source" as rule_value length=74
|
||||
,cats(&col) as rule_data length=1000
|
||||
,0 as selectbox_order
|
||||
,&col as tmp_order
|
||||
from &lib..&ds
|
||||
order by 1;
|
||||
order by tmp_order;
|
||||
/* ensure both numerics and char vals are ordered correctly */
|
||||
data work.dqdata&x (drop=tmp_order);
|
||||
set work.dqdata&x;
|
||||
selectbox_order=_n_;
|
||||
run;
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&_program
|
||||
,msg=%str(syscc=&syscc when selecting &&base_col&x from &orig_libds)
|
||||
|
@ -667,6 +676,13 @@ run;
|
|||
|
||||
%mpe_dsmeta(&libds, outds=dsmeta)
|
||||
|
||||
%mpe_getversions(&mpelib,
|
||||
%scan(&orig_libds,1,.),
|
||||
%scan(&orig_libds,2,.),
|
||||
outds=versions
|
||||
)
|
||||
|
||||
|
||||
/* send to the client */
|
||||
%webout(OPEN)
|
||||
%webout(OBJ,approvers)
|
||||
|
@ -678,6 +694,7 @@ run;
|
|||
%webout(OBJ,query)
|
||||
%webout(OBJ,sasdata1,fmt=N,missing=STRING,showmeta=YES,dslabel=sasdata)
|
||||
%webout(OBJ,sasparams)
|
||||
%webout(OBJ,versions)
|
||||
%webout(OBJ,xl_rules)
|
||||
%webout(CLOSE)
|
||||
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
@file restore.sas
|
||||
@brief Restores a data version
|
||||
@details Only applies if the history is stored in the audit table
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li dc_assignlib.sas
|
||||
@li mf_nobs.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li mp_stripdiffs.sas
|
||||
@li mpeinit.sas
|
||||
@li mpe_checkrestore.sas
|
||||
@li mpe_loader.sas
|
||||
|
||||
<h4> Service Inputs </h4>
|
||||
<h5> restore_in </h5>
|
||||
|
||||
|LOAD_REF:$32|
|
||||
|---|
|
||||
|DCXXXXXX|
|
||||
|
||||
@version 9.2
|
||||
@author 4GL Apps Ltd
|
||||
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||
and may not be re-distributed or re-sold without the express permission of
|
||||
4GL Apps Ltd.
|
||||
|
||||
**/
|
||||
|
||||
%mpeinit()
|
||||
|
||||
%let loadref=;
|
||||
data _null_;
|
||||
set work.restore_in;
|
||||
call symputx('loadref',load_ref);
|
||||
run;
|
||||
|
||||
/**
|
||||
* Check if user has basic access permission to RESTORE the table
|
||||
*/
|
||||
%put checking access;
|
||||
%global allow_restore reason;
|
||||
%mpe_checkrestore(&loadref,outresult=ALLOW_RESTORE,outreason=REASON)
|
||||
|
||||
%mp_abort(iftrue= (&ALLOW_RESTORE ne YES)
|
||||
,mac=&_program..sas
|
||||
,msg=%str(Cannot restore because: &reason)
|
||||
)
|
||||
|
||||
/* grab the base DS */
|
||||
proc sql noprint;
|
||||
select cats(base_lib,'.',base_ds) into: tgtds
|
||||
from &mpelib..mpe_submit
|
||||
where TABLE_ID="&loadref";
|
||||
|
||||
/* find the audit table */
|
||||
select coalescec(audit_libds,"&mpelib..MPE_AUDIT"), loadtype, var_txto
|
||||
into: difftable, :loadtype, :txto
|
||||
from &mpelib..MPE_TABLES
|
||||
where libref="%scan(&tgtds,1,.)"
|
||||
& dsn="%scan(&tgtds,2,.)"
|
||||
& &dc_dttmtfmt<tx_to;
|
||||
%mp_abort(iftrue= ("&difftable"="0")
|
||||
,mac=&_program..sas
|
||||
,msg=%str(No audit table configured for &tgtds)
|
||||
)
|
||||
|
||||
/* assign the library */
|
||||
%dc_assignlib(READ,%scan(&tgtds,1,.))
|
||||
|
||||
/* if the target is an SCD2 table, create a view to get current snapshot */
|
||||
%let fltr=%sysfunc(ifc(&loadtype=TXTEMPORAL,&dc_dttmtfmt<&txto,1=1));
|
||||
|
||||
/* extract the differences to be applied */
|
||||
%mp_stripdiffs(&tgtds,&loadref,&difftable
|
||||
,outds=work.mp_stripdiffs
|
||||
,filtervar=fltr
|
||||
,mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
/* abort if any issues */
|
||||
%mp_abort(iftrue= (&syscc>0)
|
||||
,mac=&_program..sas
|
||||
,msg=%str(syscc=&syscc after stripdiffs)
|
||||
)
|
||||
%mp_abort(iftrue= (%mf_nobs(work.mp_stripdiffs)=0)
|
||||
,mac=&_program..sas
|
||||
,msg=%str(THERE ARE NO DIFFERENCES TO APPLY)
|
||||
)
|
||||
|
||||
/* create a new load ref */
|
||||
%let mperef=DC%left(%sysfunc(datetime(),B8601DT19.3))_%substr(
|
||||
%sysfunc(ranuni(0)),3,6)_%substr(%str(&sysjobid ),1,4);
|
||||
|
||||
/* Create package folder */
|
||||
%let dir=&mpelocapprovals/&mperef;
|
||||
%mf_mkdir(&dir)
|
||||
options notes mprint;
|
||||
libname approve "&dir";
|
||||
|
||||
/* take copy of macvars */
|
||||
data _null_;
|
||||
file "&dir/macvars.sas";
|
||||
set sashelp.vmacro;
|
||||
where scope='GLOBAL';
|
||||
put '%let ' name '=' value ';';
|
||||
run;
|
||||
|
||||
/* copy the diffs dataset */
|
||||
data approve.jsdset;
|
||||
length _____DELETE__THIS__RECORD_____ $3;
|
||||
if 0 then call missing(_____DELETE__THIS__RECORD_____);
|
||||
set work.mp_stripdiffs;
|
||||
run;
|
||||
|
||||
/* export to csv */
|
||||
%mp_ds2csv(approve.jsdset
|
||||
,dlm=COMMA
|
||||
,outfile="&dir/%trim(&tgtds).csv"
|
||||
,outencoding="UTF-8"
|
||||
,headerformat=NAME
|
||||
,termstr=CRLF
|
||||
)
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&_program
|
||||
,msg=%str(syscc=&syscc when writing the CSV)
|
||||
)
|
||||
|
||||
%mpe_loader(mperef=&mperef
|
||||
,submitted_reason_txt=Restoring &loadref
|
||||
,dc_dttmtfmt=&dc_dttmtfmt
|
||||
)
|
||||
%mp_abort(mode=INCLUDE)
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(%sysfunc(fileexist(%sysfunc(pathname(work))/mf_abort.error))=1)
|
||||
,mac=&_program..sas
|
||||
,msg=%str(mf_abort.error=1)
|
||||
)
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&_program..sas
|
||||
,msg=%str(syscc=&syscc)
|
||||
)
|
||||
|
||||
/* send relevant SUCCESS values */
|
||||
data work.restore_out;
|
||||
loadref="&mperef";
|
||||
run;
|
||||
|
||||
|
||||
%webout(OPEN)
|
||||
%webout(OBJ,restore_out)
|
||||
%webout(CLOSE)
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
@file
|
||||
@brief testing restore process
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mx_testservice.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%let _program=&appLoc/services/editors/restore;
|
||||
|
||||
/* take a snapshot of the table for later comparison */
|
||||
proc sort data=&mpelib..mpe_x_test out=work.origds;
|
||||
by primary_key_field;
|
||||
run;
|
||||
|
||||
/* run an update */
|
||||
%mx_testservice(&appLoc/tests/services/auditors/postdata.test.1,
|
||||
viyacontext=&defaultcontext
|
||||
)
|
||||
|
||||
/* grab the loadref for later reversion */
|
||||
%let loadref=0;
|
||||
data APPROVE1;
|
||||
set &mpelib..mpe_submit end=last;
|
||||
if last then call symputx('loadref',table_id);
|
||||
run;
|
||||
|
||||
/* run another update for good measure */
|
||||
%mx_testservice(&appLoc/tests/services/auditors/postdata.test.1,
|
||||
viyacontext=&defaultcontext
|
||||
)
|
||||
|
||||
/* now we are ready to revert */
|
||||
data work.restore_in;
|
||||
load_ref="&loadref";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%mx_testservice(&_program,
|
||||
viyacontext=&defaultcontext,
|
||||
inputdatasets=work.restore_in,
|
||||
outlib=web1,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
/* check for success */
|
||||
%let loadref=0;
|
||||
data work.restore_out;
|
||||
set web1.restore_out;
|
||||
putlog (_all_)(=);
|
||||
call symputx('newref',loadref);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&newref ne 0),
|
||||
desc=Checking successful submission of a reversion,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* approve the reversion */
|
||||
data work.sascontroltable;
|
||||
ACTION='APPROVE_TABLE';
|
||||
TABLE="&newref";
|
||||
/* difftime is numeric for approve action */
|
||||
DIFFTIME="%sysfunc(datetime())";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%mx_testservice(&appLoc/services/auditors/postdata,
|
||||
viyacontext=&defaultcontext,
|
||||
inputdatasets=work.sascontroltable,
|
||||
outlib=web3,
|
||||
outref=wb3,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
%let status=0;
|
||||
data _null_;
|
||||
set web3.apparams;
|
||||
putlog (_all_)(=);
|
||||
if response='SUCCESS!' then call symputx('status',1);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&status=1 and &syscc=0),
|
||||
desc=Checking successful submission of reversion
|
||||
)
|
||||
|
||||
/* compare snapshot with latest data */
|
||||
proc sort data=&mpelib..mpe_x_test out=work.compareds;
|
||||
by primary_key_field;
|
||||
run;
|
||||
|
||||
proc compare base=work.origds compare=work.compareds
|
||||
out=work.resultds outnoequal;
|
||||
run;
|
||||
data _null_;
|
||||
set work.resultds;
|
||||
if &sasjs_mdebug=1 then putlog (_all_)(=);
|
||||
if _n_>10 then stop;
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&sysinfo le 41),
|
||||
desc=Checking compare of MPE_X_TEST,
|
||||
outds=work.test_results
|
||||
)
|
|
@ -5,8 +5,27 @@
|
|||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getengine.sas
|
||||
@li dc_assignlib.sas
|
||||
@li mp_abort.sas
|
||||
@li mpe_checkrestore.sas
|
||||
|
||||
<h4> Service Inputs </h4>
|
||||
<h5> sascontroltable </h5>
|
||||
@li table table ID or LOAD_REF used to uniquely identify a staged change
|
||||
|
||||
<h4> Service Outputs </h4>
|
||||
|
||||
<h5> work.jsparams </h5>
|
||||
Mainly sourced from MPE_SUBMIT plus some extra cols:
|
||||
|
||||
@li LIB_ENGINE Library engine
|
||||
@li allow_restore YES if a user can restore, else NO
|
||||
@li REASON reason why a restore is / is no possible
|
||||
|
||||
<h4> Data Inputs </h4>
|
||||
@li MPE_AUDIT
|
||||
@li MPE_COLUMN_LEVEL_SECURITY
|
||||
@li MPE_ROW_LEVEL_SECURITY
|
||||
@li MPE_SUBMIT
|
||||
|
||||
@version 9.2
|
||||
@author 4GL Apps Ltd
|
||||
|
@ -24,10 +43,6 @@ data _null_;
|
|||
call symputx('table',table);
|
||||
run;
|
||||
|
||||
%dc_assignlib(WRITE,%scan(&table,1,.))
|
||||
|
||||
%let max_ver_dttm=0;
|
||||
|
||||
data APPROVE1;
|
||||
set &mpelib..mpe_submit
|
||||
(rename=(SUBMITTED_ON_DTTM=submitted_on REVIEWED_ON_DTTM=REVIEWED_ON));
|
||||
|
@ -39,9 +54,18 @@ data APPROVE1;
|
|||
SUBMITTED_ON_DTTM=put(submitted_on,datetime19.);
|
||||
run;
|
||||
|
||||
data jsParams;
|
||||
/**
|
||||
* Check if user has basic access permission to RESTORE the table
|
||||
*/
|
||||
%put checking access;
|
||||
%global allow_restore reason;
|
||||
%mpe_checkrestore(&table,outresult=ALLOW_RESTORE,outreason=REASON)
|
||||
|
||||
data work.jsParams;
|
||||
set approve1;
|
||||
LIB_ENGINE="%mf_getEngine(&base_lib)";
|
||||
allow_restore="&allow_restore";
|
||||
REASON="&reason";
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
@file
|
||||
@brief testing getchangeinfo service
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_assertcolvals.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
**/
|
||||
|
||||
%let _program=&appLoc/services/public/getchangeinfo;
|
||||
|
||||
/**
|
||||
* First part - stage some data (for diffing)
|
||||
*/
|
||||
data work.sascontroltable;
|
||||
action='LOAD';
|
||||
message="getdiffs prep";
|
||||
libds="&dclib..MPE_X_TEST";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
proc sql noprint;
|
||||
select max(primary_key_field) into: maxpk from &dclib..mpe_x_test;
|
||||
data work.jsdata;
|
||||
set &dclib..mpe_x_test(rename=(
|
||||
some_date=dt2 SOME_DATETIME=dttm2 SOME_TIME=tm2)
|
||||
);
|
||||
/* for now, the adapter sends these as strings */
|
||||
some_date=put(dt2,date9.);
|
||||
SOME_DATETIME=put(dttm2,datetime19.);
|
||||
some_time=put(tm2,time.);
|
||||
drop dt2 dttm2 tm2;
|
||||
_____DELETE__THIS__RECORD_____='No';
|
||||
if _n_=1 then do;
|
||||
primary_key_field=sum(&maxpk,1);
|
||||
some_char=' leadingblanks';
|
||||
some_num=._;
|
||||
output;
|
||||
end;
|
||||
else if _n_<3 then do;
|
||||
SOME_NUM=ranuni(0);
|
||||
end;
|
||||
else stop;
|
||||
run;
|
||||
|
||||
%mx_testservice(&appLoc/services/editors/stagedata,
|
||||
viyacontext=&defaultcontext,
|
||||
inputdatasets=work.jsdata work.sascontroltable,
|
||||
outlib=web1,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
%let status=0;
|
||||
data work.sasparams;
|
||||
set web1.sasparams;
|
||||
putlog (_all_)(=);
|
||||
if status='SUCCESS' then call symputx('status',1);
|
||||
call symputx('dsid',dsid);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&status=1 and &syscc=0),
|
||||
desc=Checking successful submission
|
||||
)
|
||||
|
||||
|
||||
/* now call getchangeinfo */
|
||||
%let f3=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &f3 termstr=crlf;
|
||||
put 'TABLE:$43.';
|
||||
put "&dsid";
|
||||
run;
|
||||
%mp_testservice(&_program,
|
||||
viyacontext=&defaultcontext,
|
||||
inputfiles=&f3:sascontroltable,
|
||||
outlib=web3,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
|
||||
data work.jsparams;
|
||||
set web3.jsparams;
|
||||
putlog (_all_)(=);
|
||||
call symputx('ALLOW_RESTORE',ALLOW_RESTORE);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking successful execution
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.jsparams)=1),
|
||||
desc=Checking data was returned
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&ALLOW_RESTORE=NO),
|
||||
desc=Checking admin user cannot restore - as table was not approved
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
@file getversion.sas
|
||||
@brief get a specific (previous) version of a particular table
|
||||
@details Used to fetch a version of a table as at a previous point in time
|
||||
Delivered as part of this issue: https://git.datacontroller.io/dc/dc/issues/84
|
||||
|
||||
<h4> Service Inputs </h4>
|
||||
<h5> getversion_input </h5>
|
||||
|
||||
|LIBREF:$char8.|DS:$char32.|TS: 8.|
|
||||
|---|---|---|
|
||||
|SOMELIB|SOMEDS|1341344.804|
|
||||
|
||||
<h4> Service Outputs </h4>
|
||||
<h5> work.getversion_output </h5>
|
||||
|
||||
The data for a particular version
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuser.sas
|
||||
@li mpeinit.sas
|
||||
@li mpe_getvars.sas
|
||||
|
||||
@version 9.2
|
||||
@author 4GL Apps Ltd
|
||||
|
||||
**/
|
||||
|
||||
%mpeinit()
|
||||
|
||||
%global LIBREF DS;
|
||||
|
||||
/* load parameters */
|
||||
%mpe_getvars(getversion_input, getversion_input)
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0 )
|
||||
,mac=&_program
|
||||
,msg=%str(Issue on startup)
|
||||
)
|
||||
|
||||
/* todo */
|
||||
|
||||
%webout(OPEN)
|
||||
%webout(OBJ,getversion_output)
|
||||
%webout(CLOSE)
|
||||
|
||||
|
||||
%mpeterm()
|
|
@ -31,6 +31,9 @@
|
|||
@li TABLEURI
|
||||
@li VARS
|
||||
|
||||
<h5> versions </h5>
|
||||
history of DC versions for this particular table
|
||||
|
||||
<h5> viewdata </h5>
|
||||
The raw data from the target table.
|
||||
|
||||
|
@ -51,6 +54,7 @@
|
|||
@li mp_validatecol.sas
|
||||
@li mpe_columnlevelsecurity.sas
|
||||
@li mpe_dsmeta.sas
|
||||
@li mpe_getversions.sas
|
||||
@li mpe_filtermaster.sas
|
||||
|
||||
|
||||
|
@ -81,7 +85,7 @@
|
|||
data work.intest;
|
||||
length libds $41 filter_rk 8. searchval $100 searchtype $4;
|
||||
set work.SASCONTROLTABLE;
|
||||
|
||||
call symputx('orig_libds',libds);
|
||||
/* validate filter_rk */
|
||||
if filter_rk le 0 then filter_rk=-1;
|
||||
|
||||
|
@ -353,12 +357,19 @@ run;
|
|||
|
||||
%mpe_dsmeta(&libds, outds=dsmeta)
|
||||
|
||||
%mpe_getversions(&mpelib,
|
||||
%scan(&orig_libds,1,.),
|
||||
%scan(&orig_libds,2,.),
|
||||
outds=versions
|
||||
)
|
||||
|
||||
%webout(OPEN)
|
||||
%webout(OBJ,cls_rules)
|
||||
%webout(OBJ,cols)
|
||||
%webout(OBJ,dsmeta)
|
||||
%webout(OBJ,query)
|
||||
%webout(OBJ,sasparams)
|
||||
%webout(OBJ,versions)
|
||||
%webout(OBJ,viewData2,fmt=Y,missing=STRING,showmeta=YES,dslabel=viewdata)
|
||||
%webout(CLOSE)
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ data groups
|
|||
a=1;
|
||||
grpassn=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
|
||||
if grpassn in (-3,-4) then do;
|
||||
putlog "WARNING: No groups found for ";
|
||||
putlog "%str(WARN)ING: No groups found for ";
|
||||
end;
|
||||
else do while (grpassn > 0);
|
||||
rc=metadata_getattr(groupuri, "Name", groupname);
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
@li mf_getplatform.sas
|
||||
@li mpeinit2.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_init.sas
|
||||
@li mp_testservice.sas
|
||||
|
||||
|
||||
REMOVE THAT LAST MACRO
|
||||
**/
|
||||
|
||||
%mp_init()
|
||||
|
||||
%let syscc=0;
|
||||
%global apploc _program dclib defaultcontext _debug sasjs_mdebug dc_dttmtfmt;
|
||||
|
||||
|
|
Loading…
Reference in New Issue