Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
feat(server): add support for default variable values
Browse files Browse the repository at this point in the history
Pipeline variables can now have a _default value_.

This is a purely cosmetic addition. Clients can use this value to
pre-fill variable input fields, or select the correct value from a set
of selection constraints as the default value.

This feature works together with the selection constraints feature (see
#1), in that if a default
value is provided for a variable, and a selection constraint is provided
as well, the default value has to be part of that constraint in order
for the variable to be created successfully.

See: #3
  • Loading branch information
JeanMertz committed Jun 25, 2019
1 parent acb7dd9 commit 161f27d
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE variables DROP COLUMN default_value;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE variables ADD COLUMN default_value Text NULL;
2 changes: 2 additions & 0 deletions src/server/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ input CreateTaskFromPipelineInput {
input CreateVariableInput {
key: String!
description: String
defaultValue: String
constraints: VariableConstraintsInput!
}

Expand Down Expand Up @@ -251,6 +252,7 @@ type Variable {
id: ID!
key: String!
description: String
defaultValue: String
constraints: VariableConstraints!
pipeline: Pipeline
}
Expand Down
7 changes: 6 additions & 1 deletion src/server/src/resources/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,12 @@ impl<'a> TryFrom<&'a graphql::CreatePipelineInput> for NewPipeline<'a> {
fn try_from(input: &'a graphql::CreatePipelineInput) -> Result<Self, Self::Error> {
let mut pipeline = Self::new(&input.name, input.description.as_ref().map(String::as_ref));

let variables = input.variables.iter().map(Into::into).collect();
let variables = input
.variables
.iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, String>>()?;

let steps = input
.steps
.iter()
Expand Down
68 changes: 61 additions & 7 deletions src/server/src/resources/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::schema::variables;
use crate::Database;
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use std::convert::AsRef;
use std::convert::{AsRef, TryFrom};

/// The model representing a variable definition (without an actual value)
/// stored in the database.
Expand All @@ -43,6 +43,7 @@ pub(crate) struct Variable {
// `VariableConstraint` struct, which can also hold other constraints (such
// as `optional: bool`) in the future.
pub(crate) selection_constraint: Option<Vec<String>>,
pub(crate) default_value: Option<String>,
pub(crate) pipeline_id: i32,
}

Expand Down Expand Up @@ -104,23 +105,39 @@ pub(crate) struct NewVariable<'a> {
key: &'a str,
description: Option<&'a str>,
selection_constraint: Option<Vec<&'a str>>,
default_value: Option<&'a str>,
pipeline_id: Option<i32>,
}

impl<'a> NewVariable<'a> {
/// Initialize a `NewVariable` struct, which can be inserted into the
/// database using the [`NewVariable#add_to_pipeline`] method.
pub(crate) const fn new(
///
/// Returns an error if the `default_value` value is provided, but is not a
/// subset of the values provided in `selection_constraint`.
pub(crate) fn new(
key: &'a str,
selection_constraint: Option<Vec<&'a str>>,
default_value: Option<&'a str>,
description: Option<&'a str>,
) -> Self {
Self {
) -> Result<Self, String> {
if let Some(selection) = &selection_constraint {
if let Some(default) = &default_value {
if !selection.contains(default) {
return Err(
"default value must be included in the selection constraint".to_owned()
);
}
}
};

Ok(Self {
key,
description,
selection_constraint,
default_value,
pipeline_id: None,
}
})
}

/// Add a variable to a [`Pipeline`], by storing it in the database as an
Expand Down Expand Up @@ -173,6 +190,10 @@ pub(crate) mod graphql {
/// about to run a pipeline what the intent is of the required variable.
pub(crate) description: Option<String>,

/// An optional default value that can be used by clients to pre-fill
/// the variable value before running a pipeline.
pub(crate) default_value: Option<String>,

/// A set of constraints applied to future values attached to this
/// variable.
///
Expand Down Expand Up @@ -232,6 +253,14 @@ pub(crate) mod graphql {
self.description.as_ref().map(String::as_ref)
}

/// An (optional) default value defined for the variable.
///
/// Clients can use this to pre-fill a value, or select the correct
/// value if a selection constrained is defined for the variable.
fn default_value() -> Option<&str> {
self.default_value.as_ref().map(String::as_ref)
}

/// A set of value constraints for this variable.
///
/// This object will always be defined, but it might be empty, if no
Expand Down Expand Up @@ -274,15 +303,18 @@ pub(crate) mod graphql {
}
}

impl<'a> From<&'a graphql::CreateVariableInput> for NewVariable<'a> {
fn from(input: &'a graphql::CreateVariableInput) -> Self {
impl<'a> TryFrom<&'a graphql::CreateVariableInput> for NewVariable<'a> {
type Error = String;

fn try_from(input: &'a graphql::CreateVariableInput) -> Result<Self, Self::Error> {
Self::new(
&input.key,
input
.constraints
.selection
.as_ref()
.map(|v| v.iter().map(String::as_str).collect()),
input.default_value.as_ref().map(String::as_ref),
input.description.as_ref().map(String::as_ref),
)
}
Expand All @@ -307,6 +339,7 @@ mod tests {
key: "hello".to_owned(),
description: None,
selection_constraint: None,
default_value: None,
pipeline_id: 0,
}
}
Expand Down Expand Up @@ -358,4 +391,25 @@ mod tests {
assert_eq!(mismatch[0].0.key, galaxy.key);
assert_eq!(mismatch[0].1, &galaxy_loopy);
}

#[test]
fn test_new_variable_with_default() {
let _ = NewVariable::new("var 1", None, Some("foo"), None).unwrap();
}

#[test]
fn test_new_variable_with_selection_constraint_no_default() {
let _ = NewVariable::new("var 1", Some(vec!["foo", "bar"]), None, None).unwrap();
}

#[test]
fn test_new_variable_with_default_matching_selection_constraint() {
let _ = NewVariable::new("var 1", Some(vec!["foo", "bar"]), Some("foo"), None).unwrap();
}

#[test]
#[should_panic]
fn test_new_variable_default_not_in_selection_constraint() {
let _ = NewVariable::new("var 1", Some(vec!["foo", "bar"]), Some("baz"), None).unwrap();
}
}
1 change: 1 addition & 0 deletions src/server/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ table! {
key -> Text,
description -> Nullable<Text>,
selection_constraint -> Nullable<Array<Text>>,
default_value -> Nullable<Text>,
pipeline_id -> Integer,
}
}
Expand Down

0 comments on commit 161f27d

Please sign in to comment.