Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ NAME="github.com/odpf/guardian"
LAST_COMMIT := $(shell git rev-parse --short HEAD)
LAST_TAG := "$(shell git rev-list --tags --max-count=1)"
APP_VERSION := "$(shell git describe --tags ${LAST_TAG})-next"
PROTON_COMMIT := "0b0dfb79b4b90db1a497e678dfc8b63e5e85654b"
PROTON_COMMIT := "88b4ec297ece41c28009b165a4ded7eff9ab44e2"

.PHONY: all build test clean dist vet proto install

Expand Down
30 changes: 16 additions & 14 deletions api/handler/v1beta1/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,14 @@ func (a *adapter) FromPolicyProto(p *guardianv1beta1.Policy) (*domain.Policy, er
if p.GetSteps() != nil {
for _, s := range p.GetSteps() {
steps = append(steps, &domain.Step{
Name: s.GetName(),
Description: s.GetDescription(),
When: s.GetWhen(),
Strategy: domain.ApprovalStepStrategy(s.GetStrategy()),
ApproveIf: s.GetApproveIf(),
AllowFailed: s.GetAllowFailed(),
Approvers: s.GetApprovers(),
Name: s.GetName(),
Description: s.GetDescription(),
When: s.GetWhen(),
Strategy: domain.ApprovalStepStrategy(s.GetStrategy()),
RejectionReason: s.GetRejectionReason(),
ApproveIf: s.GetApproveIf(),
AllowFailed: s.GetAllowFailed(),
Approvers: s.GetApprovers(),
})
}
}
Expand Down Expand Up @@ -257,13 +258,14 @@ func (a *adapter) ToPolicyProto(p *domain.Policy) (*guardianv1beta1.Policy, erro
if p.Steps != nil {
for _, s := range p.Steps {
steps = append(steps, &guardianv1beta1.Policy_ApprovalStep{
Name: s.Name,
Description: s.Description,
When: s.When,
Strategy: string(s.Strategy),
ApproveIf: s.ApproveIf,
AllowFailed: s.AllowFailed,
Approvers: s.Approvers,
Name: s.Name,
Description: s.Description,
When: s.When,
Strategy: string(s.Strategy),
RejectionReason: s.RejectionReason,
ApproveIf: s.ApproveIf,
AllowFailed: s.AllowFailed,
Approvers: s.Approvers,
})
}
}
Expand Down
765 changes: 388 additions & 377 deletions api/proto/odpf/guardian/v1beta1/guardian.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions core/approval/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (s *service) AdvanceApproval(appeal *domain.Appeal) error {
}
} else {
approval.Status = domain.ApprovalStatusRejected
approval.Reason = stepConfig.RejectionReason
appeal.Status = domain.AppealStatusRejected
}
} else {
Expand Down
44 changes: 44 additions & 0 deletions core/approval/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,50 @@ func (s *ServiceTestSuite) TestAdvanceApproval() {
s.Nil(actualError)
})

s.Run("should autofill rejection reason on auto-reject", func() {
rejectionReason := "test rejection reason"
testAppeal := &domain.Appeal{
PolicyID: "test-id",
PolicyVersion: 1,
Resource: &domain.Resource{
Name: "grafana",
Details: map[string]interface{}{
"owner": "test-owner",
},
},
Policy: &domain.Policy{
ID: "test-id",
Version: 1,
Steps: []*domain.Step{
{
Name: "step-1",
Strategy: "auto",
RejectionReason: rejectionReason,
ApproveIf: `false`, // hard reject for testing purpose
},
},
},
Approvals: []*domain.Approval{
{
Status: domain.ApprovalStatusPending,
Index: 0,
},
},
}
expectedApprovals := []*domain.Approval{
{
Status: domain.ApprovalStatusRejected,
Index: 0,
Reason: rejectionReason,
},
}

actualError := s.service.AdvanceApproval(testAppeal)

s.Nil(actualError)
s.Equal(expectedApprovals, testAppeal.Approvals)
})

s.Run("should update approval statuses", func() {
resourceFlagStep := &domain.Step{
Name: "resourceFlagStep",
Expand Down
1 change: 1 addition & 0 deletions docs/docs/reference/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ requirements:
| `description` | `string` Approval step description |
| `when` | [`Expression`](#expression) Determines whether the step should be evaluated or it can be skipped. If it evaluates to be falsy, the step will automatically skipped. Otherwise, step become pending/blocked (normal). |
| `strategy` | `string` Execution behaviour of the step. Possible values are `auto` or `manual` |
| `rejection_reason` | `string` This fills `Approval.Reason` if current approval step gets rejected based on `ApproveIf` expression. If `strategy=manual`, this field ignored. |
| `approvers` | `[]string` List of email or [`Expression`](#expression) string. The `Expression` is expected to return an email address or list of email addresses. Required if `strategy` is `manual` |
| `approve_if` | [`Expression`](#expression) Determines the automatic resolution of current step when `strategy` is `auto` |
| `allow_failed` | `boolean` If `true`, and current step is rejected, it will mark the appeal status as skipped instead of rejected |
Expand Down
3 changes: 3 additions & 0 deletions domain/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ type Step struct {
// Strategy defines if the step requires manual approval or not
Strategy ApprovalStepStrategy `json:"strategy" yaml:"strategy" validate:"required,oneof=auto manual"`

// RejectionReason message fills `Approval.Reason` if the approval step gets rejected based on `ApproveIf` expression.
RejectionReason string `json:"rejection_reason" yaml:"rejection_reason"`

// Approvers is an Expression that if the evaluation returns string or []string that contains email address of the approvers.
// If human approval (manual) is required, use this field.
//
Expand Down