Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83c2e84647 | ||
|
|
429665a1e0 | ||
|
|
a174c332f3 | ||
|
|
d26b668b38 | ||
|
|
a8bd89fafd | ||
|
|
33256f4c64 | ||
|
|
fc3b574425 | ||
|
|
ba10451293 | ||
|
|
4dee759674 | ||
|
|
02858bbacb | ||
|
|
69ac051786 | ||
|
|
2c998bbb2b | ||
|
|
a79823d3b1 | ||
|
|
30d029a223 | ||
|
|
9ce25b8e83 | ||
|
|
ac5710979a | ||
|
|
af749b57bc | ||
|
|
646125f039 | ||
|
|
53aade5322 | ||
|
|
be20491d2e | ||
|
|
7d3a5227d6 | ||
|
|
8daf6129d1 | ||
|
|
6cec91b09b | ||
|
|
bc0af27afa | ||
|
|
f4cdbfc99c | ||
|
|
aaa205b4e0 | ||
|
|
c8b49beba6 | ||
|
|
6c2694999c | ||
|
|
dfc3989a38 | ||
|
|
d1fdfb2496 | ||
|
|
7b594ca906 | ||
|
|
88bb34828b | ||
|
|
3086cbf4e7 | ||
|
|
d7a4dd40ea | ||
|
|
7238a698a1 | ||
|
|
a7c5332a99 | ||
|
|
e7b1d29d9d | ||
|
|
4f7e5bb732 | ||
|
|
77aaacc8b3 | ||
|
|
ddc7fe939d | ||
|
|
767be30476 | ||
|
|
400b0b5f0b | ||
|
|
b3d0bf5685 | ||
|
|
5cb8826f4e | ||
|
|
fd1b5cfd74 | ||
|
|
6ef055bb15 | ||
|
|
5293a21e03 | ||
|
|
01a90a4d75 | ||
|
|
5dc99ef5ad | ||
|
|
488e724745 | ||
|
|
126b8944c4 | ||
|
|
b6b6eeff53 | ||
|
|
44078a78ad | ||
|
|
39ece5a2c0 | ||
|
|
02fc874ef5 | ||
|
|
5f83fa53cf | ||
|
|
f7a2f28e06 | ||
|
|
6f5e6e2b92 | ||
|
|
c19037c04d | ||
|
|
e5a12a99d0 | ||
|
|
7cde3c1143 | ||
|
|
7463f5875f | ||
|
|
da3321cd2d | ||
|
|
c460e29f75 | ||
|
|
9191b413d5 | ||
|
|
ee40b2bbef | ||
|
|
c5e6eb5bb7 | ||
|
|
8985528343 | ||
|
|
097abcd3eb | ||
|
|
1f4e60ce97 | ||
|
|
8193aac845 | ||
|
|
e22c53ea27 | ||
|
|
4500e7cfe8 | ||
|
|
5e84ffa663 | ||
|
|
08b994a4eb | ||
|
|
b9351cec5b | ||
|
|
48f65b1389 | ||
|
|
4b00b28814 | ||
|
|
5a4e9d17ec | ||
|
|
018733b1b9 | ||
|
|
1b662a40df | ||
|
|
b64eff482f | ||
|
|
3c4a093bf7 | ||
|
|
3921e69fa8 | ||
|
|
6efebd7f45 | ||
|
|
3e87ea6c1b | ||
|
|
e25b36d50e | ||
|
|
6c3a3ecb71 | ||
|
|
ff5f6c42f3 | ||
|
|
425234148e | ||
|
|
e6fe453222 | ||
|
|
327dc71c00 | ||
|
|
ac3d42ba41 | ||
|
|
bfa240cc9e | ||
|
|
cdba23e4cc | ||
|
|
6310a26661 | ||
|
|
9c7b0e529e | ||
|
|
3ce3e598bf | ||
|
|
c0164f7e66 | ||
|
|
63872ce36c | ||
|
|
a096824b20 | ||
|
|
297f2b158b | ||
|
|
22b522dba1 | ||
|
|
0ac7d96919 | ||
|
|
374614b7d1 | ||
|
|
a763498b1f | ||
|
|
743b281c72 | ||
|
|
6d47193fcc | ||
|
|
9c95e77597 | ||
|
|
ece0a09872 | ||
|
|
a5650ea104 | ||
|
|
5bb4cfc853 | ||
|
|
ba048922bb | ||
|
|
16daed592d | ||
|
|
b3809b2051 | ||
|
|
7700603735 | ||
|
|
f7dd4e6b99 | ||
|
|
c22a4c08f5 | ||
|
|
27d25808f0 | ||
|
|
f44c3d783f | ||
|
|
be866ecf0c | ||
|
|
79d1936755 | ||
|
|
3191197cbf | ||
|
|
a71c31b685 | ||
|
|
7223f5a468 | ||
|
|
21a70217f4 | ||
|
|
9e005a73bb | ||
|
|
23c461fd3e | ||
|
|
53abd4a941 | ||
|
|
651a9140e0 | ||
|
|
be5f033712 | ||
|
|
655d590353 | ||
|
|
cc151b001b | ||
|
|
6b363b9dad | ||
|
|
665e7445d3 | ||
|
|
df843b9632 | ||
|
|
58106a3f45 | ||
|
|
c3c5282a04 | ||
|
|
ff5cc71f60 | ||
|
|
590c19e839 | ||
|
|
6ba9ffc3af | ||
|
|
a63218131c | ||
|
|
afec300c99 | ||
|
|
8061bff752 | ||
|
|
fa9e3e576b | ||
|
|
dec18e3d6a | ||
|
|
d1dbc3090c | ||
|
|
d328e3ce69 | ||
|
|
d1726e9907 | ||
|
|
75d7f7dac7 | ||
|
|
6d8fddc3cb | ||
|
|
4d3fb11606 | ||
|
|
6b2286a66c | ||
|
|
8714627d6f | ||
|
|
2cbde35248 | ||
|
|
1ff91f5757 | ||
|
|
19f4ea609a | ||
|
|
b7214c55a4 | ||
|
|
05cf14e8b4 | ||
|
|
80520a2f4d | ||
|
|
e11ae54f4d | ||
|
|
a4dca84e62 | ||
|
|
50a9a980ed | ||
|
|
19d8bc44c8 | ||
|
|
bf8392ff9f | ||
|
|
74d161f6f5 | ||
|
|
b646e78e0f | ||
|
|
412f4a8008 | ||
|
|
ba73a89d8f | ||
|
|
1225774806 | ||
|
|
6cb056eba3 | ||
|
|
07aaa8fcde | ||
|
|
8a11a76b49 | ||
|
|
08c5f1db22 | ||
|
|
7c762c1050 | ||
|
|
bbd0847a21 | ||
|
|
4e41ac531d | ||
|
|
a47657edf4 | ||
|
|
1935771a70 | ||
|
|
80a82ea925 | ||
|
|
deb9847617 | ||
|
|
2f1feab9d3 | ||
|
|
f3d8db6aaf | ||
|
|
259840808c | ||
|
|
9541d2ed5c | ||
|
|
f8bb37e5af | ||
|
|
b7bf44b997 | ||
|
|
f21627a9ba | ||
|
|
6bedc710d3 | ||
|
|
dca54b2040 | ||
|
|
c1b60c634e | ||
|
|
1562f0ff07 | ||
|
|
4f8ea30d9d | ||
|
|
4472b17fc5 | ||
|
|
9ae05e3cc4 | ||
|
|
84511ba6d4 | ||
|
|
1fc0d3eae8 | ||
|
|
ac1d54565c | ||
|
|
c2ebbcb735 | ||
|
|
d1ce2ac9ea | ||
|
|
27e0daaee0 | ||
|
|
751a72a2d8 | ||
|
|
ff88e97bfe | ||
|
|
288b32f862 | ||
|
|
31575b900b | ||
|
|
8c0dbd0558 | ||
|
|
aa066b197c | ||
|
|
6e8fd95cdb | ||
|
|
d7c9d0f45b | ||
|
|
7890513889 | ||
|
|
d417a084ed | ||
|
|
824188977d | ||
|
|
bb605564ea | ||
|
|
4496a42b16 | ||
|
|
02b97f8734 | ||
|
|
c68e1d9f49 | ||
|
|
5534b25fa3 | ||
|
|
202a22a170 | ||
|
|
ef73f8dacd | ||
|
|
101e6653d4 | ||
|
|
bd8b9d738f | ||
|
|
52b5739cdc | ||
|
|
148b40195e | ||
|
|
8000002b1b | ||
|
|
3aae7c124b | ||
|
|
087e1346d3 | ||
|
|
14623a6386 | ||
|
|
d506d4a015 | ||
|
|
285f711e78 | ||
|
|
5df6b6de11 | ||
|
|
2620607cc3 | ||
|
|
499432522a | ||
|
|
6022d7fc36 | ||
|
|
360bf0ffdd | ||
|
|
322828ccc4 | ||
|
|
9ad9bdbf5e | ||
|
|
ae50e27725 | ||
|
|
91b8516cf0 | ||
|
|
74c5e1593d | ||
|
|
c1ae2e1c5b | ||
|
|
76913eb2e3 | ||
|
|
6f35594f2a | ||
|
|
fcfcc4e42a | ||
|
|
fa50bb425a | ||
|
|
1f0720ecb4 | ||
|
|
cd6f87b796 | ||
|
|
82636e3ee9 | ||
|
|
cedaad95c2 | ||
|
|
35e0e969e2 | ||
|
|
8c4a076367 | ||
|
|
df7bd5671a | ||
|
|
8bf9a5f21e | ||
|
|
0dbe073fc3 | ||
|
|
bdde9cc21d | ||
|
|
2c8370a8a8 | ||
|
|
766d232022 | ||
|
|
97d0d9057b | ||
|
|
184aaa8f64 | ||
|
|
6ed14cb1f1 | ||
|
|
c040f1188d | ||
|
|
715681b020 | ||
|
|
3254f4a050 | ||
|
|
324c5dd98c | ||
|
|
e9e6ad31fb | ||
|
|
1518b32a43 | ||
|
|
3209ac3a52 | ||
|
|
ca03aca886 | ||
|
|
dfb2673c91 | ||
|
|
4328b4c6da | ||
|
|
6645eca25d | ||
|
|
cf070fd5c4 | ||
|
|
81a381b1e7 | ||
|
|
95bea7a83b | ||
|
|
085e901512 | ||
|
|
b421d15e4e | ||
|
|
03b6d1aa9b | ||
|
|
5efc617299 | ||
|
|
f4065d9b6e | ||
|
|
544531a325 | ||
|
|
29939f02fe | ||
|
|
f3a3c24542 | ||
|
|
051add8e7d | ||
|
|
dda80dbb25 | ||
|
|
ffa774e972 | ||
|
|
449cdfe190 | ||
|
|
2d3c94b57e | ||
|
|
3ede705fe1 | ||
|
|
119efb6911 | ||
|
|
234793b06d | ||
|
|
984b3f6bce | ||
|
|
bfee928e45 | ||
|
|
76975942de | ||
|
|
093019ba33 | ||
|
|
682ec89ddc | ||
|
|
0879a0430d | ||
|
|
22c5140dce | ||
|
|
6397acb103 | ||
|
|
eb5c88ac85 | ||
|
|
d8dd0cf8bb | ||
|
|
966e18b1d7 | ||
|
|
fd13808cc9 | ||
|
|
dbadbff1b9 | ||
|
|
b8f4053c23 | ||
|
|
4d7c269d62 | ||
|
|
ace9e2a3ae | ||
|
|
b17592402b | ||
|
|
51f8a11555 | ||
|
|
a54a79715e | ||
|
|
f8655421ec | ||
|
|
24ffef643f | ||
|
|
55fca43361 | ||
|
|
96b1646614 | ||
|
|
02950980e2 | ||
|
|
18aef45e63 | ||
|
|
0c20ccfde0 | ||
|
|
833125a296 | ||
|
|
3d7a40c88f | ||
|
|
0c14f16bbb | ||
|
|
71108c3509 | ||
|
|
fc0212c3fc | ||
|
|
3a9110ade6 | ||
|
|
f1a3a3bfb2 | ||
|
|
497652efb2 | ||
|
|
e13757aabb | ||
|
|
18025e7493 | ||
|
|
5c091e1b72 | ||
|
|
1dc4c396f8 | ||
|
|
8f5c60768c | ||
|
|
fd43d8ef59 | ||
|
|
30049481fe | ||
|
|
1603aff862 | ||
|
|
f8bb1aa605 | ||
|
|
85e881aa8b | ||
|
|
f4b3b4a205 | ||
|
|
bf555f71bf | ||
|
|
a220b25449 | ||
|
|
50ef25fda1 | ||
|
|
9e6b051353 | ||
|
|
c056b27176 | ||
|
|
4db7dc89c7 | ||
|
|
2a3c65678e | ||
|
|
09b119f71b | ||
|
|
116f86b51d | ||
|
|
2b7d3a327a | ||
|
|
6b7b5fd45d | ||
|
|
684aa4a9f6 | ||
|
|
6ca5b5eaea | ||
|
|
fa15aa4076 | ||
|
|
fe0ab37bdf | ||
|
|
f2bbeb2c67 | ||
|
|
6bcbf2b2ba | ||
|
|
5874438705 | ||
|
|
c0accf1eb8 | ||
|
|
b4e6dded82 | ||
|
|
2a0174505f | ||
|
|
4717af1530 | ||
|
|
c0642f393f | ||
|
|
a07e3f4e41 | ||
|
|
5dd59a6c14 | ||
|
|
3600222178 | ||
|
|
efe6108673 | ||
|
|
af2e111dcd | ||
|
|
fe48e0eba2 | ||
|
|
e0cee85ad4 | ||
|
|
04d8b11dcd | ||
|
|
20172626cb | ||
|
|
960e91c8e5 | ||
|
|
9ad765327d | ||
|
|
82c7638277 | ||
|
|
c21e7b9f20 | ||
|
|
d19a251583 | ||
|
|
f35bd9c12a | ||
|
|
4eaf483b12 | ||
|
|
8aa7cfe5ee | ||
|
|
944ccfb1b9 | ||
|
|
9bfe3a97fb | ||
|
|
94431c9dba | ||
|
|
3a3988827d | ||
|
|
3786054f0b | ||
|
|
0bf6247936 | ||
|
|
8cd3f4671f | ||
|
|
e43e2fd4a8 | ||
|
|
9b3b7ed39f | ||
|
|
83c2adb681 | ||
|
|
2e542109b0 | ||
|
|
4d2cf122a1 | ||
|
|
ebe08911b5 | ||
|
|
c39f706ec8 | ||
|
|
a01874ed57 | ||
|
|
d398041e3a | ||
|
|
c7e901ffd6 | ||
|
|
67aef56986 | ||
|
|
4dfaa057bf | ||
|
|
83d55ab3a6 | ||
|
|
3ddde0d9b8 | ||
|
|
35b69b1cbc | ||
|
|
9df876f218 | ||
|
|
51b00c730f | ||
|
|
04bfed87fe | ||
|
|
034551d317 | ||
|
|
ca150daf25 | ||
|
|
02682cb008 | ||
|
|
f0dae60c4c | ||
|
|
aed22a174b | ||
|
|
23341da979 | ||
|
|
adc64b6db4 | ||
|
|
5874ad8d30 | ||
|
|
3386c3bba2 | ||
|
|
a2f23d5172 | ||
|
|
acceca3a43 | ||
|
|
a3a5c21098 | ||
|
|
e8090b9d91 | ||
|
|
d8d22cd787 | ||
|
|
07a3db19c5 | ||
|
|
10ec87eff6 | ||
|
|
eab78bfe2c | ||
|
|
74eaed8bb6 | ||
|
|
841495d453 | ||
|
|
435cd8800b | ||
|
|
f8b91bd9e7 | ||
|
|
a9e7b0267a | ||
|
|
4036577f6e | ||
|
|
b1f594b122 | ||
|
|
3be27cc632 | ||
|
|
84242dc014 | ||
|
|
c5222c4b54 | ||
|
|
61f75970a4 | ||
|
|
8deaaa312a | ||
|
|
4260402a90 | ||
|
|
3b10ddb8ea | ||
|
|
eb1a5db958 | ||
|
|
f449c10650 | ||
|
|
8da8fddf29 | ||
|
|
3022ca5a53 | ||
|
|
e913140055 | ||
|
|
1646132db3 | ||
|
|
20836b7717 | ||
|
|
c5c8bcf165 | ||
|
|
1e02e9a6cc | ||
|
|
cfa6c42506 | ||
|
|
d51388e4d7 | ||
|
|
bf05a9913d | ||
|
|
9774ec6cc3 | ||
|
|
ad884bbf7f | ||
|
|
ef65dcdfc6 | ||
|
|
b965fb3dd7 | ||
|
|
bc7543265c | ||
|
|
4ed09ddacc | ||
|
|
5921e34e83 | ||
|
|
e85e2c4317 | ||
|
|
021be5d689 | ||
|
|
4727a2536f | ||
|
|
05f61271a6 | ||
|
|
2fbfa8093e | ||
|
|
51b45ffd03 | ||
|
|
9db7b4d88b | ||
|
|
799dcbdccb | ||
|
|
5874eb5d55 | ||
|
|
3c6e2e01ea | ||
|
|
bfb8cd0663 | ||
|
|
b59f654774 | ||
|
|
feebe4d8bc | ||
|
|
3ecb8b8ea9 | ||
|
|
73e1f4ed55 | ||
|
|
02c6c28b8b | ||
|
|
224169c636 | ||
|
|
cc42e2fbe0 | ||
|
|
545f10b675 | ||
|
|
7265e483d4 | ||
|
|
f29a6bc5d9 | ||
|
|
f42d4cddd7 | ||
|
|
7cdded9f22 | ||
|
|
a4c1124fcb | ||
|
|
212b6a98b8 | ||
|
|
acafe97f76 | ||
|
|
68f9a9ba7f | ||
|
|
75614b5d08 | ||
|
|
5a541fc7b0 | ||
|
|
add826007d | ||
|
|
4b2e319e33 | ||
|
|
f987b908fc | ||
|
|
1e4aa6631a | ||
|
|
554d7362e5 | ||
|
|
94f6316b4a | ||
|
|
4e3a167ef7 | ||
|
|
77420d29c6 | ||
|
|
8b09f132e0 | ||
|
|
52b5a06755 | ||
|
|
c9e4369928 | ||
|
|
5858fc2827 | ||
|
|
735878b7ef | ||
|
|
7542b512e8 | ||
|
|
c7c01dc35e | ||
|
|
614b98b8fb | ||
|
|
bb8920a90c | ||
|
|
64fccf8753 | ||
|
|
fb734eb61b | ||
|
|
10da4af957 | ||
|
|
7767804426 | ||
|
|
789d8d5f5a | ||
|
|
18f6bc469c | ||
|
|
e24a4778ad | ||
|
|
dfa05e003f | ||
|
|
b740277c03 | ||
|
|
f3c10de6a1 | ||
|
|
ffaf972063 | ||
|
|
8afb80cf18 | ||
|
|
97adee1181 | ||
|
|
2d25f8571d | ||
|
|
aa5b813791 | ||
|
|
5c72168310 | ||
|
|
cd72397f24 | ||
|
|
5f2885ae35 | ||
|
|
7a16aba50e | ||
|
|
21759e809f | ||
|
|
b109b8e008 | ||
|
|
5a36cb59c8 | ||
|
|
df698a8dd3 | ||
|
|
12403e40b0 | ||
|
|
4e705931fc | ||
|
|
469c1b7a6e | ||
|
|
631d90869f | ||
|
|
41ffb196e4 | ||
|
|
627e9cc3af | ||
|
|
dd38bd934c | ||
|
|
b5c1a9d7f3 | ||
|
|
a25455f314 | ||
|
|
a815fc47e2 | ||
|
|
d19abc0dff | ||
|
|
ad2f1cf0d1 | ||
|
|
a02b43fc4e | ||
|
|
d1a2bfa378 | ||
|
|
9e347ac98f | ||
|
|
4b57f84165 | ||
|
|
c6b7b4f845 | ||
|
|
7d504c1eb5 | ||
|
|
9c423ec05b | ||
|
|
7a413820b7 | ||
|
|
3480d66dd7 | ||
|
|
33e86e5f26 | ||
|
|
aecfdbb635 | ||
|
|
0db37f9530 | ||
|
|
64553a99d5 | ||
|
|
abd20f9bd9 | ||
|
|
bedace7c4b | ||
|
|
e44cfb6ce7 | ||
|
|
1e405b318f | ||
|
|
c1c1a808ec | ||
|
|
d91088cc05 | ||
|
|
be9b46f673 | ||
|
|
612a7981eb | ||
|
|
705a210ed1 | ||
|
|
39de905559 | ||
|
|
131c809b89 | ||
|
|
2a63d977fb | ||
|
|
f75d9458b8 | ||
|
|
39bf97a2ef | ||
|
|
93672019d6 | ||
|
|
e670c138a3 | ||
|
|
126a1a6b53 | ||
|
|
afe58c3823 | ||
|
|
f89cd5d05e | ||
|
|
33e97bfe62 | ||
|
|
6a42964614 | ||
|
|
20036c2d00 | ||
|
|
fabab3e36c | ||
|
|
4f76e5fe84 | ||
|
|
73ec06abfc | ||
|
|
80d6cbc07c | ||
|
|
9d19b72f46 |
No files matched your search
@@ -12,8 +12,5 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -56,5 +56,10 @@ module.exports = {
|
||||
plugins: ['node'],
|
||||
extends: ['plugin:node/recommended'],
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
extends: ['plugin:qunit/recommended'],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for accounts.
|
||||
*/
|
||||
export default class AccountAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSetPassword() {
|
||||
return this.hasAuthorizedAction('set-password');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for groups.
|
||||
*/
|
||||
export default class GroupAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddMembers() {
|
||||
return this.hasAuthorizedAction('add-members');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveMembers() {
|
||||
return this.hasAuthorizedAction('remove-members');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for host sets.
|
||||
*/
|
||||
export default class UserAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddHosts() {
|
||||
return this.hasAuthorizedAction('add-hosts');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveHosts() {
|
||||
return this.hasAuthorizedAction('remove-hosts');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for roles.
|
||||
*/
|
||||
export default class RoleAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSetGrants() {
|
||||
return this.hasAuthorizedAction('set-grants');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddPrincipals() {
|
||||
return this.hasAuthorizedAction('add-principals');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemovePrincipals() {
|
||||
return this.hasAuthorizedAction('remove-principals');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for targets.
|
||||
*/
|
||||
export default class TargetAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canConnect() {
|
||||
return this.hasAuthorizedAction('authorize-session');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddHostSources() {
|
||||
return this.hasAuthorizedAction('add-host-sources');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveHostSources() {
|
||||
return this.hasAuthorizedAction('remove-host-sources');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddCredentialSources() {
|
||||
return this.hasAuthorizedAction('add-credential-sources');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveCredentialSources() {
|
||||
return this.hasAuthorizedAction('remove-credential-sources');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import ModelAbility from './model';
|
||||
|
||||
/**
|
||||
* Provides abilities for users.
|
||||
*/
|
||||
export default class UserAbility extends ModelAbility {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canAddAccounts() {
|
||||
return this.hasAuthorizedAction('add-accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRemoveAccounts() {
|
||||
return this.hasAuthorizedAction('remove-accounts');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import RESTAdapter from '@ember-data/adapter/rest';
|
||||
import { serializeIntoHash } from '@ember-data/adapter/-private';
|
||||
/* eslint-disable-next-line ember/no-mixins */
|
||||
import AdapterBuildURLMixin from '../mixins/adapter-build-url';
|
||||
import config from 'ember-get-config';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { get } from '@ember/object';
|
||||
import { InvalidError } from '@ember-data/adapter/error';
|
||||
import { dasherize } from '@ember/string';
|
||||
@@ -44,19 +44,36 @@ export default class ApplicationAdapter extends RESTAdapter.extend(
|
||||
) {
|
||||
// =attributes
|
||||
|
||||
#host;
|
||||
#namespace;
|
||||
|
||||
/**
|
||||
* Sets host to the `api.host` string in the application's config,
|
||||
* if set.
|
||||
* @type {string}
|
||||
*/
|
||||
host = get(config, 'api.host');
|
||||
get host() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return this.#host || get(config, 'api.host');
|
||||
}
|
||||
set host(value) {
|
||||
// Setting this value is useful for testing purposes.
|
||||
this.#host = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets namespace to the `api.namespace` string in the application's config,
|
||||
* if set.
|
||||
* @type {string}
|
||||
*/
|
||||
namespace = get(config, 'api.namespace');
|
||||
get namespace() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return this.#namespace || get(config, 'api.namespace');
|
||||
}
|
||||
set namespace(value) {
|
||||
// Setting this value is useful for testing purposes.
|
||||
this.#namespace = value;
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import BaseModel from '../../models/base';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
/**
|
||||
* A managed group is a resource that represents a collection of accounts.
|
||||
*/
|
||||
export default class GeneratedManagedGroupModel extends BaseModel {
|
||||
// =attributes
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional name for identification purposes.',
|
||||
})
|
||||
name;
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional user-set description for identification purposes.',
|
||||
})
|
||||
description;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was created',
|
||||
readOnly: true,
|
||||
})
|
||||
created_time;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was last updated.',
|
||||
readOnly: true,
|
||||
})
|
||||
updated_time;
|
||||
|
||||
@attr('number', {
|
||||
description: 'Current version number of this resource.',
|
||||
})
|
||||
version;
|
||||
|
||||
@attr('string', {
|
||||
description: 'The type of this ManagedGroup.',
|
||||
})
|
||||
type;
|
||||
|
||||
@attr('string', {
|
||||
description:
|
||||
'The ID of the Auth Method that is associated with this ManagedGroup',
|
||||
})
|
||||
auth_method_id;
|
||||
}
|
||||
@@ -47,6 +47,10 @@ export default class BaseModel extends Model {
|
||||
})
|
||||
authorized_collection_actions;
|
||||
|
||||
get scopeModel() {
|
||||
const id = this.scopeID;
|
||||
return this.store.peekRecord('scope', id);
|
||||
}
|
||||
set scopeModel(model) {
|
||||
if (model) {
|
||||
const json = model.serialize();
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
export default class FragmentHostSetModel extends Fragment {
|
||||
export default class FragmentHostSourceModel extends Fragment {
|
||||
// =attributes
|
||||
|
||||
@attr('string', {
|
||||
description: 'ID of the related host set',
|
||||
description: 'ID of the related host source',
|
||||
})
|
||||
host_set_id;
|
||||
host_source_id;
|
||||
|
||||
@attr('string', {
|
||||
description: 'ID of the catalog to which the host set belongs',
|
||||
@@ -0,0 +1,12 @@
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
export default class FragmentManagedGroupAttributesModel extends Fragment {
|
||||
// =attributes (OIDC)
|
||||
|
||||
@attr('string', {
|
||||
description:
|
||||
'The boolean expression filter to use to determine membership.',
|
||||
})
|
||||
filter;
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
import GeneratedHostCatalogModel from '../generated/models/host-catalog';
|
||||
|
||||
export default class HostCatalogModel extends GeneratedHostCatalogModel {}
|
||||
export default class HostCatalogModel extends GeneratedHostCatalogModel {
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isStatic() {
|
||||
return this.type === 'static';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import GeneratedManagedGroupModel from '../generated/models/managed-group';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragment } from 'ember-data-model-fragments/attributes';
|
||||
import { A } from '@ember/array';
|
||||
|
||||
export default class ManagedGroupModel extends GeneratedManagedGroupModel {
|
||||
// =attributes
|
||||
|
||||
// =error attributes
|
||||
// These attributes exist solely to capture errors on nested fields.
|
||||
// See the application adapter's error normalization method for
|
||||
// more information.
|
||||
|
||||
@attr('string', { readOnly: true }) attributes_filter;
|
||||
|
||||
/**
|
||||
* Members is read-only under normal circumstances. But members can
|
||||
* be persisted via calls to `addMembers()` or `removeMembers()`.
|
||||
*/
|
||||
@attr({
|
||||
readOnly: true,
|
||||
defaultValue: () => A(),
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
member_ids;
|
||||
|
||||
@fragment('fragment-managed-group-attributes', { defaultValue: {} })
|
||||
attributes;
|
||||
}
|
||||
@@ -12,23 +12,23 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
@fragment('fragment-target-attributes', { defaultValue: {} }) attributes;
|
||||
|
||||
/**
|
||||
* @type {[FragmentHostSetModel]}
|
||||
* @type {[FragmentHostSourceModel]}
|
||||
*/
|
||||
@fragmentArray('fragment-host-set', {
|
||||
@fragmentArray('fragment-host-source', {
|
||||
readOnly: true,
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
host_sets;
|
||||
host_sources;
|
||||
|
||||
/**
|
||||
* Credential library ids are read only and can be
|
||||
* persisted via a dedicated call to `addCredentialLibraries()`.
|
||||
* Credential source ids are read only and can be
|
||||
* persisted via a dedicated call to `addCredentialSources()`.
|
||||
*/
|
||||
@fragmentArray('fragment-string', {
|
||||
readOnly: true,
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
application_credential_library_ids;
|
||||
application_credential_source_ids;
|
||||
|
||||
/**
|
||||
* An array of resolved host set and host catalog instances. Model instances
|
||||
@@ -36,11 +36,11 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
* instances). Unresolvable instances are excluded from the array.
|
||||
* @type {[{model: HostSetModel, hostCatalog: HostCatalogModel}]}
|
||||
*/
|
||||
@computed('host_sets.[]', 'store')
|
||||
@computed('host_sources.[]', 'store')
|
||||
get hostSets() {
|
||||
return this.host_sets
|
||||
.map(({ host_set_id, host_catalog_id }) => ({
|
||||
model: this.store.peekRecord('host-set', host_set_id),
|
||||
return this.host_sources
|
||||
.map(({ host_source_id, host_catalog_id }) => ({
|
||||
model: this.store.peekRecord('host-set', host_source_id),
|
||||
hostCatalog: this.store.peekRecord('host-catalog', host_catalog_id),
|
||||
}))
|
||||
.filter((hostSetRef) => hostSetRef.model !== null);
|
||||
@@ -52,14 +52,11 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
* instances). Unresolvable instances are excluded from the array.
|
||||
* @type {[CredentialLibraryModel]}
|
||||
*/
|
||||
@computed('application_credential_library_ids.[]', 'store')
|
||||
@computed('application_credential_source_ids.[]', 'store')
|
||||
get credentialLibraries() {
|
||||
return this.application_credential_library_ids
|
||||
.map((credential_library_fragment) =>
|
||||
this.store.peekRecord(
|
||||
'credential-library',
|
||||
credential_library_fragment.value
|
||||
)
|
||||
return this.application_credential_source_ids
|
||||
.map((source) =>
|
||||
this.store.peekRecord('credential-library', source.value)
|
||||
)
|
||||
.filter(Boolean);
|
||||
}
|
||||
@@ -96,16 +93,16 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Adds host sets via the `add-host-sets` method.
|
||||
* Adds host sets via the `add-host-sources` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} hostSetIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
addHostSets(hostSetIDs, options = { adapterOptions: {} }) {
|
||||
addHostSources(hostSetIDs, options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'add-host-sets',
|
||||
method: 'add-host-sources',
|
||||
hostSetIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
@@ -119,16 +116,16 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete host sets via the `remove-host-sets` method.
|
||||
* Delete host sets via the `remove-host-sources` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} hostSetIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeHostSets(hostSetIDs, options = { adapterOptions: {} }) {
|
||||
removeHostSources(hostSetIDs, options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'remove-host-sets',
|
||||
method: 'remove-host-sources',
|
||||
hostSetIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
@@ -142,29 +139,26 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single host set via the `remove-host-sets` method.
|
||||
* Delete a single host set via the `remove-host-sources` method.
|
||||
* @param {number} hostSetID
|
||||
* @param {object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeHostSet(hostSetID, options) {
|
||||
return this.removeHostSets([hostSetID], options);
|
||||
removeHostSource(hostSetID, options) {
|
||||
return this.removeHostSources([hostSetID], options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds credential libraries via the `add-credential-libraries` method.
|
||||
* Adds credential libraries via the `add-credential-sources` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
addCredentialLibraries(
|
||||
credentialLibraryIDs,
|
||||
options = { adapterOptions: {} }
|
||||
) {
|
||||
addCredentialSources(credentialLibraryIDs, options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'add-credential-libraries',
|
||||
method: 'add-credential-sources',
|
||||
credentialLibraryIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
@@ -178,19 +172,19 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete credential libraries via the `remove-credential-libraries` method.
|
||||
* Delete credential libraries via the `remove-credential-sources` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeCredentialLibraries(
|
||||
removeCredentialSources(
|
||||
credentialLibraryIDs,
|
||||
options = { adapterOptions: {} }
|
||||
) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'remove-credential-libraries',
|
||||
method: 'remove-credential-sources',
|
||||
credentialLibraryIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
@@ -204,12 +198,12 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single credential library set via the `remove-credential-libraries` method.
|
||||
* Delete a single credential library set via the `remove-credential-sources` method.
|
||||
* @param {number} credentialLibraryID
|
||||
* @param {object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeCredentialLibrary(credentialLibraryID, options) {
|
||||
return this.removeCredentialLibraries([credentialLibraryID], options);
|
||||
removeCredentialSource(credentialLibraryID, options) {
|
||||
return this.removeCredentialSources([credentialLibraryID], options);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import GeneratedUserModel from '../generated/models/user';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { A } from '@ember/array';
|
||||
|
||||
export default class UserModel extends GeneratedUserModel {
|
||||
@@ -16,6 +17,35 @@ export default class UserModel extends GeneratedUserModel {
|
||||
})
|
||||
account_ids;
|
||||
|
||||
/**
|
||||
* Represents the user's primary account email, if any.
|
||||
* @type {string}
|
||||
*/
|
||||
@attr('string', { readOnly: true }) email;
|
||||
|
||||
/**
|
||||
* Represents the user's primary account full_name, if any.
|
||||
* @type {string}
|
||||
*/
|
||||
@attr('string', { readOnly: true }) full_name;
|
||||
|
||||
/**
|
||||
* Represents the user's primary account login_name, if any.
|
||||
* @type {string}
|
||||
*/
|
||||
@attr('string', { readOnly: true }) login_name;
|
||||
|
||||
/**
|
||||
* Convenience for getting a reasonable string that names the account.
|
||||
* There are many options, so we fallback in order of precedence.
|
||||
* @type {string}
|
||||
*/
|
||||
@computed('email,full_name,login_name,displayName')
|
||||
get accountName() {
|
||||
const { email, full_name, login_name } = this;
|
||||
return email || full_name || login_name;
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentHostSetSerializer extends JSONSerializer {
|
||||
export default class FragmentHostSourceSerializer extends JSONSerializer {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentHostSetSerializer extends JSONSerializer {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Renames `id` to `host_set_id` because fragments may not have IDs.
|
||||
* Otherwise normalizes as normal.
|
||||
* @param {ModelClass} typeClass
|
||||
* @param {object} resourceHash
|
||||
* @return {object}
|
||||
*/
|
||||
normalize(typeClass, { id: host_set_id, ...obj }) {
|
||||
return super.normalize(typeClass, { host_set_id, ...obj });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentHostSourceSerializer extends JSONSerializer {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Renames `id` to `host_source_id` because fragments may not have IDs.
|
||||
* Otherwise normalizes as normal.
|
||||
* @param {ModelClass} typeClass
|
||||
* @param {object} resourceHash
|
||||
* @return {object}
|
||||
*/
|
||||
normalize(typeClass, { id: host_source_id, ...obj }) {
|
||||
return super.normalize(typeClass, { host_source_id, ...obj });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentManagedGroupAttributesSerializer extends JSONSerializer {
|
||||
/**
|
||||
* If an attribute is annotated as readOnly in the model, don't serialize it.
|
||||
* Otherwise delegate to default attribute serializer.
|
||||
* @override
|
||||
* @method serializeAttribute
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {Object} json
|
||||
* @param {String} key
|
||||
* @param {Object} attribute
|
||||
*/
|
||||
serializeAttribute(snapshot, json, key, attribute) {
|
||||
const { type, options } = attribute;
|
||||
let value = super.serializeAttribute(...arguments);
|
||||
// Convert empty string to null.
|
||||
if (type === 'string' && json[key] === '') json[key] = null;
|
||||
// Do not serialize read-only attributes.
|
||||
if (options.readOnly) delete json[key];
|
||||
// Version is sent only if it has a non-nullish value
|
||||
if (key === 'version') {
|
||||
if (json[key] === null || json[key] === undefined) delete json[key];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default class ManagedGroupSerializer extends ApplicationSerializer {
|
||||
// =properties
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
serializeScopeID = false;
|
||||
}
|
||||
@@ -17,43 +17,44 @@ export default class TargetSerializer extends ApplicationSerializer {
|
||||
*/
|
||||
serialize(snapshot) {
|
||||
let serialized = super.serialize(...arguments);
|
||||
const hostSetIDs = snapshot?.adapterOptions?.hostSetIDs;
|
||||
if (hostSetIDs)
|
||||
serialized = this.serializeWithHostSets(snapshot, hostSetIDs);
|
||||
const credentialLibraryIDs = snapshot?.adapterOptions?.credentialLibraryIDs;
|
||||
if (credentialLibraryIDs)
|
||||
serialized = this.serializeWithCredentialLibraries(
|
||||
const hostSourceIDs = snapshot?.adapterOptions?.hostSetIDs;
|
||||
if (hostSourceIDs) {
|
||||
serialized = this.serializeWithHostSources(snapshot, hostSourceIDs);
|
||||
}
|
||||
const credentialSourceIDs = snapshot?.adapterOptions?.credentialLibraryIDs;
|
||||
if (credentialSourceIDs)
|
||||
serialized = this.serializeWithCredentialSources(
|
||||
snapshot,
|
||||
credentialLibraryIDs
|
||||
credentialSourceIDs
|
||||
);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a payload containing only the host_set_ids array using IDs
|
||||
* passed into the function (rather than existing host sets on the model).
|
||||
* Returns a payload containing only version and an array of passed IDs,
|
||||
* rather than existing instances on the model.
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {[string]} hostSetIDs
|
||||
* @return {object}
|
||||
*/
|
||||
serializeWithHostSets(snapshot, hostSetIDs) {
|
||||
serializeWithHostSources(snapshot, hostSourceIDs) {
|
||||
return {
|
||||
version: snapshot.attr('version'),
|
||||
host_set_ids: hostSetIDs,
|
||||
host_source_ids: hostSourceIDs,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a payload containing only the credential_library_ids array using IDs
|
||||
* passed into the function (rather than existing credential libraries on the model).
|
||||
* Returns a payload containing only version and an array of passed IDs,
|
||||
* rather than existing instances on the model.
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @param {[string]} credentialSourceIDs
|
||||
* @return {object}
|
||||
*/
|
||||
serializeWithCredentialLibraries(snapshot, credentialLibraryIDs) {
|
||||
serializeWithCredentialSources(snapshot, credentialSourceIDs) {
|
||||
return {
|
||||
version: snapshot.attr('version'),
|
||||
application_credential_library_ids: credentialLibraryIDs,
|
||||
application_credential_source_ids: credentialSourceIDs,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import Service from '@ember/service';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Takes a POJO representing a query and converts it to a boolean expression
|
||||
* string which is understood by the API.
|
||||
*
|
||||
* @example
|
||||
* const filter = new ResourceFilter({
|
||||
* type: 'oidc',
|
||||
* status: ['active', 'pending'],
|
||||
* authorized_actions: [{ contains: 'read' }]
|
||||
* });
|
||||
* log(filter.queryExpression);
|
||||
* // ("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending") and ("read" in "/item/authorized_actions")
|
||||
*/
|
||||
export class ResourceFilter {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {?object}
|
||||
*/
|
||||
#filterObject;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get queryExpression() {
|
||||
const clauses = Object.keys(this.#filterObject)
|
||||
.map((key) => {
|
||||
const value = this.#filterObject[key];
|
||||
const valuesArray = Array.isArray(value) ? value : [value];
|
||||
return this.parenthetical(
|
||||
this.or(
|
||||
valuesArray.map((value) => {
|
||||
if (value && value.contains) {
|
||||
return this.in(value.contains, key);
|
||||
}
|
||||
return this.equals(key, value);
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.flat()
|
||||
.filter((item) => item);
|
||||
return this.and(clauses);
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* @param {object} filterObject
|
||||
*/
|
||||
constructor(filterObject = {}) {
|
||||
this.#filterObject = Object.assign({}, filterObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param value
|
||||
* @return {string}
|
||||
*/
|
||||
equals(key, value) {
|
||||
return value !== null ? `"/item/${key}" == "${value}"` : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param value
|
||||
* @return {string}
|
||||
*/
|
||||
in(value, key) {
|
||||
return value !== null ? `"${value}" in "/item/${key}"` : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} clauses
|
||||
* @return {string}
|
||||
*/
|
||||
and(clauses) {
|
||||
return clauses.join(' and ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} clauses
|
||||
* @return {string}
|
||||
*/
|
||||
or(clauses) {
|
||||
return clauses.join(' or ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @return {string}
|
||||
*/
|
||||
parenthetical(expression) {
|
||||
return expression ? `(${expression})` : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default class ResourceFilterStoreService extends Service {
|
||||
// =services
|
||||
|
||||
@service store;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Similar to Ember's built-in `store.query`, except this method accepts
|
||||
* an additional argument `filterObject` representing a query filter
|
||||
* (see ResourceFilter class above).
|
||||
* @param {string} modelName
|
||||
* @param {object} filterObject
|
||||
* @param {?object} storeQuery
|
||||
* @return {Promise}
|
||||
*/
|
||||
queryBy(modelName, filterObject = {}, storeQuery = {}) {
|
||||
const filter = new ResourceFilter(filterObject);
|
||||
const query = Object.assign({}, storeQuery);
|
||||
// Apply filters to store query when available
|
||||
if (filter.queryExpression) query.filter = filter.queryExpression;
|
||||
return this.store.query(modelName, query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/account';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/group';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/host-set';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/role';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/target';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/user';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from 'api/models/fragment-host-set';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/fragment-host-source';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/fragment-managed-group-attributes';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/managed-group';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from 'api/serializers/fragment-host-set';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/fragment-host-source';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/fragment-managed-group-attributes';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/managed-group';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/services/resource-filter-store';
|
||||
+62
-31
@@ -1,12 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
import config from '../config/environment';
|
||||
import { authHandler, deauthHandler } from './route-handlers/auth';
|
||||
import { pickRandomStatusString } from './factories/session';
|
||||
import { Response } from 'miragejs';
|
||||
import initializeMockIPC from './scenarios/ipc';
|
||||
import makeBooleanFilter from './helpers/bexpr-filter';
|
||||
|
||||
const isTesting = config.environment === 'test';
|
||||
|
||||
export default function () {
|
||||
initializeMockIPC(this);
|
||||
initializeMockIPC(this, config);
|
||||
|
||||
// make this `http://localhost:8080`, for example, if your API is on a different server
|
||||
// this.urlPrefix = '';
|
||||
@@ -24,16 +26,18 @@ export default function () {
|
||||
|
||||
this.get(
|
||||
'/scopes',
|
||||
({ scopes }, { queryParams: { scope_id, recursive } }) => {
|
||||
({ scopes }, { queryParams: { scope_id, recursive, filter } }) => {
|
||||
let resultSet;
|
||||
// Default parent scope is global
|
||||
if (!scope_id) scope_id = 'global';
|
||||
if (recursive && scope_id === 'global') {
|
||||
return scopes.all();
|
||||
resultSet = scopes.all();
|
||||
} else {
|
||||
return scopes.where((scope) => {
|
||||
resultSet = scopes.where((scope) => {
|
||||
return scope.scope ? scope.scope.id === scope_id : false;
|
||||
});
|
||||
}
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.post('/scopes', function ({ scopes }) {
|
||||
@@ -53,8 +57,9 @@ export default function () {
|
||||
|
||||
this.get(
|
||||
'/auth-methods',
|
||||
({ authMethods }, { queryParams: { scope_id: scopeId } }) => {
|
||||
return authMethods.where({ scopeId });
|
||||
({ authMethods }, { queryParams: { scope_id: scopeId, filter } }) => {
|
||||
const resultSet = authMethods.where({ scopeId });
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.post('/auth-methods');
|
||||
@@ -75,8 +80,17 @@ export default function () {
|
||||
// Auth Method Accounts
|
||||
this.get(
|
||||
'/accounts',
|
||||
({ accounts }, { queryParams: { auth_method_id: authMethodId } }) => {
|
||||
return accounts.where({ authMethodId });
|
||||
(
|
||||
{ accounts },
|
||||
{ queryParams: { auth_method_id: authMethodId, filter } }
|
||||
) => {
|
||||
let resultSet;
|
||||
if (authMethodId) {
|
||||
resultSet = accounts.where({ authMethodId });
|
||||
} else {
|
||||
resultSet = accounts.all();
|
||||
}
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.post('/accounts');
|
||||
@@ -335,19 +349,22 @@ export default function () {
|
||||
|
||||
this.get(
|
||||
'/targets',
|
||||
function ({ targets }, { queryParams: { scope_id, recursive } }) {
|
||||
function ({ targets }, { queryParams: { scope_id, recursive, filter } }) {
|
||||
let resultSet;
|
||||
if (recursive && scope_id === 'global') {
|
||||
return targets.all();
|
||||
resultSet = targets.all();
|
||||
} else if (recursive) {
|
||||
return targets.where((target) => {
|
||||
resultSet = targets.where((target) => {
|
||||
const targetModel = targets.find(target.id);
|
||||
return (
|
||||
target.scopeId === scope_id ||
|
||||
targetModel?.scope?.scope?.id === scope_id
|
||||
);
|
||||
});
|
||||
} else {
|
||||
resultSet = targets.where((target) => target.scopeId === scope_id);
|
||||
}
|
||||
return targets.where((target) => target.scopeId === scope_id);
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.post('/targets', function ({ targets }) {
|
||||
@@ -367,37 +384,37 @@ export default function () {
|
||||
const updatedAttrs = {
|
||||
version: attrs.version,
|
||||
};
|
||||
// If adding host sets, push them into the array
|
||||
if (method === 'add-host-sets') {
|
||||
// If adding host sources, push them into the array
|
||||
if (method === 'add-host-sources') {
|
||||
updatedAttrs.hostSetIds = target.hostSetIds;
|
||||
attrs.hostSetIds.forEach((id) => {
|
||||
attrs.hostSourceIds.forEach((id) => {
|
||||
if (!updatedAttrs.hostSetIds.includes(id)) {
|
||||
updatedAttrs.hostSetIds.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
// If deleting host sets, filter them out of the array
|
||||
if (method === 'remove-host-sets') {
|
||||
// If deleting host sources, filter them out of the array
|
||||
if (method === 'remove-host-sources') {
|
||||
updatedAttrs.hostSetIds = target.hostSetIds;
|
||||
updatedAttrs.hostSetIds = updatedAttrs.hostSetIds.filter((id) => {
|
||||
return !attrs.hostSetIds.includes(id);
|
||||
return !attrs.hostSourceIds.includes(id);
|
||||
});
|
||||
}
|
||||
// If adding credential libraries, push them into the array
|
||||
if (method === 'add-credential-libraries') {
|
||||
// If adding credential sources, push them into the array
|
||||
if (method === 'add-credential-sources') {
|
||||
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
|
||||
attrs.applicationCredentialLibraryIds.forEach((id) => {
|
||||
attrs.applicationCredentialSourceIds.forEach((id) => {
|
||||
if (!updatedAttrs.credentialLibraryIds.includes(id)) {
|
||||
updatedAttrs.credentialLibraryIds.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
// If deleting credential libraries, filter them out of the array
|
||||
if (method === 'remove-credential-libraries') {
|
||||
// If deleting credential sources, filter them out of the array
|
||||
if (method === 'remove-credential-sources') {
|
||||
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
|
||||
updatedAttrs.credentialLibraryIds =
|
||||
updatedAttrs.credentialLibraryIds.filter((id) => {
|
||||
return !attrs.applicationCredentialLibraryIds.includes(id);
|
||||
return !attrs.applicationCredentialSourceIds.includes(id);
|
||||
});
|
||||
}
|
||||
return target.update(updatedAttrs);
|
||||
@@ -408,14 +425,15 @@ export default function () {
|
||||
|
||||
this.get(
|
||||
'/sessions',
|
||||
function ({ sessions }, { queryParams: { scope_id, recursive } }) {
|
||||
function ({ sessions }, { queryParams: { scope_id, recursive, filter } }) {
|
||||
let resultSet;
|
||||
// To simulate changes to `session.status` that may occur in the backend,
|
||||
// we quietly randomize the value of the field on GET.
|
||||
// To populate sessions for logged in user,
|
||||
// update alternate sessions to auth user.
|
||||
// But only if not in testing mode.
|
||||
// In tests, we need deterministic statuses.
|
||||
if (!Ember.testing) {
|
||||
if (!isTesting) {
|
||||
sessions.all().models.forEach((session) => {
|
||||
session.update({
|
||||
status: pickRandomStatusString(),
|
||||
@@ -427,22 +445,24 @@ export default function () {
|
||||
});
|
||||
}
|
||||
if (recursive && scope_id === 'global') {
|
||||
return sessions.all();
|
||||
resultSet = sessions.all();
|
||||
} else if (recursive) {
|
||||
return sessions.where((session) => {
|
||||
resultSet = sessions.where((session) => {
|
||||
const sessionModel = sessions.find(session.id);
|
||||
return (
|
||||
session.scopeId === scope_id ||
|
||||
sessionModel?.scope?.scope?.id === scope_id
|
||||
);
|
||||
});
|
||||
} else {
|
||||
resultSet = sessions.where((session) => session.scopeId === scope_id);
|
||||
}
|
||||
return sessions.where((session) => session.scopeId === scope_id);
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.get('/sessions/:id', function ({ sessions }, { params: { id } }) {
|
||||
const session = sessions.find(id);
|
||||
if (!Ember.testing) {
|
||||
if (!isTesting) {
|
||||
session.update({
|
||||
status: pickRandomStatusString(),
|
||||
});
|
||||
@@ -509,6 +529,17 @@ export default function () {
|
||||
this.del('/credential-libraries/:id');
|
||||
this.patch('/credential-libraries/:id');
|
||||
|
||||
// managed-groups
|
||||
this.get(
|
||||
'/managed-groups',
|
||||
({ managedGroups }, { queryParams: { auth_method_id: authMethodId } }) => {
|
||||
return managedGroups.where({ authMethodId });
|
||||
}
|
||||
);
|
||||
this.get('/managed-groups/:id');
|
||||
this.del('/managed-groups/:id');
|
||||
this.post('/managed-groups');
|
||||
|
||||
/* Uncomment the following line and the Response import above
|
||||
* Then change the response code to simulate error responses.
|
||||
* this.get('/scopes', () => new Response(505));
|
||||
|
||||
@@ -9,6 +9,7 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'set-password',
|
||||
],
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
|
||||
@@ -15,6 +15,7 @@ export default factory.extend({
|
||||
authorized_collection_actions: () => {
|
||||
return {
|
||||
accounts: ['create', 'list'],
|
||||
'managed-groups': ['create', 'list'],
|
||||
};
|
||||
},
|
||||
id: (i) => `auth-method-id-${i}`,
|
||||
@@ -23,15 +24,30 @@ export default factory.extend({
|
||||
type: (i) => types[i % types.length],
|
||||
|
||||
/**
|
||||
* Adds accounts (with associated users) to auth method.
|
||||
* Adds accounts (with associated users) to auth method and managed groups.
|
||||
*/
|
||||
withAccountsAndUsers: trait({
|
||||
withAccountsAndUsersAndManagedGroups: trait({
|
||||
afterCreate(authMethod, server) {
|
||||
const { scope, type } = authMethod;
|
||||
|
||||
server.createList('user', 5, { scope }).map((user) => {
|
||||
const { id } = server.create('account', { scope, type, authMethod });
|
||||
user.update({ accountIds: [id] });
|
||||
});
|
||||
|
||||
if (type === 'oidc') {
|
||||
server
|
||||
.createList('managed-group', 2, { scope, authMethod })
|
||||
.map((managedGroup) => {
|
||||
const accounts = server.createList('account', 2, {
|
||||
scope,
|
||||
type,
|
||||
authMethod,
|
||||
});
|
||||
const accountIds = accounts.map((account) => account.id);
|
||||
managedGroup.update({ memberIds: accountIds });
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -9,6 +9,8 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-members',
|
||||
'remove-members',
|
||||
],
|
||||
id: (i) => `group-${i}`,
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ export default factory.extend({
|
||||
},
|
||||
withChildren: trait({
|
||||
afterCreate(hostCatalog, server) {
|
||||
const { scope } = hostCatalog;
|
||||
const hosts = server.createList('host', 10, { scope, hostCatalog });
|
||||
server.createList('host-set', 3, { scope, hostCatalog, hosts });
|
||||
const { scope, type } = hostCatalog;
|
||||
const hosts = server.createList('host', 10, { scope, hostCatalog, type });
|
||||
server.createList('host-set', 3, { scope, hostCatalog, hosts, type });
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -8,6 +8,8 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-hosts',
|
||||
'remove-hosts',
|
||||
],
|
||||
id: (i) => `host-set-id-${i}`,
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import factory from '../generated/factories/managed-group';
|
||||
import { random } from 'faker';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
const types = ['oidc'];
|
||||
|
||||
export default factory.extend({
|
||||
id: (i) => `managed-groups-id-${i}`,
|
||||
type: (i) => types[i % types.length],
|
||||
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('managed-group') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
case 'oidc':
|
||||
return {
|
||||
filter: random.words(),
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -9,6 +9,9 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'set-grants',
|
||||
'add-principals',
|
||||
'remove-principals',
|
||||
],
|
||||
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
|
||||
grant_strings: () => [
|
||||
|
||||
@@ -13,7 +13,13 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'authorize-session',
|
||||
'add-host-sources',
|
||||
'remove-host-sources',
|
||||
'add-credential-sources',
|
||||
'remove-credential-sources',
|
||||
],
|
||||
|
||||
/**
|
||||
* -1 means "unlimited" and we want to generate these on occasion.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,8 @@ export default factory.extend({
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-accounts',
|
||||
'remove-accounts',
|
||||
],
|
||||
id: (i) => `user-${i}`,
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { random, date, datatype } from 'faker';
|
||||
* GeneratedHostCatalogModelFactory
|
||||
*/
|
||||
export default Factory.extend({
|
||||
type: 'static',
|
||||
type: () => random.arrayElement(['static', 'plugin']),
|
||||
name: () => random.words(),
|
||||
description: () => random.words(),
|
||||
created_time: () => date.recent(),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
import { random, date, datatype } from 'faker';
|
||||
|
||||
/**
|
||||
* GeneratedManagedGroupFactory
|
||||
* A managed group is a resource that represents a collection of accounts.
|
||||
*/
|
||||
export default Factory.extend({
|
||||
name: () => random.words(),
|
||||
description: () => random.words(),
|
||||
created_time: () => date.recent(),
|
||||
updated_time: () => date.recent(),
|
||||
version: () => datatype.number(),
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
import { random, date, datatype } from 'faker';
|
||||
import { random, date, datatype, internet } from 'faker';
|
||||
|
||||
/**
|
||||
* GeneratedUserModelFactory
|
||||
@@ -11,4 +11,5 @@ export default Factory.extend({
|
||||
created_time: () => date.recent(),
|
||||
updated_time: () => date.recent(),
|
||||
disabled: () => datatype.boolean(),
|
||||
email: () => internet.email().toLowerCase(),
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { makeEvaluator } from 'js-bexpr';
|
||||
import { underscore } from '@ember/string';
|
||||
|
||||
/**
|
||||
* Because Mirage, a record cannot be serialized outside of a route handler
|
||||
* context. Here is a quick and dirty attempt to serialize attribute names
|
||||
* to underscore format. This is not entirely realistic, but may suffice.
|
||||
*/
|
||||
const quickSerialize = (item) => {
|
||||
const json = item.toJSON();
|
||||
Object.keys(json).forEach((key) => {
|
||||
const newKey = underscore(key);
|
||||
json[newKey] = json[key];
|
||||
});
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a filter function that operates on a single object, which itself
|
||||
* returns the result of evaluating the expression against the object.
|
||||
*/
|
||||
export default (expression) => {
|
||||
if (expression) {
|
||||
const evaluator = makeEvaluator(expression);
|
||||
// Since API query selectors begin with `/item`, the instance is wrapped.
|
||||
return (record) => {
|
||||
const item = quickSerialize(record);
|
||||
return evaluator({ item });
|
||||
};
|
||||
} else {
|
||||
// If no expression was passed, always return true,
|
||||
// thus filtering out none.
|
||||
return () => true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
scope: belongsTo(),
|
||||
authMethod: belongsTo(),
|
||||
});
|
||||
@@ -44,7 +44,7 @@ const commandHandlers = {
|
||||
id: 'token123',
|
||||
token: 'thetokenstring',
|
||||
account_id: '1',
|
||||
user_id: 'user123',
|
||||
user_id: 'authenticateduser',
|
||||
auth_method_id: 'authmethod123',
|
||||
created_time: '',
|
||||
updated_time: '',
|
||||
@@ -93,7 +93,7 @@ const commandHandlers = {
|
||||
id: 'token123',
|
||||
token: 'thetokenstring',
|
||||
account_id: '1',
|
||||
user_id: 'user123',
|
||||
user_id: 'authenticateduser',
|
||||
auth_method_id: 'authmethod123',
|
||||
created_time: '',
|
||||
updated_time: '',
|
||||
|
||||
@@ -24,13 +24,13 @@ export default function (server) {
|
||||
'auth-method',
|
||||
1,
|
||||
{ scope: globalScope },
|
||||
'withAccountsAndUsers'
|
||||
'withAccountsAndUsersAndManagedGroups'
|
||||
);
|
||||
const orgAuthMethods = server.createList(
|
||||
'auth-method',
|
||||
3,
|
||||
{ scope: orgScope },
|
||||
'withAccountsAndUsers'
|
||||
'withAccountsAndUsersAndManagedGroups'
|
||||
);
|
||||
// Assign primary auth methods per scope
|
||||
// TODO make this generic
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import config from 'ember-get-config';
|
||||
import { datatype } from 'faker';
|
||||
|
||||
export default function initializeMockIPC(server) {
|
||||
export default function initializeMockIPC(server, config) {
|
||||
const isTesting = config.environment === 'test';
|
||||
|
||||
/**
|
||||
* We strive to make this application runnable in a regular web browser, since
|
||||
* it is a convenient environment for development and testing. But only an
|
||||
@@ -120,17 +120,17 @@ export default function initializeMockIPC(server) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for browsers running on mac OS
|
||||
* Check for window chrome on MacOS
|
||||
*/
|
||||
isMacOS() {
|
||||
return Boolean(window.navigator.userAgent.match(/(macintosh)/i));
|
||||
hasMacOSChrome() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for browsers running on windows OS
|
||||
* Check for OS chrome state
|
||||
*/
|
||||
isWindowsOS() {
|
||||
return Boolean(window.navigator.userAgent.match(/(windows)/i));
|
||||
showWindowActions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ export default function initializeMockIPC(server) {
|
||||
*
|
||||
* Initializes mock IPC only in a non-Electron non-testing context.
|
||||
*/
|
||||
if (!config.isElectron && !Ember.testing) {
|
||||
if (!config.isElectron && !isTesting) {
|
||||
const mockIPC = new MockIPC();
|
||||
window.addEventListener('message', async function (event) {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
modelName: 'managed-group',
|
||||
|
||||
_hashForModel(model) {
|
||||
const json = ApplicationSerializer.prototype._hashForModel.apply(
|
||||
this,
|
||||
arguments
|
||||
);
|
||||
json.auth_method_id = model.authMethodId;
|
||||
return json;
|
||||
},
|
||||
});
|
||||
@@ -17,14 +17,14 @@ export default ApplicationSerializer.extend({
|
||||
);
|
||||
const { hostSets } = this.schema;
|
||||
if (model.hostSetIds?.length) {
|
||||
json.host_sets = model.hostSetIds.map((host_set_id) => {
|
||||
json.host_sources = model.hostSetIds.map((host_set_id) => {
|
||||
const hostSet = hostSets.find(host_set_id);
|
||||
const host_catalog_id = hostSet?.hostCatalog?.id;
|
||||
return { id: host_set_id, host_catalog_id };
|
||||
});
|
||||
}
|
||||
if (model.credentialLibraryIds?.length) {
|
||||
json.application_credential_library_ids = model.credentialLibraryIds;
|
||||
json.application_credential_source_ids = model.credentialLibraryIds;
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
+28
-27
@@ -25,54 +25,55 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-can": "^3.1.0",
|
||||
"ember-cli-babel": "^7.23.0",
|
||||
"ember-cli-htmlbars": "^5.3.1",
|
||||
"ember-cli-mirage": "^1.1.8",
|
||||
"ember-cli-babel": "^7.26.6",
|
||||
"ember-cli-htmlbars": "^5.7.1",
|
||||
"ember-cli-mirage": "^2.2.0",
|
||||
"ember-data": "^3.20.0",
|
||||
"ember-data-model-fragments": "5.0.0-beta.2",
|
||||
"ember-fetch": "^8.1.0"
|
||||
"ember-fetch": "^8.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
|
||||
"@babel/core": "^7.15.8",
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"@ember/test-helpers": "^2.4.2",
|
||||
"@glimmer/component": "^1.0.4",
|
||||
"@glimmer/tracking": "^1.0.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.28.0",
|
||||
"doctoc": "^2.0.1",
|
||||
"ember-auto-import": "^1.12.0",
|
||||
"ember-cli": "~3.28.3",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
"ember-cli-update": "^0.54.6",
|
||||
"ember-copy": "^1.0.0",
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-copy": "^2.0.1",
|
||||
"ember-disable-prototype-extensions": "^1.1.3",
|
||||
"ember-export-application-global": "^2.0.1",
|
||||
"ember-get-config": "^0.3.0",
|
||||
"ember-load-initializers": "^2.1.2",
|
||||
"ember-maybe-import-regenerator": "^0.1.6",
|
||||
"ember-page-title": "^6.0.3",
|
||||
"ember-qunit": "^5.1.1",
|
||||
"ember-resolver": "^8.0.2",
|
||||
"ember-source": "~3.24.0",
|
||||
"ember-maybe-import-regenerator": "^1.0.0",
|
||||
"ember-page-title": "^6.2.2",
|
||||
"ember-qunit": "^5.1.4",
|
||||
"ember-resolver": "^8.0.3",
|
||||
"ember-source": "~3.28.4",
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-template-lint": "^3.7.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-ember": "^10.5.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-qunit": "^6.2.0",
|
||||
"loader.js": "^4.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.2.1",
|
||||
"qunit": "^2.13.0",
|
||||
"qunit-dom": "^1.6.0"
|
||||
"prettier": "^2.4.0",
|
||||
"qunit": "^2.17.1",
|
||||
"qunit-dom": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10 <=15"
|
||||
"node": ">=12 <=17"
|
||||
},
|
||||
"ember": {
|
||||
"edition": "octane"
|
||||
|
||||
@@ -6,13 +6,6 @@ const browsers = [
|
||||
'last 1 Safari versions',
|
||||
];
|
||||
|
||||
const isCI = Boolean(process.env.CI);
|
||||
const isProduction = process.env.EMBER_ENV === 'production';
|
||||
|
||||
if (isCI || isProduction) {
|
||||
browsers.push('ie 11');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
browsers,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Account', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given account may set password based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['set-password'],
|
||||
};
|
||||
assert.ok(service.can('setPassword target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('setPassword target', model));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Group', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given group resource may add members based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-members'],
|
||||
};
|
||||
assert.ok(service.can('addMembers group', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addMembers group', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given group resource may remove members based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-members'],
|
||||
};
|
||||
assert.ok(service.can('removeMembers group', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removeMembers group', model));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Host Set', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given host set resource may add hosts based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-hosts'],
|
||||
};
|
||||
assert.ok(service.can('addHosts hostSet', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addHosts hostSet', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given hostSet resource may remove hosts based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-hosts'],
|
||||
};
|
||||
assert.ok(service.can('removeHosts hostSet', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removeHosts hostSet', model));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Role', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given role may set grants based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['set-grants'],
|
||||
};
|
||||
assert.ok(service.can('setGrants role', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('setGrants role', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given role may add principals based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-principals'],
|
||||
};
|
||||
assert.ok(service.can('addPrincipals role', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addPrincipals role', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given role may remove principals based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-principals'],
|
||||
};
|
||||
assert.ok(service.can('removePrincipals role', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removePrincipals role', model));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Target', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given target may connect based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['authorize-session'],
|
||||
};
|
||||
assert.ok(service.can('connect target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('connect target', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given target may add host sources', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-host-sources'],
|
||||
};
|
||||
assert.ok(service.can('addHostSources target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addHostSources target', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given target may remove host sources', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-host-sources'],
|
||||
};
|
||||
assert.ok(service.can('removeHostSources target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removeHostSources target', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given target may add credential sources', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-credential-sources'],
|
||||
};
|
||||
assert.ok(service.can('addCredentialSources target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addCredentialSources target', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given target may remove crednetial sources', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-credential-sources'],
|
||||
};
|
||||
assert.ok(service.can('removeCredentialSources target', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removeCredentialSources target', model));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | User', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given user resource may add accounts based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['add-accounts'],
|
||||
};
|
||||
assert.ok(service.can('addAccounts user', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('addAccounts user', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given user resource may remove accounts based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['remove-accounts'],
|
||||
};
|
||||
assert.ok(service.can('removeAccounts user', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('removeAccounts user', model));
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import config from 'ember-get-config';
|
||||
import RESTAdapter from '@ember-data/adapter/rest';
|
||||
import { InvalidError } from '@ember-data/adapter/error';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
@@ -10,6 +9,12 @@ module('Unit | Adapter | application', function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let config;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
config = this.owner.resolveRegistration('config:environment');
|
||||
});
|
||||
|
||||
test('its namespace is equal to the configured namespace', function (assert) {
|
||||
assert.expect(2);
|
||||
const adapter = this.owner.lookup('adapter:application');
|
||||
|
||||
@@ -13,8 +13,8 @@ module('Unit | Model | base', function (hooks) {
|
||||
assert.ok(model);
|
||||
});
|
||||
|
||||
test('it may have a scope fragment', function (assert) {
|
||||
assert.expect(1);
|
||||
test('it may have a scope fragment (and thus a scopeModel)', function (assert) {
|
||||
assert.expect(2);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
@@ -37,6 +37,7 @@ module('Unit | Model | base', function (hooks) {
|
||||
const scope = store.peekRecord('scope', 'o_1');
|
||||
const model = store.peekRecord('user', '123abc');
|
||||
assert.equal(model.scope.scope_id, scope.id);
|
||||
assert.equal(model.scopeModel.id, scope.id);
|
||||
});
|
||||
|
||||
test('it may accept a `scopeModel` for convenience, instead of a fragment', function (assert) {
|
||||
@@ -47,7 +48,7 @@ module('Unit | Model | base', function (hooks) {
|
||||
assert.notEqual(model.scopeID, scope.id);
|
||||
model.scopeModel = scope;
|
||||
assert.equal(model.scopeID, scope.id);
|
||||
assert.equal(model.scope.isOrg, true);
|
||||
assert.true(model.scope.isOrg);
|
||||
});
|
||||
|
||||
test('it has a displayName attribute', function (assert) {
|
||||
@@ -78,18 +79,18 @@ module('Unit | Model | base', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('user', '1');
|
||||
assert.equal(model.canSave, false);
|
||||
assert.equal(model.cannotSave, true);
|
||||
assert.false(model.canSave);
|
||||
assert.true(model.cannotSave);
|
||||
// Should be able to save if dirty
|
||||
model.name = 'User';
|
||||
assert.equal(model.hasDirtyAttributes, true);
|
||||
assert.equal(model.canSave, true);
|
||||
assert.equal(model.cannotSave, false);
|
||||
assert.true(model.hasDirtyAttributes);
|
||||
assert.true(model.canSave);
|
||||
assert.false(model.cannotSave);
|
||||
// Should not be able to save while currently saving
|
||||
model.transitionTo('updated.inFlight');
|
||||
assert.equal(model.isSaving, true);
|
||||
assert.equal(model.canSave, false);
|
||||
assert.equal(model.cannotSave, true);
|
||||
assert.true(model.isSaving);
|
||||
assert.false(model.canSave);
|
||||
assert.true(model.cannotSave);
|
||||
model.transitionTo('loaded');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | fragment host source', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('fragment-host-source', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | fragment managed group attributes', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('fragment-managed-group-attributes', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -9,4 +9,14 @@ module('Unit | Model | host catalog', function (hooks) {
|
||||
let model = store.createRecord('host-catalog', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
|
||||
test('it has isStatic function and returns the expected values', async function (assert) {
|
||||
assert.expect(3);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const modelA = store.createRecord('host-catalog', { type: 'static' });
|
||||
const modelB = store.createRecord('host-catalog', { type: 'plugin' });
|
||||
assert.equal(typeof modelA.isStatic, 'boolean');
|
||||
assert.true(modelA.isStatic);
|
||||
assert.false(modelB.isStatic);
|
||||
});
|
||||
});
|
||||
@@ -68,7 +68,7 @@ module('Unit | Model | host set', function (hooks) {
|
||||
await model.addHost('foobar');
|
||||
});
|
||||
|
||||
test('it has a `removeHostSets` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
test('it has a `removeHosts` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post('/v1/host-sets/123abc:remove-hosts', (schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
|
||||
@@ -5,12 +5,14 @@ module('Unit | Model | host', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function (assert) {
|
||||
assert.expect(1);
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('host', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
|
||||
test('it contains attributes', function (assert) {
|
||||
assert.expect(1);
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('host', {
|
||||
type: 'static',
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | fragment host set', function (hooks) {
|
||||
module('Unit | Model | managed group', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('fragment-host-set', {});
|
||||
let model = store.createRecord('managed-group', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -14,18 +14,18 @@ module('Unit | Model | target', function (hooks) {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '2' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '2' },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
const target = store.peekRecord('target', '123abc');
|
||||
assert.equal(
|
||||
target.host_sets.length,
|
||||
target.host_sources.length,
|
||||
2,
|
||||
'Target has two entires in host_sets'
|
||||
'Target has two entires in host_sources'
|
||||
);
|
||||
assert.equal(
|
||||
target.hostSets.length,
|
||||
@@ -46,14 +46,14 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {},
|
||||
},
|
||||
});
|
||||
// Since `hostSets` is computed on `host_sets`, not the store itself,
|
||||
// Since `hostSets` is computed on `host_sources`, not the store itself,
|
||||
// it's necessary to do this assignment to kick-off the computed update.
|
||||
// eslint-disable-next-line no-self-assign
|
||||
target.host_sets = target.host_sets;
|
||||
target.host_sources = target.host_sources;
|
||||
assert.equal(
|
||||
target.host_sets.length,
|
||||
target.host_sources.length,
|
||||
2,
|
||||
'Target has two entires in host_sets'
|
||||
'Target has two entires in host_sources'
|
||||
);
|
||||
assert.equal(target.hostSets.length, 2, 'Target has two resolved hostSets');
|
||||
assert.notOk(
|
||||
@@ -68,52 +68,18 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-self-assign
|
||||
target.host_sets = target.host_sets;
|
||||
target.host_sources = target.host_sources;
|
||||
assert.ok(target.hostSets[0].hostCatalog, 'Host catalog is resolved');
|
||||
});
|
||||
|
||||
test('it has an `addHostSets` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post('/v1/targets/123abc:add-host-sets', (schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
host_set_ids: ['123_abc', 'foobar'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
});
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.addHostSets(['123_abc', 'foobar']);
|
||||
});
|
||||
|
||||
test('it has a `removeHostSets` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
test('it has an `addHostSources` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-host-sets',
|
||||
'/v1/targets/123abc:add-host-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
host_set_ids: ['1', '3'],
|
||||
host_source_ids: ['123_abc', 'foobar'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
@@ -127,9 +93,9 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
version: 1,
|
||||
scope: {
|
||||
@@ -140,17 +106,17 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeHostSets(['1', '3']);
|
||||
await model.addHostSources(['123_abc', 'foobar']);
|
||||
});
|
||||
|
||||
test('it has a `removeHostSet` method that deletes a single host set using `removeHostSets` method', async function (assert) {
|
||||
test('it has a `removeHostSources` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-host-sets',
|
||||
'/v1/targets/123abc:remove-host-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
host_set_ids: ['3'],
|
||||
host_source_ids: ['1', '3'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
@@ -164,9 +130,9 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
version: 1,
|
||||
scope: {
|
||||
@@ -177,7 +143,44 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeHostSet('3');
|
||||
await model.removeHostSources(['1', '3']);
|
||||
});
|
||||
|
||||
test('it has a `removeHostSource` method that deletes a single host set using `removeHostSources` method', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-host-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
host_source_ids: ['3'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeHostSource('3');
|
||||
});
|
||||
|
||||
test('it has a `sessions` array of session instances associated with the target (if those instances are already in the store)', function (assert) {
|
||||
@@ -232,15 +235,15 @@ module('Unit | Model | target', function (hooks) {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
},
|
||||
},
|
||||
});
|
||||
const target = store.peekRecord('target', '123abc');
|
||||
assert.equal(
|
||||
target.application_credential_library_ids.length,
|
||||
target.application_credential_source_ids.length,
|
||||
2,
|
||||
'Target has two entires in application_credential_library_ids'
|
||||
'Target has two entires in application_credential_source_ids'
|
||||
);
|
||||
assert.equal(
|
||||
target.credentialLibraries.length,
|
||||
@@ -261,17 +264,17 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {},
|
||||
},
|
||||
});
|
||||
// Since `credentialLibraries` is computed on `application_credential_library_ids`,
|
||||
// Since `credentialLibraries` is computed on `application_credential_source_ids`,
|
||||
// not the store itself, it's necessary to do this assignment to kick-off the
|
||||
// computed update.
|
||||
/* eslint-disable no-self-assign */
|
||||
target.application_credential_library_ids =
|
||||
target.application_credential_library_ids;
|
||||
target.application_credential_source_ids =
|
||||
target.application_credential_source_ids;
|
||||
/* eslint-enable no-self-assign */
|
||||
assert.equal(
|
||||
target.application_credential_library_ids.length,
|
||||
target.application_credential_source_ids.length,
|
||||
2,
|
||||
'Target has two entires in application_credential_library_ids'
|
||||
'Target has two entires in application_credential_source_ids'
|
||||
);
|
||||
assert.equal(
|
||||
target.credentialLibraries.length,
|
||||
@@ -280,14 +283,14 @@ module('Unit | Model | target', function (hooks) {
|
||||
);
|
||||
});
|
||||
|
||||
test('it has an `addCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
test('it has an `addCredentialSources` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:add-credential-libraries',
|
||||
'/v1/targets/123abc:add-credential-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['123_abc', 'foobar'],
|
||||
application_credential_source_ids: ['123_abc', 'foobar'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
@@ -301,7 +304,7 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
@@ -311,17 +314,17 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.addCredentialLibraries(['123_abc', 'foobar']);
|
||||
await model.addCredentialSources(['123_abc', 'foobar']);
|
||||
});
|
||||
|
||||
test('it has a `removeCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
test('it has a `removeCredentialSources` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-credential-libraries',
|
||||
'/v1/targets/123abc:remove-credential-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['1', '2'],
|
||||
application_credential_source_ids: ['1', '2'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
@@ -335,7 +338,7 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
@@ -345,17 +348,17 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeCredentialLibraries(['1', '2']);
|
||||
await model.removeCredentialSources(['1', '2']);
|
||||
});
|
||||
|
||||
test('it has a `removeCredentialLibrary` method that deletes a single credential library using `removeCredentialLibraries` method', async function (assert) {
|
||||
test('it has a `removeCredentialSource` method that deletes a single credential library using `removeCredentialSources` method', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-credential-libraries',
|
||||
'/v1/targets/123abc:remove-credential-sources',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['2'],
|
||||
application_credential_source_ids: ['2'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
@@ -369,7 +372,7 @@ module('Unit | Model | target', function (hooks) {
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
@@ -379,6 +382,6 @@ module('Unit | Model | target', function (hooks) {
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeCredentialLibrary('2');
|
||||
await model.removeCredentialSource('2');
|
||||
});
|
||||
});
|
||||
@@ -204,8 +204,8 @@ module('Unit | Serializer | application', function (hooks) {
|
||||
authorized_actions: [],
|
||||
name: 'Target 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_sets: [],
|
||||
application_credential_library_ids: [],
|
||||
host_sources: [],
|
||||
application_credential_source_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
+7
-7
@@ -1,27 +1,27 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import FragmentHostSet from 'api/models/fragment-host-set';
|
||||
import FragmentHostSource from 'api/models/fragment-host-source';
|
||||
|
||||
module('Unit | Serializer | fragment host set', function (hooks) {
|
||||
module('Unit | Serializer | fragment host source', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it normalizes `id` into `host_set_id`', function (assert) {
|
||||
test('it normalizes `id` into `host_source_id`', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('fragment-host-set');
|
||||
const normalized = serializer.normalize(FragmentHostSet, {
|
||||
const serializer = store.serializerFor('fragment-host-source');
|
||||
const normalized = serializer.normalize(FragmentHostSource, {
|
||||
id: '2',
|
||||
host_catalog_id: '1',
|
||||
});
|
||||
assert.deepEqual(normalized, {
|
||||
data: {
|
||||
attributes: {
|
||||
host_set_id: '2',
|
||||
host_source_id: '2',
|
||||
host_catalog_id: '1',
|
||||
},
|
||||
id: null,
|
||||
relationships: {},
|
||||
type: 'fragment-host-set',
|
||||
type: 'fragment-host-source',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module(
|
||||
'Unit | Serializer | fragment managed groups attributes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes empty string to null', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord('fragment-managed-group-attributes', {
|
||||
filter: '',
|
||||
});
|
||||
const serializedRecord = record.serialize();
|
||||
assert.deepEqual(serializedRecord, {
|
||||
filter: null,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,161 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Serializer | Managed group', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes correctly on create with no filter provided', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('managed-group');
|
||||
const record = store.createRecord('managed-group', {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
member_ids: ['1', '2'],
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: null,
|
||||
attributes: {
|
||||
filter: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes correctly on create when filter provided', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('managed-group');
|
||||
const record = store.createRecord('managed-group', {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
member_ids: ['1', '2'],
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not serialize version when it has null or undefined value', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('managed-group');
|
||||
store.push({
|
||||
data: {
|
||||
id: '1',
|
||||
type: 'managed-group',
|
||||
attributes: {
|
||||
name: 'Group Test',
|
||||
description: 'Description for the test',
|
||||
member_ids: ['1', '2'],
|
||||
version: undefined,
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const record = store.peekRecord('managed-group', '1');
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
name: 'Group Test',
|
||||
description: 'Description for the test',
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not serialize readOnly attributes', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('managed-group');
|
||||
const record = store.createRecord('managed-group', {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
member_ids: ['1', '2'],
|
||||
created_time: Date.now(),
|
||||
updated_time: Date.now(),
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
name: 'Group',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: null,
|
||||
attributes: {
|
||||
filter: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes correctly on update', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('managed-group');
|
||||
store.push({
|
||||
data: {
|
||||
id: '1',
|
||||
type: 'managed-group',
|
||||
attributes: {
|
||||
name: 'Group Test',
|
||||
description: 'Description for the test',
|
||||
member_ids: ['1', '2'],
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const record = store.peekRecord('managed-group', '1');
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
name: 'Group Test',
|
||||
description: 'Description for the test',
|
||||
version: 1,
|
||||
auth_method_id: '1234',
|
||||
type: 'oidc',
|
||||
attributes: {
|
||||
filter: 'key=value',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,11 +11,11 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
scope: {
|
||||
scope_id: 'org_1',
|
||||
type: 'org',
|
||||
@@ -40,16 +40,16 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only host sets and version when an `adapterOptions.hostSetIDs` array is passed', function (assert) {
|
||||
test('it serializes only host sources and version when an `adapterOptions.hostSetIDs` array is passed', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
version: 1,
|
||||
});
|
||||
@@ -59,19 +59,19 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
host_set_ids: ['4', '5'],
|
||||
host_source_ids: ['4', '5'],
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only credential libraries and version when an `adapterOptions.credentialLibraryIDs` array is passed', function (assert) {
|
||||
test('it serializes only credential sources and version when an `adapterOptions.credentialLibraryIDs` array is passed', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
application_credential_source_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
@@ -80,7 +80,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
application_credential_library_ids: ['4', '5'],
|
||||
application_credential_source_ids: ['4', '5'],
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
@@ -93,11 +93,11 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
const payload = {
|
||||
id: '1',
|
||||
name: 'Target 1',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: ['1'],
|
||||
application_credential_source_ids: ['1'],
|
||||
};
|
||||
const normalized = serializer.normalizeSingleResponse(
|
||||
store,
|
||||
@@ -112,18 +112,18 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Target 1',
|
||||
host_sets: [
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
host_sources: [
|
||||
{ host_source_id: '1', host_catalog_id: '2' },
|
||||
{ host_source_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: [{ value: '1' }],
|
||||
application_credential_source_ids: [{ value: '1' }],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it normalizes missing host_sets and credential libraries to empty array', function (assert) {
|
||||
test('it normalizes missing host_sources and application_credential_source_ids to empty array', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
@@ -147,8 +147,8 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
authorized_actions: [],
|
||||
name: 'Target 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_sets: [],
|
||||
application_credential_library_ids: [],
|
||||
host_sources: [],
|
||||
application_credential_source_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { ResourceFilter } from 'api/services/resource-filter-store';
|
||||
|
||||
module('Unit | Service | resource-filter-store', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exports a ResourceFilter class that converts filterObjects to query expressions', function (assert) {
|
||||
assert.ok(ResourceFilter);
|
||||
const filter = new ResourceFilter({
|
||||
type: 'oidc',
|
||||
status: ['active', 'pending'],
|
||||
authorized_actions: [{ contains: 'read' }],
|
||||
});
|
||||
const expected =
|
||||
'("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending") and ("read" in "/item/authorized_actions")';
|
||||
assert.equal(filter.queryExpression, expected);
|
||||
});
|
||||
});
|
||||
@@ -30,7 +30,7 @@ module('Unit | Service | storage', function (hooks) {
|
||||
assert.equal(service.getItem('foo'), 'bar');
|
||||
});
|
||||
|
||||
test("it's in-memory storage is implicitly refreshed for every test case", function (assert) {
|
||||
test("it's in-memory storage is implicitly refreshed for every test case, and thus values from previous runs are not present", function (assert) {
|
||||
assert.expect(1);
|
||||
const service = this.owner.lookup('service:storage');
|
||||
assert.notOk(service.getItem('foo'));
|
||||
|
||||
@@ -12,8 +12,5 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -56,5 +56,10 @@ module.exports = {
|
||||
plugins: ['node'],
|
||||
extends: ['plugin:node/recommended'],
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
extends: ['plugin:qunit/recommended'],
|
||||
},
|
||||
],
|
||||
};
|
||||
+25
-24
@@ -21,51 +21,52 @@
|
||||
"doc:toc": "doctoc README.md"
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-cli-babel": "^7.23.0",
|
||||
"ember-cli-htmlbars": "^5.3.1",
|
||||
"ember-simple-auth": "^3.0.1"
|
||||
"ember-cli-babel": "^7.26.6",
|
||||
"ember-cli-htmlbars": "^5.7.1",
|
||||
"ember-fetch": "^8.1.1",
|
||||
"ember-simple-auth": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"@ember/test-helpers": "^2.4.2",
|
||||
"@glimmer/component": "^1.0.4",
|
||||
"@glimmer/tracking": "^1.0.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"core": "*",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.28.0",
|
||||
"doctoc": "^2.0.1",
|
||||
"ember-auto-import": "^1.12.0",
|
||||
"ember-cli": "~3.28.3",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
"ember-cli-update": "^0.54.6",
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-cookies": "^0.5.2",
|
||||
"ember-disable-prototype-extensions": "^1.1.3",
|
||||
"ember-export-application-global": "^2.0.1",
|
||||
"ember-load-initializers": "^2.1.2",
|
||||
"ember-maybe-import-regenerator": "^0.1.6",
|
||||
"ember-page-title": "^6.0.3",
|
||||
"ember-qunit": "^5.1.1",
|
||||
"ember-resolver": "^8.0.2",
|
||||
"ember-source": "~3.24.0",
|
||||
"ember-maybe-import-regenerator": "^1.0.0",
|
||||
"ember-page-title": "^6.2.2",
|
||||
"ember-qunit": "^5.1.4",
|
||||
"ember-resolver": "^8.0.3",
|
||||
"ember-source": "~3.28.4",
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-template-lint": "^3.7.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-ember": "^10.5.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-qunit": "^6.2.0",
|
||||
"loader.js": "^4.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.2.1",
|
||||
"qunit": "^2.13.0",
|
||||
"qunit-dom": "^1.6.0"
|
||||
"prettier": "^2.4.0",
|
||||
"qunit": "^2.17.1",
|
||||
"qunit-dom": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10 <=15"
|
||||
"node": ">=12 <=17"
|
||||
},
|
||||
"ember": {
|
||||
"edition": "octane"
|
||||
|
||||
@@ -6,13 +6,6 @@ const browsers = [
|
||||
'last 1 Safari versions',
|
||||
];
|
||||
|
||||
const isCI = Boolean(process.env.CI);
|
||||
const isProduction = process.env.EMBER_ENV === 'production';
|
||||
|
||||
if (isCI || isProduction) {
|
||||
browsers.push('ie 11');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
browsers,
|
||||
};
|
||||
@@ -12,8 +12,5 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -56,5 +56,10 @@ module.exports = {
|
||||
plugins: ['node'],
|
||||
extends: ['plugin:node/recommended'],
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
extends: ['plugin:qunit/recommended'],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -4,5 +4,5 @@
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
title={{t "actions.get-topic-help"}}>
|
||||
<Rose::Icon @name="flight-icons/help-24" @size={{@iconSize}} />
|
||||
<Rose::Icon @name="flight-icons/svg/help-24" @size={{@iconSize}} />
|
||||
</a>
|
||||
@@ -28,11 +28,11 @@ export default class ErrorMessageComponent extends MessageComponent {
|
||||
get icon() {
|
||||
switch (this.args.status) {
|
||||
case '404':
|
||||
return 'flight-icons/help-16';
|
||||
return 'flight-icons/svg/help-16';
|
||||
case '401':
|
||||
case '403':
|
||||
default:
|
||||
return 'flight-icons/alert-circle-16';
|
||||
return 'flight-icons/svg/alert-circle-16';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
import EmberObject from '@ember/object';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { typeOf } from '@ember/utils';
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* @return {boolean} true if `value` is a function
|
||||
*/
|
||||
const isFunction = (value) => typeOf(value) === 'function';
|
||||
|
||||
/**
|
||||
* Represents a filter on a specific route and maintains filter state.
|
||||
* @class
|
||||
*/
|
||||
class RouteResourceFilter extends EmberObject {
|
||||
// =attributes
|
||||
|
||||
route;
|
||||
name;
|
||||
defaultValue;
|
||||
allowed;
|
||||
serialize;
|
||||
findBySerialized;
|
||||
|
||||
// =computed
|
||||
|
||||
/**
|
||||
* Query parameter name where the selected value(s) for this filter are
|
||||
* stored in the URL.
|
||||
* @type {string}
|
||||
*/
|
||||
get filterKey() {
|
||||
return `filter-${this.name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The allowed values for this filter. See `allowed`.
|
||||
* @type {?array}
|
||||
*/
|
||||
get allowedValues() {
|
||||
if (isFunction(this.allowed)) {
|
||||
return this.allowed(this.route);
|
||||
}
|
||||
return this.allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently selected value(s) for this filter, deserialized from the value of
|
||||
* the associated route query parameter.
|
||||
* @type {?array}
|
||||
*/
|
||||
get value() {
|
||||
const decodedValue =
|
||||
this.route.paramsFor(this.route.routeName)[this.filterKey] ||
|
||||
JSON.stringify(this.defaultValue);
|
||||
const value = decodedValue ? JSON.parse(decodedValue) : null;
|
||||
const deserializedValue = value
|
||||
? value.map((serializedValue) =>
|
||||
this.allowedValues.find((item) =>
|
||||
this.deserializeValue(item, serializedValue)
|
||||
)
|
||||
)
|
||||
: null;
|
||||
return deserializedValue;
|
||||
}
|
||||
set value(value) {
|
||||
const queryParams = {};
|
||||
const serialized = value.map((value) => this.serializeValue(value));
|
||||
queryParams[this.filterKey] = JSON.stringify(serialized);
|
||||
this.route.transitionTo({ queryParams });
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Calls `serialize` (if set) on the passed value. Otherwise returns the
|
||||
* value unaltered.
|
||||
*/
|
||||
serializeValue(value) {
|
||||
if (this.serialize) {
|
||||
return this.serialize(...arguments);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if `item` is represented by `serializedValue` when deserializing
|
||||
* items from the query string. If `findBySerialized` is unspecified,
|
||||
* a simple equality test is used.
|
||||
* @param item
|
||||
* @param {string} serializedValue
|
||||
* @return {boolean} item is represented by `serializedValue`
|
||||
*/
|
||||
deserializeValue(item, serializedValue) {
|
||||
if (this.findBySerialized) {
|
||||
return this.findBySerialized(...arguments);
|
||||
}
|
||||
return item === serializedValue;
|
||||
}
|
||||
|
||||
// =static methods
|
||||
|
||||
/**
|
||||
* Configures the specified route with a query parameter of `name` that, when
|
||||
* changed, refreshes the route model and replaces history (rather than
|
||||
* advancing history).
|
||||
* @static
|
||||
* @param {Route} routeClass
|
||||
* @param {string} name
|
||||
*/
|
||||
static setupRouteQueryParams(routeClass, name) {
|
||||
const filterKey = `filter-${name}`;
|
||||
|
||||
// If the route has no query params specified yet, declare them.
|
||||
if (!Object.keys(routeClass.queryParams).length)
|
||||
routeClass.queryParams = {};
|
||||
|
||||
// If the route has no resource filters list specified yet, create one.
|
||||
if (!routeClass.resourceFilterParams) routeClass.resourceFilterParams = [];
|
||||
|
||||
// Add the resource filter query parameter to the route. Resource filter
|
||||
// param changes refresh the model but do not contribute to browser history.
|
||||
// See https://guides.emberjs.com/release/routing/query-params/
|
||||
routeClass.queryParams[filterKey] = {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to instantiate an instance of this class.
|
||||
* @static
|
||||
* @param {Route} routeInstance
|
||||
* @param {string} name
|
||||
* @param {array|function} allowed
|
||||
* @param {?array} defaultValue
|
||||
* @param {?function} serialize
|
||||
* @param {?function} findBySerialized
|
||||
* @return {RouteResourceFilter}
|
||||
*/
|
||||
static getOrCreateRouteResourceFilter(
|
||||
routeInstance,
|
||||
name,
|
||||
allowed,
|
||||
defaultValue,
|
||||
serialize,
|
||||
findBySerialized
|
||||
) {
|
||||
const owner = getOwner(routeInstance);
|
||||
const containerKey = `resource-filter:${name}@${routeInstance.routeName}`;
|
||||
const factory = owner.factoryFor(containerKey);
|
||||
if (!factory) {
|
||||
// create factory
|
||||
class AppliedRouteResourceFilter extends this {
|
||||
route = routeInstance;
|
||||
name = name;
|
||||
allowed = allowed;
|
||||
defaultValue = defaultValue;
|
||||
serialize = serialize;
|
||||
findBySerialized = findBySerialized;
|
||||
}
|
||||
owner.register(containerKey, AppliedRouteResourceFilter);
|
||||
}
|
||||
const instance = owner.lookup(containerKey);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This decorator provides a concise way to express a route resource filter.
|
||||
* Resource filter params abstract away a route's query parameter
|
||||
* interface, treating a query param as just an attribute on a route instance.
|
||||
* Getting the attribute value is equivalent to looking up the associated query
|
||||
* parameter. Setting the attribute is equivalent to transitioning to the route
|
||||
* and passing the associated query parameter value.
|
||||
*
|
||||
* Additionally, resource filter params may declare `allowed`, an array
|
||||
* (or function returning an array) containing the set of values the parameter
|
||||
* may take. While unenforced, `allowed` are useful metadata about a resource
|
||||
* filter and may be fetch via `ResourceFilterParamHelper`. An optional default
|
||||
* value may be specified via `defaultValue` passed to the decorator.
|
||||
*
|
||||
* @see ResourceFilterParamHelper
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* export default class MyRoute extends Route {
|
||||
*
|
||||
* @resourceFilter({
|
||||
* allowed: ['active', 'pending'],
|
||||
* defaultvalue: ['active'], // optional
|
||||
* })
|
||||
* status;
|
||||
*
|
||||
* @action
|
||||
* changeStatus(status) {
|
||||
* // Sets query param `filter-status` on the current route, which occurs
|
||||
* // via a route transition.
|
||||
* this.status = status;
|
||||
* // Log the value of query param `filter-status`.
|
||||
* log(this.status);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export function resourceFilter({
|
||||
allowed,
|
||||
defaultValue,
|
||||
serialize,
|
||||
findBySerialized,
|
||||
}) {
|
||||
/**
|
||||
* @param {object} target
|
||||
* @param {string} name
|
||||
* @param {object} descriptor
|
||||
* @return {object{get, set}}
|
||||
*/
|
||||
return function (target, name /*, descriptor*/) {
|
||||
RouteResourceFilter.setupRouteQueryParams(target, name);
|
||||
|
||||
let instance;
|
||||
|
||||
// Override the decorated attribute with a getter and setter.
|
||||
return {
|
||||
/**
|
||||
* Returns the current value of the resource filter.
|
||||
*/
|
||||
get() {
|
||||
instance = RouteResourceFilter.getOrCreateRouteResourceFilter(
|
||||
this,
|
||||
name,
|
||||
allowed,
|
||||
defaultValue,
|
||||
serialize,
|
||||
findBySerialized
|
||||
);
|
||||
return instance.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the resource filter param to the stringified representation
|
||||
* of `value`. NOTE: this is equivalent to transitioning into the
|
||||
* current route, changing the associated query param.
|
||||
* @param value
|
||||
*/
|
||||
set(value) {
|
||||
instance.value = value;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import config from 'ember-get-config';
|
||||
import Helper from '@ember/component/helper';
|
||||
import { getOwner } from '@ember/application';
|
||||
|
||||
export default helper(function appName() {
|
||||
return config.appName;
|
||||
});
|
||||
export default class extends Helper {
|
||||
compute() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return config.appName;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import config from 'ember-get-config';
|
||||
import Helper from '@ember/component/helper';
|
||||
import { getOwner } from '@ember/application';
|
||||
|
||||
export default helper(function companyCopyright() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return `© ${currentYear} ${config.companyName}`;
|
||||
});
|
||||
export default class extends Helper {
|
||||
compute() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
const currentYear = new Date().getFullYear();
|
||||
return `© ${currentYear} ${config.companyName}`;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import config from 'ember-get-config';
|
||||
import Helper from '@ember/component/helper';
|
||||
import { getOwner } from '@ember/application';
|
||||
|
||||
export default helper(function companyName() {
|
||||
return config.companyName;
|
||||
});
|
||||
export default class extends Helper {
|
||||
compute() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return config.companyName;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,23 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import Helper from '@ember/component/helper';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { assert } from '@ember/debug';
|
||||
import config from 'ember-get-config';
|
||||
|
||||
export default helper(function docUrl(params /*, hash*/) {
|
||||
const baseURL = config.documentation.baseURL;
|
||||
const docKey = params[0] || '';
|
||||
const configuredPath = config.documentation.topics[docKey];
|
||||
if (docKey) {
|
||||
assert(
|
||||
`
|
||||
Documentation for "${docKey}" could not be found. Please ensure that
|
||||
this key exists under "documentation" in your app config.
|
||||
`,
|
||||
configuredPath
|
||||
);
|
||||
export default class extends Helper {
|
||||
compute(params) {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
const baseURL = config.documentation.baseURL;
|
||||
const docKey = params[0] || '';
|
||||
const configuredPath = config.documentation.topics[docKey];
|
||||
if (docKey) {
|
||||
assert(
|
||||
`
|
||||
Documentation for "${docKey}" could not be found. Please ensure that
|
||||
this key exists under "documentation" in your app config.
|
||||
`,
|
||||
configuredPath
|
||||
);
|
||||
}
|
||||
const path = configuredPath || '';
|
||||
return `${baseURL}${path}`;
|
||||
}
|
||||
const path = configuredPath || '';
|
||||
return `${baseURL}${path}`;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import Helper from '@ember/component/helper';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
/**
|
||||
* Use this helper to determine if a resource filter for a route has any
|
||||
* currently selected value.
|
||||
*
|
||||
* @see resourceFilterParam (decorator)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* {{#if (has-resource-filter-selections 'route.name' 'field')}}
|
||||
* Field has filter selections.
|
||||
* {{/if}}
|
||||
*/
|
||||
export default class HasRouteResourceFilterSelectionsHelper extends Helper {
|
||||
// =services
|
||||
|
||||
@service router;
|
||||
|
||||
// =methods
|
||||
|
||||
// =lifecycle management methods
|
||||
|
||||
/**
|
||||
* Recompute this helper when the route changes.
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.router.on('routeDidChange', this.routeDidChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to route events when this helper is destroyed.
|
||||
*/
|
||||
willDestroy() {
|
||||
this.router.off('routeDidChange', this.routeDidChange);
|
||||
super.willDestroy();
|
||||
}
|
||||
|
||||
// =compute method
|
||||
|
||||
/**
|
||||
* Returns true if the specified route has a value set for
|
||||
* any resource filter passed into the helper.
|
||||
* @param {string} routeName
|
||||
* @param {string[]} names - names of resource filter fields
|
||||
* @return {boolean}
|
||||
*/
|
||||
compute([routeName, ...names]) {
|
||||
const owner = getOwner(this);
|
||||
const route = owner.lookup(`route:${routeName}`);
|
||||
const selectedValues = names.map((name) => route[name] || null).flat();
|
||||
const anyTruthy = selectedValues.reduce(
|
||||
(previousValue, currentValue) => previousValue || currentValue,
|
||||
false
|
||||
);
|
||||
return Boolean(anyTruthy);
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Triggers a recompute (see above) when the route changes, especially when
|
||||
* route query params change.
|
||||
*/
|
||||
@action
|
||||
routeDidChange() {
|
||||
this.recompute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import Helper from '@ember/component/helper';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
/**
|
||||
* Use this helper to fetch the current value of a resource filter param,
|
||||
* as well as its allowedValues.
|
||||
*
|
||||
* @see resourceFilter (decorator)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* {{#with resource-filter 'status' as |param|}}
|
||||
* {{param.name}} / {{param.value}} / {{param.selectedValue}}
|
||||
* {{/with}}
|
||||
*/
|
||||
export default class ResourceFilterHelper extends Helper {
|
||||
// =services
|
||||
|
||||
@service router;
|
||||
|
||||
// =methods
|
||||
|
||||
// =lifecycle management methods
|
||||
|
||||
/**
|
||||
* Recompute this helper when the route changes.
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.router.on('routeDidChange', this.routeDidChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to route events when this helper is destroyed.
|
||||
*/
|
||||
willDestroy() {
|
||||
this.router.off('routeDidChange', this.routeDidChange);
|
||||
super.willDestroy();
|
||||
}
|
||||
|
||||
// =compute method
|
||||
|
||||
/**
|
||||
* Returns an object containing `name`, `value`, and `selectedValue`.
|
||||
* `name` - The name of the resource filter param.
|
||||
* `value` - The value of the resource filter param
|
||||
* @return {object}
|
||||
*/
|
||||
compute([routeName, name]) {
|
||||
const owner = getOwner(this);
|
||||
const containerKey = `resource-filter:${name}@${routeName}`;
|
||||
const resourceFilter = owner.lookup(containerKey);
|
||||
const { allowedValues, value: selectedValue } = resourceFilter;
|
||||
return { name, allowedValues, selectedValue };
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Triggers a recompute (see above) when the route changes, especially when
|
||||
* route query params change.
|
||||
*/
|
||||
@action
|
||||
routeDidChange() {
|
||||
this.recompute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { registerDeprecationHandler } from '@ember/debug';
|
||||
|
||||
export function initialize(application) {
|
||||
const config = application.resolveRegistration('config:environment');
|
||||
const isTesting = config.environment === 'test';
|
||||
if (isTesting) {
|
||||
// Disable all deprecations for now in tests.
|
||||
registerDeprecationHandler((/*message, options, next*/) => {
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { filterBy } from '@ember/object/computed';
|
||||
import { A } from '@ember/array';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { defer, resolve } from 'rsvp';
|
||||
import config from 'ember-get-config';
|
||||
import { getOwner } from '@ember/application';
|
||||
|
||||
/**
|
||||
* A simple service that emits Confirmation instances and
|
||||
@@ -17,13 +17,21 @@ import config from 'ember-get-config';
|
||||
export default class ConfirmationsService extends Service {
|
||||
// =attributes
|
||||
|
||||
#enabled;
|
||||
|
||||
/**
|
||||
* When the confirm service is disabled, it always returns resolving promises.
|
||||
* By default the service is enabled but it is useful to disable it for test
|
||||
* purposes.
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled = config.enableConfirmService ?? true;
|
||||
get enabled() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return this.#enabled ?? config.enableConfirmService ?? true;
|
||||
}
|
||||
set enabled(value) {
|
||||
this.#enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Array}
|
||||
|
||||
Loaded 100 of 378 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user