Skip to content

Commit 6c5d6bd

Browse files
authored
Merge pull request #74 from nextflow-io/2.2.0-dev
Release 2.2.0
2 parents 1c64182 + 5d529a6 commit 6c5d6bd

18 files changed

+437
-103
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# nextflow-io/nf-schema: Changelog
22

3+
# Version 2.2.0 - Kitakata
4+
5+
## New features
6+
7+
1. Added a new configuration option `validation.failUnrecognisedHeaders`. This is the analogue to `failUnrecognisedParams`, but for samplesheet headers. The default is `false` which means that unrecognized headers throw a warning instead of an error.
8+
2. Added a new configuration option `validation.summary.hideParams`. This option takes a list of parameter names to hide from the parameters summary created by `paramsSummaryMap()` and `paramsSummaryLog()`
9+
10+
## Bug fixes
11+
12+
1. Fixed a bug in `samplesheetToList` that caused output mixing when the function was used more than once in channel operators.
13+
2. Added a missing depencency for email format validation.
14+
3. All path formats (with exception to `file-path-pattern`) will now give a proper error message when a `file-path-pattern` has been used.
15+
16+
## Improvements
17+
18+
1. Improved the `exists` keyword documentation with a warning about an edge case.
19+
2. Updated the error messages. Custom error messages provided in the JSON schema will now be appended to the original error messages instead of overwriting them.
20+
321
# Version 2.1.2
422

523
## Bug fixes

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Declare the plugin in your Nextflow pipeline configuration file:
2525

2626
```groovy title="nextflow.config"
2727
plugins {
28-
id 'nf-schema@2.1.2'
28+
id 'nf-schema@2.2.0'
2929
}
3030
```
3131

docs/configuration/configuration.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ By default the `validateParameters()` function will only give a warning if an un
5454
validation.failUnrecognisedParams = <true|false> // default: false
5555
```
5656

57+
## failUnrecognisedHeaders
58+
59+
By default the `samplesheetToList()` function will only give a warning if an unrecognised header is present in the samplesheet. This usually indicates that a typo has been made and can be easily overlooked when the plugin only emits a warning. You can turn this warning into an error with the `failUnrecognisedHeaders` option.
60+
61+
```groovy
62+
validation.failUnrecognisedHeaders = <true|false> // default: false
63+
```
64+
5765
## showHiddenParams
5866

5967
!!! deprecated
@@ -218,3 +226,11 @@ validation.summary.afterText = "Please cite the pipeline owners when using this
218226
!!! info
219227

220228
All color values (like `\033[0;31m`, which means the color red) will be filtered out when `validation.monochromeLogs` is set to `true`
229+
230+
### hideParams
231+
232+
Takes a list of parameter names to exclude from the parameters summary created by `paramsSummaryMap()` and `paramsSummaryLog()`
233+
234+
```groovy
235+
validation.summary.hideParams = ["param1", "nested.param"] // default: []
236+
```

docs/nextflow_schema/nextflow_schema_specification.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ If validation fails, an error message is printed to the terminal, so that the en
231231
However, these messages are not always very clear - especially to newcomers.
232232

233233
To improve this experience, pipeline developers can set a custom `errorMessage` for a given parameter in a the schema.
234-
If validation fails, this `errorMessage` is printed instead, and the raw JSON schema validation message goes to the Nextflow debug log output.
234+
If validation fails, this `errorMessage` is printed after the original error message to guide the pipeline users to an easier solution.
235235

236236
For example, instead of printing:
237237

@@ -252,7 +252,7 @@ We can set
252252
and get:
253253

254254
```
255-
* --input (samples.yml): File name must end in '.csv' cannot contain spaces
255+
* --input (samples.yml): "samples.yml" does not match regular expression [^\S+\.csv$] (File name must end in '.csv' cannot contain spaces)
256256
```
257257

258258
### `deprecated`
@@ -389,7 +389,11 @@ Example usage is as follows:
389389

390390
!!! note
391391

392-
If the parameter is an S3 URL path, this validation is ignored.
392+
If the parameter is an S3, Azure or Google Cloud URI path, this validation is ignored.
393+
394+
!!! warning
395+
396+
Make sure to only use the `exists` keyword in combination with any file path format. Using `exists` on a normal string will assume that it's a file and will probably fail unexpectedly.
393397

394398
### `mimetype`
395399

plugins/nf-schema/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies {
5656
compileOnly 'org.pf4j:pf4j:3.4.1'
5757
implementation 'org.json:json:20240303'
5858
implementation 'dev.harrel:json-schema:1.5.0'
59+
implementation 'com.sanctionco.jmail:jmail:1.6.3' // Needed for e-mail format validation
5960

6061
// test configuration
6162
testImplementation "io.nextflow:nextflow:$nextflowVersion"

plugins/nf-schema/src/main/nextflow/validation/CustomEvaluators/FormatDirectoryPathEvaluator.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class FormatDirectoryPathEvaluator implements Evaluator {
3333

3434
// Actual validation logic
3535
def Path file = Nextflow.file(value) as Path
36+
if (file instanceof List) {
37+
return Evaluator.Result.failure("'${value}' is not a directory, but a file path pattern" as String)
38+
}
3639
if (file.exists() && !file.isDirectory()) {
3740
return Evaluator.Result.failure("'${value}' is not a directory, but a file" as String)
3841
}

plugins/nf-schema/src/main/nextflow/validation/CustomEvaluators/FormatFilePathEvaluator.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class FormatFilePathEvaluator implements Evaluator {
3333

3434
// Actual validation logic
3535
def Path file = Nextflow.file(value) as Path
36+
if (file instanceof List) {
37+
return Evaluator.Result.failure("'${value}' is not a file, but a file path pattern" as String)
38+
}
3639
if (file.exists() && file.isDirectory()) {
3740
return Evaluator.Result.failure("'${value}' is not a file, but a directory" as String)
3841
}

plugins/nf-schema/src/main/nextflow/validation/CustomEvaluators/FormatPathEvaluator.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class FormatPathEvaluator implements Evaluator {
3333

3434
// Actual validation logic
3535
def Path file = Nextflow.file(value) as Path
36+
if (file instanceof List) {
37+
return Evaluator.Result.failure("'${value}' is not a path, but a file path pattern" as String)
38+
}
3639
return Evaluator.Result.success()
3740
}
3841
}

plugins/nf-schema/src/main/nextflow/validation/JsonSchemaValidator.groovy

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import java.util.regex.Matcher
2222
@CompileStatic
2323
public class JsonSchemaValidator {
2424

25-
private static ValidatorFactory validator
26-
private static Pattern uriPattern = Pattern.compile('^#/(\\d*)?/?(.*)$')
27-
private static ValidationConfig config
25+
private ValidatorFactory validator
26+
private Pattern uriPattern = Pattern.compile('^#/(\\d*)?/?(.*)$')
27+
private ValidationConfig config
2828

2929
JsonSchemaValidator(ValidationConfig config) {
3030
this.validator = new ValidatorFactory()
@@ -34,14 +34,14 @@ public class JsonSchemaValidator {
3434
this.config = config
3535
}
3636

37-
private static List<String> validateObject(JsonNode input, String validationType, Object rawJson, String schemaString) {
37+
private List<String> validateObject(JsonNode input, String validationType, Object rawJson, String schemaString) {
3838
def JSONObject schema = new JSONObject(schemaString)
3939
def String draft = Utils.getValueFromJson("#/\$schema", schema)
4040
if(draft != "https://json-schema.org/draft/2020-12/schema") {
4141
log.error("""Failed to load the meta schema:
4242
The used schema draft (${draft}) is not correct, please use \"https://json-schema.org/draft/2020-12/schema\" instead.
4343
- If you are a pipeline developer, check our migration guide for more information: https://nextflow-io.github.io/nf-schema/latest/migration_guide/
44-
- If you are a pipeline user, pin the previous version of the plugin (1.1.3) to avoid this error: https://www.nextflow.io/docs/latest/plugins.html#using-plugins, i.e. set `plugins {
44+
- If you are a pipeline user, revert back to nf-validation to avoid this error: https://www.nextflow.io/docs/latest/plugins.html#using-plugins, i.e. set `plugins {
4545
id 'nf-validation@1.1.3'
4646
}` in your `nextflow.config` file
4747
""")
@@ -50,11 +50,11 @@ public class JsonSchemaValidator {
5050

5151
def Validator.Result result = this.validator.validate(schema, input)
5252
def List<String> errors = []
53-
for (error : result.getErrors()) {
53+
result.getErrors().each { error ->
5454
def String errorString = error.getError()
5555
// Skip double error in the parameter schema
5656
if (errorString.startsWith("Value does not match against the schemas at indexes") && validationType == "parameter") {
57-
continue
57+
return
5858
}
5959

6060
def String instanceLocation = error.getInstanceLocation()
@@ -68,48 +68,47 @@ public class JsonSchemaValidator {
6868
}
6969

7070
// Change some error messages to make them more clear
71-
if (customError == "") {
72-
def String keyword = error.getKeyword()
73-
if (keyword == "required") {
74-
def Matcher matcher = errorString =~ ~/\[\[([^\[\]]*)\]\]$/
75-
def String missingKeywords = matcher.findAll().flatten().last()
76-
customError = "Missing required ${validationType}(s): ${missingKeywords}"
77-
}
71+
def String keyword = error.getKeyword()
72+
if (keyword == "required") {
73+
def Matcher matcher = errorString =~ ~/\[\[([^\[\]]*)\]\]$/
74+
def String missingKeywords = matcher.findAll().flatten().last()
75+
errorString = "Missing required ${validationType}(s): ${missingKeywords}"
7876
}
7977

8078
def List<String> locationList = instanceLocation.split("/").findAll { it != "" } as List
8179

80+
def String printableError = "${validationType == 'field' ? '->' : '*'} ${errorString}" as String
8281
if (locationList.size() > 0 && Utils.isInteger(locationList[0]) && validationType == "field") {
8382
def Integer entryInteger = locationList[0] as Integer
8483
def String entryString = "Entry ${entryInteger + 1}" as String
85-
def String fieldError = ""
84+
def String fieldError = "${errorString}" as String
8685
if(locationList.size() > 1) {
87-
fieldError = "Error for ${validationType} '${locationList[1..-1].join("/")}' (${value}): ${customError ?: errorString}"
88-
} else {
89-
fieldError = "${customError ?: errorString}" as String
86+
fieldError = "Error for ${validationType} '${locationList[1..-1].join("/")}' (${value}): ${errorString}"
9087
}
91-
errors.add("-> ${entryString}: ${fieldError}" as String)
88+
printableError = "-> ${entryString}: ${fieldError}" as String
9289
} else if (validationType == "parameter") {
9390
def String fieldName = locationList.join(".")
9491
if(fieldName != "") {
95-
errors.add("* --${fieldName} (${value}): ${customError ?: errorString}" as String)
96-
} else {
97-
errors.add("* ${customError ?: errorString}" as String)
92+
printableError = "* --${fieldName} (${value}): ${errorString}" as String
9893
}
99-
} else {
100-
errors.add("-> ${customError ?: errorString}" as String)
10194
}
10295

96+
if(customError != "") {
97+
printableError = printableError + " (${customError})"
98+
}
99+
100+
errors.add(printableError)
101+
103102
}
104103
return errors
105104
}
106105

107-
public static List<String> validate(JSONArray input, String schemaString) {
106+
public List<String> validate(JSONArray input, String schemaString) {
108107
def JsonNode jsonInput = new OrgJsonNode.Factory().wrap(input)
109108
return this.validateObject(jsonInput, "field", input, schemaString)
110109
}
111110

112-
public static List<String> validate(JSONObject input, String schemaString) {
111+
public List<String> validate(JSONObject input, String schemaString) {
113112
def JsonNode jsonInput = new OrgJsonNode.Factory().wrap(input)
114113
return this.validateObject(jsonInput, "parameter", input, schemaString)
115114
}

0 commit comments

Comments
 (0)