Skip to content

Commit 62dfee8

Browse files
fix: address Claude review on interactive Seqera buttons
Add progress/complete message button tests, validate unknown action button modes with a link-mode fallback, and document custom status phase mapping. Generated by Codex Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 5881cfe commit 62dfee8

4 files changed

Lines changed: 72 additions & 1 deletion

File tree

src/main/groovy/nextflow/slack/SeqeraPlatformActionButtonsConfig.groovy

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package nextflow.slack
1818

1919
import groovy.transform.CompileStatic
20+
import groovy.util.logging.Slf4j
2021

2122
/**
2223
* Configuration for Seqera Platform action buttons in Slack messages.
@@ -25,6 +26,7 @@ import groovy.transform.CompileStatic
2526
* Interactive mode emits action_id buttons for an external Slack interactivity handler.
2627
*/
2728
@CompileStatic
29+
@Slf4j
2830
class SeqeraPlatformActionButtonsConfig {
2931

3032
static final String MODE_LINK = 'link'
@@ -37,7 +39,12 @@ class SeqeraPlatformActionButtonsConfig {
3739

3840
SeqeraPlatformActionButtonsConfig(Map config) {
3941
config = config ?: [:]
40-
this.mode = (config.mode as String ?: MODE_LINK).toLowerCase()
42+
def configuredMode = (config.mode as String ?: MODE_LINK).toLowerCase()
43+
if (configuredMode != MODE_LINK && configuredMode != MODE_INTERACTIVE) {
44+
log.warn "Slack plugin: Unknown seqeraPlatform.actionButtons.mode '${configuredMode}'; falling back to '${MODE_LINK}'"
45+
configuredMode = MODE_LINK
46+
}
47+
this.mode = configuredMode
4148
this.cancel = config.cancel != null ? config.cancel as boolean : true
4249
this.resume = config.resume != null ? config.resume as boolean : true
4350
this.relaunch = config.relaunch != null ? config.relaunch as boolean : true

src/main/groovy/nextflow/slack/SlackMessageBuilder.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ class SlackMessageBuilder {
620620
case 'failed':
621621
return SeqeraMessagePhase.FAILED
622622
default:
623+
// Custom statuses (e.g. paused) are treated as RUNNING for button selection.
623624
return SeqeraMessagePhase.RUNNING
624625
}
625626
}

src/test/groovy/nextflow/slack/SeqeraPlatformActionButtonsTest.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,14 @@ class SeqeraPlatformActionButtonsTest extends Specification {
117117
config.isLinkMode()
118118
!config.isInteractiveMode()
119119
}
120+
121+
def 'should fall back to link mode for unknown mode values'() {
122+
when:
123+
def config = new SeqeraPlatformActionButtonsConfig([mode: 'url'])
124+
125+
then:
126+
config.mode == 'link'
127+
config.isLinkMode()
128+
!config.isInteractiveMode()
129+
}
120130
}

src/test/groovy/nextflow/slack/SlackMessageBuilderTest.groovy

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,59 @@ class SlackMessageBuilderTest extends Specification {
678678
actionsBlock.elements*.text.text == ['🔗 View in Seqera Platform', '▶️ Resume', '🔄 Relaunch']
679679
}
680680

681+
def 'should include Seqera Platform buttons in progress update when watchUrl is available'() {
682+
given:
683+
def platformConfig = new SlackConfig([
684+
webhook: 'https://hooks.slack.com/test',
685+
seqeraPlatform: [enabled: true]
686+
])
687+
def mockMetadata = Mock(WorkflowMetadata)
688+
mockMetadata.scriptName >> 'test-workflow.nf'
689+
def towerSession = Mock(Session)
690+
towerSession.config >> [:]
691+
towerSession.workflowMetadata >> mockMetadata
692+
towerSession.runName >> 'crazy_einstein'
693+
towerSession.uniqueId >> UUID.fromString('00000000-0000-0000-0000-000000000000')
694+
def builder = Spy(new SlackMessageBuilder(platformConfig, towerSession))
695+
builder.getTowerClientWatchUrl() >> 'https://cloud.seqera.io/orgs/myorg/workspaces/myws/watch/abc123'
696+
697+
when:
698+
def message = builder.buildProgressUpdateMessage(10, 5, 2, 0, 60000L, null)
699+
def json = new JsonSlurper().parseText(message)
700+
701+
then:
702+
def actionsBlock = json.blocks.find { it.type == 'actions' }
703+
actionsBlock != null
704+
actionsBlock.elements*.text.text == ['🔗 View in Seqera Platform', '⏹ Cancel']
705+
}
706+
707+
def 'should include View and Relaunch buttons on workflow complete message'() {
708+
given:
709+
def platformConfig = new SlackConfig([
710+
webhook: 'https://hooks.slack.com/test',
711+
seqeraPlatform: [enabled: true]
712+
])
713+
def mockMetadata = Mock(WorkflowMetadata)
714+
mockMetadata.scriptName >> 'test-workflow.nf'
715+
mockMetadata.duration >> nextflow.util.Duration.of('1h')
716+
mockMetadata.stats >> null
717+
def towerSession = Mock(Session)
718+
towerSession.config >> [:]
719+
towerSession.workflowMetadata >> mockMetadata
720+
towerSession.runName >> 'crazy_einstein'
721+
towerSession.uniqueId >> UUID.fromString('00000000-0000-0000-0000-000000000000')
722+
def builder = Spy(new SlackMessageBuilder(platformConfig, towerSession))
723+
builder.getTowerClientWatchUrl() >> 'https://cloud.seqera.io/orgs/myorg/workspaces/myws/watch/abc123'
724+
725+
when:
726+
def message = builder.buildWorkflowCompleteMessage()
727+
def json = new JsonSlurper().parseText(message)
728+
729+
then:
730+
def actionsBlock = json.blocks.find { it.type == 'actions' }
731+
actionsBlock.elements*.text.text == ['🔗 View in Seqera Platform', '🔄 Relaunch']
732+
}
733+
681734
def 'should not include Seqera Platform button when no TowerClient present'() {
682735
when:
683736
def message = messageBuilder.buildWorkflowStartMessage()

0 commit comments

Comments
 (0)