package mysql import ( "context" "database/sql" "errors" "git.yuntuo.hk/k2safe/orbit/internal/fault" core "git.yuntuo.hk/k2safe/orbit/model" "git.yuntuo.hk/k2safe/orbit/platform/auth" "git.yuntuo.hk/k2safe/orbit/weave/engine" "git.yuntuo.hk/k2safe/orbit/weave/instance" ) func (s *WorkflowInstanceStore) Step(ctx context.Context, t auth.Token, id core.ID, key string) (instance.StepDetail, error) { out := instance.StepDetail{InstanceID: id} err := s.transaction(ctx, t, instance.ReadAction, func(tx *sql.Tx, p auth.Permissions) error { v, gate, version, err := readWorkflowForQuery(ctx, tx, t.NamespaceID, id, p) if err != nil { return err } out.Revision = v.Revision steps, err := readWorkflowStepViews(ctx, tx, t.NamespaceID, id, gate, version, key) if err != nil { return err } if len(steps) != 1 { return fault.NotFound } out.Step = steps[0] a := out.Step.ActivationID if a == nil { return nil } var input, output []byte if err = tx.QueryRowContext(ctx, `SELECT input,output,created_at,completed_at FROM weave_activations WHERE namespace_id=? AND workflow_id=? AND step_id=? AND id=?`, t.NamespaceID, id, out.Step.ID, a).Scan(&input, &output, &out.ActivatedAt, &out.CompletedAt); err != nil { return err } out.Input, out.Output = input, output program, err := engine.Compile(version.Specification) if err != nil { return err } node, _ := program.Step(key) if node.Poll != nil { q := &instance.PollView{} var owned, queue *core.ID err = tx.QueryRowContext(ctx, "SELECT p.used_rounds,p.first_round,p.first_round_attempts_used,p.max_rounds,p.max_attempts_per_round,p.interval_ms,p.deadline_at,p.next_poll_at,p.current_task_id,r.task_id,p.resolved_at,t.id,t.queue_id FROM weave_poll_states p JOIN weave_poll_rounds r ON r.namespace_id=p.namespace_id AND r.workflow_id=p.workflow_id AND r.activation_id=p.activation_id AND r.`round`=p.used_rounds LEFT JOIN pulse_tasks t ON t.namespace_id=r.namespace_id AND t.id=r.task_id AND t.owner_kind='workflow_activation' AND t.owner_id=r.activation_id AND t.gate_id=? WHERE p.namespace_id=? AND p.workflow_id=? AND p.activation_id=?", gate, t.NamespaceID, id, a).Scan(&q.UsedRounds, &q.FirstRound, &q.FirstRoundAttemptsUsed, &q.MaxRounds, &q.MaxAttemptsPerRound, &q.IntervalMS, &q.DeadlineAt, &q.NextPollAt, &q.CurrentTaskID, &q.LastTaskID, &q.ResolvedAt, &owned, &queue) if err != nil { return err } if owned == nil || *owned != q.LastTaskID || queue == nil || *queue != node.Poll.Task.QueueID || q.CurrentTaskID != nil && *q.CurrentTaskID != q.LastTaskID || q.MaxRounds != node.Poll.MaxRounds || q.MaxAttemptsPerRound != node.Poll.Task.Retry.MaxAttempts || q.IntervalMS != node.Poll.IntervalMS { return errors.New("workflow poll detail ownership or policy invalid") } out.Poll = q } if node.Signal != nil || node.Timer != nil || node.Poll != nil && node.Poll.FinalSignal != nil { w := &instance.WaitView{} if err = tx.QueryRowContext(ctx, `SELECT id,kind,state,signal_name,payload_version,correlation_key,deadline_at,winner_signal_id,resolved_at FROM weave_waits WHERE namespace_id=? AND workflow_id=? AND activation_id=?`, t.NamespaceID, id, a).Scan(&w.ID, &w.Kind, &w.State, &w.Name, &w.PayloadVersion, &w.CorrelationKey, &w.DeadlineAt, &w.WinnerSignalID, &w.ResolvedAt); err != nil { return err } if w.Kind != string(out.Step.Kind) { return errors.New("workflow wait kind mismatch") } if w.WinnerSignalID != nil { var n int if err = tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM weave_signals WHERE namespace_id=? AND workflow_id=? AND id=? AND consumed_wait_id=? AND target_step_key=? AND target_activation_sequence=? AND state='CONSUMED'`, t.NamespaceID, id, w.WinnerSignalID, w.ID, key, out.Step.ActivationSequence).Scan(&n); err != nil { return err } if n != 1 { return errors.New("workflow wait winner association invalid") } } out.Wait = w } return nil }) return out, err }