Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUN-551: Vue conversion of job option editor #8922

Merged
merged 167 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 146 commits
Commits
Show all changes
167 commits
Select commit Hold shift + click to select a range
d135de4
wip: job options editor convert to Vue
gschueler Dec 12, 2023
ff7fdc9
experiment with option values plugin display
gschueler Dec 13, 2023
3b9b6f3
i18n updates
gschueler Dec 14, 2023
acd8f0a
most list management controls, duplicate, delete, edit
gschueler Dec 14, 2023
1dcd316
Updates
gschueler Dec 15, 2023
68cc9d2
show new options edit only with nexUi=true
gschueler Dec 15, 2023
047ce39
fix: label text and spacing
gschueler Jan 25, 2024
4f64280
option name validation
gschueler Jan 26, 2024
5397094
fix: button spacing
gschueler Jan 26, 2024
7f13e85
add option validation
gschueler Feb 8, 2024
6bb7676
add option validation api endpoint
gschueler Feb 8, 2024
6fd08b4
parse value list if not set
gschueler Feb 8, 2024
5d31cc1
Add undo/redo
gschueler Feb 9, 2024
cac0136
add i18n
gschueler Feb 9, 2024
eca3574
prop types
gschueler Feb 9, 2024
161581b
optional fields
gschueler Feb 9, 2024
458e389
basic tests
gschueler Feb 9, 2024
5b1702e
change param types for validateFileOptConfig
gschueler Feb 12, 2024
4f62855
call file opt validation
gschueler Feb 12, 2024
ff88a22
update openapi annotation
gschueler Feb 12, 2024
aa1cba8
fix: pass configRemoteUrl for insert
gschueler Feb 12, 2024
1069c17
fix: field name
gschueler Feb 12, 2024
0135c1a
create JobOptionConfigRemoteUrl from map
gschueler Feb 12, 2024
b2e4fa3
correct field for new ui
gschueler Feb 12, 2024
7059cfe
pass valuesType field in validation request
gschueler Feb 12, 2024
dfa6c15
cleanup
gschueler Feb 12, 2024
e87d18a
fix: missing field
gschueler Feb 12, 2024
44f75b8
fix test
gschueler Feb 12, 2024
7391ea5
remove experiment
gschueler Feb 12, 2024
f251571
use field name realValuesUrl
gschueler Feb 12, 2024
6097970
support nested validation error, use configRemoteUrl.jsonFilter
gschueler Feb 12, 2024
7e403c1
show missing validation errors for some fields
gschueler Feb 12, 2024
5602ed9
fix: multivalued should be false if secure input type is selected
gschueler Feb 12, 2024
6993805
clear validation before revalidating
gschueler Feb 12, 2024
1f3db67
fix: options li style affects dropdowns within options list
gschueler Feb 12, 2024
240a8cc
add schema declaration
gschueler Feb 12, 2024
70bf665
cleanup: javadoc, lombok, generic type
gschueler Feb 12, 2024
4919ce1
missing javadoc
gschueler Feb 12, 2024
091abd2
define a prototype object
gschueler Feb 13, 2024
ab997e0
required field
gschueler Feb 13, 2024
e407658
cleanup
gschueler Feb 13, 2024
220e96a
correct input field names to canonical map names
gschueler Feb 13, 2024
07ebdff
move class definition
gschueler Feb 13, 2024
0be9e6f
pass configremoteurl from original option
gschueler Feb 14, 2024
ea276ad
fix: blank constraint does not apply to URL
gschueler Feb 15, 2024
027781c
fix: validation of valuesUrl on import
gschueler Feb 15, 2024
fff9f08
support json input for job options when modifying jobs
gschueler Feb 15, 2024
2f2b784
fix: field name for validation
gschueler Feb 15, 2024
8ca70ee
Require API version 47 for option validation endpoint
gschueler Feb 15, 2024
045f78a
update API description
gschueler Feb 15, 2024
53c1714
Add API functional tests for option validate endpoint
gschueler Feb 15, 2024
efa97b6
show validation errors for defaultStoragePath
gschueler Feb 15, 2024
f9293d6
test for scheduled job
gschueler Feb 15, 2024
a4d0650
add validation message for storage path
gschueler Feb 16, 2024
ba64f28
change url validation
gschueler Feb 16, 2024
2d06e8f
fix: remove storage path when changing input type
gschueler Feb 16, 2024
7316089
fix comment
gschueler Feb 16, 2024
a51a444
cleanup: prettier reformat
gschueler Feb 16, 2024
3fd0f1a
test fix: use messageSource mock instead of actual message values
gschueler Feb 16, 2024
b3726a6
fix: clone input instead of modifying original array
gschueler Feb 20, 2024
6d0e089
manage sortIndex of array to maintain ordering
gschueler Feb 20, 2024
08a14b9
fix: some changes were not calling job changed method, avoid referenc…
gschueler Feb 20, 2024
859697c
fix: undo/redo does not emit output data or update indexing
gschueler Feb 20, 2024
7d32850
fix: array not cloned deeply
gschueler Feb 20, 2024
8b0483f
add vuedraggable
gschueler Feb 20, 2024
0060433
implement drag and drop with vue draggable
gschueler Feb 20, 2024
51912d1
update i18n
gschueler Feb 20, 2024
d7fb6df
enable nextUI checkbox in job edit page, use interceptor to set param…
gschueler Feb 21, 2024
e97168e
fix: test for prepareCreateEditJob
gschueler Feb 21, 2024
5b588f7
update i18n
gschueler Feb 21, 2024
f41429d
move file
gschueler Feb 21, 2024
4a3162f
cleanup: unused
gschueler Feb 21, 2024
f3576fe
fix: title
gschueler Feb 21, 2024
6d8cf85
move and update tests
gschueler Feb 21, 2024
3dbc78a
update i18n keys
gschueler Feb 21, 2024
5d8e138
i18n for option view actions
gschueler Feb 21, 2024
4f9d9d6
tests for OptionItem
gschueler Feb 21, 2024
cfb231e
update tests for OptionView
gschueler Feb 21, 2024
678c5e3
fix: incorrect prototype values
gschueler Feb 21, 2024
3fa175c
classes for testing
gschueler Feb 21, 2024
a96f72d
fix: wrong prop for secure boolean
gschueler Feb 21, 2024
ca472d3
define computed prop for description
gschueler Feb 21, 2024
6a2f908
remove unneeded computed
gschueler Feb 21, 2024
5491703
fix: return value
gschueler Feb 21, 2024
d55b033
add target for watching
gschueler Feb 21, 2024
d5932b1
cleanup: unused
gschueler Feb 22, 2024
801dbc3
fix: incorrect field name, use type
gschueler Feb 22, 2024
bd7eb5e
initial tests for OptionsEditor
gschueler Feb 22, 2024
9f9a825
typescript fixes
gschueler Feb 22, 2024
c6fa180
wip: selenium tests
gschueler Feb 23, 2024
fd0e836
test edit and create actions
gschueler Feb 27, 2024
450f386
more tests
gschueler Feb 27, 2024
3b2f877
add types
gschueler Feb 27, 2024
a3f9454
operation move test
gschueler Feb 27, 2024
1fe2d05
fix: operationMove incorrect implementation
gschueler Feb 27, 2024
1b11034
move change event types to file
gschueler Feb 27, 2024
f9ec500
cleanup: remove async
gschueler Feb 27, 2024
96c1824
test operationInsert
gschueler Feb 27, 2024
c9a9be1
test operation
gschueler Feb 27, 2024
d93d304
test sortIndex values after actions
gschueler Feb 27, 2024
ef18a9a
fix: sortIndex not set after manual operation
gschueler Feb 28, 2024
59996ed
fix: validation errors not shown for some fields
gschueler Feb 28, 2024
090e68a
fix: correctly set validation error class for multivalue delmiter sec…
gschueler Feb 28, 2024
d4fced8
cleanup: valuesType watch
gschueler Feb 28, 2024
b9f7594
cleanup: enforcedType computed setter
gschueler Feb 28, 2024
b96efa5
remove ignorerest
gschueler Feb 28, 2024
8cb87fe
fix test
gschueler Feb 28, 2024
5ca661a
update option name input accessor
gschueler Feb 29, 2024
c496708
add todo note
gschueler Feb 29, 2024
672df32
fix: spacing for option name, description, default, and values and re…
gschueler Feb 29, 2024
4992708
remove bunched up pseudo-headers
gschueler Feb 29, 2024
f3c6246
fix: job search result test
gschueler Feb 29, 2024
846aba0
fix: tests for duplicate option
gschueler Feb 29, 2024
2522a04
move option usage preview to component
gschueler Mar 1, 2024
20ebb2c
test for file type in option edit
gschueler Mar 1, 2024
6464fbb
fix test
gschueler Mar 1, 2024
951c4f3
fix selector in next ui
gschueler Mar 1, 2024
81de41f
fix test
gschueler Mar 1, 2024
2cfeb08
fix: storage test
gschueler Mar 1, 2024
04602f3
test option name validation
gschueler Mar 1, 2024
7db9044
test label max length validation
gschueler Mar 1, 2024
3f529d7
validate name max length
gschueler Mar 1, 2024
9766f36
add maxlength
gschueler Mar 1, 2024
dd92ad8
max length error message
gschueler Mar 1, 2024
14a0a5c
change test attribute format
gschueler Mar 1, 2024
99a0d8e
test validation error section shown for field errors
gschueler Mar 1, 2024
e151fba
move config remote url section to separate component
gschueler Mar 1, 2024
6bef4c1
test updates
gschueler Mar 1, 2024
e53752c
selector fix
gschueler Mar 1, 2024
c7c0a32
fix test for secure option
gschueler Mar 1, 2024
7260e45
pass index
gschueler Mar 4, 2024
6c591a0
Merge branch 'main' into RUN-551
gschueler Mar 4, 2024
3fa0102
fix selector used
gschueler Mar 4, 2024
c7594eb
fix: warning about not closing OkHttp responses
gschueler Mar 5, 2024
53b31ec
add optional revert all behavior for undo/redo
gschueler Mar 5, 2024
cb2df8a
tests for undo redo component
gschueler Mar 5, 2024
ea39c15
fix spacing for buttons
gschueler Mar 5, 2024
7a49c30
add revertall behavior
gschueler Mar 5, 2024
e56c3a4
fix: revert all test
gschueler Mar 5, 2024
70cdd24
Merge branch 'main' into RUN-551
gschueler Mar 5, 2024
c539983
cleanup validation
gschueler Mar 5, 2024
79af193
test input type
gschueler Mar 5, 2024
d1a8113
cleanup prototype data
gschueler Mar 5, 2024
aea04ba
test option.valuesType
gschueler Mar 5, 2024
36e70da
reorder assignment
gschueler Mar 5, 2024
ab965c3
test remote validation
gschueler Mar 6, 2024
6298a26
update validation message
gschueler Mar 13, 2024
b9d0867
fix: use canonical field name storagePath
gschueler Mar 13, 2024
d24884a
move validation types to a file
gschueler Mar 13, 2024
e486596
use canonical field name type
gschueler Mar 13, 2024
fd4dc12
fix: remove incorrect prop for key-storage-selector
gschueler Mar 13, 2024
43e94f5
cleanup: logic
gschueler Mar 13, 2024
d8a50a3
fix: pass correct arg for length validation message
gschueler Mar 13, 2024
07ed6ab
fix: infer correct inputType value from input modelValue
gschueler Mar 13, 2024
fd05b3c
cleanup: format
gschueler Mar 13, 2024
a1e2e11
fix: field name for nextUi option default storage path
gschueler Mar 13, 2024
1206ab5
update validation for hidden field to highlight the required field
gschueler Mar 13, 2024
d378f97
Merge branch 'main' into RUN-551
gschueler Mar 14, 2024
0bd578b
update option validation api test
gschueler Mar 14, 2024
a5d2f1e
fix: job scheduled boolean should be used in option validation
gschueler Mar 14, 2024
670a6e0
emit an event when schedule data changes
gschueler Mar 14, 2024
1696ade
fix: listen for schedule data change to use scheduled boolean in vali…
gschueler Mar 14, 2024
449162e
test remote validate should use jobWasScheduled param
gschueler Mar 14, 2024
ea7a493
fix, add jobWasScheduled
gschueler Mar 14, 2024
afdb066
test jobWasScheduled prop is passed to OptionEdit
gschueler Mar 14, 2024
24105b2
Merge branch 'main' into RUN-551
gschueler Mar 15, 2024
dac3c17
fix: required field should show validation error messages and state
gschueler Mar 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package com.dtolabs.rundeck.core.jobs.options;

import java.util.ArrayList;
import java.util.List;

import lombok.Getter;

import java.util.Map;
import java.util.TreeMap;

/**
* Defines the model for storing generic configuration data for a job option
*/
@Getter
public class JobOptionConfigData {
private final Map<String,JobOptionConfigEntry> jobOptionConfigEntries;
private final Map<String, JobOptionConfigEntry> jobOptionConfigEntries;

public JobOptionConfigData() {
this.jobOptionConfigEntries = new TreeMap<>();
}

public void addConfig(JobOptionConfigEntry jobOptionConfigEntry){
this.jobOptionConfigEntries.put(jobOptionConfigEntry.configType(),jobOptionConfigEntry);
public JobOptionConfigData(Map<String, JobOptionConfigEntry> values) {
this.jobOptionConfigEntries = new TreeMap<>(values);
}

public Map<String, JobOptionConfigEntry> getJobOptionConfigEntries() {
return jobOptionConfigEntries;
public void addConfig(JobOptionConfigEntry jobOptionConfigEntry) {
this.jobOptionConfigEntries.put(jobOptionConfigEntry.configType(), jobOptionConfigEntry);
}

public JobOptionConfigEntry getJobOptionEntry(Class classType){
return jobOptionConfigEntries.values().stream().filter(it->classType.isInstance(it)).findFirst().orElse(null);
public JobOptionConfigEntry getJobOptionEntry(Class<?> classType) {
return jobOptionConfigEntries.values().stream().filter(classType::isInstance).findFirst().orElse(null);
}

public JobOptionConfigEntry getJobOptionEntry(String configType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package org.rundeck.tests.functional.api.job

import org.rundeck.util.annotations.APITest
import org.rundeck.util.container.BaseContainer

@APITest
class JobOptionValidationSpec extends BaseContainer {

def setupSpec() {
startEnvironment()
setupProject()
}

def "validate job option fails for version<47"() {
given:
def client = getClient()
client.apiVersion = version
when:
def response = client.doPost(
"/project/${PROJECT_NAME}/jobs/validateOption",
[
"name" : "opt1",
"required": true
]
)
then:
verifyAll {
!response.successful
response.code() == 400
def json = client.jsonValue(response.body(), Map)
json.errorCode == 'api.error.api-version.unsupported'
}
where:
version << [14, 46]
}

def "validate job option ok for version>46"() {
given:
def client = getClient()
client.apiVersion = 47
when:
def response = client.doPost(
"/project/${PROJECT_NAME}/jobs/validateOption",
[
"name" : "opt1",
"required": true
]
)
then:
verifyAll {
response.successful
response.code() == 200
def json = client.jsonValue(response.body(), Map)
json.valid == true
}
}
def "validate job option invalid field for version>46"() {
given:
def client = getClient()
client.apiVersion = 47
when:
def response = client.doPost(
"/project/${PROJECT_NAME}/jobs/validateOption?jobWasScheduled=${sched}",
[
"name" : "opt1",
"required": true
]+extra
)
then:
verifyAll {
!response.successful
response.code() == 400
def json = client.jsonValue(response.body(), Map)
json.valid == false
json.messages!=null
json.messages[fieldName]!=null
json.messages[fieldName].size()>0
json.messages[fieldName].any{it.contains(message)}
}
where:
sched | extra | fieldName | message
false | [valuesUrl: 'notaurl'] | 'valuesUrl' | 'Not a valid URL'
false | [name: 'in valid'] | 'name' | 'does not match the required pattern'
false | [remoteUrlAuthenticationType: 'in valid'] | 'remoteUrlAuthenticationType' | 'must be in the list'
false | [defaultStoragePath: 'in valid'] | 'defaultStoragePath' | 'Default key storage path must start with: keys/: in valid'
false | [hidden: true] | 'hidden' | 'Hidden options must have a default value'
false | [valuesType: 'url', enforced:true] | 'valuesUrl' | 'Allowed values (list or remote URL) must be specified if values are enforced'
false | [valuesType: 'list', enforced:true] | 'values' | 'Allowed values (list or remote URL) must be specified if values are enforced'
false | [value: 'b',values:['a'], enforced:true] | 'value' | 'Default Value was not in the allowed values list, and values are enforced'
false | [multivalued:true,delimiter:null] | 'delimiter' | 'You must specify a delimiter for multivalued options'
false | [value: 'a,b',values:['a'], enforced:true,multivalued:true,delimiter:','] | 'value' | 'Default Value contains a string that was not in the allowed values list, and values are enforced'
false | [regex:'asdf['] | 'regex' | 'Invalid Regular Expression:'
false | [regex:'[a-f]+',value:'z'] | 'value' | 'Default value "z" does not match the regex: [a-f]+'
false | [regex:'[a-f]+',values:['z']] | 'values' | 'Allowed value "z" does not match the regex: [a-f]+'
false | [multivalued: true, secure:true] | 'multivalued' | 'Secure input cannot be used with multi-valued input'
true | [required: true, optionType:'file'] | 'required' | 'File option type cannot be Required when the Job is scheduled'
true | [required: true] | 'value' | 'Specify a Default Value for Required options when the Job is scheduled'
true | [valuesUrl:'http://x.com',configRemoteUrl:[jsonFilter:'bad[.']] | 'configRemoteUrl.jsonFilter' | 'The Remote URL Json Path Filter has an invalid syntax'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,25 @@ class BasicJobsSpec extends SeleniumBase {

def "create valid job basic options"() {
when:
def jobCreatePage = go JobCreatePage, SELENIUM_BASIC_PROJECT
def jobCreatePage = page JobCreatePage, SELENIUM_BASIC_PROJECT
jobCreatePage.nextUi=nextUi
jobCreatePage.go()
def jobShowPage = page JobShowPage
def optionName = 'seleniumOption1'
then:
jobCreatePage.fillBasicJob 'a job with options'
jobCreatePage.fillBasicJob specificationContext.currentIteration.name+" ${nextUi ? "next ui" : "old ui"}"
jobCreatePage.optionButton.click()
jobCreatePage.optionName 0 sendKeys optionName
jobCreatePage.optionNameNew() sendKeys optionName
jobCreatePage.executeScript "arguments[0].scrollIntoView(true);", jobCreatePage.saveOptionButton
jobCreatePage.saveOptionButton.click()
jobCreatePage.waitFotOptLi 0
jobCreatePage.createJobButton.click()
then:
jobCreatePage.waitForUrlToContain('/job/show')
jobShowPage.jobLinkTitleLabel.getText().contains('a job with options')
jobShowPage.jobLinkTitleLabel.getText().contains('create valid job basic options')
jobShowPage.optionInputText(optionName) != null
where:
nextUi<<[false,true]
}

def "edit job set description"() {
Expand Down Expand Up @@ -201,7 +205,7 @@ class BasicJobsSpec extends SeleniumBase {
jobShowPage.optionValidationWarningText.getText().contains 'Option \'reqOpt1\' is required'
}

def "job filter by name 3 results"() {
def "job filter by name results"() {
when:
def jobShowPage = go JobShowPage, SELENIUM_BASIC_PROJECT
then:
Expand All @@ -210,11 +214,18 @@ class BasicJobsSpec extends SeleniumBase {
jobShowPage.waitForModal 1
jobShowPage.jobSearchNameField.sendKeys 'option'
jobShowPage.jobSearchSubmitButton.click()
jobShowPage.waitForNumberOfElementsToBe jobShowPage.jobRowBy, 3
jobShowPage.jobRowLink.size() == 3
jobShowPage.waitForNumberOfElementsToBe jobShowPage.jobRowBy, expected.size()
jobShowPage.jobRowLink.size() == expected.size()
jobShowPage.jobRowLink.collect {
it.getText()
}.containsAll(["selenium-option-test1", "predefined job with options", "a job with options"])
}.containsAll(expected)
where:
expected = [
"selenium-option-test1",
"predefined job with options",
"create valid job basic options next ui",
"create valid job basic options old ui"
]
}

def "job filter by name and group 1 results"() {
Expand All @@ -233,18 +244,26 @@ class BasicJobsSpec extends SeleniumBase {
}

def "job filter by name and - top group 2 results"() {
when:
given:
def jobShowPage = go JobShowPage, SELENIUM_BASIC_PROJECT
then:
when:
jobShowPage.validatePage()
jobShowPage.jobSearchButton.click()
jobShowPage.waitForModal 1
jobShowPage.jobSearchNameField.sendKeys 'option'
jobShowPage.jobSearchGroupField.sendKeys '-'
jobShowPage.jobSearchSubmitButton.click()
expect:
jobShowPage.waitForNumberOfElementsToBe jobShowPage.jobRowBy, 2
jobShowPage.jobRowLink.collect { it.getText() } == ["a job with options", "predefined job with options"]
then:
jobShowPage.waitForNumberOfElementsToBe jobShowPage.jobRowBy, expected.size()
def names = jobShowPage.jobRowLink.collect { it.getText() }
names.size() == expected.size()
names.containsAll expected
where:
expected = [
"predefined job with options",
"create valid job basic options next ui",
"create valid job basic options old ui"
]
}

def "view jobs list page"() {
Expand Down