Compare commits

...

98 Commits
v6.6.2 ... 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
semantic-release-bot 5171d07441 chore(release): 6.6.3 [skip ci]
## [6.6.3](https://git.datacontroller.io/dc/dc/compare/v6.6.2...v6.6.3) (2024-02-26)

### Bug Fixes

* allow empty clause value when NE or CONTAINS ([432450a](432450a15b))
2024-02-26 14:17:24 +00:00
allan 9a0b9573d5 Merge pull request 'Allow empty clause value when operator is NE or CONTAINS' (#83) from issue-82 into main
Release / Build-production-and-ng-test (push) Successful in 4m18s Details
Release / Build-and-test-development (push) Successful in 7m46s Details
Release / release (push) Successful in 6m18s Details
Reviewed-on: #83
2024-02-26 14:03:36 +00:00
Mihajlo Medjedovic 4733311ef3 style: lint
Build / Build-and-ng-test (pull_request) Successful in 1m0s Details
2024-02-26 14:15:34 +01:00
Mihajlo Medjedovic 432450a15b fix: allow empty clause value when NE or CONTAINS 2024-02-26 14:14:51 +01:00
90 changed files with 9042 additions and 28416 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,80 @@
## [6.8.3](https://git.datacontroller.io/dc/dc/compare/v6.8.2...v6.8.3) (2024-05-09)
### Bug Fixes
* updating core to increase filename length, closes [#103](https://git.datacontroller.io/dc/dc/issues/103) ([ee58fd5](https://git.datacontroller.io/dc/dc/commit/ee58fd5b4bc0dd3e3f232c4f26bb85b2e7fe2b54))
## [6.8.2](https://git.datacontroller.io/dc/dc/compare/v6.8.1...v6.8.2) (2024-05-03)
### Bug Fixes
* dc_request_logs option feature ([93758ef](https://git.datacontroller.io/dc/dc/commit/93758efb275966c181f1ee8b6c752010909a0282))
* release process ([c0dc919](https://git.datacontroller.io/dc/dc/commit/c0dc9191e3b95ea6f7e5021fc0bdbcab0af4cc64))
## [6.8.1](https://git.datacontroller.io/dc/dc/compare/v6.8.0...v6.8.1) (2024-05-02)
### Bug Fixes
* hide approve button when table revertable ([ec0f539](https://git.datacontroller.io/dc/dc/commit/ec0f539a337b176c83a661ff520a6892d47efa02))
# [6.8.0](https://git.datacontroller.io/dc/dc/compare/v6.7.0...v6.8.0) (2024-05-02)
### Bug Fixes
* ci sheet lib, submit message auto focus ([c5e4650](https://git.datacontroller.io/dc/dc/commit/c5e46503272f3f3d9cd83ac04225babf79d4de44))
* **clarity:** new version style issues ([8c7de5a](https://git.datacontroller.io/dc/dc/commit/8c7de5aad7e7e32a64769696af9b93eb9a6225d3))
* cypress tests ([3dd85cc](https://git.datacontroller.io/dc/dc/commit/3dd85cc60bd5ac99bc930b6b9c89a8e707e4d51d))
* ensuring that only restorable versions are restorable ([a402856](https://git.datacontroller.io/dc/dc/commit/a4028562ce91b32ff971ab9821328b97cd23f381))
* ensuring version history only includes loaded versions ([51ebd25](https://git.datacontroller.io/dc/dc/commit/51ebd25aa362aa8e66c83b29b2c64aa0f206f5bd))
* final testing on restore feature ([297a84d](https://git.datacontroller.io/dc/dc/commit/297a84d3a4ebb47bef7f3ca9758978d727afed8d))
* issue with multiple adds/deletes, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([904ca30](https://git.datacontroller.io/dc/dc/commit/904ca30f918da085fa05dae066367b512933d1a9))
* load_ref var ([aaad9f7](https://git.datacontroller.io/dc/dc/commit/aaad9f7207115599a006980fff099d59738dd2cd))
* removing alerts dummy data, closes [#93](https://git.datacontroller.io/dc/dc/issues/93) ([eba21e9](https://git.datacontroller.io/dc/dc/commit/eba21e96b4fa34e63b4477281f47d9a01d621f2e))
* restore table version improvement ([549f357](https://git.datacontroller.io/dc/dc/commit/549f35766ba7b5bbe55694845e85bfefc4193375))
* **sas:** viewer versions fix ([c6595c1](https://git.datacontroller.io/dc/dc/commit/c6595c1f618803d9202cba1a1fe76986449cf2e2))
* stage and approve buttons renaming ([ef81e33](https://git.datacontroller.io/dc/dc/commit/ef81e33f704d0b4f99405d96498e1d29ac817982))
* supporting SCD2 data reversions ([fa8396f](https://git.datacontroller.io/dc/dc/commit/fa8396f0394cbddb6dbacb4b355de078fad49980))
* table info modal, versions - column names ([801c8c6](https://git.datacontroller.io/dc/dc/commit/801c8c6a9fb95388a06a6c6284fec4dc25bb77c5))
* **updates:** angular, clarity, resolved legacy-peer-deps ([c60dd65](https://git.datacontroller.io/dc/dc/commit/c60dd65a1637333f11a0c39ef697c7292a6ede07))
### Features
* backend to show in getchangeinfo whether a user is allowed to restore ([8769841](https://git.datacontroller.io/dc/dc/commit/8769841f08694f672ef7ae1a17beacd0dbedda52))
* list versions of target tables (backend) ([f8a14d4](https://git.datacontroller.io/dc/dc/commit/f8a14d4bdef055b99930491d1f6fabe55a50a497))
* restore ([604c2e7](https://git.datacontroller.io/dc/dc/commit/604c2e70bdeeeb1aa5bb18b94f525ebd049397fa))
* SAS services & tests for RESTORE, [#84](https://git.datacontroller.io/dc/dc/issues/84) ([9ad7ae4](https://git.datacontroller.io/dc/dc/commit/9ad7ae47b5e793ce68ab21c9eeb8dee6cb85e496))
* staging page, restore buttons ([02a8a1c](https://git.datacontroller.io/dc/dc/commit/02a8a1c5654350cafc53b749cceb686fc6848c33))
* table metadata modal, versions tab (and link) ([b27fea5](https://git.datacontroller.io/dc/dc/commit/b27fea5b91e33b4673a3a991aedae558e366ca29))
* **versions:** getting list of versions (plus test) ([8003da9](https://git.datacontroller.io/dc/dc/commit/8003da94e615463ed3ddfd60b0cbf2e58615eab1))
# [6.7.0](https://git.datacontroller.io/dc/dc/compare/v6.6.4...v6.7.0) (2024-04-01)
### Features
* numeric values in hot dropdown aligned right ([9635626](https://git.datacontroller.io/dc/dc/commit/963562621ddf0e8d24a29a8481c5e6da1b040708))
## [6.6.4](https://git.datacontroller.io/dc/dc/compare/v6.6.3...v6.6.4) (2024-04-01)
### Bug Fixes
* ordering SOFTSELECT numerically in dropdown ([f522038](https://git.datacontroller.io/dc/dc/commit/f522038b8ddb1da14b8adbf8346d0a4539a94cc8)), closes [#85](https://git.datacontroller.io/dc/dc/issues/85)
* reverting col ([fbbcf90](https://git.datacontroller.io/dc/dc/commit/fbbcf90956bf538b032b0107c07b8576d20353b9))
* typo ([31d4e5c](https://git.datacontroller.io/dc/dc/commit/31d4e5c727f790d428fb2ea8da60dca929561805))
## [6.6.3](https://git.datacontroller.io/dc/dc/compare/v6.6.2...v6.6.3) (2024-02-26)
### Bug Fixes
* allow empty clause value when NE or CONTAINS ([432450a](https://git.datacontroller.io/dc/dc/commit/432450a15b51a269821ba1d430854f5d1dd04703))
## [6.6.2](https://git.datacontroller.io/dc/dc/compare/v6.6.1...v6.6.2) (2024-02-22)

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
@ -980,15 +982,18 @@ export class EditorComponent implements OnInit, AfterViewInit {
// Apply licence rows limitation if exists, it is only affecting data
// which will be send to SAS
const strippedCsvArrayData = csvArrayData.slice(0, this.licenceState.value.submit_rows_limit)
const strippedCsvArrayData = csvArrayData.slice(
0,
this.licenceState.value.submit_rows_limit
)
// To submit to sas service, we need clean version of CSV of file
// attached. XLSX will do the parsing and heavy lifting
// First we create worksheet of json (data we extracted)
let ws = XLSX.utils.json_to_sheet(strippedCsvArrayData, {
skipHeader: true
});
})
// create CSV to be uploaded from worksheet
let csvContentClean = XLSX.utils.sheet_to_csv(ws);
let csvContentClean = XLSX.utils.sheet_to_csv(ws)
// Prepend headers
csvContentClean = csvArrayHeaders.join(',') + '\n' + csvContentClean
@ -1953,13 +1958,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
if (entry.values.length > 0) {
hot.setCellMeta(entry.row, entry.col, 'renderer', 'autocomplete')
hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete')
hot.setCellMeta(entry.row, entry.col, 'editor', 'autocomplete.custom')
hot.setCellMeta(entry.row, entry.col, 'strict', entry.strict)
hot.setCellMeta(entry.row, entry.col, 'filter', false)
this.currentEditRecordValidator?.updateRule(entry.col, {
renderer: 'autocomplete',
editor: 'autocomplete',
editor: 'autocomplete.custom',
strict: entry.strict,
filter: false
})
@ -2054,13 +2059,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
}
hot.setCellMeta(row, cellCol, 'renderer', 'autocomplete')
hot.setCellMeta(row, cellCol, 'editor', 'autocomplete')
hot.setCellMeta(row, cellCol, 'editor', 'autocomplete.custom')
hot.setCellMeta(row, cellCol, 'strict', cellValidationEntry.strict)
hot.setCellMeta(row, cellCol, 'filter', false)
this.currentEditRecordValidator?.updateRule(cellCol, {
renderer: 'autocomplete',
editor: 'autocomplete',
editor: 'autocomplete.custom',
strict: cellValidationEntry.strict,
filter: false
})
@ -2258,8 +2263,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
setTimeout(() => {
let txt: any = document.getElementById('formFields_8')
txt.focus()
})
if (txt) txt.focus()
}, 200)
})
// let cnt = 0;
@ -2703,13 +2708,13 @@ export class EditorComponent implements OnInit, AfterViewInit {
const strict = this.cellValidationSource[validationSourceIndex].strict
hot.setCellMeta(row, column, 'renderer', 'autocomplete')
hot.setCellMeta(row, column, 'editor', 'autocomplete')
hot.setCellMeta(row, column, 'editor', 'autocomplete.custom')
hot.setCellMeta(row, column, 'strict', strict)
hot.setCellMeta(row, column, 'filter', false)
this.currentEditRecordValidator?.updateRule(column, {
renderer: 'autocomplete',
editor: 'autocomplete',
editor: 'autocomplete.custom',
strict: strict,
filter: false
})
@ -2931,6 +2936,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
}
}
datasetInfoModalRowClicked(value: Version | DSMeta) {
if ((<Version>value).LOAD_REF !== undefined) {
// Type is Version
const row = value as Version
const url = `/stage/${row.LOAD_REF}`
this.router.navigate([url])
}
}
viewboxManager() {
this.viewboxes = true
}
@ -2943,6 +2958,37 @@ export class EditorComponent implements OnInit, AfterViewInit {
)
}
/**
* Function checks if selected hot cell is solo cell selected
* and if it is, set the `filter` property based on filter param.
*
* @param filter
*/
private setCellFilter(filter: boolean) {
const hotSelected = this.hotInstance.getSelected()
const selection = hotSelected ? hotSelected[0] : hotSelected
// When we open a dropdown we want filter disabled so value in cell
// don't filter out items, since we want to see them all.
// But when we start typing we want to be able to start filtering values
// again
if (selection) {
const startRow = selection[0]
const endRow = selection[2]
const startCell = selection[1]
const endCell = selection[3]
if (startRow === endRow && startCell === endCell) {
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
// If filter is not already set at the value in the param, set it
if (cellMeta && cellMeta.filter === !filter) {
this.hotInstance.setCellMeta(startRow, startCell, 'filter', filter)
}
}
}
}
async ngOnInit() {
this.licenceService.hot_license_key.subscribe(
(hot_license_key: string | undefined) => {
@ -3010,6 +3056,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.cols = response.data.cols
this.dsmeta = response.data.dsmeta
this.versions = response.data.versions || []
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
@ -3298,28 +3345,16 @@ export class EditorComponent implements OnInit, AfterViewInit {
}
)
hot.addHook('beforeKeyDown', (e: any) => {
const hotSelected = this.hotInstance.getSelected()
const selection = hotSelected ? hotSelected[0] : hotSelected
hot.addHook('afterBeginEditing', () => {
// When we open a dropdown we want filter disabled so value in cell
// don't filter out items, since we want to see them all.
this.setCellFilter(false)
})
hot.addHook('beforeKeyDown', () => {
// When we start typing, we are enabling the filter since we want to find
// values faster.
if (selection) {
const startRow = selection[0]
const endRow = selection[2]
const startCell = selection[1]
const endCell = selection[3]
if (startRow === endRow && startCell === endCell) {
const cellMeta = this.hotInstance.getCellMeta(startRow, startCell)
if (cellMeta && cellMeta.filter === false) {
this.hotInstance.setCellMeta(startRow, startCell, 'filter', true)
}
}
}
this.setCellFilter(true)
})
hot.addHook('afterChange', (source: any, change: any) => {

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

@ -878,17 +878,25 @@ export class QueryComponent
*/
public hasInvalidCluase(clauses: any): boolean {
for (let clause of clauses) {
clause['invalidClause'] = false
if (
clause.variable === null ||
clause.operator === null ||
clause.value === null ||
clause.value === ''
clause.value === '' &&
!(clause.operator === 'NE' || clause.operator === 'CONTAINS')
) {
clause['invalidClause'] = true
return true
}
if (
clause.variable === null ||
clause.operator === null ||
clause.value === null
) {
clause['invalidClause'] = true
return true
} else {
clause['invalidClause'] = false
}
}

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

@ -18,4 +18,4 @@ In any case, you must not make any such use of this software as to develop softw
UNLESS EXPRESSLY AGREED OTHERWISE, 4GL APPS PROVIDES THIS SOFTWARE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, AND IN NO EVENT AND UNDER NO LEGAL THEORY, SHALL 4GL APPS BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING FROM USE OR INABILITY TO USE THIS SOFTWARE.
`
`

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.2",
"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;