Skip to content

Commit

Permalink
feat: api to create product features (#546)
Browse files Browse the repository at this point in the history
- raystack/proton#346

Signed-off-by: Kush Sharma <[email protected]>
  • Loading branch information
kushsharma authored Mar 17, 2024
1 parent f281633 commit 70d6aa8
Show file tree
Hide file tree
Showing 16 changed files with 13,064 additions and 10,840 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/setup-go@v4
with:
go-version: '1.22.0'
go-version: "1.22.1"
- uses: actions/checkout@v3
with:
fetch-depth: 0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22.0"
go-version: "1.22.1"
- name: Login to DockerHub
uses: docker/login-action@v3
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22.0"
go-version: "1.22.1"
- name: Login to DockerHub
uses: docker/login-action@v3
with:
Expand All @@ -41,7 +41,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22.0"
go-version: "1.22.1"
- name: Login to DockerHub
uses: docker/login-action@v1
with:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22.0'
go-version: "1.22.1"
- name: install dependencies
run: go mod tidy
- name: run unit tests
Expand All @@ -42,7 +42,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22.0'
go-version: "1.22.1"
- name: install dependencies
run: go mod tidy
- name: install spicedb binary
Expand All @@ -65,7 +65,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22.0'
go-version: "1.22.1"
- name: install dependencies
run: go mod tidy
- name: run regression tests
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TAG := $(shell git rev-list --tags --max-count=1)
VERSION := $(shell git describe --tags ${TAG})
.PHONY: build check fmt lint test test-race vet test-cover-html help install proto ui
.DEFAULT_GOAL := build
PROTON_COMMIT := "7b7b4b803e0e030782f9203acf5c3687148f6bab"
PROTON_COMMIT := "2d9c53b797c61b6ba1ca68d2a511db38c61d3b38"

ui:
@echo " > generating ui build"
Expand Down
6 changes: 3 additions & 3 deletions billing/product/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,15 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Product, error) {
}

func (s *Service) UpsertFeature(ctx context.Context, feature Feature) (Feature, error) {
if len(feature.ID) == 0 {
feature.ID = uuid.New().String()
}
if len(feature.Name) == 0 {
return Feature{}, fmt.Errorf("feature name is required: %w", ErrInvalidFeatureDetail)
}
feature.ProductIDs = utils.Deduplicate(feature.ProductIDs)
existingFeature, err := s.GetFeatureByID(ctx, feature.Name)
if err != nil && errors.Is(err, ErrFeatureNotFound) {
if len(feature.ID) == 0 {
feature.ID = uuid.New().String()
}
return s.featureRepository.Create(ctx, feature)
}

Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/raystack/frontier

go 1.22
go 1.22.1

require (
github.com/MakeNowJust/heredoc v1.0.0
Expand Down Expand Up @@ -85,11 +85,11 @@ require (
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
Expand Down Expand Up @@ -203,7 +203,7 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2438,8 +2438,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
Expand Down Expand Up @@ -2478,8 +2478,8 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -2939,8 +2939,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
82 changes: 82 additions & 0 deletions internal/api/v1beta1/billing_product.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1beta1

import (
"context"
"errors"

"github.com/raystack/frontier/pkg/metadata"

Expand All @@ -15,6 +16,8 @@ type ProductService interface {
Create(ctx context.Context, product product.Product) (product.Product, error)
Update(ctx context.Context, product product.Product) (product.Product, error)
List(ctx context.Context, filter product.Filter) ([]product.Product, error)
UpsertFeature(ctx context.Context, feature product.Feature) (product.Feature, error)
GetFeatureByID(ctx context.Context, id string) (product.Feature, error)
ListFeatures(ctx context.Context, filter product.Filter) ([]product.Feature, error)
}

Expand Down Expand Up @@ -196,3 +199,82 @@ func (h Handler) ListFeatures(ctx context.Context, request *frontierv1beta1.List
Features: featuresPB,
}, nil
}

func (h Handler) CreateFeature(ctx context.Context, request *frontierv1beta1.CreateFeatureRequest) (*frontierv1beta1.CreateFeatureResponse, error) {
logger := grpczap.Extract(ctx)

metaDataMap := metadata.Build(request.GetBody().GetMetadata().AsMap())
newFeature, err := h.productService.UpsertFeature(ctx, product.Feature{
Name: request.GetBody().GetName(),
Title: request.GetBody().GetTitle(),
ProductIDs: request.GetBody().GetProductIds(),
Metadata: metaDataMap,
})
if err != nil {
logger.Error(err.Error())
if errors.Is(err, product.ErrInvalidFeatureDetail) {
return nil, grpcBadBodyError
}
return nil, grpcInternalServerError
}

featurePB, err := transformFeatureToPB(newFeature)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &frontierv1beta1.CreateFeatureResponse{
Feature: featurePB,
}, nil
}

func (h Handler) UpdateFeature(ctx context.Context, request *frontierv1beta1.UpdateFeatureRequest) (*frontierv1beta1.UpdateFeatureResponse, error) {
logger := grpczap.Extract(ctx)

metaDataMap := metadata.Build(request.GetBody().GetMetadata().AsMap())
updatedFeature, err := h.productService.UpsertFeature(ctx, product.Feature{
ID: request.GetId(),
Name: request.GetBody().GetName(),
Title: request.GetBody().GetTitle(),
ProductIDs: request.GetBody().GetProductIds(),
Metadata: metaDataMap,
})
if err != nil {
logger.Error(err.Error())
if errors.Is(err, product.ErrInvalidFeatureDetail) {
return nil, grpcBadBodyError
}
return nil, grpcInternalServerError
}

featurePB, err := transformFeatureToPB(updatedFeature)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &frontierv1beta1.UpdateFeatureResponse{
Feature: featurePB,
}, nil
}

func (h Handler) GetFeature(ctx context.Context, request *frontierv1beta1.GetFeatureRequest) (*frontierv1beta1.GetFeatureResponse, error) {
logger := grpczap.Extract(ctx)

feature, err := h.productService.GetFeatureByID(ctx, request.GetId())
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

featurePB, err := transformFeatureToPB(feature)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &frontierv1beta1.GetFeatureResponse{
Feature: featurePB,
}, nil
}
9 changes: 9 additions & 0 deletions pkg/server/interceptors/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ var authorizationSkipEndpoints = map[string]bool{
"/raystack.frontier.v1beta1.FrontierService/GetProduct": true,
"/raystack.frontier.v1beta1.FrontierService/ListProducts": true,
"/raystack.frontier.v1beta1.FrontierService/ListFeatures": true,
"/raystack.frontier.v1beta1.FrontierService/GetFeature": true,

// TODO(kushsharma): for now we are allowing all requests to billing
// entitlement checks. Ideally we should only allow requests for
Expand Down Expand Up @@ -724,6 +725,14 @@ var authorizationValidationMap = map[string]func(ctx context.Context, handler *v
return handler.IsSuperUser(ctx)
},

// features
"/raystack.frontier.v1beta1.FrontierService/CreateFeature": func(ctx context.Context, handler *v1beta1.Handler, req any) error {
return handler.IsSuperUser(ctx)
},
"/raystack.frontier.v1beta1.FrontierService/UpdateFeature": func(ctx context.Context, handler *v1beta1.Handler, req any) error {
return handler.IsSuperUser(ctx)
},

// usage
"/raystack.frontier.v1beta1.FrontierService/CreateBillingUsage": func(ctx context.Context, handler *v1beta1.Handler, req any) error {
pbReq := req.(*frontierv1beta1.CreateBillingUsageRequest)
Expand Down
Loading

0 comments on commit 70d6aa8

Please sign in to comment.