Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1915 from hashicorp/ui-config-variables
Browse files Browse the repository at this point in the history
UI: Project-level Config Variables
  • Loading branch information
gregone authored Sep 7, 2021
2 parents 0bb53cf + b3c1ef8 commit cf53aa7
Show file tree
Hide file tree
Showing 28 changed files with 686 additions and 47 deletions.
3 changes: 3 additions & 0 deletions .changelog/1915.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
ui: Allow config variables to be managed in the browser UI
```
2 changes: 1 addition & 1 deletion ui/app/components/app-form/project-repository-settings.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
@offLabel='git sync off'
@name="Git Sync"
/>
<toggle.label for="git-sync-enabled">
<toggle.label for="git-sync-enabled" @value={{not this.project.dataSourcePoll.enabled}}>
<span class="pds-toggleLabel">
{{t "form.project_git_settings.git_sync_description_label"}}
</span>
Expand Down
145 changes: 145 additions & 0 deletions ui/app/components/project-config-variables/list-item.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<li data-test-config-variables-list-item>
{{#if (or this.isEditing this.isCreating)}}
<form class="pds-form" data-test-config-variables-form {{on "submit" this.saveVariable}}>
<div part="fields" class="card">
<div class="card-header">
<h4>{{t "form.config_variables_settings.title"}}</h4>
</div>
<fieldset class="pds-formFieldSet">
<div>
<div class="pds-formField">
<label for="config-variable-name" class="pds-fieldName">
{{t "form.config_variables_settings.variable_name"}}
</label>
<Pds::Input
@type="text"
data-test-config-variables-var-name
id="config-variable-name"
placeholder={{t "form.config_variables_settings.variable_name_placeholder"}}
value={{this.variable.name}}
{{on "input" (pick "target.value" (set this "variable.name"))}}
/>
</div>
<div class="pds-formField">
<label for="config-variable-value" class="pds-fieldName">
{{t "form.config_variables_settings.variable_value"}}
</label>
<div class="pds-formField">
<Pds::Input
@type="text"
id="config-variable-value"
data-test-config-variables-var-static
placeholder={{t "form.config_variables_settings.variable_value_placeholder"}}
value={{this.variable.pb_static}}
{{on "input" (pick "target.value" (set this "variable.pb_static"))}}
/>
</div>
</div>
</div>
<div class="pds-formField">
<div class="pds-formFieldToggle">
<XToggle
@showLabels={{true}}
@value={{this.variable.nameIsPath}}
@size="small"
@onToggle={{fn (mut this.variable.nameIsPath)}} as |toggle|>
<toggle.switch
@showLabels={{false}}
@name="name-is-path"
/>
<toggle.label
data-test-config-variables-name-is-path-toggle
for="name-is-path"
@value={{not this.variable.nameIsPath}}>
<span class="pds-toggleLabel">
{{t "form.config_variables_settings.nameIsPath.label"}}
</span>
</toggle.label>
</XToggle>
</div>
</div>
<div class="pds-formField">
<div class="pds-formFieldToggle">
<XToggle
@showLabels={{true}}
@value={{this.variable.internal}}
@size="small"
@onToggle={{fn (mut this.variable.internal)}} as |toggle|>
<toggle.switch
@showLabels={{false}}
@name="internal"
/>
<toggle.label
data-test-config-variables-internal-toggle
for="internal"
@value={{not this.variable.internal}}>
<span class="pds-toggleLabel">
{{t "form.config_variables_settings.internal.label"}}
</span>
</toggle.label>
</XToggle>
</div>
</div>
</fieldset>
<hr/>
<div class="card-footer">
<Pds::ButtonSet>
<Pds::Button
data-test-config-variables-edit-save
@variant="primary"
@type="submit">
{{t "form.config_variables_settings.button_submit"}}
</Pds::Button>
<Pds::Button
data-test-config-variables-edit-cancel
@variant="secondary"
{{on "click"
(if this.isCreating this.cancelCreate this.cancelEdit)
}}>
{{t "form.config_variables_settings.button_cancel"}}
</Pds::Button>
</Pds::ButtonSet>
</div>
</div>
</form>
{{else}}
<div class="variables--list-item">
<span class="variables--list-item-name" data-test-config-variables-var-name>{{this.variable.name}}</span>
<span class="variables--list-item-value" data-test-config-variables-var-value>
{{#if this.variable.pb_static}}
<pre><code>{{this.variable.pb_static}}</code></pre>
{{/if}}
{{#if this.variable.dynamic.from}}
<pre><code>dynamic via {{this.variable.dynamic.from}}</code></pre>
{{/if}}
</span>
<span class="variables--list-item-internal" data-test-config-variables-var-internal><pre><code>{{this.variable.internal}}</code></pre></span>
<span class="variables--list-item-name-is-path" data-test-config-variables-var-name-is-path><pre><code>{{this.variable.nameIsPath}}</code></pre></span>
{{#if this.isEditable}}
<Pds::Dropdown @align="right" as |D|>
<D.Trigger
data-test-config-variables-dropdown
@variant="ghost">
{{t "form.config_variables_settings.variable_dropdown.trigger"}}
</D.Trigger>
<D.Dialog >
<section>
<Pds::Button
data-test-config-variables-dropdown-edit
@variant="secondary"
{{on "click" (fn this.editVariable this.variable)}}>
{{t "form.config_variables_settings.variable_dropdown.edit"}}
</Pds::Button>
<Pds::Button
data-test-config-variables-dropdown-delete
@variant="warning"
{{on "click" (fn this.deleteVariable this.variable)}}>
{{t "form.config_variables_settings.variable_dropdown.delete"}}
</Pds::Button>
</section>
</D.Dialog>
</Pds::Dropdown>
{{/if}}
</div>
{{/if}}
</li>
81 changes: 81 additions & 0 deletions ui/app/components/project-config-variables/list-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { ConfigVar, Project } from 'waypoint-pb';
import { inject as service } from '@ember/service';
import ApiService from 'waypoint/services/api';
import FlashMessagesService from 'waypoint/services/flash-messages';

interface VariableArgs {
variable: ConfigVar.AsObject;
isEditing: boolean;
isCreating: boolean;
saveVariableSettings: (variable: ConfigVar.AsObject, deleteVariable?: boolean) => Promise<Project.AsObject>;
deleteVariable: (variable: ConfigVar.AsObject) => Promise<void>;
cancelCreate: () => void;
}

export default class ProjectConfigVariablesListItemComponent extends Component<VariableArgs> {
@service api!: ApiService;
@service flashMessages!: FlashMessagesService;

initialVariable!: ConfigVar.AsObject;
@tracked variable: ConfigVar.AsObject;
@tracked isCreating: boolean;
@tracked isEditing: boolean;

constructor(owner: unknown, args: VariableArgs) {
super(owner, args);
let { variable, isEditing, isCreating } = args;
this.variable = variable;
this.isEditing = isEditing;
this.isCreating = isCreating;
this.storeInitialVariable();
}

get isEditable(): boolean {
// Temporarily making Dynamic vars uneditable
return !this.variable.dynamic;
}

storeInitialVariable(): void {
this.initialVariable = JSON.parse(JSON.stringify(this.variable));
}

@action
async deleteVariable(variable: ConfigVar.AsObject): Promise<void> {
await this.args.deleteVariable(variable);
}

@action
editVariable(): void {
this.isEditing = true;
this.storeInitialVariable();
}

@action
async saveVariable(e: Event): Promise<void> {
e.preventDefault();
if (this.variable.name === '' || this.variable.pb_static === '') {
this.flashMessages.error('Variable keys or values can not be empty');
return;
}
await this.args.saveVariableSettings(this.variable, false);
this.isCreating = false;
this.isEditing = false;
}

@action
cancelCreate(): void {
this.isCreating = false;
this.isEditing = false;
this.args.cancelCreate();
}

@action
cancelEdit(): void {
this.isCreating = false;
this.isEditing = false;
this.variable = this.initialVariable;
}
}
39 changes: 39 additions & 0 deletions ui/app/components/project-config-variables/list.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{{#if this.variablesList}}
<ul class="variables-list">
<li>
<div class="variables--list-item variables--list-item__header">
<span class="variables--list-item-name">{{t "form.config_variables_settings.variable_name"}}</span>
<span class="variables--list-item-value">{{t "form.config_variables_settings.variable_value"}}</span>
<span class="variables--list-item-internal">{{t "form.config_variables_settings.variable_internal"}}</span>
<span class="variables--list-item-name-is-path">{{t "form.config_variables_settings.variable_nameIsPath"}}</span>
<span class="variables--list-item-actions"></span>
</div>
</li>
{{#each this.variablesList as |variable|}}
<ProjectConfigVariables::ListItem
@variable={{variable}}
@isEditing={{eq variable.name this.activeVariable.name}}
@isCreating={{this.isCreating}}
@cancelCreate={{this.cancelCreate}}
@saveVariableSettings={{this.saveVariableSettings}}
@deleteVariable={{this.deleteVariable}} />
{{/each}}
</ul>
<Pds::ButtonSet>
<Pds::Button data-test-config-variables-add-variable disabled={{this.isCreating}} @variant="primary" {{on "click" this.addVariable}}>
<Pds::Icon @type="plus-plain" />{{t "form.config_variables_settings.button_apply"}}
</Pds::Button>
</Pds::ButtonSet>
{{else}}
<Pds::EmptyState as |P|>
<P.Header>{{t "form.config_variables_settings.empty_state_heading"}}</P.Header>
<P.Body>
{{t "form.config_variables_settings.empty_state_content"}}
</P.Body>
<P.Footer>
<Pds::Button data-test-config-variables-add-variable @variant="primary" {{on "click" this.addVariable}}>
<Pds::Icon @type="plus-plain" />{{t "form.config_variables_settings.button_apply"}}
</Pds::Button>
</P.Footer>
</Pds::EmptyState>
{{/if}}
101 changes: 101 additions & 0 deletions ui/app/components/project-config-variables/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { ConfigSetRequest, ConfigGetRequest, ConfigVar, Project, Ref } from 'waypoint-pb';
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { inject as service } from '@ember/service';
import ApiService from 'waypoint/services/api';
import FlashMessagesService from 'waypoint/services/flash-messages';

interface ProjectConfigArgs {
variablesList: ConfigVar.AsObject[];
project: Project.AsObject;
}

export default class ProjectConfigVariablesListComponent extends Component<ProjectConfigArgs> {
@service api!: ApiService;
@service flashMessages!: FlashMessagesService;

@tracked variablesList: Array<ConfigVar.AsObject>;
@tracked project: Project.AsObject;
@tracked isCreating: boolean;
@tracked activeVariable;

constructor(owner: unknown, args: ProjectConfigArgs) {
super(owner, args);
let { variablesList, project } = args;
this.variablesList = variablesList;
this.project = project;
this.activeVariable = null;
this.isCreating = false;
}

@action
cancelCreate(): void {
this.variablesList.splice(0, 1);
this.variablesList = [...this.variablesList];
this.isCreating = false;
}

@action
async deleteVariable(variable: ConfigVar.AsObject): Promise<void> {
await this.saveVariableSettings(variable, true);
}

@action
addVariable(): void {
this.isCreating = true;
let newVar = new ConfigVar();
let newVarObj = newVar.toObject();
this.variablesList = [newVarObj, ...this.variablesList];
this.activeVariable = newVarObj;
}

@action
async saveVariableSettings(variable: ConfigVar.AsObject, deleteVariable?: boolean): Promise<void> {
let req = new ConfigSetRequest();

let projectRef = new Ref.Project();
projectRef.setProject(this.project.name);

let newVar = new ConfigVar();

newVar.setProject(projectRef);

newVar.setName(variable.name);

if (variable?.pb_static) {
newVar.setStatic(variable.pb_static);
}

if (variable.internal) {
newVar.setInternal(variable.internal);
}

if (variable.nameIsPath) {
newVar.setNameIsPath(variable.nameIsPath);
}

if (deleteVariable) {
newVar.setUnset(new Empty());
}

req.setVariablesList([newVar]);
try {
await this.api.client.setConfig(req, this.api.WithMeta());
// If Config Var was saved correctly, refetch Config
let configRef = new Ref.Project();
configRef.setProject(this.project.name);
let configReq = new ConfigGetRequest();
configReq.setProject(configRef);

let config = await this.api.client.getConfig(configReq, this.api.WithMeta());

this.variablesList = config?.toObject().variablesList;
this.activeVariable = null;
this.isCreating = false;
} catch (err) {
this.flashMessages.error('Failed to save Variable', { content: err.message, sticky: true });
}
}
}
6 changes: 3 additions & 3 deletions ui/app/components/project-input-variables/list-item.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
</div>
</form>
{{else}}
<div class="project-input-variables--list-item">
<span class="project-input-variables--list-item-name" data-test-input-variables-var-name>{{this.variable.name}}</span>
<span class="project-input-variables--list-item-value" data-test-input-variables-var-value>
<div class="variables--list-item">
<span class="variables--list-item-name" data-test-input-variables-var-name>{{this.variable.name}}</span>
<span class="variables--list-item-value" data-test-input-variables-var-value>
{{#if this.isHcl}}
<b class="badge" data-test-input-variables-list-item-is-hcl>
HCL
Expand Down
Loading

0 comments on commit cf53aa7

Please sign in to comment.