Skip to content

Commit

Permalink
feat(api,hook): allow user to set payload on outgoing workflow hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
yesnault authored Nov 26, 2018
1 parent 335e6db commit 5c7edc8
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 10 deletions.
3 changes: 1 addition & 2 deletions engine/api/workflow/execute_node_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@ func NodeBuildParametersFromRun(wr sdk.WorkflowRun, id int64) ([]sdk.Parameter,

//NodeBuildParametersFromWorkflow returns build_parameters for a node given its id
func NodeBuildParametersFromWorkflow(ctx context.Context, db gorp.SqlExecutor, store cache.Store, proj *sdk.Project, wf *sdk.Workflow, refNode *sdk.Node, ancestorsIds []int64) ([]sdk.Parameter, error) {

runContext := nodeRunContext{}
if refNode != nil && refNode.Context != nil {
if refNode.Context.PipelineID != 0 && wf.Pipelines != nil {
Expand Down Expand Up @@ -610,7 +609,7 @@ func NodeBuildParametersFromWorkflow(ctx context.Context, db gorp.SqlExecutor, s
res := []sdk.Parameter{}
if len(res) == 0 {
var err error
res, err = GetNodeBuildParameters(proj, wf, runContext, refNode.Context.DefaultPipelineParameters, refNode.Context.DefaultPayload)
res, err = GetNodeBuildParameters(proj, wf, runContext, refNode.Context.DefaultPipelineParameters, refNode.Context.DefaultPayload, nil)
if err != nil {
return nil, sdk.WrapError(sdk.ErrWorkflowNodeNotFound, "getWorkflowTriggerConditionHandler> Unable to get workflow node parameters: %v", err)
}
Expand Down
17 changes: 12 additions & 5 deletions engine/api/workflow/process_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func getNodeJobRunParameters(db gorp.SqlExecutor, j sdk.Job, run *sdk.WorkflowNo
}

// GetNodeBuildParameters returns build parameters with default values for cds.version, cds.run, cds.run.number, cds.run.subnumber
func GetNodeBuildParameters(proj *sdk.Project, w *sdk.Workflow, runContext nodeRunContext, pipelineParameters []sdk.Parameter, payload interface{}) ([]sdk.Parameter, error) {
func GetNodeBuildParameters(proj *sdk.Project, w *sdk.Workflow, runContext nodeRunContext, pipelineParameters []sdk.Parameter, payload interface{}, hookEvent *sdk.WorkflowNodeRunHookEvent) ([]sdk.Parameter, error) {
tmpProj := sdk.ParametersFromProjectVariables(*proj)
vars := make(map[string]string, len(tmpProj))
for k, v := range tmpProj {
Expand Down Expand Up @@ -132,6 +132,13 @@ func GetNodeBuildParameters(proj *sdk.Project, w *sdk.Workflow, runContext nodeR
delete(vars, "git.http.user")
}

if hookEvent != nil {
vars["parent.project"] = hookEvent.ParentWorkflow.Key
vars["parent.run"] = fmt.Sprintf("%d", hookEvent.ParentWorkflow.Run)
vars["parent.workflow"] = hookEvent.ParentWorkflow.Name
vars["parent.outgoinghook"] = hookEvent.WorkflowNodeHookUUID
}

params := []sdk.Parameter{}
for k, v := range vars {
sdk.AddParameter(&params, k, sdk.StringParameter, v)
Expand Down Expand Up @@ -193,16 +200,16 @@ func getParentParameters(w *sdk.WorkflowRun, nodeRuns []*sdk.WorkflowNodeRun, pa
return params, nil
}

func getNodeRunBuildParameters(ctx context.Context, proj *sdk.Project, w *sdk.WorkflowRun, run *sdk.WorkflowNodeRun, runContext nodeRunContext) ([]sdk.Parameter, error) {
func getNodeRunBuildParameters(ctx context.Context, proj *sdk.Project, wr *sdk.WorkflowRun, run *sdk.WorkflowNodeRun, runContext nodeRunContext) ([]sdk.Parameter, error) {
ctx, end := observability.Span(ctx, "workflow.getNodeRunBuildParameters",
observability.Tag(observability.TagWorkflow, w.Workflow.Name),
observability.Tag(observability.TagWorkflowRun, w.Number),
observability.Tag(observability.TagWorkflow, wr.Workflow.Name),
observability.Tag(observability.TagWorkflowRun, wr.Number),
observability.Tag(observability.TagWorkflowNodeRun, run.ID),
)
defer end()

//Get node build parameters
params, errparam := GetNodeBuildParameters(proj, &w.Workflow, runContext, run.PipelineParameters, run.Payload)
params, errparam := GetNodeBuildParameters(proj, &wr.Workflow, runContext, run.PipelineParameters, run.Payload, run.HookEvent)
if errparam != nil {
return nil, sdk.WrapError(errparam, "getNodeRunParameters> Unable to compute node build parameters")
}
Expand Down
25 changes: 24 additions & 1 deletion engine/hooks/outgoing_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
Expand All @@ -12,6 +13,7 @@ import (
"strconv"
"time"

dump "github.com/fsamin/go-dump"
"github.com/mitchellh/hashstructure"

"github.com/ovh/cds/sdk"
Expand Down Expand Up @@ -185,9 +187,30 @@ func (s *Service) doOutgoingWorkflowExecution(t *sdk.TaskExecution) error {
return handleError(errors.New("unable to find hook" + hookRunID))
}

payloadValues := map[string]string{}
if payload, ok := hookRun.OutgoingHook.Config[sdk.Payload]; ok && payload.Value != "{}" {
var payloadInt interface{}
if err := json.Unmarshal([]byte(payload.Value), &payloadInt); err == nil {
e := dump.NewDefaultEncoder(new(bytes.Buffer))
e.Formatters = []dump.KeyFormatterFunc{dump.WithDefaultLowerCaseFormatter()}
e.ExtraFields.DetailedMap = false
e.ExtraFields.DetailedStruct = false
e.ExtraFields.Len = false
e.ExtraFields.Type = false
m1, errm1 := e.ToStringMap(payloadInt)
if errm1 != nil {
log.Error("Hooks> doOutgoingWorkflowExecution> Cannot convert payload to map %s", errm1)
} else {
payloadValues = m1
}
} else {
log.Error("Hooks> doOutgoingWorkflowExecution> Cannot unmarshall payload %s", err)
}
}

evt := sdk.WorkflowNodeRunHookEvent{
WorkflowNodeHookUUID: targetHook,
Payload: sdk.ParametersToMap(hookRun.BuildParameters),
Payload: payloadValues,
}
evt.ParentWorkflow.Key = pkey
evt.ParentWorkflow.Name = workflow
Expand Down
5 changes: 5 additions & 0 deletions sdk/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ var (
Configurable: true,
Type: HookConfigTypeHook,
},
Payload: {
Value: "{}",
Configurable: true,
Type: HookConfigTypeString,
},
},
}
)
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/shared/workflow/node/hook/form/hook.form.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ <h3 class="inline">{{ 'workflow_node_hook_form_title' | translate }}</h3>
</sui-select>
</ng-container>
<ng-container *ngIf="_hook.model && displayConfig">
<h3>{{ 'workflow_node_hook_form_config' | translate }}</h3>
<ng-container *ngIf="_hook.config && _hook.model.name !== 'Workflow'">
<h3>{{ 'workflow_node_hook_form_config' | translate }}</h3>
<div class="inline fields" *ngFor="let k of hook.config | keys">
<div class="four wide field"><label>{{k}}</label></div>
<div class="twelve wide field">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class WorkflowNodeOutGoingHookFormComponent implements OnInit {
availableHooks: Array<WNodeHook>;
wSub: Subscription;
invalidJSON = false;
outgoing_default_payload: {};

constructor(private _hookService: HookService, private _workflowStore: WorkflowStore) {
}
Expand Down Expand Up @@ -106,6 +107,7 @@ export class WorkflowNodeOutGoingHookFormComponent implements OnInit {
let key = this.project.key + '-' + this.hook.outgoing_hook.config['target_workflow'].value;
let wf = data.get(key);
if (wf) {
this.outgoing_default_payload = wf.workflow_data.node.context.default_payload;
let allHooks = Workflow.getAllHooks(wf);
if (allHooks) {
this.availableHooks = allHooks.filter(h => wf.hook_models[h.hook_model_id].name === 'Workflow');
Expand All @@ -129,7 +131,12 @@ export class WorkflowNodeOutGoingHookFormComponent implements OnInit {
this.wSub.unsubscribe();
}
}
}

updateWorkflowOutgoingHook(): void {
if (this.hook.outgoing_hook.config['target_hook']) {
this.hook.outgoing_hook.config['payload'].value = JSON.stringify(this.outgoing_default_payload, undefined, 4);
}
}

changeCodeMirror(code: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ <h3>{{ 'workflow_node_hook_form_config' | translate }}</h3>
<div class="inline fields">
<div class="four wide field"><label>project</label></div>
<div class="twelve wide field">

<input type="text" [(ngModel)]="hook.outgoing_hook.config['target_project'].value" readonly/>
</div>
</div>
Expand All @@ -73,6 +72,7 @@ <h3>{{ 'workflow_node_hook_form_config' | translate }}</h3>
<div class="twelve wide field">
<sui-select class="selection" placeholder="{{'hook' | translate}}"
[(ngModel)]="hook.outgoing_hook.config['target_hook'].value"
(ngModelChange)="updateWorkflowOutgoingHook()"
[options]="availableHooks"
[isSearchable]="true"
labelField="uuid"
Expand All @@ -82,6 +82,18 @@ <h3>{{ 'workflow_node_hook_form_config' | translate }}</h3>
</sui-select>
</div>
</div>
<div class="inline fields" *ngIf="availableHooks && hook.outgoing_hook.config['target_hook'].value">
<div class="four wide field"><label>payload</label></div>
<div class="twelve wide field">
<codemirror
[class.invalid]="invalidJSON"
[(ngModel)]="hook.outgoing_hook.config['payload'].value"
[config]="codeMirrorConfig"
(change)="changeCodeMirror($event)"
#textareaCodeMirror>
</codemirror>
</div>
</div>
</ng-container>
<div class="ui info message" *ngIf="!hook.outgoing_hook.config">{{ 'workflow_node_hook_no_configuration' | translate }}</div>
</ng-container>
Expand Down

0 comments on commit 5c7edc8

Please sign in to comment.