Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add projects api #25

Merged
merged 12 commits into from
Nov 14, 2021
23 changes: 8 additions & 15 deletions api/handler/v1/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"

"github.com/odpf/shield/internal/org"
modelv1 "github.com/odpf/shield/model/v1"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand All @@ -18,20 +19,12 @@ import (
)

type OrganizationService interface {
GetOrganization(ctx context.Context, id string) (org.Organization, error)
CreateOrganization(ctx context.Context, org org.Organization) (org.Organization, error)
ListOrganizations(ctx context.Context) ([]org.Organization, error)
UpdateOrganization(ctx context.Context, toUpdate org.Organization) (org.Organization, error)
GetOrganization(ctx context.Context, id string) (modelv1.Organization, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krtkvrm I think we don't need to version internal models. So would suggest removing versioning from here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah.. makes sense 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

CreateOrganization(ctx context.Context, org modelv1.Organization) (modelv1.Organization, error)
ListOrganizations(ctx context.Context) ([]modelv1.Organization, error)
UpdateOrganization(ctx context.Context, toUpdate modelv1.Organization) (modelv1.Organization, error)
}

var (
grpcInternalServerError = status.Errorf(codes.Internal, internalServerError.Error())
grpcBadBodyError = status.Error(codes.InvalidArgument, badRequestError.Error())
)

// HTTP Codes defined here:
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L36

func (v Dep) ListOrganizations(ctx context.Context, request *shieldv1.ListOrganizationsRequest) (*shieldv1.ListOrganizationsResponse, error) {
logger := grpczap.Extract(ctx)
var orgs []*shieldv1.Organization
Expand Down Expand Up @@ -76,7 +69,7 @@ func (v Dep) CreateOrganization(ctx context.Context, request *shieldv1.CreateOrg
slug = generateSlug(request.GetBody().Name)
}

newOrg, err := v.OrgService.CreateOrganization(ctx, org.Organization{
newOrg, err := v.OrgService.CreateOrganization(ctx, modelv1.Organization{
Name: request.GetBody().Name,
Slug: slug,
Metadata: metaDataMap,
Expand Down Expand Up @@ -142,7 +135,7 @@ func (v Dep) UpdateOrganization(ctx context.Context, request *shieldv1.UpdateOrg
return nil, grpcBadBodyError
}

updatedOrg, err := v.OrgService.UpdateOrganization(ctx, org.Organization{
updatedOrg, err := v.OrgService.UpdateOrganization(ctx, modelv1.Organization{
Id: request.GetId(),
Name: request.GetBody().Name,
Slug: request.GetBody().Slug,
Expand All @@ -163,7 +156,7 @@ func (v Dep) UpdateOrganization(ctx context.Context, request *shieldv1.UpdateOrg
return &shieldv1.UpdateOrganizationResponse{Organization: &orgPB}, nil
}

func transformOrgToPB(org org.Organization) (shieldv1.Organization, error) {
func transformOrgToPB(org modelv1.Organization) (shieldv1.Organization, error) {
metaData, err := structpb.NewStruct(mapOfInterfaceValues(org.Metadata))
if err != nil {
return shieldv1.Organization{}, err
Expand Down
38 changes: 19 additions & 19 deletions api/handler/v1/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"testing"
"time"

"github.com/odpf/shield/internal/org"

"github.com/stretchr/testify/assert"

modelv1 "github.com/odpf/shield/model/v1"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
Expand All @@ -18,7 +18,7 @@ import (
shieldv1 "go.buf.build/odpf/gw/odpf/proton/odpf/shield/v1"
)

var testOrgMap = map[string]org.Organization{
var testOrgMap = map[string]modelv1.Organization{
"9f256f86-31a3-11ec-8d3d-0242ac130003": {
Id: "9f256f86-31a3-11ec-8d3d-0242ac130003",
Name: "Org 1",
Expand All @@ -42,15 +42,15 @@ func TestListOrganizations(t *testing.T) {
}{
{
title: "error in Org Service",
mockOrgSrv: mockOrgSrv{ListOrganizationsFunc: func(ctx context.Context) (organizations []org.Organization, err error) {
return []org.Organization{}, errors.New("some error")
mockOrgSrv: mockOrgSrv{ListOrganizationsFunc: func(ctx context.Context) (organizations []modelv1.Organization, err error) {
return []modelv1.Organization{}, errors.New("some error")
}},
want: nil,
err: status.Errorf(codes.Internal, internalServerError.Error()),
}, {
title: "success",
mockOrgSrv: mockOrgSrv{ListOrganizationsFunc: func(ctx context.Context) (organizations []org.Organization, err error) {
var testOrgList []org.Organization
mockOrgSrv: mockOrgSrv{ListOrganizationsFunc: func(ctx context.Context) (organizations []modelv1.Organization, err error) {
var testOrgList []modelv1.Organization
for _, o := range testOrgMap {
testOrgList = append(testOrgList, o)
}
Expand Down Expand Up @@ -98,8 +98,8 @@ func TestCreateOrganization(t *testing.T) {
}{
{
title: "error in fetching org list",
mockOrgSrv: mockOrgSrv{CreateOrganizationFunc: func(ctx context.Context, o org.Organization) (org.Organization, error) {
return org.Organization{}, errors.New("some error")
mockOrgSrv: mockOrgSrv{CreateOrganizationFunc: func(ctx context.Context, o modelv1.Organization) (modelv1.Organization, error) {
return modelv1.Organization{}, errors.New("some error")
}},
req: &shieldv1.CreateOrganizationRequest{Body: &shieldv1.OrganizationRequestBody{
Name: "some org",
Expand All @@ -125,8 +125,8 @@ func TestCreateOrganization(t *testing.T) {
},
{
title: "success",
mockOrgSrv: mockOrgSrv{CreateOrganizationFunc: func(ctx context.Context, o org.Organization) (org.Organization, error) {
return org.Organization{
mockOrgSrv: mockOrgSrv{CreateOrganizationFunc: func(ctx context.Context, o modelv1.Organization) (modelv1.Organization, error) {
return modelv1.Organization{
Id: "new-abc",
Name: "some org",
Slug: "abc",
Expand Down Expand Up @@ -167,24 +167,24 @@ func TestCreateOrganization(t *testing.T) {
}

type mockOrgSrv struct {
GetOrganizationFunc func(ctx context.Context, id string) (org.Organization, error)
CreateOrganizationFunc func(ctx context.Context, org org.Organization) (org.Organization, error)
ListOrganizationsFunc func(ctx context.Context) ([]org.Organization, error)
UpdateOrganizationFunc func(ctx context.Context, toUpdate org.Organization) (org.Organization, error)
GetOrganizationFunc func(ctx context.Context, id string) (modelv1.Organization, error)
CreateOrganizationFunc func(ctx context.Context, org modelv1.Organization) (modelv1.Organization, error)
ListOrganizationsFunc func(ctx context.Context) ([]modelv1.Organization, error)
UpdateOrganizationFunc func(ctx context.Context, toUpdate modelv1.Organization) (modelv1.Organization, error)
}

func (m mockOrgSrv) GetOrganization(ctx context.Context, id string) (org.Organization, error) {
func (m mockOrgSrv) GetOrganization(ctx context.Context, id string) (modelv1.Organization, error) {
return m.GetOrganizationFunc(ctx, id)
}

func (m mockOrgSrv) CreateOrganization(ctx context.Context, org org.Organization) (org.Organization, error) {
func (m mockOrgSrv) CreateOrganization(ctx context.Context, org modelv1.Organization) (modelv1.Organization, error) {
return m.CreateOrganizationFunc(ctx, org)
}

func (m mockOrgSrv) ListOrganizations(ctx context.Context) ([]org.Organization, error) {
func (m mockOrgSrv) ListOrganizations(ctx context.Context) ([]modelv1.Organization, error) {
return m.ListOrganizationsFunc(ctx)
}

func (m mockOrgSrv) UpdateOrganization(ctx context.Context, toUpdate org.Organization) (org.Organization, error) {
func (m mockOrgSrv) UpdateOrganization(ctx context.Context, toUpdate modelv1.Organization) (modelv1.Organization, error) {
return m.UpdateOrganizationFunc(ctx, toUpdate)
}
143 changes: 139 additions & 4 deletions api/handler/v1/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,160 @@ package v1

import (
"context"
"errors"
"strings"

grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"

"github.com/odpf/shield/internal/project"
modelv1 "github.com/odpf/shield/model/v1"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"

shieldv1 "go.buf.build/odpf/gw/odpf/proton/odpf/shield/v1"
)

var grpcProjectNotFoundErr = status.Errorf(codes.NotFound, "project doesn't exist")

type ProjectService interface {
Get(ctx context.Context, id string) (modelv1.Project, error)
Create(ctx context.Context, project modelv1.Project) (modelv1.Project, error)
List(ctx context.Context) ([]modelv1.Project, error)
Update(ctx context.Context, toUpdate modelv1.Project) (modelv1.Project, error)
}

func (v Dep) ListProjects(ctx context.Context, request *shieldv1.ListProjectsRequest) (*shieldv1.ListProjectsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method not implemented")
logger := grpczap.Extract(ctx)
var projects []*shieldv1.Project

projectList, err := v.ProjectService.List(ctx)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

for _, v := range projectList {
projectPB, err := transformProjectToPB(v)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

projects = append(projects, &projectPB)
}

return &shieldv1.ListProjectsResponse{Projects: projects}, nil
}

func (v Dep) CreateProject(ctx context.Context, request *shieldv1.CreateProjectRequest) (*shieldv1.CreateProjectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method not implemented")
logger := grpczap.Extract(ctx)
metaDataMap, err := mapOfStringValues(request.GetBody().Metadata.AsMap())
if err != nil {
logger.Error(err.Error())
return nil, grpcBadBodyError
}

slug := request.GetBody().Slug
if strings.TrimSpace(slug) == "" {
slug = generateSlug(request.GetBody().Name)
}

newProject, err := v.ProjectService.Create(ctx, modelv1.Project{
Name: request.GetBody().Name,
Slug: slug,
Metadata: metaDataMap,
//Organization: org.Organization{Id: "ACCEPT"},
})

if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

metaData, err := structpb.NewStruct(mapOfInterfaceValues(newProject.Metadata))
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &shieldv1.CreateProjectResponse{Project: &shieldv1.Project{
Id: newProject.Id,
Name: newProject.Name,
Slug: newProject.Slug,
Metadata: metaData,
CreatedAt: timestamppb.New(newProject.CreatedAt),
UpdatedAt: timestamppb.New(newProject.UpdatedAt),
}}, nil
}

func (v Dep) GetProject(ctx context.Context, request *shieldv1.GetProjectRequest) (*shieldv1.GetProjectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method not implemented")
logger := grpczap.Extract(ctx)

fetchedProject, err := v.ProjectService.Get(ctx, request.GetId())
if err != nil {
logger.Error(err.Error())
switch {
case errors.Is(err, project.ProjectDoesntExist):
return nil, grpcProjectNotFoundErr
case errors.Is(err, project.InvalidUUID):
return nil, grpcBadBodyError
default:
return nil, grpcInternalServerError
}
}

projectPB, err := transformProjectToPB(fetchedProject)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &shieldv1.GetProjectResponse{Project: &projectPB}, nil
}

func (v Dep) UpdateProject(ctx context.Context, request *shieldv1.UpdateProjectRequest) (*shieldv1.UpdateProjectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method not implemented")
logger := grpczap.Extract(ctx)

metaDataMap, err := mapOfStringValues(request.GetBody().Metadata.AsMap())
if err != nil {
return nil, grpcBadBodyError
}

updatedProject, err := v.ProjectService.Update(ctx, modelv1.Project{
Id: request.GetId(),
Name: request.GetBody().Name,
Slug: request.GetBody().Slug,
Metadata: metaDataMap,
})
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

projectPB, err := transformProjectToPB(updatedProject)
if err != nil {
logger.Error(err.Error())
return nil, grpcInternalServerError
}

return &shieldv1.UpdateProjectResponse{Project: &projectPB}, nil
}

func transformProjectToPB(prj modelv1.Project) (shieldv1.Project, error) {
metaData, err := structpb.NewStruct(mapOfInterfaceValues(prj.Metadata))
if err != nil {
return shieldv1.Project{}, err
}

return shieldv1.Project{
Id: prj.Id,
Name: prj.Name,
Slug: prj.Slug,
Metadata: metaData,
CreatedAt: timestamppb.New(prj.CreatedAt),
UpdatedAt: timestamppb.New(prj.UpdatedAt),
}, nil
}
Loading