-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI: [VAULT-19096] Customizable banners (#23945)
* UI: [VAULT-21521] Initial config-ui engine and routes set up (#23922) * UI: [VAULT-21526] Create adapter, serializer, and model files (#23947) * UI: [VAULT-21588] Add Custom Messages to the sidebar (#23946) * UI: [VAULT-21527] Mirage setup (#24000) * UI: [VAULT-21530] Custom Messages List View w/ Pagination and LazyPaginatedQuery (#24133) * UI: Add list to adapter query param (#24187) * UI: [VAULT-21532] Create message (#24407) * WIP create message * Add breadcrumns * Create and edit form * Add save to create/edit form * Add cancel and todo * Fix cancel route * Fix breadcrumb label to be title case * add start time logic * Update breadcrumb * Fix breadcrumbs and merge conflict test * Update create form description * Fix sidenav so it always highlights * Fix up forms * Mostly working create form * Form cleanup * Fix link title and href form fields * Default startTime * Fix messages * Update dropdown to use the updated ConfirmAction component * Update create and edit form * Add wip tests * Fix breadcrumb formatter * Comment out test * Update create message test * Update more tests * Add comment for fixing date on edit * Update Message form * Code cleanup! * Add validation tests * Remove authenticated from route model * SOme more code cleanup * Add controller so authenticated is parsed * Working radio buttons * Use an object instead of arrays * Wip date form * Fix license headers * Fix license headers addition of files * Fix copyright format issues and clean up code * Fix tests * Rename FormField radio getter and ay11 improvements * Address feedback * Fix specific date so it remembers the values * Address feedback! * Update more form fields * Use formfield action instead * Update to every * Update syntax of onchange * Fix tests * Update willDestroy so it doesnt break tests * Remove set and brodcast datetimelocal * Put FormField back the way it was in favor of putting FormField to a seperate PR * Remove getter in formfield component file * Address more feedback * Put back test * Update datetime string format var name and location * UI: [VAULT-21534 VAULT-21533 VAULT-21536] edit, preview, and delete custom message (#24603) * Working edit * VAULT-21536 update delete message and create/update flash message * VAULT-21533 add preview modal * Update serializer * Preview refinements * Move preview to its own component * Move breadcrumbs to setupController * Add more tests * Address some feedback * Address more feedback! * Update serailizer * Remove stylesheet * Add comment * UI: [VAULT-21435] Message details (#24645) * WIP * Fix timezone bug * Fix date issues on create/edit form * Add details screen * Use allFields instead of formFields * Fix tests * Address comments! * UI: VAULT-21538 unauth endpoint message display (#24665) * WIP unauth display * Add modal custom message * Close multiple modals * Update todo with ticket number * On init make custom message request * Use serializer * Update fetchMessages * Add copyright headers * Add services and serializers * Send null instead of empty strings * Fix tests! * Add copywrite headers * Add some acceptance tests * Test cleanup * Put tests back * pass hooks to module * Move module out * Seperate tests * Copywrite * Add aria-prohibited-attr runList options * Code cleanup * Add date-time-local transform * Add copyright headers * Remove comments * Remove date transform stuff for now! * Put getISODateFormat back into the serailize function * UI: Date time local transform (#24694) * Date time local * Add deserialize * Add copyright header * check if date exists * Use parseISO for date strings since datefns requires this in new update * Update tests * Ensure we cehck for an ISOString * Add checks so tests wont fail * Update parseISO * Address feedback * UI: multiple banner message on create and edit form (#24742) * WIP multiple banner message on create and edit form * Fix tests * Put checks back * Add try/catch to query * Fix breadcrumbs * Add page size to pagination * Add multiple modal message tests * Address feedback * Check for valid form first * Add extra checks * Address feedback * Move getter to the route * Fix tests! * Address more feedback * Use still when cancelling * Update multiple banner modal * Fix tests * Set user confirmation to empty string * UI: VAULT-21539 auth messages display (#24842) * WIP auth message display * Move block to show only when authenticated * VAULT-22046 working search by name * Some code clean up * Fix merge conflict * Add tests * Fetch messages again after creation * UI: [VAULT-22908] Update kv object editor, add max number of messages reached modal, small improvements (#24918) * Update kv object editor to only use a single row * continute using kv editype * Fix failing dashboard tests! * Fix failing test on sidebranch * Fix tests and update validations * Add optional tag * Address feedback * Add documentation * Clear messages when logging out * Fix tests! * Add 100 message limit modal * Add max message modal test * Do more checks! * Pair with Claire on the refactor of validator! * Only show validationerror for multiple rows * Update pageSize to 100 since when paginations are active it causes accessbility errors * Fix tests! * Add links to test * Make banners dismissable * Add cancel button * Address feedback! * Update test selectors * Update validator * Remove validations check in kvobjecteditor * Revert validationError in kvobjecteditor template * Put back if/else statements for link * Add changelog * UI: fix link bug and add colors (#24977) * Fix edit bug and put transform back * Edit badgeColor * Add tests * Revert changes to transform * Edit badge colors * remove universal object transform * Update changelog filename * UI: Add form inline warning (#24986) * Add form inline warning * Remove title * Only show form warning for unauth * Address feedback!
- Loading branch information
1 parent
b49c673
commit b85365e
Showing
64 changed files
with
2,452 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:feature | ||
**Custom messages**: Introduces custom messages settings, allowing users to view, and operators to configure system-wide messages. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import ApplicationAdapter from '../application'; | ||
|
||
export default class MessageAdapter extends ApplicationAdapter { | ||
pathForType() { | ||
return 'config/ui/custom-messages'; | ||
} | ||
|
||
query(store, type, query) { | ||
const { authenticated } = query; | ||
return super.query(store, type, { authenticated, list: true }); | ||
} | ||
|
||
queryRecord(store, type, id) { | ||
return this.ajax(`${this.buildURL(type)}/${id}`, 'GET'); | ||
} | ||
|
||
updateRecord(store, type, snapshot) { | ||
return this.ajax(`${this.buildURL(type)}/${snapshot.record.id}`, 'POST', { | ||
data: this.serialize(snapshot.record), | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
import Model, { attr } from '@ember-data/model'; | ||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; | ||
import { isAfter, addDays, startOfDay, parseISO } from 'date-fns'; | ||
import { withModelValidations } from 'vault/decorators/model-validations'; | ||
import { withFormFields } from 'vault/decorators/model-form-fields'; | ||
|
||
const validations = { | ||
title: [{ type: 'presence', message: 'Title is required.' }], | ||
message: [{ type: 'presence', message: 'Message is required.' }], | ||
link: [ | ||
{ | ||
validator(model) { | ||
if (!model?.link) return true; | ||
const [title] = Object.keys(model.link); | ||
const [href] = Object.values(model.link); | ||
return title || href ? !!(title && href) : true; | ||
}, | ||
message: 'Link title and url are required.', | ||
}, | ||
], | ||
}; | ||
|
||
@withModelValidations(validations) | ||
@withFormFields(['authenticated', 'type', 'title', 'message', 'link', 'startTime', 'endTime']) | ||
export default class MessageModel extends Model { | ||
@attr('boolean') active; | ||
@attr('string', { | ||
label: 'Type', | ||
editType: 'radio', | ||
possibleValues: [ | ||
{ | ||
label: 'Alert message', | ||
subText: | ||
'A banner that appears on the top of every page to display brief but high-signal messages like an update or system alert.', | ||
value: 'banner', | ||
}, | ||
{ | ||
label: 'Modal', | ||
subText: 'A pop-up window used to bring immediate attention for important notifications or actions.', | ||
value: 'modal', | ||
}, | ||
], | ||
defaultValue: 'banner', | ||
}) | ||
type; | ||
// The authenticated attr is a boolean. The authenticatedString getter and setter is used only in forms to get and set the boolean via | ||
// strings values. The server and query params expects the attr to be boolean values. | ||
@attr({ | ||
label: 'Where should we display this message?', | ||
editType: 'radio', | ||
fieldValue: 'authenticatedString', | ||
possibleValues: [ | ||
{ | ||
label: 'After the user logs in', | ||
subText: 'Display to users after they have successfully logged in to Vault.', | ||
value: 'authenticated', | ||
}, | ||
{ | ||
label: 'On the login page', | ||
subText: 'Display to users on the login page before they have authenticated.', | ||
value: 'unauthenticated', | ||
}, | ||
], | ||
defaultValue: true, | ||
}) | ||
authenticated; | ||
|
||
get authenticatedString() { | ||
return this.authenticated ? 'authenticated' : 'unauthenticated'; | ||
} | ||
|
||
set authenticatedString(value) { | ||
this.authenticated = value === 'authenticated' ? true : false; | ||
} | ||
|
||
@attr('string') | ||
title; | ||
@attr('string', { | ||
editType: 'textarea', | ||
}) | ||
message; | ||
@attr('dateTimeLocal', { | ||
editType: 'dateTimeLocal', | ||
label: 'Message starts', | ||
subText: 'Defaults to 12:00 a.m. the following day (local timezone).', | ||
defaultValue: addDays(startOfDay(new Date()), 1).toISOString(), | ||
}) | ||
startTime; | ||
@attr('dateTimeLocal', { editType: 'yield', label: 'Message expires' }) endTime; | ||
|
||
@attr('object', { | ||
editType: 'kv', | ||
keyPlaceholder: 'Display text (e.g. Learn more)', | ||
valuePlaceholder: 'Link URL (e.g. https://www.learnmore.com)', | ||
label: 'Link (optional)', | ||
isSingleRow: true, | ||
allowWhiteSpace: true, | ||
}) | ||
link; | ||
|
||
// date helpers | ||
get isStartTimeAfterToday() { | ||
return isAfter(parseISO(this.startTime), new Date()); | ||
} | ||
|
||
// capabilities | ||
@lazyCapabilities(apiPath`sys/config/ui/custom-messages`) customMessagesPath; | ||
|
||
get canCreateCustomMessages() { | ||
return this.customMessagesPath.get('canCreate') !== false; | ||
} | ||
get canReadCustomMessages() { | ||
return this.customMessagesPath.get('canRead') !== false; | ||
} | ||
get canEditCustomMessages() { | ||
return this.customMessagesPath.get('canUpdate') !== false; | ||
} | ||
get canDeleteCustomMessages() { | ||
return this.customMessagesPath.get('canDelete') !== false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import { decodeString, encodeString } from 'core/utils/b64'; | ||
import ApplicationSerializer from '../application'; | ||
|
||
export default class MessageSerializer extends ApplicationSerializer { | ||
attrs = { | ||
active: { serialize: false }, | ||
start_time: { serialize: false }, | ||
end_time: { serialize: false }, | ||
}; | ||
|
||
normalizeResponse(store, primaryModelClass, payload, id, requestType) { | ||
if (requestType === 'query' && !payload.meta) { | ||
const transformed = this.mapPayload(payload); | ||
return super.normalizeResponse(store, primaryModelClass, transformed, id, requestType); | ||
} | ||
if (requestType === 'queryRecord') { | ||
const transformed = { | ||
...payload.data, | ||
message: decodeString(payload.data.message), | ||
}; | ||
return super.normalizeResponse(store, primaryModelClass, transformed, id, requestType); | ||
} | ||
return super.normalizeResponse(store, primaryModelClass, payload, id, requestType); | ||
} | ||
|
||
serialize() { | ||
const json = super.serialize(...arguments); | ||
json.message = encodeString(json.message); | ||
return json; | ||
} | ||
|
||
mapPayload(payload) { | ||
if (payload.data) { | ||
if (payload.data?.keys && Array.isArray(payload.data.keys)) { | ||
return payload.data.keys.map((key) => { | ||
const data = { | ||
id: key, | ||
...payload.data.key_info[key], | ||
}; | ||
if (data.message) data.message = decodeString(data.message); | ||
return data; | ||
}); | ||
} | ||
Object.assign(payload, payload.data); | ||
delete payload.data; | ||
} | ||
return payload; | ||
} | ||
|
||
extractLazyPaginatedData(payload) { | ||
return this.mapPayload(payload); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import { action } from '@ember/object'; | ||
import Service, { inject as service } from '@ember/service'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { TrackedObject } from 'tracked-built-ins'; | ||
export default class CustomMessagesService extends Service { | ||
@service store; | ||
@service namespace; | ||
@service auth; | ||
@tracked messages = []; | ||
@tracked showMessageModal = true; | ||
bannerState = new TrackedObject(); | ||
|
||
constructor() { | ||
super(...arguments); | ||
this.fetchMessages(this.namespace.path); | ||
} | ||
|
||
get bannerMessages() { | ||
if (!this.messages || !this.messages.length) return []; | ||
return this.messages?.filter((message) => message?.type === 'banner'); | ||
} | ||
|
||
get modalMessages() { | ||
if (!this.messages || !this.messages.length) return []; | ||
return this.messages?.filter((message) => message?.type === 'modal'); | ||
} | ||
|
||
async fetchMessages(ns) { | ||
try { | ||
const url = this.auth.currentToken | ||
? '/v1/sys/internal/ui/authenticated-messages' | ||
: '/v1/sys/internal/ui/unauthenticated-messages'; | ||
const opts = { | ||
method: 'GET', | ||
headers: {}, | ||
}; | ||
if (this.auth.currentToken) opts.headers['X-Vault-Token'] = this.auth.currentToken; | ||
if (ns) opts.headers['X-Vault-Namespace'] = ns; | ||
const result = await fetch(url, opts); | ||
const body = await result.json(); | ||
if (body.errors) return (this.messages = []); | ||
const serializer = this.store.serializerFor('config-ui/message'); | ||
this.messages = serializer.mapPayload(body); | ||
this.bannerMessages?.forEach((bm) => (this.bannerState[bm.id] = true)); | ||
} catch (e) { | ||
return e; | ||
} | ||
} | ||
|
||
clearCustomMessages() { | ||
this.messages = []; | ||
} | ||
|
||
@action | ||
onBannerDismiss(id) { | ||
this.bannerState[id] = false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.