Compare commits

...
34 Commits
Author SHA1 Message Date
Dharini Jeeva c5edd4dd52 Update auth methods form (#1981)
Update auth methods form (#1981)
2023-11-07 11:27:48 -08:00
hashicorp-copywrite[bot] f102e7fca2 [COMPLIANCE] Add Copyright and License Headers (#1986)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-11-06 09:28:39 -08:00
Lisbet AlvarezandZhihe Li f593e6a8b1 chore: 🤖 update cli version & changelog in desktop (#1985)
* chore: 🤖 update cli version in desktop

* docs: ✏️ update changelog

* Update ui/desktop/CHANGELOG.md

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2023-11-03 16:03:07 -07:00
Cameron PereraandDharini Jeeva f54d48c380 refactor: 💡 allow target connection even with no read ability (#1984)
* refactor: 💡 allow target connection even with no read ability

* Update ui/desktop/tests/acceptance/projects/targets/index-test.js

Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>

* refactor: 💡 remove unneeded await from transitionTo

---------

Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>
2023-11-03 16:01:51 -05:00
0129784322 fix url when user tries to access resource with incorrect url (#1958)
* refactor: 💡 auth-method, account, & managed abilities & routes

* refactor: 💡 credential store resources abilities & routes

* refactor: 💡 group abilities & routes

* refactor: 💡 host-catalog resources abilities & routes

* refactor: 💡 role abilities & routes

* test: 💍 fix route-info-test

* test: 💍 fix host serializer tests

* test: 💍 fix create-test for host-sets

* refactor: 💡 session recording resources abilities & routes

* refactor: 💡 storage bucket abilities & routes

* refactor: 💡 target abilities & routes

* refactor: 💡 user abilities & routes

* refactor: 💡 worker abilities & routes

* refactor: 💡 remove ability changes and add comments

* Update ui/admin/app/utils/route-info.js

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* test: 💍 add test cases and rename util file

* refactor: 💡 remove dup file and update imports

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11282

* docs: ✏️ update comments & remove console.log

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11282

* Update ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js

Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>
2023-11-02 16:08:25 -07:00
Zhihe Li 7317ca2ff5 fix: 🐛 Fix issue with brokered credentials that overflow (#1982)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11368
2023-11-02 15:16:22 -04:00
Zhihe Li 1ddabf4980 feat: 🎸 Add checks for cancel permissions in desktop sessions (#1980)
* feat: 🎸 Add checks for cancel permissions in desktop sessions

* chore: 🤖 Move model hook from session to session.index
2023-11-02 13:42:51 -04:00
Devon AndersonandLisbet Alvarez a574023c05 chore: 🤖 Upgrade addons/rose dependencies (#1973)
* chore: 🤖 Upgrade addons/rose dependencies

Upgraded `addons/rose` dependencies to match other addons. Lint fixes,
and fixed scripts for concurrently

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11587

* v4.4.1...v4.12.2

* Update ember-try.js

change scenarios to match 4.12.2

* Update addons/rose/tests/integration/components/rose/button-test.js

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>

* Update ember-try.js

originally had the right scenarios for 4.
12.2

* chore: 🤖 upgrade ember-cli-babel

Upgrade ember-cli-babel to match other addons

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11587

* Update button-test.js

fix test declaration statement

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
2023-11-01 15:52:00 -04:00
Cameron Perera 861562201b refactor: 💡 remove unused target/sessions controller (#1979) 2023-11-01 13:54:36 -05:00
4cdcaefe15 Result of tsccr-helper -log-level=info -pin-all-workflows . (#1977)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>
2023-11-01 11:22:41 -07:00
Dharini Jeeva e2487b7e98 Fix electron clipboard action (#1976)
* fix: 🐛 allow clipboard write action
2023-11-01 10:06:03 -07:00
Cameron Perera f879bbb1ec Session permissions issue (#1975)
* refactor: 💡 session isCancelable to isAvailable

* refactor: 💡 check for target info for showing target details

* refactor: 💡 add fallback for session data from connect command

* refactor: 💡 remove extra divs

* refactor: 💡 remove await from peekRecord of a session

* test: 💍 add test for no read permission for sessions

* test: 💍 reset global qunit after test is done
2023-11-01 11:22:18 -05:00
Zhihe Li 933e272764 fix: 🐛 Add read:self permission for sessions in ability check (#1974) 2023-11-01 12:06:40 -04:00
dependabot[bot]andZhihe Li 2953635aeb chore(deps): bump browserify-sign from 4.2.1 to 4.2.2 (#1969)
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2023-10-31 12:38:55 -04:00
hashicorp-copywrite[bot]andZhihe Li 194561e1f8 [COMPLIANCE] Add Copyright and License Headers (#1972)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2023-10-31 12:17:53 -04:00
Zhihe Li 4a3d8eee63 feat: 🎸 Allow auto connect to SSH targets on windows (#1971) 2023-10-30 18:16:40 -04:00
Dharini Jeeva 7d6f3df140 chore: update HDS to 3.0.2 (#1970)
* chore: 🤖 update to hds 3.0.2
2023-10-30 12:56:38 -07:00
Dharini Jeeva 722acdc1fc chore: 🤖 refactor key value list component, text input support (#1957)
refactor: 💡 refactor list wrapper component and fix test
2023-10-30 12:30:05 -07:00
Carlos Alcaide Corvoandcameronperera 3f8e39d0e1 Host set permission issue (#1968)
* refactor: 💡 allow for graceful error handling when connecting

to a target where host-sets cannot be fetched

* refactor: 💡 update for loop to for...of loop

* Move fetching host from target to session domain

* Decouple getting target host-sets and getting hosts from the host-sets, now this code is extracted in 2 methods. Now if the user does not have permissions to fetch host or host-sets we do not show an error and we allow for quick-connect approach

* Fix a check within getHostsFromHostSets

* Change a for loop for Promise.all within getTargetHostSets

* Change a for loop for Promise.All within getHostsFromHostSets

* Delete unnecessary condition

* Delete unnecessary condition

* chore: 🤖 remove host sources from target mirage factory

by default it uses address when using 'withAssociations'

* test: 💍 update target address tests to use 'withOneHost'

* test: 💍 remove connect tests from scopes acceptance tests

* refactor: 💡 remove can initiate connect target ability

and associated code and tests

* refactor: 💡 update target model afterModel hook, and preconnect

* refactor: 💡 revert 'withAssociations' mirage change

* test: 💍 add tests for permissions issues for host-sets and host

* test: 💍 visiting session without permissions to read hosts

---------

Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
2023-10-30 12:54:37 -05:00
Devon Anderson 9e7852f18d chore: 🤖 Upgrade addons/core to Ember 4.12.2 (#1965)
* chore: 🤖 Upgrade `addons/core` to Ember 4.12.2

Upgrade `addons/core` to Ember 4.12.2

✅ Closes: ICU-11341

* refactor: 💡 testing updating plugin-proposal-decorators version

Update `@babel/plugin-proposal-decorators` to latest version.

* refactor: 💡 testing legacy plugin-proposa-decorators version

Testin `legacy` version for `plugin-proposal-decorators` plugin

* refactor: 💡 testing plugin-proposal-decorators

Testing `@babel/plugin-proposal-decorators`

* refactor: 💡 Revert @babel/exlint-parser to previous version

Revert `@babel/eslint-parser` to use version 2018 EcmaScript version
until further research is done on latest versions

* Update package.json

put @babel/core back into `devDependencies`

* refactor: 💡 testing linging changes

Pushed ember-tempalate-lint, prettier, and ember template lint plugin
for prettier to latest versions to test why CI builds are not passing.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11341
2023-10-27 11:10:30 -04:00
hashicorp-copywrite[bot]andZhihe Li c548c381bf [COMPLIANCE] Add Copyright and License Headers (#1966)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2023-10-24 14:31:07 -04:00
Zhihe Li 63be0f3aec chore: 🤖 Update commitizen hook to add jira link automatically (#1959) 2023-10-24 13:07:27 -04:00
Susmitha Girumala 8e4fac5ce2 Manually trigger admin ui build (#1964) 2023-10-20 10:16:06 +02:00
Dharini Jeeva 2a9666285a fix: 🐛 name (#1963) 2023-10-20 00:41:25 -07:00
Dharini Jeeva bff94d6b4d fix: 🐛 release name (#1962) 2023-10-20 00:12:08 -07:00
Michael Li 5b8fc06e21 test(admin/e2e): Update test timeout to 60s (#1955) 2023-10-17 13:06:21 -04:00
Lisbet AlvarezandZhihe Li a20483d1af update breadcrumbs to use hds::breadcrumbs (#1934)
* refactor: 💡 initial changes for updating breadcrumbs to use hds

* refactor: 💡 add breadcrumbs to all paths

* refactor: 💡 add more hds breadcrumbs to remaining paths

* refactor: 💡 remove BreadCrumbs from templates

* refactor: 💡 remove breadcrumb only controller files

* test: 💍 remove unnused test files & add missing tests

* test: 💍 update tests

* test: 💍 add missing scope in test

* test: 💍 fix test

* refactor: 💡 remove icons

* refactor: 💡 remove addon and use service instead

* docs: ✏️ add comment to breadcrumb service

* test: 💍 update service unit test

* docs: ✏️ add comments to breadcrumb components

* Update ui/admin/app/components/breadcrumbs/container/index.js

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2023-10-17 09:45:34 -07:00
dependabot[bot] 0bf5221389 chore(deps): bump @babel/traverse from 7.15.0 to 7.23.2 (#1954)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.15.0 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 15:59:41 +02:00
hashicorp-copywrite[bot] 297db470bd [COMPLIANCE] Add Copyright and License Headers (#1953)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-10-16 13:29:31 -04:00
Dharini Jeeva 91a6023145 Refactor key value list component (#1949)
* refactor: 💡 update keyvalue list component to allow only rows
2023-10-12 14:42:23 -07:00
Devon Anderson 7b96dd1f81 Upgrade addons/auth to Ember 4.12 (#1947)
* Ember `addon/auth` upgrade v4.4.1...v4.12.2

* Update .npmignore

* refactor: 💡 Quick refactor

Refactor `.eslintrc.js` to current `ecmaVersion` and use latest
`babelOptions`. Revert `.prettier.js` to use previous overrides. Setup
scripts to use `concurrently` instead of `npm-run-all`

* refactor: 💡 removed `npm-run-all`

Removed `npm-run-all` from package.json
2023-10-11 17:17:42 -04:00
Carlos Alcaide Corvo aee55cb171 Chore icu 9086 audit resolutions (#1928) 2023-10-11 12:09:23 +02:00
Carlos Alcaide Corvo 370b3ea1fb Chore icu 9085 audit resolutions (#1927)
* chore: 🤖 Delete shell-quote from resolutions

* chore: 🤖 Delete hawk from resolutions

* chore: 🤖 Modify resolutions for async
2023-10-11 11:38:34 +02:00
Carlos Alcaide Corvo 4f972a82d6 chore: 🤖 Modify ansi-html resolution (#1922) 2023-10-11 10:48:06 +02:00
470 changed files with 4817 additions and 5067 deletions

No files matched your search

+14 -12
View File
@@ -3,21 +3,23 @@ on:
push:
branches:
- main
- "release/**"
workflow_dispatch:
jobs:
dependencies:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
path: '**/node_modules'
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn install
@@ -26,16 +28,16 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
path: '**/node_modules'
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn run build:ui:admin:oss
@@ -54,16 +56,16 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
path: '**/node_modules'
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn run build:ui:admin:enterprise
@@ -77,16 +79,16 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
path: '**/node_modules'
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn run build:ui:admin:hcp
+3 -3
View File
@@ -30,11 +30,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8e0b1c74b1d5a0077b04d064c76ee714d3da7637 # codeql-bundle-v2.14.6
uses: github/codeql-action/init@bad341350a2f5616f9e048e51360cedc49181ce8 # codeql-bundle-v2.15.1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,4 +54,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8e0b1c74b1d5a0077b04d064c76ee714d3da7637 # codeql-bundle-v2.14.6
uses: github/codeql-action/analyze@bad341350a2f5616f9e048e51360cedc49181ce8 # codeql-bundle-v2.15.1
+8 -8
View File
@@ -6,7 +6,7 @@ jobs:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
@@ -14,7 +14,7 @@ jobs:
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn install
@@ -24,7 +24,7 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
@@ -32,7 +32,7 @@ jobs:
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn run compliance:licenses
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
@@ -51,7 +51,7 @@ jobs:
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn lint
@@ -62,13 +62,13 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 16
- run: yarn test
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
- run: |
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
trigger-gha-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Release description
run: |
echo "Triggering release with a TAG ${GITHUB_REF#refs/tags/}"
+13 -2
View File
@@ -6,11 +6,22 @@ COMMIT_SOURCE=$2
SHA1=$3
# Gets the current branch name but if we're in a detached head state, return "HEAD"
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
# Regex to match the ICU-#### branch name pattern we use
ISSUE_REGEX="^(ICU-[0-9]+)"
# Don't run commitizen for merge or squash commits, as well as when rebasing
if [ "${COMMIT_SOURCE}" = merge ] || [ "${COMMIT_SOURCE}" = squash ] || [ "${COMMIT_SOURCE}" = "message" -a "${BRANCH_NAME}" = "HEAD" ];then
exit 0
fi
# This is to allow interaction with the terminal for commitizen
exec < /dev/tty && npx git-cz --hook || true
# If the branch name matches, set the JIRA link automatically
if [[ $BRANCH_NAME =~ $ISSUE_REGEX ]]
then
issue="${BASH_REMATCH[1]}"
url="https://hashicorp.atlassian.net/browse/${issue}"
exec < /dev/tty && npx git-cz --issues="$url" --hook || true
else
# This is to allow interaction with the terminal for commitizen
exec < /dev/tty && npx git-cz --hook || true
fi
-1
View File
@@ -38,7 +38,6 @@ module.exports = {
files: [
'./.eslintrc.js',
'./.prettierrc.js',
'./.stylelintrc.js',
'./.template-lintrc.js',
'./ember-cli-build.js',
'./index.js',
+17 -2
View File
@@ -14,10 +14,25 @@ export default class SessionAbility extends ModelAbility {
// =permissions
/**
* Only "active" or "pending" sessions may be read.
* Only available sessions may be read.
* @type {boolean}
*/
get canRead() {
return (this.model.isActive || this.model.isPending) && super.canRead;
return (
this.model.isAvailable &&
(this.hasAuthorizedAction('read:self') || super.canRead)
);
}
/**
* Only available sessions may be canceled.
* @type {boolean}
*/
get canCancel() {
return (
this.model.isAvailable &&
(this.hasAuthorizedAction('cancel:self') ||
this.hasAuthorizedAction('cancel'))
);
}
}
@@ -54,4 +54,10 @@ export default class GeneratedHostModel extends BaseModel {
readOnly: true,
})
plugin;
@attr({
emptyArrayIfMissing: true,
readOnly: true,
})
host_set_ids;
}
+4 -4
View File
@@ -4,7 +4,6 @@
*/
import GeneratedSessionModel from '../generated/models/session';
import { computed } from '@ember/object';
import { equal } from '@ember/object/computed';
import { tracked } from '@glimmer/tracking';
import { A } from '@ember/array';
@@ -127,8 +126,7 @@ export default class SessionModel extends GeneratedSessionModel {
/**
* @type {boolean}
*/
@computed('isActive', 'isPending')
get isCancelable() {
get isAvailable() {
return this.isActive || this.isPending;
}
@@ -136,7 +134,6 @@ export default class SessionModel extends GeneratedSessionModel {
* The full proxy address and port if address exists, otherwise null.
* @type {?string}
*/
@computed('proxy_address', 'proxy_port')
get proxy() {
return this.proxy_address
? `${this.proxy_address}:${this.proxy_port}`
@@ -148,6 +145,9 @@ export default class SessionModel extends GeneratedSessionModel {
* @type {TargetModel}
*/
get target() {
if (!this.target_id) {
return null;
}
return this.store.peekRecord('target', this.target_id);
}
+8
View File
@@ -431,9 +431,17 @@ function routes() {
return hostSets.where({ hostCatalogId });
}
);
this.post('/host-sets', function ({ hostCatalogs, hostSets }) {
const attrs = this.normalizedRequestAttrs();
const hostCatalog = hostCatalogs.find(attrs.hostCatalogId);
//this needs to be attached while creating a plugin type host set to mimic API
if (hostCatalog.plugin) {
attrs.type = 'plugin';
attrs.plugin = {
name: hostCatalog.plugin.name,
};
}
attrs.scopeId = hostCatalog.scope.id;
return hostSets.create(attrs);
});
+12 -5
View File
@@ -9,6 +9,7 @@ import permissions from '../helpers/permissions';
import generateId from '../helpers/id';
import {
TYPE_AUTH_METHOD_PASSWORD,
TYPE_AUTH_METHOD_OIDC,
TYPE_AUTH_METHOD_LDAP,
TYPES_AUTH_METHOD,
@@ -23,11 +24,17 @@ export default factory.extend({
'delete',
'authenticate',
],
authorized_collection_actions: () => {
return {
accounts: ['create', 'list'],
'managed-groups': ['create', 'list'],
};
authorized_collection_actions() {
switch (this.type) {
case TYPE_AUTH_METHOD_PASSWORD:
return { accounts: ['create', 'list'] };
case TYPE_AUTH_METHOD_LDAP:
case TYPE_AUTH_METHOD_OIDC:
return {
accounts: ['create', 'list'],
'managed-groups': ['create', 'list'],
};
}
},
id: () => generateId('am_'),
+10 -1
View File
@@ -85,7 +85,16 @@ export default factory.extend({
afterCreate(hostCatalog, server) {
const { scope, type } = hostCatalog;
const hosts = server.createList('host', 10, { scope, hostCatalog, type });
server.createList('host-set', 3, { scope, hostCatalog, hosts, type });
const hostSets = server.createList('host-set', 3, {
scope,
hostCatalog,
hosts,
type,
});
const hostSetIds = hostSets.map((hostSet) => hostSet.id);
hosts.forEach((host) => {
host.update({ hostSetIds });
});
},
}),
});
+9 -6
View File
@@ -22,14 +22,8 @@ export default factory.extend({
authorized_collection_actions() {
const collectionActions = {
scopes: ['create', 'list'],
users: ['create', 'list'],
sessions: ['list'],
groups: ['create', 'list'],
roles: ['create', 'list'],
targets: ['create', 'list'],
'credential-stores': ['create', 'list'],
'auth-methods': ['create', 'list'],
'host-catalogs': ['create', 'list'],
};
// Worker permissions only available on the global scope
@@ -41,6 +35,15 @@ export default factory.extend({
if (this.type === 'global' || this.type === 'org') {
collectionActions['storage-buckets'] = ['create', 'list'];
collectionActions['session-recordings'] = ['list'];
collectionActions.users = ['create', 'list'];
collectionActions['auth-methods'] = ['create', 'list'];
}
if (this.type === 'project') {
collectionActions['credential-stores'] = ['create', 'list'];
collectionActions['host-catalogs'] = ['create', 'list'];
collectionActions.sessions = ['list'];
collectionActions.targets = ['create', 'list'];
}
return collectionActions;
+3 -1
View File
@@ -7,6 +7,7 @@ import factory from '../generated/factories/session';
import { trait } from 'miragejs';
import { faker } from '@faker-js/faker';
import generateId from '../helpers/id';
import permissions from '../helpers/permissions';
const statusStrings = ['pending', 'active', 'canceling', 'terminated'];
@@ -15,7 +16,8 @@ export const pickRandomStatusString = () =>
export default factory.extend({
id: () => generateId('ss_'),
authorized_actions: () =>
permissions.authorizedActionsFor('session') || ['read', 'cancel'],
status: pickRandomStatusString(),
/**
+9
View File
@@ -7,4 +7,13 @@ import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
modelName: 'host',
_hashForModel(model) {
const json = ApplicationSerializer.prototype._hashForModel.apply(
this,
arguments
);
json.host_catalog_id = model.hostCatalogId;
return json;
},
});
+3 -3
View File
@@ -14,7 +14,7 @@
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint:js": "eslint .",
"lint:js:fix": "eslint . --fix",
"format": "yarn run format:js",
"format": "concurrently \"npm:format:*\" --names \"format:\"",
"format:js": "prettier --write '{addon,app,config,stories,tests,mirage}/**/*.js' *.js",
"start": "ember serve",
"test": "ember exam --test-port 0 --split=4 --parallel=1 --random",
@@ -31,8 +31,8 @@
"ember-fetch": "^8.1.1"
},
"devDependencies": {
"@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.21.3",
"@babel/core": "^7.21.0",
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
@@ -97,4 +97,96 @@ module('Unit | Ability | session', function (hooks) {
assert.false(canService.can('read session', session));
});
test('can read a session that has read:self', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['read:self'],
status: STATUS_SESSION_ACTIVE,
});
assert.true(canService.can('read session', session));
});
test('can cancel a session that is active', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['cancel'],
status: STATUS_SESSION_ACTIVE,
});
assert.true(canService.can('cancel session', session));
});
test('can cancel a session that has cancel:self', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['cancel:self'],
status: STATUS_SESSION_ACTIVE,
});
assert.true(canService.can('cancel session', session));
});
test('can cancel a session that is pending', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['cancel'],
status: STATUS_SESSION_PENDING,
});
assert.true(canService.can('cancel session', session));
});
test('cannot cancel a session that is canceling', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['cancel'],
status: STATUS_SESSION_CANCELING,
});
assert.false(canService.can('cancel session', session));
});
test('cannot cancel a session that is terminated', function (assert) {
assert.expect(1);
const session = store.createRecord('session', {
authorized_actions: ['cancel'],
status: STATUS_SESSION_TERMINATED,
});
assert.false(canService.can('cancel session', session));
});
test('cannot cancel a session that is not authorized', function (assert) {
assert.expect(4);
const activeSession = store.createRecord('session', {
authorized_actions: [],
status: STATUS_SESSION_ACTIVE,
});
const pendingSession = store.createRecord('session', {
authorized_actions: [],
status: STATUS_SESSION_PENDING,
});
const cancelingSession = store.createRecord('session', {
authorized_actions: [],
status: STATUS_SESSION_CANCELING,
});
const terminatedSession = store.createRecord('session', {
authorized_actions: [],
status: STATUS_SESSION_TERMINATED,
});
assert.false(canService.can('cancel session', activeSession));
assert.false(canService.can('cancel session', pendingSession));
assert.false(canService.can('cancel session', cancelingSession));
assert.false(canService.can('cancel session', terminatedSession));
});
});
+3 -3
View File
@@ -13,7 +13,7 @@ module('Unit | Model | session', function (hooks) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('session', {});
assert.ok(model);
assert.notOk(model.isCancelable);
assert.notOk(model.isAvailable);
});
test('it allows cancellation of an active session', function (assert) {
@@ -21,7 +21,7 @@ module('Unit | Model | session', function (hooks) {
let model = store.createRecord('session', {
status: 'active',
});
assert.ok(model.isCancelable);
assert.ok(model.isAvailable);
});
test('it allows cancellation of a pending session', function (assert) {
@@ -29,7 +29,7 @@ module('Unit | Model | session', function (hooks) {
let model = store.createRecord('session', {
status: 'pending',
});
assert.ok(model.isCancelable);
assert.ok(model.isAvailable);
});
test('it computes isActive', function (assert) {
@@ -57,6 +57,7 @@ module('Unit | Serializer | host', function (hooks) {
address: '10.0.0.1',
dns_names: [],
ip_addresses: [],
host_set_ids: [],
},
relationships: {},
},
@@ -78,6 +79,7 @@ module('Unit | Serializer | host', function (hooks) {
authorized_actions: ['no-op', 'read'],
ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'],
dns_names: ['test.example.internal', 'test.example.external'],
host_set_ids: ['hst_12345'],
};
const normalized = serializer.normalizeSingleResponse(store, host, payload);
assert.deepEqual(normalized, {
@@ -92,6 +94,7 @@ module('Unit | Serializer | host', function (hooks) {
ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'],
dns_names: ['test.example.internal', 'test.example.external'],
plugin: { name: 'aws' },
host_set_ids: ['hst_12345'],
},
relationships: {},
},
+2 -2
View File
@@ -8,8 +8,8 @@
"disableAnalytics": false,
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
*/
"isTypeScriptProject": false
}
+5 -4
View File
@@ -9,12 +9,14 @@ module.exports = {
root: true,
parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2018,
ecmaVersion: 'latest',
sourceType: 'module',
// This is specific babel-config. If grows consider creating a babel config file
requireConfigFile: false,
babelOptions: {
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
plugins: [
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
],
},
// end of babel-config
},
@@ -51,8 +53,7 @@ module.exports = {
browser: false,
node: true,
},
plugins: ['node'],
extends: ['plugin:node/recommended'],
extends: ['plugin:n/recommended'],
},
{
// Test files:
-1
View File
@@ -22,7 +22,6 @@
/.travis.yml
/.watchmanconfig
/bower.json
/config/ember-try.js
/CONTRIBUTING.md
/ember-cli-build.js
/testem.js
+1 -1
View File
@@ -9,7 +9,7 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
const { maybeEmbroider } = require('@embroider/test-setup');
module.exports = function (defaults) {
let app = new EmberAddon(defaults, {
const app = new EmberAddon(defaults, {
'ember-simple-auth': {
// https://github.com/mainmatter/ember-simple-auth/blob/master/guides/upgrade-to-v4.md#call-sessionsetup-method-in-your-applicationroute
useSessionSetupMethod: true,
+21 -20
View File
@@ -8,8 +8,8 @@
"ember-addon"
],
"scripts": {
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint:js": "eslint .",
"lint:js:fix": "eslint . --fix",
"format": "yarn run format:js",
@@ -22,22 +22,24 @@
},
"dependencies": {
"ember-cli-babel": "^7.26.11",
"ember-cli-htmlbars": "^6.1.0",
"ember-cli-htmlbars": "^6.2.0",
"ember-fetch": "^8.1.1",
"ember-simple-auth": "^4.2.2"
},
"devDependencies": {
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-proposal-decorators": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.8.1",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^2.9.3",
"@embroider/test-setup": "^2.1.1",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"doctoc": "^2.2.0",
"ember-auto-import": "^2.4.3",
"ember-cli": "^4.4.1",
"ember-auto-import": "^2.6.3",
"ember-cli": "^4.12.2",
"ember-cli-dependency-checker": "^3.3.1",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
@@ -47,23 +49,22 @@
"ember-export-application-global": "^2.0.1",
"ember-load-initializers": "^2.1.2",
"ember-page-title": "^7.0.0",
"ember-qunit": "^6.0.0",
"ember-resolver": "^8.0.3",
"ember-source": "~4.4.0",
"ember-qunit": "^6.2.0",
"ember-resolver": "^10.0.0",
"ember-source": "~4.12.0",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^4.14.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-ember": "^10.6.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-qunit": "^7.3.0",
"ember-template-lint": "^5.7.2",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-ember": "^11.5.0",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-qunit": "^7.3.4",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"pretender": "^3.4.3",
"prettier": "^2.6.2",
"qunit": "^2.19.1",
"prettier": "^2.8.7",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"webpack": "^5.76.0"
"webpack": "^5.78.0"
},
"engines": {
"node": "16.* || 18.*"
@@ -3,7 +3,7 @@
"packages": [
{
"name": "ember-cli",
"version": "4.4.1",
"version": "4.12.2",
"blueprints": [
{
"name": "addon",
@@ -11,14 +11,6 @@ const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup');
module.exports = async function () {
return {
scenarios: [
{
name: 'ember-lts-3.28',
npm: {
devDependencies: {
'ember-source': '~3.28.0',
},
},
},
{
name: 'ember-lts-4.4',
npm: {
@@ -27,6 +19,14 @@ module.exports = async function () {
},
},
},
{
name: 'ember-lts-4.8',
npm: {
devDependencies: {
'ember-source': '~4.8.0',
},
},
},
{
name: 'ember-release',
npm: {
@@ -6,20 +6,17 @@
'use strict';
module.exports = function (environment) {
let ENV = {
const ENV = {
modulePrefix: 'dummy',
environment,
rootURL: '/',
locationType: 'history',
EmberENV: {
EXTEND_PROTOTYPES: false,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false,
},
},
APP: {
View File
Whitespace-only changes.
+2 -2
View File
@@ -8,8 +8,8 @@
"disableAnalytics": false,
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
*/
"isTypeScriptProject": false
}
+5 -4
View File
@@ -9,12 +9,14 @@ module.exports = {
root: true,
parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2018,
ecmaVersion: 'latest',
sourceType: 'module',
// This is specific babel-config. If grows consider creating a babel config file
requireConfigFile: false,
babelOptions: {
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
plugins: [
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
],
},
// end of babel-config
},
@@ -51,8 +53,7 @@ module.exports = {
browser: false,
node: true,
},
plugins: ['node'],
extends: ['plugin:node/recommended'],
extends: ['plugin:n/recommended'],
},
{
// Test files:
+5
View File
@@ -8,4 +8,9 @@
module.exports = {
plugins: ['ember-template-lint-plugin-prettier'],
extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'],
rules: {
'no-curly-component-invocation': {
allow: ['app-name', 'company-name', 'company-copyright'],
},
},
};
@@ -3,12 +3,10 @@
SPDX-License-Identifier: MPL-2.0
}}
{{! //TODO fix the issue and delete disabling the rule }}
{{! template-lint-disable no-passed-in-event-handlers }}
{{#if @model.type}}
{{component
(concat 'form/authenticate/' @model.type)
submit=@submit
onSubmit=@submit
disabled=@disabled
}}
{{/if}}
@@ -3,4 +3,4 @@
SPDX-License-Identifier: MPL-2.0
}}
<Form::Authenticate::Details @onSubmit={{@submit}} @disabled={{@disabled}} />
<Form::Authenticate::Details @onSubmit={{@onSubmit}} @disabled={{@disabled}} />
@@ -5,7 +5,7 @@
<Rose::Form
class='full-width'
@onSubmit={{@submit}}
@onSubmit={{@onSubmit}}
@disabled={{@disabled}}
as |form|
>
@@ -3,4 +3,4 @@
SPDX-License-Identifier: MPL-2.0
}}
<Form::Authenticate::Details @onSubmit={{@submit}} @disabled={{@disabled}} />
<Form::Authenticate::Details @onSubmit={{@onSubmit}} @disabled={{@disabled}} />
+1 -1
View File
@@ -9,7 +9,7 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
const { maybeEmbroider } = require('@embroider/test-setup');
module.exports = function (defaults) {
let app = new EmberAddon(defaults, {
const app = new EmberAddon(defaults, {
// Add options here
'ember-simple-auth': {
useSessionSetupMethod: true,
+27 -24
View File
@@ -15,13 +15,13 @@
"scripts": {
"build:development": "ember build",
"build": "ember build --environment=production",
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint:hbs": "ember-template-lint .",
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint .",
"lint:js:fix": "eslint . --fix",
"format": "npm-run-all format:*",
"format": "concurrently \"npm:format:*\" --names \"format:\"",
"format:js": "prettier --write '{addon,app,config,stories,tests}/**/*.js' *.js",
"format:hbs": "prettier --write '{addon,app,tests}/**/*.hbs'",
"start": "ember serve",
@@ -33,10 +33,10 @@
"dependencies": {
"api": "*",
"auth": "*",
"ember-auto-import": "^2.4.2",
"ember-auto-import": "^2.6.3",
"ember-browser-services": "^4.0.3",
"ember-cli-babel": "^7.26.11",
"ember-cli-htmlbars": "^6.1.0",
"ember-cli-htmlbars": "^6.2.0",
"ember-feature-flags": "^6.0.0",
"ember-intl": "^6.0.0-beta.6",
"ember-loading": "^2.0.0",
@@ -46,43 +46,46 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-proposal-decorators": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.8.1",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^2.9.3",
"@embroider/test-setup": "^2.1.1",
"@faker-js/faker": "^8.0.2",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"doctoc": "^2.2.0",
"ember-cli": "^4.4.1",
"ember-cli": "^4.12.2",
"ember-cli-dependency-checker": "^3.3.1",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-export-application-global": "^2.0.1",
"ember-inline-svg": "^1.0.1",
"ember-load-initializers": "^2.1.2",
"ember-page-title": "^7.0.0",
"ember-qunit": "^6.0.0",
"ember-resolver": "^8.0.3",
"ember-source": "~4.4.0",
"ember-template-lint": "^4.14.0",
"ember-template-lint-plugin-prettier": "^4.0.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-ember": "^10.6.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-qunit": "^7.3.0",
"ember-qunit": "^6.2.0",
"ember-resolver": "^10.0.0",
"ember-source": "~4.12.0",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^5.7.2",
"ember-template-lint-plugin-prettier": "^5.0.0",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-ember": "^11.5.0",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-qunit": "^7.3.4",
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"qunit": "^2.19.1",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"rose": "*",
"webpack": "^5.76.0"
"webpack": "^5.78.0"
},
"peerDependencies": {
"ember-source": "^4.0.0"
},
"engines": {
"node": "16.* || 18.*"
@@ -3,7 +3,7 @@
"packages": [
{
"name": "ember-cli",
"version": "4.4.1",
"version": "4.12.2",
"blueprints": [
{
"name": "addon",
@@ -0,0 +1,58 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
'use strict';
const getChannelURL = require('ember-source-channel-url');
const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup');
module.exports = async function () {
return {
scenarios: [
{
name: 'ember-lts-4.4',
npm: {
devDependencies: {
'ember-source': '~4.4.0',
},
},
},
{
name: 'ember-lts-4.8',
npm: {
devDependencies: {
'ember-source': '~4.8.0',
},
},
},
{
name: 'ember-release',
npm: {
devDependencies: {
'ember-source': await getChannelURL('release'),
},
},
},
{
name: 'ember-beta',
npm: {
devDependencies: {
'ember-source': await getChannelURL('beta'),
},
},
},
{
name: 'ember-canary',
npm: {
devDependencies: {
'ember-source': await getChannelURL('canary'),
},
},
},
embroiderSafe(),
embroiderOptimized(),
],
};
};
@@ -6,20 +6,17 @@
'use strict';
module.exports = function (environment) {
let ENV = {
const ENV = {
modulePrefix: 'dummy',
environment,
rootURL: '/',
locationType: 'history',
EmberENV: {
EXTEND_PROTOTYPES: false,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false,
},
},
APP: {
@@ -13,7 +13,7 @@ module('Integration | Component | doc-link', function (hooks) {
test('it renders', async function (assert) {
assert.expect(1);
await render(hbs`<DocLink @doc="account" @iconSize="large" />`);
await render(hbs`<DocLink @doc='account' @iconSize='large' />`);
assert.ok(find('.doc-link .rose-icon.large'));
});
});
@@ -29,15 +29,16 @@ module('Integration | Component | form | authenticate', function (hooks) {
this.submit = () => {};
this.disabled = () => {};
await render(hbs`<Form::Authenticate
@model={{this.model}}
@submit={{this.submit}}
@disabled={{this.disabled}}/>`);
await render(hbs`<Form::Authenticate
@model={{this.model}}
@onSubmit={{this.submit}}
@disabled={{this.disabled}}
/>`);
assert.ok(find('.full-width'));
assert.ok(find('.hds-form-text-input'));
assert.strictEqual(
find('button[type="submit"]').textContent.trim(),
'Sign In'
'Sign In',
);
});
@@ -52,15 +53,16 @@ module('Integration | Component | form | authenticate', function (hooks) {
this.submit = () => {};
this.disabled = () => {};
await render(hbs`<Form::Authenticate
@model={{this.model}}
@submit={{this.submit}}
@disabled={{this.disabled}}/>`);
await render(hbs`<Form::Authenticate
@model={{this.model}}
@onSubmit={{this.submit}}
@disabled={{this.disabled}}
/>`);
assert.ok(find('.full-width'));
assert.ok(find('.hds-form-text-input'));
assert.strictEqual(
find('button[type="submit"]').textContent.trim(),
'Sign In'
'Sign In',
);
});
@@ -75,15 +77,16 @@ module('Integration | Component | form | authenticate', function (hooks) {
this.submit = () => {};
this.disabled = () => {};
await render(hbs`<Form::Authenticate
@model={{this.model}}
@submit={{this.submit}}
@disabled={{this.disabled}}/>`);
await render(hbs`<Form::Authenticate
@model={{this.model}}
@onSubmit={{this.submit}}
@disabled={{this.disabled}}
/>`);
assert.ok(find('.full-width'));
assert.notOk(find('.hds-form-text-input'));
assert.strictEqual(
find('button[type="submit"]').textContent.trim(),
'Sign In'
'Sign In',
);
});
});
@@ -13,7 +13,7 @@ module('Integration | Component | Image', function (hooks) {
test('it renders', async function (assert) {
assert.expect(1);
await render(hbs`<Image @name="logo-app" />`);
await render(hbs`<Image @name='logo-app' />`);
assert.ok(find('.image-container'));
});
});
@@ -18,7 +18,7 @@ module('Integration | Component | loading-button', function (hooks) {
test('it executes a function on refresh button click', async function (assert) {
assert.expect(2);
this.onClick = () => assert.ok(true, 'refresh was clicked');
await render(hbs`<LoadingButton @onClick={{this.onClick}}/>`);
await render(hbs`<LoadingButton @onClick={{this.onClick}} />`);
assert.ok(find('.loading-button'));
await click('button');
});
@@ -15,26 +15,21 @@ module('Integration | Component | pending-confirmations', function (hooks) {
assert.expect(2);
const service = this.owner.lookup('service:confirm');
service.enabled = true;
await render(hbs`
<PendingConfirmations as |confirmations|>
<div class="pending">Pending</div>
</PendingConfirmations>
`);
await render(hbs`<PendingConfirmations as |confirmation|>
<div class='pending'>{{confirmation.text}}</div>
</PendingConfirmations>`);
assert.strictEqual(
findAll('.pending').length,
0,
'No pending confirmations'
'No pending confirmations',
);
service.confirm();
await render(hbs`
<PendingConfirmations as |confirmations|>
<div class="pending">Pending</div>
</PendingConfirmations>
`);
service.confirm('Text', { type: 'pending' });
await render(hbs`<PendingConfirmations as |confirmation|>
<div class='pending'>{{confirmation.text}}</div></PendingConfirmations>`);
assert.strictEqual(
findAll('.pending').length,
1,
'One pending confirmations'
'One pending confirmations',
);
});
@@ -44,11 +39,9 @@ module('Integration | Component | pending-confirmations', function (hooks) {
service.enabled = true;
const confirmation = service.confirm();
confirmation.then(() => assert.ok(true, 'confirmation.then was called'));
await render(hbs`
<PendingConfirmations as |confirmations confirm|>
<button {{on "click" confirm}}>Confirm</button>
</PendingConfirmations>
`);
await render(hbs`<PendingConfirmations as |confirmations confirm|>
<button type='button' {{on 'click' confirm}}>Confirm</button>
</PendingConfirmations>`);
assert.notOk(confirmation.done, 'Confirmation is not done');
await click('button');
assert.ok(confirmation.done, 'Confirmation is done');
@@ -60,11 +53,9 @@ module('Integration | Component | pending-confirmations', function (hooks) {
service.enabled = true;
const confirmation = service.confirm();
confirmation.catch(() => assert.ok(true, 'confirmation.catch was called'));
await render(hbs`
<PendingConfirmations as |confirmations confirm dismiss|>
<button {{on "click" dismiss}}>Dismiss</button>
</PendingConfirmations>
`);
await render(hbs`<PendingConfirmations as |confirmations confirm dismiss|>
<button type='button' {{on 'click' dismiss}}>Dismiss</button>
</PendingConfirmations>`);
assert.notOk(confirmation.done, 'Confirmation is not done');
await click('button');
assert.ok(confirmation.done, 'Confirmation is done');
@@ -24,7 +24,7 @@ module('Integration | Component | session-status', function (hooks) {
assert.expect(3);
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'canceling' });
await render(hbs`<SessionStatus @model={{this.model}}/>`);
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-alert-triangle'));
assert.ok(find('.hds-badge--color-warning'));
assert.ok(find('.hds-badge--type-filled'));
@@ -34,7 +34,7 @@ module('Integration | Component | session-status', function (hooks) {
assert.expect(3);
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'terminated' });
await render(hbs`<SessionStatus @model={{this.model}}/>`);
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-x'));
assert.ok(find('.hds-badge--color-critical'));
assert.ok(find('.hds-badge--type-filled'));
@@ -44,7 +44,7 @@ module('Integration | Component | session-status', function (hooks) {
assert.expect(3);
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'pending' });
await render(hbs`<SessionStatus @model={{this.model}}/>`);
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-delay'));
assert.ok(find('.hds-badge--color-neutral'));
assert.ok(find('.hds-badge--type-filled'));
@@ -54,7 +54,7 @@ module('Integration | Component | session-status', function (hooks) {
assert.expect(3);
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'any string' });
await render(hbs`<SessionStatus @model={{this.model}}/>`);
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.notOk(find('.flight-icon'));
assert.ok(find('.hds-badge--color-neutral'));
assert.ok(find('.hds-badge--type-outlined'));
@@ -14,7 +14,7 @@ module('Integration | Helper | format-date-iso-human', function (hooks) {
test('it renders', async function (assert) {
this.set('inputValue', new Date('2020-01-01T00:00:00.999Z'));
await render(hbs`{{format-date-iso-human this.inputValue }}`);
await render(hbs`{{format-date-iso-human this.inputValue}}`);
assert.strictEqual(this.element.textContent.trim(), '2020-01-01 00:00:00');
});
@@ -22,7 +22,7 @@ module('Integration | Helper | format-date-iso-human', function (hooks) {
test('it renders when null', async function (assert) {
this.set('inputValue', null);
await render(hbs`{{format-date-iso-human this.inputValue }}`);
await render(hbs`{{format-date-iso-human this.inputValue}}`);
assert.strictEqual(this.element.textContent.trim(), '');
});
@@ -17,26 +17,26 @@ module('Integration | Helper | reverse-indexed-display-name', function (hooks) {
this.set('array', ['Boundary', 'is', 'cool!']);
await render(hbs`{{reverse-indexed-display-name
'resources.session-recording.connection.title_index'
this.array
'Boundary'
}}`);
'resources.session-recording.connection.title_index'
this.array
'Boundary'
}}`);
assert.dom(this.element).hasText('Connection 3');
await render(hbs`{{reverse-indexed-display-name
'resources.session-recording.connection.title_index'
this.array
'is'
}}`);
'resources.session-recording.connection.title_index'
this.array
'is'
}}`);
assert.dom(this.element).hasText('Connection 2');
await render(hbs`{{reverse-indexed-display-name
'resources.session-recording.connection.title_index'
this.array
'cool!'
}}`);
'resources.session-recording.connection.title_index'
this.array
'cool!'
}}`);
assert.dom(this.element).hasText('Connection 1');
});
@@ -45,10 +45,10 @@ module('Integration | Helper | reverse-indexed-display-name', function (hooks) {
this.set('array', ['Boundary', 'is', 'cool!']);
await render(hbs`{{reverse-indexed-display-name
'resources.session-recording.connection.title_index'
this.array
'Watchtower'
}}`);
'resources.session-recording.connection.title_index'
this.array
'Watchtower'
}}`);
assert.dom(this.element).hasText('Connection');
});
+2 -2
View File
@@ -93,7 +93,7 @@ disable_discovered_config_validation:
If the Authorization Server's discovered configuration contains values that do not match the configuration set on this auth method, this can be set to force the local configuration to override the discovered values. This is useful in the case of slow-changing or incorrect values announced by the Authorization Server. NOTE: For safety, not providing this value will be treated the same as "false"; that is, every time the method is updated or the state is changed, this value must be set to "true" or it will be disabled.
idp_ca_certs:
label: Certificates
help: Optional PEM-encoded X.509 CA certificates that can be used as trust anchors when connecting to an OIDC provider.
help: PEM-encoded X.509 CA certificates that can be used as trust anchors when connecting to an OIDC provider.
certificate:
label: Certificate
allowed_audiences:
@@ -103,7 +103,7 @@ claim:
label: Claim
claims_scopes:
label: Claims Scopes
help: Optional claims scopes that will be requested during authentication.
help: Claims scopes that will be requested during authentication.
claims_scope:
label: Claims Scope
account_claim_maps:
@@ -233,7 +233,7 @@ host-set:
help: Create a filter to select resources using values such as tag name or tag value. The Azure filter format is:tagName eq ‘<tag-name>’ and tagValue eq ‘<tag-value>'. For example:tagName eq 'application' and tagValue eq 'production'
preferred_endpoints:
label: Preferred Endpoints
help: Optional. Preferred address at which the host should be accessed when multiple options are present, in cidr:<valid IPv4/6 CIDR> or dns:<globbed name> format.
help: Preferred address at which the host should be accessed when multiple options are present, in cidr:<valid IPv4/6 CIDR> or dns:<globbed name> format.
messages:
welcome:
title: Welcome to Host Sets
@@ -551,9 +551,6 @@ target:
connect-more-info:
title: Connect for more info
description: Additional information for this target will be provided here after your session is established.
cannot-connect:
title: Cannot connect to {targetDisplayName}
description: There are no host sources and no address configured for this target.
titles:
new: New Target
actions:
@@ -828,6 +825,8 @@ scope:
managed-group:
title: Managed Group
title_plural: Managed Groups
titles:
new: New Managed Group
description: A managed group represents a collection of accounts.
actions:
delete: Delete Managed Group
@@ -848,8 +847,9 @@ managed-group:
help: The list of groups that this Managed Group belongs to.
worker:
title: Worker
title_pki: New PKI Worker
title_plural: Workers
titles:
new: New PKI Worker
description: Workers are the server components that perform the session handling.
actions:
register: Register Worker
View File
Whitespace-only changes.
+2 -2
View File
@@ -8,8 +8,8 @@
"disableAnalytics": false,
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
*/
"isTypeScriptProject": false
}
+5 -4
View File
@@ -9,12 +9,14 @@ module.exports = {
root: true,
parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2018,
ecmaVersion: 'latest',
sourceType: 'module',
// This is specific babel-config. If grows consider creating a babel config file
requireConfigFile: false,
babelOptions: {
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
plugins: [
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
],
},
// end of babel-config
},
@@ -60,8 +62,7 @@ module.exports = {
browser: false,
node: true,
},
plugins: ['node'],
extends: ['plugin:node/recommended'],
extends: ['plugin:n/recommended'],
},
{
// Test files:
+2
View File
@@ -18,6 +18,8 @@
/.gitignore
/.prettierignore
/.prettierrc.js
/.stylelintignore
/.stylelintrc.js
/.template-lintrc.js
/.travis.yml
/.watchmanconfig
+8
View File
@@ -0,0 +1,8 @@
# unconventional files
/blueprints/*/files/
# compiled output
/dist/
# addons
/.node_modules.ember-try/
+6
View File
@@ -31,5 +31,11 @@ module.exports = {
'no-passed-in-event-handlers': false,
},
},
{
files: '**/tests/**/*.{js,gts,gjs}',
rules: {
prettier: false,
},
},
],
};
@@ -5,7 +5,7 @@
<button
...attributes
type={{if @submit 'submit' 'button'}}
type={{if @onSubmit 'submit' 'button'}}
disabled={{if @disabled 'disabled'}}
class='{{if @style (concat "rose-button-" @style)}}
{{if @iconLeft "has-icon-left"}}
@@ -5,7 +5,7 @@
<button
...attributes
type={{if @submit 'submit' 'button'}}
type={{if @onSubmit 'submit' 'button'}}
disabled={{if @disabled 'disabled'}}
class='rose-dropdown-button
{{if @style (concat "rose-dropdown-button-" @style)}}
@@ -8,7 +8,7 @@
.rose-layout-body-content {
display: grid;
grid-template-areas: 'body body sidebar';
grid-template-columns: 1fr 1fr 300px;
grid-template-columns: 1fr 1fr fit-content(300px);
grid-template-rows: auto 1fr;
gap: 2rem;
+1 -1
View File
@@ -9,7 +9,7 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
const { maybeEmbroider } = require('@embroider/test-setup');
module.exports = function (defaults) {
let app = new EmberAddon(defaults, {
const app = new EmberAddon(defaults, {
// Add options here
});
+19 -14
View File
@@ -10,15 +10,15 @@
"scripts": {
"build:development": "ember build",
"build": "ember build --environment=production && cp metadata.json dist/metadata.json",
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint:hbs": "ember-template-lint .",
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint .",
"lint:js:fix": "eslint . --fix",
"lint:sass": "stylelint 'app/styles/**/*.scss'",
"lint:sass:fix": "stylelint 'app/styles/**/*.scss' --fix",
"format": "npm-run-all format:*",
"format": "concurrently \"npm:format:*\" --names \"format:\"",
"format:hbs": "prettier --write '{addon,tests}/**/*.hbs'",
"format:js": "prettier --write '{addon,app,config,stories,tests}/**/*.js' *.js",
"format:mdx": "prettier --write 'addon/**/*.mdx'",
@@ -31,10 +31,10 @@
},
"dependencies": {
"@embroider/test-setup": "^2.1.1",
"@hashicorp/design-system-components": "^2.11.0",
"@hashicorp/design-system-components": "^3.0.2",
"codemirror": "5.65.7",
"ember-auto-import": "^2.6.3",
"ember-cli-babel": "^7.26.10",
"ember-cli-babel": "^7.26.11",
"ember-cli-clipboard": "^1.0.0",
"ember-cli-htmlbars": "^6.2.0",
"ember-cli-sass": "^11.0.1",
@@ -48,9 +48,10 @@
},
"devDependencies": {
"@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-proposal-decorators": "^7.18.6",
"@babel/eslint-parser": "^7.21.3",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^2.9.3",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
@@ -58,8 +59,9 @@
"babel-plugin-ember-modules-api-polyfill": "^2.12.0",
"babel-plugin-htmlbars-inline-precompile": "3",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"doctoc": "^2.2.0",
"ember-cli": "^4.12.1",
"ember-cli": "^4.12.2",
"ember-cli-dependency-checker": "^3.3.1",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
@@ -72,21 +74,21 @@
"ember-maybe-import-regenerator": "^1.0.0",
"ember-modifier": "^3.2.7",
"ember-page-title": "^7.0.0",
"ember-qunit": "^6.0.0",
"ember-qunit": "^6.2.0",
"ember-resolver": "^10.0.0",
"ember-source": "~4.12.0",
"ember-template-lint": "^4.14.0",
"ember-template-lint-plugin-prettier": "^4.0.0",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^5.7.2",
"ember-template-lint-plugin-prettier": "^5.0.0",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-ember": "^11.5.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-qunit": "^7.3.4",
"jest": "^27.4.7",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.31",
"prettier": "^2.7.1",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"react": "^16",
@@ -97,6 +99,9 @@
"stylelint-config-standard-scss": "^6.0.0",
"webpack": "^5.78.0"
},
"peerDependencies": {
"ember-source": "^4.0.0"
},
"engines": {
"node": "16.* || 18.*"
},
@@ -3,7 +3,7 @@
"packages": [
{
"name": "ember-cli",
"version": "4.4.1",
"version": "4.12.2",
"blueprints": [
{
"name": "addon",
@@ -0,0 +1,58 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
'use strict';
const getChannelURL = require('ember-source-channel-url');
const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup');
module.exports = async function () {
return {
scenarios: [
{
name: 'ember-lts-4.4',
npm: {
devDependencies: {
'ember-source': '~4.4.0',
},
},
},
{
name: 'ember-lts-4.8',
npm: {
devDependencies: {
'ember-source': '~4.8.0',
},
},
},
{
name: 'ember-release',
npm: {
devDependencies: {
'ember-source': await getChannelURL('release'),
},
},
},
{
name: 'ember-beta',
npm: {
devDependencies: {
'ember-source': await getChannelURL('beta'),
},
},
},
{
name: 'ember-canary',
npm: {
devDependencies: {
'ember-source': await getChannelURL('canary'),
},
},
},
embroiderSafe(),
embroiderOptimized(),
],
};
};
@@ -6,20 +6,17 @@
'use strict';
module.exports = function (environment) {
let ENV = {
const ENV = {
modulePrefix: 'dummy',
environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
EXTEND_PROTOTYPES: false,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false,
},
},
APP: {
@@ -13,9 +13,9 @@ module('Integration | Component | rose/anonymous', function (hooks) {
test('it renders', async function (assert) {
await render(hbs`
{{#rose/anonymous tagName='main'}}
<Rose::Anonymous @tagName="main">
template block text
{{/rose/anonymous}}
</Rose::Anonymous>
`);
assert.ok(find('main'));
assert.strictEqual(this.element.textContent.trim(), 'template block text');
@@ -21,8 +21,8 @@ module('Integration | Component | rose/button', function (hooks) {
assert.strictEqual(find('button').type, 'button');
});
test('it is type="submit" when @submit={{true}}', async function (assert) {
await render(hbs`<Rose::Button @submit={{true}} />`);
test('it is type="submit" when @onSubmit={{true}}', async function (assert) {
await render(hbs`<Rose::Button @onSubmit={{true}} />`);
assert.strictEqual(find('button').type, 'submit');
});
@@ -40,18 +40,18 @@ module('Integration | Component | rose/button', function (hooks) {
test('it supports left and right icons', async function (assert) {
await render(
hbs`<Rose::Button @iconLeft="flight-icons/svg/chevron-left-16" />`
hbs`<Rose::Button @iconLeft="flight-icons/svg/chevron-left-16" />`,
);
assert.ok(find('.has-icon-left .rose-icon'));
await render(
hbs`<Rose::Button @iconRight="flight-icons/svg/chevron-left-16" />`
hbs`<Rose::Button @iconRight="flight-icons/svg/chevron-left-16" />`,
);
assert.ok(find('.has-icon-right .rose-icon'));
});
test('it supports an icon-only type', async function (assert) {
await render(
hbs`<Rose::Button @iconOnly="flight-icons/svg/chevron-left-16" />`
hbs`<Rose::Button @iconOnly="flight-icons/svg/chevron-left-16" />`,
);
assert.ok(find('.has-icon-only .rose-icon'));
});
@@ -25,7 +25,7 @@ module('Integration | Component | rose/card/link', function (hooks) {
assert.strictEqual(find('a').title, 'card title');
assert.strictEqual(
find('.rose-card-link-title').textContent.trim(),
'card title'
'card title',
);
});
@@ -33,7 +33,7 @@ module('Integration | Component | rose/card/link', function (hooks) {
await render(hbs`<Rose::Card::Link @description="card description" />`);
assert.strictEqual(
find('.rose-card-link-body').textContent.trim(),
'card description'
'card description',
);
});
@@ -41,13 +41,13 @@ module('Integration | Component | rose/card/link', function (hooks) {
await render(hbs`<Rose::Card::Link @id="card_id" />`);
assert.strictEqual(
find('.rose-card-link-footer-id').textContent.trim(),
'card_id'
'card_id',
);
});
test('it renders with @icon', async function (assert) {
await render(
hbs`<Rose::Card::Link @title="foo" @icon="flight-icons/svg/alert-circle-16" />`
hbs`<Rose::Card::Link @title="foo" @icon="flight-icons/svg/alert-circle-16" />`,
);
assert.ok(find('.rose-icon'));
});
@@ -59,7 +59,7 @@ module('Integration | Component | rose/card/link', function (hooks) {
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Card::Link>
<button/>
<button type="button" />
</Rose::Card::Link>`);
assert.ok(find('button'));
});
@@ -21,8 +21,8 @@ module('Integration | Component | rose/dropdown/button', function (hooks) {
assert.strictEqual(find('button').type, 'button');
});
test('it is type="submit" when @submit={{true}}', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @submit={{true}} />`);
test('it is type="submit" when @onSubmit={{true}}', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @onSubmit={{true}} />`);
assert.strictEqual(find('button').type, 'submit');
});
@@ -38,7 +38,7 @@ module('Integration | Component | rose/dropdown/button', function (hooks) {
test('it supports an icon-only type', async function (assert) {
await render(
hbs`<Rose::Dropdown::Button @iconOnly="flight-icons/svg/chevron-left-16" />`
hbs`<Rose::Dropdown::Button @iconOnly="flight-icons/svg/chevron-left-16" />`,
);
assert.ok(find('.has-icon-only .rose-icon'));
});
@@ -15,7 +15,13 @@ module('Integration | Component | rose/form/actions', function (hooks) {
assert.expect(2);
this.cancel = () => {};
await render(
hbs`{{rose/form/actions cancel=this.cancel submitText='Save' cancelText='Cancel'}}`
hbs`
<Rose::Form::Actions
@cancel={{this.cancel}}
@submitText='Save'
@cancelText='Cancel'
/>
`,
);
assert.ok(find('[type="submit"]'));
assert.ok(find('button:not([type="submit"])'));
@@ -25,7 +31,15 @@ module('Integration | Component | rose/form/actions', function (hooks) {
assert.expect(2);
this.cancel = () => {};
await render(
hbs`{{rose/form/actions cancel=this.cancel submitDisabled=true cancelDisabled=true submitText='Save' cancelText='Cancel'}}`
hbs`
<Rose::Form::Actions
@cancel={{this.cancel}}
@submitText='Save'
@cancelText='Cancel'
@submitDisabled={{true}}
@cancelDisabled={{true}}
/>
`,
);
assert.ok(find('[type="submit"]:disabled'));
assert.ok(find('button:not([type="submit"]):disabled'));
@@ -33,7 +47,9 @@ module('Integration | Component | rose/form/actions', function (hooks) {
test('it optionally does not render the cancel button', async function (assert) {
assert.expect(2);
await render(hbs`{{rose/form/actions submitText='Save' showCancel=false}}`);
await render(hbs`
<Rose::Form::Actions @submitText='Save' @showCancel={{false}} />
`);
assert.ok(find('[type="submit"]'));
assert.notOk(find('button:not([type="submit"])'));
});
@@ -42,7 +58,13 @@ module('Integration | Component | rose/form/actions', function (hooks) {
assert.expect(1);
this.cancel = () => assert.ok(true, 'cancel was clicked');
await render(
hbs`{{rose/form/actions cancel=this.cancel submitText='Save' cancelText='Cancel'}}`
hbs`
<Rose::Form::Actions
@cancel={{this.cancel}}
@submitText='Save'
@cancelText='Cancel'
/>
`,
);
await click('button:not([type="submit"])');
});
@@ -17,7 +17,12 @@ module(
assert.expect(1);
this.enableEdit = () => {};
await render(
hbs`{{rose/form/actions/edit-toggle enableEdit=this.enableEdit enableEditText='Edit'}}`
hbs`
<Rose::Form::Actions::EditToggle
@enableEdit={{this.enableEdit}}
@enableEditText='Edit'
/>
`,
);
assert.ok(find('button'));
});
@@ -26,9 +31,14 @@ module(
assert.expect(1);
this.enableEdit = () => assert.ok(true, 'edit was clicked');
await render(
hbs`{{rose/form/actions/edit-toggle enableEdit=this.enableEdit enableEditText='Edit'}}`
hbs`
<Rose::Form::Actions::EditToggle
@enableEdit={{this.enableEdit}}
@enableEditText='Edit'
/>
`,
);
await click('button');
});
}
},
);
@@ -26,7 +26,7 @@ module('Integration | Component | rose/layout/body-content', function (hooks) {
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Layout::BodyContent as |bc|>
<bc.Body><button id="button" /></bc.Body>
<bc.Body><button id="button" type="button" /></bc.Body>
<bc.Sidebar><p id="details">Resource Details</p></bc.Sidebar>
</Rose::Layout::BodyContent>`);
@@ -24,7 +24,7 @@ module('Integration | Component | rose/layout/page', function (hooks) {
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Layout::Page>
<button id="content" />
<button id="content" type="button" />
</Rose::Layout::Page>`);
assert.ok(find('#content'));
});
@@ -44,7 +44,7 @@ module('Integration | Component | rose/table/row', function (hooks) {
test('it can be hidden', async function (assert) {
assert.expect(1);
await render(hbs`<Rose::Table::Row @hidden=true />`);
await render(hbs`<Rose::Table::Row @hidden={{true}} />`);
assert.ok(find('.rose-table-row-visually-hidden'));
});
});
@@ -50,7 +50,7 @@ module('Integration | Component | rose/table/row/cell', function (hooks) {
test('it renders with align center in a header cell', async function (assert) {
assert.expect(2);
await render(
hbs`<Rose::Table::Row::Cell @tagName='th' @alignCenter=true />`
hbs`<Rose::Table::Row::Cell @tagName='th' @alignCenter={{true}} />`,
);
assert.ok(find('.rose-table-header-cell'));
assert.ok(find('.align-center'));
@@ -58,7 +58,7 @@ module('Integration | Component | rose/table/row/cell', function (hooks) {
test('it renders with align center in a body', async function (assert) {
assert.expect(2);
await render(hbs`<Rose::Table::Row::Cell @alignCenter=true />`);
await render(hbs`<Rose::Table::Row::Cell @alignCenter={{true}} />`);
assert.ok(find('.rose-table-cell'));
assert.ok(find('.align-center'));
});
@@ -66,7 +66,7 @@ module('Integration | Component | rose/table/row/cell', function (hooks) {
test('it renders with align right in a header cell', async function (assert) {
assert.expect(2);
await render(
hbs`<Rose::Table::Row::Cell @tagName='th' @alignRight=true />`
hbs`<Rose::Table::Row::Cell @tagName='th' @alignRight={{true}} />`,
);
assert.ok(find('.rose-table-header-cell'));
assert.ok(find('.align-right'));
@@ -74,7 +74,7 @@ module('Integration | Component | rose/table/row/cell', function (hooks) {
test('it renders with align right', async function (assert) {
assert.expect(2);
await render(hbs`<Rose::Table::Row::Cell @alignRight=true />`);
await render(hbs`<Rose::Table::Row::Cell @alignRight={{true}} />`);
assert.ok(find('.rose-table-cell'));
assert.ok(find('.align-right'));
});
@@ -25,7 +25,7 @@ module('Integration | Helper | set-from-event', function (hooks) {
assert.expect(2);
this.set('inputValue', '1234');
await render(
hbs`<button value="foobar" {{on 'click' (set-from-event this 'inputValue')}} />`
hbs`<button value="foobar" type="button" {{on 'click' (set-from-event this 'inputValue')}} />`,
);
assert.deepEqual(this.inputValue, '1234');
await click('button');
+8
View File
@@ -0,0 +1,8 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
module.exports = {
questions: ["type", "subject", "body", "issues"],
};
+4 -7
View File
@@ -50,12 +50,6 @@
},
"resolutions": {
"request": "^2.88.0",
"ansi-html": "^0.0.8",
"shell-quote": "^1.7.3",
"hawk": "^9.0.1",
"async": "^2.6.4",
"terser": "^5.14.2",
"jpeg-js": "^0.4.4",
"loader-utils": "^2.0.4",
"**/ember-electron/**/minimatch": "^3.0.5",
"**/babel-core/json5": "^2.2.2",
@@ -74,7 +68,10 @@
"**/jsprim/json-schema": "^0.4.0",
"**/ember-cli/markdown-it-terminal/markdown-it": "^12.3.2",
"**/ember-cli-mirage/@embroider/macros": "^1.0.0",
"**/ember-electron/**/got": "^11.8.6"
"**/ember-electron/**/got": "^11.8.6",
"**/ember-cli/**/ansi-html": "^0.0.9",
"**/ember-cli/testem/fireworm/async": "^2.6.4",
"**/desktop/@doyensec/electronegativity/winston/async": "^2.6.4"
},
"config": {
"commitizen": {
-42
View File
@@ -1,42 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Rose::Nav::Breadcrumbs as |crumbs|>
{{#if this.scope.org}}
{{#unless this.scope.org.isGlobal}}
{{#if this.session.data.authenticated.isGlobal}}
<crumbs.link @route='scopes.scope.scopes' @model='global'>
{{t 'resources.org.title_plural'}}
</crumbs.link>
<crumbs.link @route='scopes.scope.edit' @model={{this.scope.org.id}}>
{{this.scope.org.displayName}}
</crumbs.link>
{{/if}}
{{/unless}}
{{/if}}
{{#if this.scope.project}}
<crumbs.link @route='scopes.scope.scopes' @model={{this.scope.org.id}}>
{{t 'resources.project.title_plural'}}
</crumbs.link>
<crumbs.link @route='scopes.scope.edit' @model={{this.scope.project.id}}>
{{this.scope.project.displayName}}
</crumbs.link>
{{/if}}
{{#each this.breadCrumbs as |crumb|}}
{{#if crumb.model}}
<crumbs.link @route={{crumb.path}} @model={{crumb.model}}>
{{crumb.label}}
</crumbs.link>
{{else}}
<crumbs.link @route={{crumb.path}}>
{{crumb.label}}
</crumbs.link>
{{/if}}
{{/each}}
</Rose::Nav::Breadcrumbs>
-118
View File
@@ -1,118 +0,0 @@
import Component from '@glimmer/component';
import EmberObject, { computed, get } from '@ember/object';
import { inject as service } from '@ember/service';
import { isBlank, isPresent } from '@ember/utils';
import { A } from '@ember/array';
// This component copies and modifies source from ember-breadcrumbs 0.2.3,
// updating it for Ember v4 compatibility.
// The original copyright is below.
/*
The MIT License (MIT)
Copyright (c) 2014-2019 Chris Farber
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
export default class BreadcrumbsComponent extends Component {
// =services
@service session;
@service scope;
@service router;
// =properties
/* eslint-disable-next-line ember/require-computed-property-dependencies */
@computed('router.currentRouteName')
get routeInfos() {
/* eslint-disable-next-line ember/no-private-routing-service */
return this.router._router._routerMicrolib.currentRouteInfos;
}
/*
For the pathNames and controllers properties, we must be careful not to NOT
specify the properties of the route in our dependent keys.
Observing the controller property of the route causes some serious problems:
https://github.com/chrisfarber/ember-breadcrumbs/issues/21
*/
@computed('routeInfos.[]')
get pathNames() {
return this.routeInfos.map(function (routeInfo) {
return routeInfo.name;
});
}
@computed('routeInfos.[]')
get controllers() {
return this.routeInfos.map(function (routeInfo) {
var route = routeInfo.route || routeInfo.handler;
return route.controller;
});
}
@computed(
'controllers.@each.{breadCrumbs,breadCrumb,breadCrumbPath,breadCrumbModel}',
'pathNames.[]'
)
get breadCrumbs() {
var controllers = this.controllers;
var defaultPaths = this.pathNames;
var breadCrumbs = A([]);
controllers.forEach(function (controller, index) {
var crumbs = controller.breadCrumbs || A([]);
var singleCrumb = controller.breadCrumb;
if (!isBlank(singleCrumb)) {
crumbs.push({
label: singleCrumb,
path: controller.breadCrumbPath,
model: controller.breadCrumbModel,
});
}
crumbs.forEach(function (crumb) {
breadCrumbs.addObject(
EmberObject.create({
label: crumb.label,
path: crumb.path || defaultPaths[index],
model: crumb.model,
linkable: isPresent(crumb.linkable)
? crumb.linkable
: crumb.path !== false,
isCurrent: false,
})
);
});
});
var deepestCrumb = get(breadCrumbs, 'lastObject');
if (deepestCrumb) {
deepestCrumb.isCurrent = true;
}
return breadCrumbs;
}
}
@@ -0,0 +1,12 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
~}}
<Hds::Breadcrumb
...attributes
data-test-breadcrumbs-container
{{did-insert this.registerContainer}}
>
{{yield}}
</Hds::Breadcrumb>
@@ -0,0 +1,37 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
/*
The `containers` property from the `breadcrumbsService` is tracked
so registering or unregistering a container will cause the DOM
to re-render a container with the correct breadcrumbs.
*/
export default class BreadcrumbsContainerComponent extends Component {
@service('breadcrumbs') breadcrumbsService;
container = null;
@action
registerContainer(element) {
// A child `ol` is rendered in the Hds::Breadcrumb container and this is
// the element that the in-element helper should drop breadcrumbs into.
this.container = {
element: element.querySelector('ol'),
};
this.breadcrumbsService.registerContainer(this.container);
}
willDestroy() {
super.willDestroy(...arguments);
this.breadcrumbsService.unregisterContainer(this.container);
}
}
@@ -0,0 +1,21 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
~}}
{{#each this.breadcrumbsService.containers as |container|}}
{{#in-element container.element insertBefore=null}}
<Hds::Breadcrumb::Item
@text={{@text}}
@icon={{@icon}}
@route={{@route}}
@models={{@models}}
@query={{@query}}
@model={{@model}}
@current={{this.current}}
...attributes
{{did-insert this.registerSelf}}
data-test-breadcrumbs-item
/>
{{/in-element}}
{{/each}}
@@ -0,0 +1,31 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
/*
`BreadcrumbsItem` inserts the crumbs to an existing container.
For our purposes, there will always be at most one container.
*/
export default class BreadcrumbsItemComponent extends Component {
@service('breadcrumbs') breadcrumbsService;
@tracked element = null;
@action
registerSelf(element) {
this.element = element;
}
get current() {
const crumbs = this.breadcrumbsService.containers[0].element.children;
const lastCrumb = crumbs[crumbs.length - 1];
return lastCrumb.isEqualNode(this.element);
}
}
@@ -76,73 +76,37 @@
{{/if}}
</Hds::Form::TextInput::Field>
<Hds::Form::Fieldset @isOptional={{true}} as |F|>
<F.Legend>{{t 'resources.auth-method.form.certificates.label'}}</F.Legend>
<F.HelperText>{{t
'resources.auth-method.form.certificates.help'
}}</F.HelperText>
{{#let (unique-id) as |valueHeaderID|}}
<F.Control>
<Hds::Table class='key-value-list' name='certificates'>
<:head as |H|>
<H.Tr>
<H.Th id={{valueHeaderID}}>{{t 'titles.value'}}</H.Th>
<H.Th>{{t 'titles.actions'}}</H.Th>
</H.Tr>
</:head>
<:body as |B|>
{{#each @model.certificates as |cert i|}}
<B.Tr>
<B.Td>
<Hds::Form::Textarea::Field
@value={{cert.value}}
@isOptional={{true}}
disabled={{form.disabled}}
aria-labelledby={{valueHeaderID}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{form.disabled}}
{{on 'click' (fn @removeItemByIndex 'certificates' i)}}
/>
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::Textarea::Field
@value={{this.newCertificate}}
@isOptional={{true}}
disabled={{form.disabled}}
aria-labelledby={{valueHeaderID}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.add'}}
@color='secondary'
disabled={{or form.disabled (not this.newCertificate)}}
{{on 'click' this.addCertificate}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
</F.Control>
{{/let}}
{{#if @model.errors.certificates}}
<F.Error as |E|>
{{#each @model.errors.certificates as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Fieldset>
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.auth-method.form.certificates.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.auth-method.form.certificates.help'}}
</F.HelperText>
{{#if @model.errors.certificates}}
<F.Error as |E|>
{{#each @model.errors.certificates as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.Textarea
@name='certificates'
@options={{@model.certificates}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
{{#if @model.isNew}}
<Hds::Form::TextInput::Field
@@ -152,6 +116,7 @@
@type='password'
name='client_certificate'
disabled={{form.disabled}}
@hasVisibilityToggle={{false}}
{{on 'input' (set-from-event @model 'client_certificate')}}
as |F|
>
@@ -177,6 +142,7 @@
@type='password'
name='client_certificate_key'
disabled={{form.disabled}}
@hasVisibilityToggle={{false}}
{{on 'input' (set-from-event @model 'client_certificate_key')}}
as |F|
>
@@ -277,6 +243,7 @@
@type='password'
name='bind_password'
disabled={{form.disabled}}
@hasVisibilityToggle={{false}}
{{on 'input' (set-from-event @model 'bind_password')}}
as |F|
>
@@ -424,7 +391,7 @@
}}</F.HelperText>
{{#let (unique-id) (unique-id) as |fromHeaderID toHeaderID|}}
<F.Control>
<Hds::Table class='key-value-list' name='account_attribute_maps'>
<Hds::Table class='list-wrapper-field' name='account_attribute_maps'>
<:head as |H|>
<H.Tr>
<H.Th id={{fromHeaderID}}>{{t
@@ -122,6 +122,7 @@
@helperText={{t 'form.client_secret.help'}}
@error={{@model.errors.client_secret}}
readonly={{false}}
@hasVisibilityToggle={{false}}
as |field|
>
{{#if @model.errors.client_secret}}
@@ -254,176 +255,71 @@
</Rose::Form::Fieldset>
{{! Allowed Audiences }}
<Rose::Form::Fieldset>
<:title>{{t 'form.allowed_audiences.label'}}</:title>
<:description>{{t 'form.allowed_audiences.help'}}</:description>
<:body>
{{#if @model.errors.allowed_audiences}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.allowed_audiences as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell>{{t 'titles.actions'}}</row.headerCell>
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.allowed_audiences as |audience i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='allowed_audiences'
@value={{audience.value}}
@error={{@model.errors.allowed_audiences}}
@contextual={{true}}
as |field|
>
<field.label>{{t 'form.claim.label'}}</field.label>
<field.field />
</form.input>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'allowed_audiences' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'form.allowed_audiences.label'}}
</F.Legend>
<F.HelperText>
{{t 'form.allowed_audiences.help'}}
</F.HelperText>
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='allowed_audiences'
@value={{this.newAllowedAudience}}
@label={{t 'resources.auth-method.titles.new-allowed-audience'}}
/>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn
@addStringItem 'allowed_audiences' this.newAllowedAudience
)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
{{#if @model.errors.allowed_audiences}}
<F.Error as |E|>
{{#each @model.errors.allowed_audiences as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.TextInput
@name='allowed_audiences'
@options={{@model.allowed_audiences}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
{{! Claims Scopes }}
<Rose::Form::Fieldset>
<:title>{{t 'form.claims_scopes.label'}}</:title>
<:description>{{t 'form.claims_scopes.help'}}</:description>
<:body>
{{#if @model.errors.claims_scopes}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.claims_scopes as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell />
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.claims_scopes as |claimsScope i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='claims_scopes'
@value={{claimsScope.value}}
@error={{@model.errors.claims_scopes}}
@contextual={{true}}
as |field|
>
<field.label>{{t 'form.claims_scope.label'}}</field.label>
<field.field />
</form.input>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'claims_scopes' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='claims_scopes'
@value={{this.newClaimsScope}}
@label={{t 'resources.auth-method.titles.new-claims-scope'}}
/>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @addStringItem 'claims_scopes' this.newClaimsScope)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'form.claims_scopes.label'}}
</F.Legend>
<F.HelperText>
{{t 'form.claims_scopes.help'}}
</F.HelperText>
{{#if @model.errors.claims_scopes}}
<F.Error as |E|>
{{#each @model.errors.claims_scopes as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.TextInput
@name='claims_scopes'
@options={{@model.claims_scopes}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
{{! Account Claim Maps }}
<Rose::Form::Fieldset>
@@ -560,94 +456,37 @@
</Rose::Form::Fieldset>
{{! Certificates }}
<Rose::Form::Fieldset>
<:title>{{t 'form.idp_ca_certs.label'}}</:title>
<:description>{{t 'form.idp_ca_certs.help'}}</:description>
<:body>
{{#if @model.errors.idp_ca_certs}}
<Rose::Form::Errors as |errors|>
{{#each @model.errors.idp_ca_certs as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</Rose::Form::Errors>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell>{{t 'titles.actions'}}</row.headerCell>
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.idp_ca_certs as |cert i|}}
<body.row as |row|>
<row.cell>
<Hds::Form::Textarea::Field
@value={{cert.value}}
@isOptional={{true}}
@isInvalid={{@model.errors.idp_ca_certs}}
disabled={{form.disabled}}
aria-label={{t 'form.certificate.label'}}
as |F|
>
{{#if @model.errors.idp_ca_certs}}
<F.Error as |E|>
{{#each @model.errors.idp_ca_certs as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Textarea::Field>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'idp_ca_certs' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'form.idp_ca_certs.label'}}
</F.Legend>
<F.HelperText>
{{t 'form.idp_ca_certs.help'}}
</F.HelperText>
<body.row as |row|>
<row.cell>
<Hds::Form::Textarea::Field
@value={{this.newCertificate}}
@isOptional={{true}}
disabled={{form.disabled}}
aria-label={{t 'resources.auth-method.titles.new-certificate'}}
/>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @addStringItem 'idp_ca_certs' this.newCertificate)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
{{#if @model.errors.idp_ca_certs}}
<F.Error as |E|>
{{#each @model.errors.idp_ca_certs as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.Textarea
@name='idp_ca_certs'
@options={{@model.idp_ca_certs}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
<form.input
@name='max_age'
@@ -236,55 +236,69 @@
{{/if}}
</Hds::Form::TextInput::Field>
<Form::Field::KeyValueList
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@name='critical_options'
@options={{@model.critical_options}}
@addOption={{fn this.addOption 'critical_options'}}
@removeOptionByIndex={{fn this.removeOptionByIndex 'critical_options'}}
@disabled={{form.disabled}}
as |M|
>
<M.Legend>
{{t 'resources.credential-library.form.critical_options.label'}}
</M.Legend>
<M.HelperText>
{{t 'resources.credential-library.form.critical_options.help'}}
</M.HelperText>
{{#if @model.errors.critical_options}}
<M.Error as |E|>
{{#each @model.errors.critical_options as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</M.Error>
{{/if}}
</Form::Field::KeyValueList>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.credential-library.form.critical_options.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.credential-library.form.critical_options.help'}}
</F.HelperText>
<Form::Field::KeyValueList
{{#if @model.errors.critical_options}}
<F.Error as |E|>
{{#each @model.errors.critical_options as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.KeyValue
@name='critical_options'
@options={{@model.critical_options}}
@model={{@model}}
@disabled={{form.disabled}}
/>
</:field>
</Form::Field::ListWrapper>
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@name='extensions'
@options={{@model.extensions}}
@addOption={{fn this.addOption 'extensions'}}
@removeOptionByIndex={{fn this.removeOptionByIndex 'extensions'}}
@disabled={{form.disabled}}
as |M|
>
<M.Legend>
{{t 'resources.credential-library.form.extensions.label'}}
</M.Legend>
<M.HelperText>
{{t 'resources.credential-library.form.extensions.help'}}
</M.HelperText>
{{#if @model.errors.extensions}}
<M.Error as |E|>
{{#each @model.errors.extensions as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</M.Error>
{{/if}}
</Form::Field::KeyValueList>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.credential-library.form.extensions.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.credential-library.form.extensions.help'}}
</F.HelperText>
{{#if @model.errors.extensions}}
<F.Error as |E|>
{{#each @model.errors.extensions as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.KeyValue
@name='extensions'
@options={{@model.extensions}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
{{#if (can 'save model' @model)}}
<form.actions
@@ -4,7 +4,7 @@
*/
import Component from '@glimmer/component';
import { action, set } from '@ember/object';
import {
options,
TYPES_CREDENTIAL_LIBRARY,
@@ -31,30 +31,4 @@ export default class FormCredentialLibraryVaultSshCertComponent extends Componen
const keyType = this.args.model.key_type;
return keyType === 'rsa' || keyType === 'ecdsa';
}
/**
* Adds a key/value option object. We recreate a new array after adding
* so that ember is aware that the array has been modified.
* @param field {string}
* @param key {string}
* @param value {string}
*/
@action
addOption(field, { key, value }) {
const existingArray = this.args.model[field] ?? [];
const newArray = [...existingArray, { key, value }];
set(this.args.model, field, newArray);
}
/**
* Removes an option by index. We recreate a new array after
* splicing out the item so that ember is aware that the array has been modified.
* @param field {string}
* @param index {number}
*/
@action
removeOptionByIndex(field, index) {
const newArray = this.args.model[field].filter((_, i) => i !== index);
set(this.args.model, field, newArray);
}
}
@@ -124,6 +124,7 @@
@label={{t 'resources.credential.form.private_key_passphrase.label'}}
@helperText={{t 'resources.credential.form.private_key_passphrase.help'}}
@error={{@model.errors.private_key_passphrase}}
@hasVisibilityToggle={{false}}
autocomplete='new-password'
as |field|
>
@@ -1,100 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Hds::Form::Fieldset
@layout={{@layout}}
@isRequired={{@isRequired}}
@isOptional={{@isOptional}}
as |F|
>
{{yield F}}
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
<F.Control>
<Hds::Table class='key-value-list' name={{@name}}>
<:head as |H|>
<H.Tr>
<H.Th id={{keyHeaderID}}>{{t 'form.key.label'}}</H.Th>
<H.Th id={{valueHeaderID}}>{{t 'form.value.label'}}</H.Th>
<H.Th>
{{#if @removeOptionByIndex}}
{{t 'titles.actions'}}
{{/if}}
</H.Th>
</H.Tr>
</:head>
<:body as |B|>
{{#each @options as |option index|}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{option.key}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{keyHeaderID}}
{{on 'input' (set-from-event option 'key')}}
/>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{option.value}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'input' (set-from-event option 'value')}}
/>
</B.Td>
<B.Td>
{{#if @removeOptionByIndex}}
<Hds::Button
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn @removeOptionByIndex index)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionKey}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{keyHeaderID}}
{{on 'input' (set-from-event this 'newOptionKey')}}
/>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionValue}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'input' (set-from-event this 'newOptionValue')}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionKey)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
</F.Control>
{{/let}}
</Hds::Form::Fieldset>
@@ -1,47 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { assert } from '@ember/debug';
export default class MappingListComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionKey = '';
/**
* @type {string}
*/
@tracked newOptionValue = '';
// =actions
/**
* If a new key value is entered and an addOption method was specified,
* calls addOption with the new key and value. Resets key and value.
*/
@action
addOption() {
assert(
'[boundary-admin-key-value-list-field] `@addOption` is required.',
this.args.addOption
);
if (this.newOptionKey) {
this.args.addOption({
key: this.newOptionKey,
value: this.newOptionValue,
});
}
this.newOptionKey = '';
this.newOptionValue = '';
}
}
@@ -0,0 +1,32 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Hds::Form::Fieldset
@layout={{@layout}}
@isRequired={{@isRequired}}
@isOptional={{@isOptional}}
as |F|
>
{{yield F to='fieldset'}}
<F.Control>
{{yield
(hash
KeyValue=(component
'form/field/list-wrapper/key-value' disabled=@disabled
)
TextInput=(component
'form/field/list-wrapper/text-input' disabled=@disabled
)
Textarea=(component
'form/field/list-wrapper/textarea' disabled=@disabled
)
)
to='field'
}}
</F.Control>
</Hds::Form::Fieldset>
@@ -0,0 +1,92 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
<Hds::Table class='list-wrapper-field' name={{@name}}>
<:head as |H|>
<H.Tr>
<H.Th id={{keyHeaderID}}>{{t 'form.key.label'}}</H.Th>
<H.Th id={{valueHeaderID}}>{{t 'form.value.label'}}</H.Th>
<H.Th>
{{t 'titles.actions'}}
</H.Th>
</H.Tr>
</:head>
<:body as |B|>
{{#each this.options as |option index|}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{option.key}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{keyHeaderID}}
{{on 'input' (set-from-event option 'key')}}
/>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{option.value}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'input' (set-from-event option 'value')}}
/>
</B.Td>
<B.Td>
{{#if this.removeOptionByIndex}}
<Hds::Button
data-test-remove-button
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn this.removeOptionByIndex index)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionKey}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{keyHeaderID}}
{{on 'input' (set-from-event this 'newOptionKey')}}
/>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionValue}}
@type='text'
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'input' (set-from-event this 'newOptionValue')}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionKey)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
{{/let}}
@@ -0,0 +1,79 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { set } from '@ember/object';
export default class MappingListComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionKey = '';
/**
* @type {string}
*/
@tracked newOptionValue = '';
/**
* Returns an array of key/value pair that the user enters
* @type {object}
*/
get options() {
return this.args?.options || this.args?.model?.[this.args.name];
}
// =actions
/**
* If a new key value is entered and an addOption method was specified,
* calls addOption with the new key and value. Resets key and value.
* Otherwise use the model argument to create the array and update the model
*/
@action
addOption() {
if (this.args.addOption) {
if (this.newOptionKey) {
this.args.addOption({
key: this.newOptionKey,
value: this.newOptionValue,
});
}
} else {
const field = this.args.name;
const existingArray = this.args.model[field] ?? [];
const newArray = [
...existingArray,
{ key: this.newOptionKey, value: this.newOptionValue },
];
set(this.args.model, field, newArray);
}
this.newOptionKey = '';
this.newOptionValue = '';
}
/**
* If removeOptionByIndex method was passed, use that.
* Otherwise, use the model to remove an option by index. We recreate a new array after
* splicing out the item so that ember is aware that the array has been modified.
* @param index {number}
*/
@action
removeOptionByIndex(index) {
if (this.args.removeOptionByIndex) {
this.args.removeOptionByIndex(index);
} else {
const field = this.args.name;
const newArray = this.args.model[field].filter((_, i) => i !== index);
set(this.args.model, field, newArray);
}
}
}
@@ -0,0 +1,62 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Hds::Table class='list-wrapper-field' name={{@name}}>
<:body as |B|>
{{#each @options as |option index|}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{option.value}}
@type='text'
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
{{on 'input' (set-from-event option 'value')}}
/>
</B.Td>
<B.Td>
{{#if this.removeOptionByIndex}}
<Hds::Button
data-test-remove-button
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn this.removeOptionByIndex index)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionValue}}
@type='text'
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
{{on 'input' (set-from-event this 'newOptionValue')}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionValue)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
@@ -0,0 +1,61 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { set } from '@ember/object';
export default class MappingListTextInputComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionValue = '';
// =actions
/**
* If a new input is entered and an addOption method was specified,
* calls addOption with the new input. Resets previous value.
* Otherwise use the model argument to create the array and update the model
*/
@action
addOption() {
if (this.args.addOption) {
if (this.newOptionValue) {
this.args.addOption({
value: this.newOptionValue,
});
}
} else {
const field = this.args.name;
const existingArray = this.args.model[field] ?? [];
const newArray = [...existingArray, { value: this.newOptionValue }];
set(this.args.model, field, newArray);
}
this.newOptionValue = '';
}
/**
* If removeOptionByIndex method was passed, use that.
* Otherwise, use the model to remove an option by index. We recreate a new array after
* splicing out the item so that ember is aware that the array has been modified.
* @param index {number}
*/
@action
removeOptionByIndex(index) {
if (this.args.removeOptionByIndex) {
this.args.removeOptionByIndex(index);
} else {
const field = this.args.name;
const newArray = this.args.model[field].filter((_, i) => i !== index);
set(this.args.model, field, newArray);
}
}
}
@@ -0,0 +1,60 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Hds::Table class='list-wrapper-field' name={{@name}}>
<:body as |B|>
{{#each @options as |option index|}}
<B.Tr>
<B.Td>
<Hds::Form::Textarea::Field
@value={{option.value}}
@type='text'
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
/>
</B.Td>
<B.Td>
{{#if this.removeOptionByIndex}}
<Hds::Button
data-test-remove-button
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn this.removeOptionByIndex index)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::Textarea::Field
@value={{this.newOptionValue}}
@type='text'
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
/>
</B.Td>
<B.Td>
<Hds::Button
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionValue)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
@@ -0,0 +1,61 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { set } from '@ember/object';
export default class MappingListTextAreaComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionValue = '';
// =actions
/**
* If a new input is entered and an addOption method was specified,
* calls addOption with the new input. Resets previous value.
* Otherwise use the model argument to create the array and update the model
*/
@action
addOption() {
if (this.args.addOption) {
if (this.newOptionValue) {
this.args.addOption({
value: this.newOptionValue,
});
}
} else {
const field = this.args.name;
const existingArray = this.args.model[field] ?? [];
const newArray = [...existingArray, { value: this.newOptionValue }];
set(this.args.model, field, newArray);
}
this.newOptionValue = '';
}
/**
* If removeOptionByIndex method was passed, use that.
* Otherwise, use the model to remove an option by index. We recreate a new array after
* splicing out the item so that ember is aware that the array has been modified.
* @param index {number}
*/
@action
removeOptionByIndex(index) {
if (this.args.removeOptionByIndex) {
this.args.removeOptionByIndex(index);
} else {
const field = this.args.name;
const newArray = this.args.model[field].filter((_, i) => i !== index);
set(this.args.model, field, newArray);
}
}
}
@@ -14,6 +14,7 @@
@type={{(or @type 'password')}}
name={{@name}}
disabled={{or @isDisabled (not this.isEditing)}}
@hasVisibilityToggle={{false}}
...attributes
as |F|
>
@@ -69,178 +69,61 @@
</F.HelperText>
</InfoField>
<Rose::Form::Fieldset
@helperText={{t 'resources.host-set.form.preferred_endpoints.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-set.preferred-endpoints'}}
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:title>{{t 'resources.host-set.form.preferred_endpoints.label'}}</:title>
<:body>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.host-set.form.preferred_endpoints.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.host-set.form.preferred_endpoints.help'}}
</F.HelperText>
{{#if @model.errors.preferred_endpoints}}
<Rose::Form::Errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.preferred_endpoints as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</Rose::Form::Errors>
</F.Error>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell />
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.preferred_endpoints as |endpoint i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='preferred_endpoints'
@value={{endpoint.value}}
@error={{@model.errors.preferred_endpoints}}
@contextual={{true}}
placeholder='cidr:2001:4860:4860::8888/32'
as |field|
>
<field.field />
</form.input>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'preferred_endpoints' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
</:fieldset>
<:field as |F|>
<F.TextInput
@name='preferred_endpoints'
@options={{@model.preferred_endpoints}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='preferred_endpoints'
@value={{@model.preferred_endpoint}}
placeholder='cidr:2001:4860:4860::8888/32'
/>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn
@addStringItem
'preferred_endpoints'
@model.preferred_endpoint
)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
<Form::Field::ListWrapper @layout='horizontal' @disabled={{form.disabled}}>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.host-set.form.filter.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.host-set.form.filter.aws.help'}}
</F.HelperText>
<Rose::Form::Fieldset>
<:title>{{t 'resources.host-set.form.filter.label'}}</:title>
<:description>{{t 'resources.host-set.form.filter.aws.help'}}</:description>
<:body>
{{#if @model.errors.filters}}
<Rose::Form::Errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.filters as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</Rose::Form::Errors>
</F.Error>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell />
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.filters as |filter i|}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='filters'
@value={{filter.value}}
@error={{@model.errors.filters}}
@contextual={{true}}
as |field|
>
<field.field />
</form.input>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'filters' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
<body.row as |row|>
<row.cell>
<form.input
@type='text'
@name='filters'
@value={{@model.filter_string}}
/>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn @addStringItem 'filters' @model.filter_string)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
</:fieldset>
<:field as |F|>
<F.TextInput
@name='filters'
@options={{@model.filters}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
<form.input
@name='sync_interval_seconds'
Loaded 100 of 470 files, more files were not shown because too many files have changed in this diff. Show more