diff --git a/modules/nextflow/src/main/groovy/nextflow/script/ProcessEntryHandler.groovy b/modules/nextflow/src/main/groovy/nextflow/script/ProcessEntryHandler.groovy index 6a76ad8dfc..99cae80e2e 100644 --- a/modules/nextflow/src/main/groovy/nextflow/script/ProcessEntryHandler.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/script/ProcessEntryHandler.groovy @@ -238,7 +238,7 @@ class ProcessEntryHandler { } /** - * Load mapping of input types from the module spec if available. Returns null + * Load mapping of input types from the module spec if available. Returns empty map * if module spec is absent or unreadable. */ private static Map getModuleSpecInputTypes(Path scriptPath) { @@ -294,6 +294,10 @@ class ProcessEntryHandler { final value = namedArgs.get(name) if( value == null ) { + if( param instanceof FileInParam ) { + log.warn "Path input '--${name}' not provided, defaulting to empty list" + return [] + } throw new IllegalArgumentException("Missing required parameter: --${name}") } @@ -397,6 +401,8 @@ class ProcessEntryHandler { final value = namedArgs.get(name) if( value == null ) { + if( param.isOptional() ) + return null throw new IllegalArgumentException("Missing required parameter: --${name}") } @@ -433,10 +439,10 @@ class ProcessEntryHandler { * Otherwise returns a single file object. * * @param fileInput String representation of file path(s) - * @return Single file object or List of file objects + * @return Single file or list of files */ protected Object parseFileInput(String fileInput) { - if (fileInput.contains(',')) { + if( fileInput.contains(',') ) { // Split by comma, trim whitespace, and convert each to a file return fileInput.tokenize(',') .collect { it.trim() } diff --git a/modules/nextflow/src/test/groovy/nextflow/script/ProcessEntryHandlerTest.groovy b/modules/nextflow/src/test/groovy/nextflow/script/ProcessEntryHandlerTest.groovy index d3420d1b3f..02c8fff412 100644 --- a/modules/nextflow/src/test/groovy/nextflow/script/ProcessEntryHandlerTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/script/ProcessEntryHandlerTest.groovy @@ -392,4 +392,75 @@ class ProcessEntryHandlerTest extends Specification { '/path/to/file1.txt,,/path/to/file2.txt, ,/path/to/file3.txt' | [Path.of('/path/to/file1.txt'), Path.of('/path/to/file2.txt'), Path.of('/path/to/file3.txt')] 'file1.txt,file2.txt' | [Path.of('file1.txt').toAbsolutePath(), Path.of('file2.txt').toAbsolutePath()] } + + def 'should return empty list for missing path input (v1)' () { + given: + def session = Mock(Session) + def script = Mock(BaseScript) + def meta = Mock(ScriptMeta) { + getLocalProcessNames() >> [ 'hello' ] + } + def handler = new ProcessEntryHandler(script, session, meta) + def pathParam = Mock(FileInParam) { getName() >> 'intervals' } + + when: 'the input is not present in params for a path input' + def result = handler.getValueForInputV1(pathParam, [:], [:]) + + then: 'returns an empty list instead of throwing' + result == [] + } + + def 'should throw error for missing val input (v1)' () { + given: + def session = Mock(Session) + def script = Mock(BaseScript) + def meta = Mock(ScriptMeta) { + getLocalProcessNames() >> [ 'hello' ] + } + def handler = new ProcessEntryHandler(script, session, meta) + def valParam = Mock(ValueInParam) { getName() >> 'id' } + + when: 'a val input is absent' + handler.getValueForInputV1(valParam, [:], [:]) + + then: + def e = thrown(IllegalArgumentException) + e.message == 'Missing required parameter: --id' + } + + def 'should return null for missing optional input (v2)' () { + // Regression test for https://github.com/nextflow-io/nextflow/issues/7161 + given: + def session = Mock(Session) + def script = Mock(BaseScript) + def meta = Mock(ScriptMeta) { + getLocalProcessNames() >> [ 'hello' ] + } + def handler = new ProcessEntryHandler(script, session, meta) + def param = new ProcessInput('gzi', Path, true) + + when: 'a v2 optional path input is not provided' + def result = handler.getValueForInputV2(param, [:]) + + then: 'returns null instead of throwing' + result == null + } + + def 'should throw error for missing required input (v2)' () { + given: + def session = Mock(Session) + def script = Mock(BaseScript) + def meta = Mock(ScriptMeta) { + getLocalProcessNames() >> [ 'hello' ] + } + def handler = new ProcessEntryHandler(script, session, meta) + def param = new ProcessInput('reads', Path, false) + + when: + handler.getValueForInputV2(param, [:]) + + then: + def e = thrown(IllegalArgumentException) + e.message == 'Missing required parameter: --reads' + } }