Compare commits

...

94 Commits
v6.6.3 ... main

Author SHA1 Message Date
semantic-release-bot 39c3e5411f chore(release): 6.8.3 [skip ci]
## [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](#103) ([ee58fd5](ee58fd5b4b))
2024-05-09 11:47:48 +00:00
allan aad419c55d Merge pull request 'fix: updating core to increase filename length, closes #103' (#104) from issue103 into main
Release / Build-production-and-ng-test (push) Successful in 4m48s Details
Release / Build-and-test-development (push) Successful in 8m51s Details
Release / release (push) Successful in 10m9s Details
Reviewed-on: #104
2024-05-09 11:29:37 +00:00
^ ee58fd5b4b fix: updating core to increase filename length, closes #103
Build / Build-and-ng-test (pull_request) Successful in 5m2s Details
2024-05-09 12:23:04 +01:00
allan aedd2c451b Merge pull request 'release-ci' (#100) from release-ci into main
Release / Build-production-and-ng-test (push) Successful in 5m1s Details
Release / Build-and-test-development (push) Successful in 8m54s Details
Release / release (push) Failing after 4m31s Details
Reviewed-on: #100
2024-05-06 21:33:57 +00:00
Mihajlo Medjedovic 6d597611b6 chore(git): Merge branch 'main' into release-ci
Build / Build-and-ng-test (pull_request) Successful in 4m9s Details
2024-05-06 09:04:34 +02:00
Mihajlo Medjedovic 9ffe5efe5d ci: added viya.json to the release assets 2024-05-06 09:04:19 +02:00
semantic-release-bot 2715950d86 chore(release): 6.8.2 [skip ci]
## [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](93758efb27))
* release process ([c0dc919](c0dc9191e3))
2024-05-03 07:19:26 +00:00
Mihajlo Medjedovic 77a7190f4d ci: fixing release script
Release / Build-production-and-ng-test (push) Successful in 3m58s Details
Release / Build-and-test-development (push) Successful in 7m39s Details
Release / release (push) Successful in 8m23s Details
2024-05-03 09:03:56 +02:00
allan cc65890fea Merge pull request 'fix: dc_request_logs option feature closes #96' (#97) from issue96 into main
Release / Build-production-and-ng-test (push) Successful in 3m58s Details
Release / Build-and-test-development (push) Successful in 7m52s Details
Release / release (push) Failing after 1m18s Details
Reviewed-on: #97
2024-05-02 22:25:29 +00:00
^ 93758efb27 fix: dc_request_logs option feature
Build / Build-and-ng-test (pull_request) Successful in 4m3s Details
see: https://docs.datacontroller.io/dcc-options/#dc_request_logs
2024-05-02 23:09:14 +01:00
Mihajlo Medjedovic c0dc9191e3 fix: release process
Release / Build-production-and-ng-test (push) Successful in 3m57s Details
Release / Build-and-test-development (push) Successful in 7m44s Details
Release / release (push) Failing after 1m17s Details
2024-05-02 18:41:25 +02:00
semantic-release-bot 485783a782 chore(release): 6.8.1 [skip ci]
## [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](ec0f539a33))
2024-05-02 16:10:44 +00:00
mihajlo ee07bef2b8 Merge pull request 'fix: hide approve button when table revertable' (#95) from ci-fix into main
Release / Build-production-and-ng-test (push) Successful in 3m59s Details
Release / Build-and-test-development (push) Successful in 7m45s Details
Release / release (push) Failing after 1m55s Details
Reviewed-on: #95
2024-05-02 15:57:22 +00:00
Mihajlo Medjedovic 0de8481314 ci: ng build
Build / Build-and-ng-test (pull_request) Successful in 4m4s Details
2024-05-02 17:48:30 +02:00
Mihajlo Medjedovic ec0f539a33 fix: hide approve button when table revertable
Build / Build-and-ng-test (pull_request) Failing after 2m1s Details
2024-05-02 17:44:53 +02:00
semantic-release-bot 173ee2daff chore(release): 6.8.0 [skip ci]
# [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](c5e4650327))
* **clarity:** new version style issues ([8c7de5a](8c7de5aad7))
* cypress tests ([3dd85cc](3dd85cc60b))
* ensuring that only restorable versions are restorable ([a402856](a4028562ce))
* ensuring version history only includes loaded versions ([51ebd25](51ebd25aa3))
* final testing on restore feature ([297a84d](297a84d3a4))
* issue with multiple adds/deletes, [#84](#84) ([904ca30](904ca30f91))
* load_ref var ([aaad9f7](aaad9f7207))
* removing alerts dummy data, closes [#93](#93) ([eba21e9](eba21e96b4))
* restore table version improvement ([549f357](549f35766b))
* **sas:** viewer versions fix ([c6595c1](c6595c1f61))
* stage and approve buttons renaming ([ef81e33](ef81e33f70))
* supporting SCD2 data reversions ([fa8396f](fa8396f039))
* table info modal, versions - column names ([801c8c6](801c8c6a9f))
* **updates:** angular, clarity, resolved legacy-peer-deps ([c60dd65](c60dd65a16))

### Features

* backend to show in getchangeinfo whether a user is allowed to restore ([8769841](8769841f08))
* list versions of target tables (backend) ([f8a14d4](f8a14d4bde))
* restore ([604c2e7](604c2e70bd))
* SAS services & tests for RESTORE, [#84](#84) ([9ad7ae4](9ad7ae47b5))
* staging page, restore buttons ([02a8a1c](02a8a1c565))
* table metadata modal, versions tab (and link) ([b27fea5](b27fea5b91))
* **versions:** getting list of versions (plus test) ([8003da9](8003da94e6))
2024-05-02 12:08:05 +00:00
Mihajlo Medjedovic 5c0091b5e8 chore: test workaround
Release / Build-production-and-ng-test (push) Successful in 3m58s Details
Release / Build-and-test-development (push) Successful in 7m44s Details
Release / release (push) Failing after 1m55s Details
2024-05-02 13:54:44 +02:00
allan 84dce7f6e8 Merge pull request 'Restore Previous State / Data Rollback' (#94) from restore into main
Release / Build-production-and-ng-test (push) Successful in 4m1s Details
Release / Build-and-test-development (push) Failing after 8m26s Details
Release / release (push) Has been skipped Details
Reviewed-on: #94
2024-05-02 11:18:28 +00:00
Mihajlo Medjedovic e8a943a35a chore: ng test
Build / Build-and-ng-test (pull_request) Successful in 2m1s Details
2024-05-02 13:08:06 +02:00
Mihajlo Medjedovic b3171a8125 ci: fix
Build / Build-and-ng-test (pull_request) Failing after 1m57s Details
2024-05-02 12:56:52 +02:00
Mihajlo Medjedovic d77f2eb674 chore(git): Merge branch 'deps-update' into restore
Build / Build-and-ng-test (pull_request) Failing after 1m30s Details
2024-05-02 12:53:32 +02:00
Mihajlo Medjedovic 5474fad9cc chore: package-lock
Build / Build-and-ng-test (pull_request) Successful in 1m2s Details
2024-05-02 12:46:50 +02:00
Mihajlo Medjedovic 3dd85cc60b fix: cypress tests 2024-05-02 12:39:07 +02:00
Mihajlo Medjedovic 510e412ff2 chore: cypress tests fix
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-05-02 11:25:46 +02:00
Mihajlo Medjedovic 3dfdccbc6b ci: sheet lib
Build / Build-and-ng-test (pull_request) Successful in 1m2s Details
2024-05-02 10:04:31 +02:00
Mihajlo Medjedovic a55661548a chore: licence checker
Build / Build-and-ng-test (pull_request) Successful in 1m2s Details
2024-05-02 09:59:21 +02:00
Mihajlo Medjedovic 69363b37e9 ci: sheet lib
Build / Build-and-ng-test (pull_request) Failing after 1m2s Details
2024-05-02 09:54:10 +02:00
Mihajlo Medjedovic 2d6a753921 ci: sheet lib
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-05-02 09:52:22 +02:00
Mihajlo Medjedovic dc989e5668 ci: fix
Build / Build-and-ng-test (pull_request) Failing after 58s Details
2024-05-02 09:38:43 +02:00
Mihajlo Medjedovic 227ac480d5 style: lint
Build / Build-and-ng-test (pull_request) Failing after 16s Details
2024-05-02 09:30:23 +02:00
Mihajlo Medjedovic c5e4650327 fix: ci sheet lib, submit message auto focus
Build / Build-and-ng-test (pull_request) Failing after 16s Details
2024-05-02 09:28:39 +02:00
allan 56cf271e77 Merge branch 'main' into deps-update
Build / Build-and-ng-test (pull_request) Failing after 57s Details
2024-04-30 17:41:54 +00:00
^ fa8396f039 fix: supporting SCD2 data reversions
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-04-30 18:39:00 +01:00
^ 904ca30f91 fix: issue with multiple adds/deletes, #84
Build / Build-and-ng-test (pull_request) Failing after 16s Details
2024-04-30 17:46:00 +01:00
Mihajlo Medjedovic 549f35766b fix: restore table version improvement
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-04-30 15:43:17 +02:00
Mihajlo Medjedovic 1589c799ec chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-04-30 15:26:07 +02:00
Mihajlo Medjedovic 604c2e70bd feat: restore 2024-04-30 15:25:58 +02:00
^ 297a84d3a4 fix: final testing on restore feature
Build / Build-and-ng-test (pull_request) Failing after 59s Details
2024-04-30 14:21:02 +01:00
^ aaad9f7207 fix: load_ref var
Build / Build-and-ng-test (pull_request) Failing after 58s Details
2024-04-30 12:34:22 +01:00
^ a4028562ce fix: ensuring that only restorable versions are restorable
Build / Build-and-ng-test (pull_request) Failing after 58s Details
2024-04-30 11:14:47 +01:00
Mihajlo Medjedovic 5ab3f98855 chore(git): Merge branch 'main' into restore
Build / Build-and-ng-test (pull_request) Failing after 59s Details
2024-04-30 09:04:46 +02:00
^ eba21e96b4 fix: removing alerts dummy data, closes #93
Build / Build-and-ng-test (pull_request) Failing after 18s Details
2024-04-30 00:22:44 +01:00
^ 9ad7ae47b5 feat: SAS services & tests for RESTORE, #84 2024-04-30 00:20:40 +01:00
allan cf54e4c8f3 Update README.md
Release / Build-production-and-ng-test (push) Failing after 1m31s Details
Release / Build-and-test-development (push) Has been skipped Details
Release / release (push) Has been skipped Details
2024-04-23 13:06:51 +00:00
Mihajlo Medjedovic 57aa6fa0fc ci: fix
Build / Build-and-ng-test (pull_request) Failing after 58s Details
2024-04-15 09:37:20 +02:00
Mihajlo Medjedovic 4a01f3d490 ci: fix
Build / Build-and-ng-test (pull_request) Failing after 57s Details
2024-04-12 21:37:49 +02:00
Mihajlo Medjedovic 80a0db951d chore: angular testing
Build / Build-and-ng-test (pull_request) Failing after 1m5s Details
2024-04-12 15:48:00 +02:00
Mihajlo Medjedovic 3202cb8e08 ci: ng test fix
Build / Build-and-ng-test (pull_request) Failing after 1m4s Details
2024-04-12 14:23:04 +02:00
Mihajlo Medjedovic ddf36230bf ci: ng test fix
Build / Build-and-ng-test (pull_request) Failing after 1m4s Details
2024-04-12 14:06:29 +02:00
Mihajlo Medjedovic b67c2be968 chore: package json test script fix
Build / Build-and-ng-test (pull_request) Failing after 1m3s Details
2024-04-12 13:53:35 +02:00
Mihajlo Medjedovic dc2c8da92b ci: added angular tests on PR
Build / Build-and-ng-test (pull_request) Failing after 1m10s Details
2024-04-12 13:30:44 +02:00
Mihajlo Medjedovic 5d6c3701d0 chore: licence checker update
Build / Build-and-ng-test (pull_request) Successful in 1m3s Details
2024-04-12 13:28:18 +02:00
Mihajlo Medjedovic b024e263b4 chore: updated package-lock
Build / Build-and-ng-test (pull_request) Failing after 1m2s Details
2024-04-12 13:20:08 +02:00
allan b706864e40 Merge pull request 'Angular and Clarity Update' (#91) from deps-update into main
Release / Build-production-and-ng-test (push) Failing after 1m37s Details
Release / Build-and-test-development (push) Has been skipped Details
Release / release (push) Has been skipped Details
Reviewed-on: #91
2024-04-12 10:45:57 +00:00
Mihajlo Medjedovic 60cc666b67 chore: licence checker
Build / Build-and-ng-test (pull_request) Successful in 1m15s Details
2024-04-12 12:34:01 +02:00
Mihajlo Medjedovic efff4dd553 chore(cypress): bigger viewport size
Build / Build-and-ng-test (pull_request) Failing after 1m1s Details
2024-04-12 11:48:23 +02:00
Mihajlo Medjedovic 2b1dad8e48 style: lint
Build / Build-and-ng-test (pull_request) Failing after 1m2s Details
2024-04-12 10:51:32 +02:00
Mihajlo Medjedovic 8c7de5aad7 fix(clarity): new version style issues 2024-04-12 10:51:11 +02:00
Mihajlo Medjedovic 1db8bc2573 style: lint
Build / Build-and-ng-test (pull_request) Failing after 1m12s Details
2024-04-11 13:23:40 +02:00
Mihajlo Medjedovic c60dd65a16 fix(updates): angular, clarity, resolved legacy-peer-deps 2024-04-11 13:23:24 +02:00
allan f7f59a4b0a Merge pull request 'feat: Display Previous Versions' (#88) from restore into main
Release / Build-production-and-ng-test (push) Successful in 4m18s Details
Release / Build-and-test-development (push) Failing after 15m54s Details
Release / release (push) Has been skipped Details
Reviewed-on: #88
2024-04-04 13:24:25 +00:00
^ ec7615e7e3 chore: adding sheet-crypto to gitignoreg
Build / Build-and-ng-test (pull_request) Successful in 1m5s Details
2024-04-03 10:56:49 +01:00
Mihajlo Medjedovic f411c33754 chore(git): Merge branch 'main' into restore
Build / Build-and-ng-test (pull_request) Successful in 1m4s Details
2024-04-01 13:51:33 +02:00
Mihajlo Medjedovic 79121168e4 style: lint
Build / Build-and-ng-test (pull_request) Successful in 1m4s Details
2024-04-01 13:50:50 +02:00
Mihajlo Medjedovic ef81e33f70 fix: stage and approve buttons renaming
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-04-01 13:37:41 +02:00
semantic-release-bot 96066c66cb chore(release): 6.7.0 [skip ci]
# [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](963562621d))
2024-04-01 11:23:31 +00:00
Mihajlo Medjedovic b1819b776d chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-04-01 13:09:34 +02:00
Mihajlo Medjedovic bd7a392ffc chore(git): Merge branch 'main' into restore 2024-04-01 13:09:27 +02:00
mihajlo 7997b77158 Merge pull request 'Numeric values in hot dropdown aligned right' (#86) from numeric-values-diff into main
Release / Build-production-and-ng-test (push) Successful in 4m26s Details
Release / Build-and-test-development (push) Successful in 8m10s Details
Release / release (push) Successful in 6m32s Details
Reviewed-on: #86
2024-04-01 11:08:53 +00:00
Mihajlo Medjedovic d1966bcdc5 chore(git): Merge branch 'main' into numeric-values-diff
Build / Build-and-ng-test (pull_request) Successful in 1m3s Details
2024-04-01 12:33:43 +02:00
semantic-release-bot 7b5bbe024d chore(release): 6.6.4 [skip ci]
## [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](f522038b8d)), closes [#85](#85)
* reverting col ([fbbcf90](fbbcf90956))
* typo ([31d4e5c](31d4e5c727))
2024-04-01 09:51:54 +00:00
mihajlo 4d84f15aca Merge pull request 'ci: install sheet temporarily' (#89) from ci into main
Release / Build-production-and-ng-test (push) Successful in 4m16s Details
Release / Build-and-test-development (push) Successful in 8m1s Details
Release / release (push) Successful in 6m22s Details
Reviewed-on: #89
2024-04-01 09:37:56 +00:00
Mihajlo Medjedovic 928937daab ci: sheet
Build / Build-and-ng-test (pull_request) Successful in 1m4s Details
2024-04-01 11:14:00 +02:00
Mihajlo Medjedovic 3bd8d247e5 ci: sheet
Build / Build-and-ng-test (pull_request) Successful in 1m3s Details
2024-04-01 10:59:03 +02:00
Mihajlo Medjedovic cf6c9dd5f2 ci: sheet
Build / Build-and-ng-test (pull_request) Failing after 1m0s Details
2024-04-01 10:55:59 +02:00
Mihajlo Medjedovic ff55cbbaad ci: sheet
Build / Build-and-ng-test (pull_request) Failing after 17s Details
2024-04-01 10:45:16 +02:00
Mihajlo Medjedovic 3eda4e2c58 ci: install sheet temporarily
Build / Build-and-ng-test (pull_request) Failing after 17s Details
2024-04-01 10:39:30 +02:00
Mihajlo Medjedovic 02a8a1c565 feat: staging page, restore buttons 2024-03-29 13:08:48 +01:00
^ 8769841f08 feat: backend to show in getchangeinfo whether a user is allowed to restore
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-03-28 16:03:27 +00:00
Mihajlo Medjedovic 7208fe1c3b chore(git): Merge branch 'restore' of ssh://git.datacontroller.io:29419/dc/dc into restore
Build / Build-and-ng-test (pull_request) Failing after 15s Details
2024-03-26 19:14:53 +01:00
Mihajlo Medjedovic 801c8c6a9f fix: table info modal, versions - column names 2024-03-26 19:14:41 +01:00
^ 51ebd25aa3 fix: ensuring version history only includes loaded versions
Build / Build-and-ng-test (pull_request) Failing after 48s Details
2024-03-26 14:25:58 +00:00
Mihajlo Medjedovic c6595c1f61 fix(sas): viewer versions fix
Build / Build-and-ng-test (pull_request) Failing after 46s Details
2024-03-26 14:20:01 +01:00
Mihajlo Medjedovic a267666e99 style: lint
Build / Build-and-ng-test (pull_request) Failing after 46s Details
2024-03-25 22:41:25 +01:00
Mihajlo Medjedovic b27fea5b91 feat: table metadata modal, versions tab (and link) 2024-03-25 22:41:00 +01:00
^ f8a14d4bde feat: list versions of target tables (backend)
Build / Build-and-ng-test (pull_request) Failing after 47s Details
2024-03-25 12:37:11 +00:00
^ 633e35338d chore: remove unnecessary concatenation 2024-03-20 21:38:44 +00:00
^ 8003da94e6 feat(versions): getting list of versions (plus test) 2024-03-20 21:37:13 +00:00
allan c3af97ef57 Merge pull request 'issue85' (#87) from issue85 into main
Release / Build-production-and-ng-test (push) Failing after 1m13s Details
Release / Build-and-test-development (push) Has been skipped Details
Release / release (push) Has been skipped Details
Reviewed-on: #87
2024-03-19 22:41:04 +00:00
Allan 31d4e5c727 fix: typo
Build / Build-and-ng-test (pull_request) Failing after 47s Details
2024-03-19 22:36:00 +00:00
Allan fbbcf90956 fix: reverting col 2024-03-19 22:35:16 +00:00
Allan f522038b8d fix: ordering SOFTSELECT numerically in dropdown
Closes #85
2024-03-19 22:33:39 +00:00
Mihajlo Medjedovic ace599b39f style: lint
Build / Build-and-ng-test (pull_request) Failing after 46s Details
2024-03-19 10:01:30 +01:00
Mihajlo Medjedovic 963562621d feat: numeric values in hot dropdown aligned right
Build / Build-and-ng-test (pull_request) Failing after 29s Details
2024-03-18 17:32:29 +01:00
88 changed files with 9014 additions and 28406 deletions

View File

@ -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

View File

@ -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

2
.gitignore vendored
View File

@ -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

View File

@ -1,3 +1,73 @@
## [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)

View File

@ -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)!

View File

@ -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"
}
},

View File

@ -9,6 +9,8 @@ export default defineConfig({
html: true,
json: false,
},
viewportHeight: 900,
viewportWidth: 1600,
chromeWebSecurity: false,
defaultCommandTimeout: 30000,

View File

@ -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

View File

@ -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

View File

@ -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`)

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,4 +42,4 @@ module.exports = function (config) {
}
},
});
};
};

Binary file not shown.

View File

@ -10,7 +10,7 @@ const check = (cwd) => {
onlyAllow:
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
excludePackages:
'@cds/city@1.1.0;@handsontable/angular@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) {

25698
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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"
}

View File

@ -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="

View File

@ -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;
}
}

View File

@ -46,7 +46,6 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer'
SharedModule,
ClarityModule,
AppSharedModule,
HomeModule,
PipesModule,
DirectivesModule,
NgxJsonViewerModule

View File

@ -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

View File

@ -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)
)
"

View File

@ -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>

View File

@ -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

View File

@ -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
@ -1956,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
})
@ -2057,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
})
@ -2261,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;
@ -2706,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
})
@ -2934,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
}
@ -2946,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) => {
@ -3013,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')
@ -3301,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) => {

View File

@ -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,

View File

@ -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&#64;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&#64;datacontroller.io
</span>
</clr-tooltip-content>
</ng-container>
</clr-tooltip>
</clr-tree-node>
</clr-tree-node>

View File

@ -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>

View File

@ -33,7 +33,6 @@
.apply-keys {
height: 40px;
width: 200px;
}
.drop-area {

View File

@ -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 {

View File

@ -0,0 +1,9 @@
import { BaseSASResponse } from './common/BaseSASResponse'
export interface EditorsRestoreServiceResponse extends BaseSASResponse {
restore_out: RestoreOut[]
}
export interface RestoreOut {
LOADREF: string
}

View File

@ -143,7 +143,6 @@
(ngModelChange)="
setVariableOperator(queryIndex, query.operator, clauseIndex)
"
class="mt-2"
clrSelect
>
<option *ngFor="let opr of query.operators">{{ opr }}</option>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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) => {

View File

@ -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')
}
}

View File

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

View File

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

View File

@ -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,

View File

@ -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>

View File

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

View File

@ -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()

View File

@ -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>

View File

@ -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>

View File

@ -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')

View File

@ -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'

View File

@ -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&#64;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&#64;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>

View File

@ -57,6 +57,7 @@ clr-tree-node button {
.viewerTitle {
text-align:center;
margin-bottom: 15px;
margin-top: 16px;
}
.dropdown-menu {

View File

@ -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')

View File

@ -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>

View File

@ -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' }
)

View File

@ -57,7 +57,7 @@
>
</sasjs>
<body class="m-0">
<body cds-theme="light" class="m-0">
<my-app></my-app>
</body>
</html>

View File

@ -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;
}

9226
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "dcfrontend",
"version": "6.6.3",
"version": "6.8.3",
"description": "Data Controller",
"devDependencies": {
"@saithodev/semantic-release-gitea": "^2.1.0",

View File

@ -14,6 +14,10 @@ _webout = `{"SYSDATE" : "26SEP22"
"APPROVER": "sasdemo"
}
]
, "histparams":
[
{"HIST":100 ,"STARTROW":1 ,"NOBS":3 }
]
,"_DEBUG" : ""
,"_METAUSER": "sasdemo@SAS"
,"_METAPERSON": "sasdemo"

14
sas/package-lock.json generated
View File

@ -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",

View File

@ -29,6 +29,6 @@
"private": true,
"dependencies": {
"@sasjs/cli": "^4.11.1",
"@sasjs/core": "^4.49.0"
"@sasjs/core": "^4.52.1"
}
}

View File

@ -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.';

View File

@ -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..;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"

View File

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

View File

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

View File

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

View File

@ -233,7 +233,7 @@
"streamWeb": true,
"streamWebFolder": "web",
"webSourcePath": "../client/dist",
"streamServiceName": "DataController",
"streamServiceName": "DataController2",
"streamLogo": "favicon.ico",
"assetPaths": []
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()

View File

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

View File

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

View File

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