Skip to content
31 changes: 31 additions & 0 deletions internal/api/identity/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package identity

import (
"net/http"

"github.com/JSYoo5B/SandStack/internal/api/respond"
"github.com/go-chi/chi/v5"
)

func (h Handler) listEndpoints(w http.ResponseWriter, r *http.Request) {
baseURL := h.baseURL(r)
respond.JSON(w, http.StatusOK, endpointsResponse{
Endpoints: h.service.Endpoints(baseURL),
Links: identityLinks{
Self: baseURL + "/identity/v3/endpoints",
},
})
}

func (h Handler) getEndpoint(w http.ResponseWriter, r *http.Request) {
baseURL := h.baseURL(r)
endpoint, ok := h.service.EndpointByID(baseURL, chi.URLParam(r, "endpoint_id"))
if !ok {
respond.Error(w, http.StatusNotFound, "endpoint not found")
return
}

respond.JSON(w, http.StatusOK, endpointResponse{
Endpoint: endpoint,
})
}
12 changes: 12 additions & 0 deletions internal/api/identity/endpoint_dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package identity

import "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints"

type endpointsResponse struct {
Endpoints []endpoints.Endpoint `json:"endpoints"`
Links identityLinks `json:"links"`
}

type endpointResponse struct {
Endpoint endpoints.Endpoint `json:"endpoint"`
}
72 changes: 72 additions & 0 deletions internal/api/identity/endpoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package identity_test

import (
"net/http/httptest"
"testing"

"github.com/JSYoo5B/SandStack/internal/api/identity"
"github.com/JSYoo5B/SandStack/internal/testhelper"
"github.com/gophercloud/gophercloud/v2"
"github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints"
"github.com/stretchr/testify/suite"
)

type EndpointSuite struct {
suite.Suite
server *httptest.Server
}

func TestEndpointSuite(t *testing.T) {
suite.Run(t, new(EndpointSuite))
}

func (s *EndpointSuite) SetupTest() {
s.server = httptest.NewServer(
identity.NewRouter(testhelper.DefaultConfig()),
)
}

func (s *EndpointSuite) TearDownTest() {
s.server.Close()
}

func (s *EndpointSuite) TestListEndpoints() {
pages, err := endpoints.List(
testhelper.ServiceClient(s.server.URL),
nil,
).AllPages(s.T().Context())
s.Require().NoError(err)

listed, err := endpoints.ExtractEndpoints(pages)
s.Require().NoError(err)

s.Assert().Contains(
identityEndpointIDs(listed),
"compute-public",
)
}

func (s *EndpointSuite) TestGetEndpoint() {
result := endpoints.Get(
s.T().Context(),
testhelper.ServiceClient(s.server.URL),
"compute-public",
)
endpoint, err := result.Extract()
s.Require().NoError(err)
s.Require().NotNil(endpoint)

s.Assert().Equal("compute-public", endpoint.ID)
s.Assert().Equal("compute", endpoint.ServiceID)
s.Assert().Equal(gophercloud.AvailabilityPublic, endpoint.Availability)
s.Assert().Equal(s.server.URL+"/compute/v2.1/demo", endpoint.URL)
}

func identityEndpointIDs(listed []endpoints.Endpoint) []string {
ids := make([]string, 0, len(listed))
for _, endpoint := range listed {
ids = append(ids, endpoint.ID)
}

return ids
}
29 changes: 29 additions & 0 deletions internal/api/identity/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package identity

import (
"net/http"

"github.com/JSYoo5B/SandStack/internal/api/respond"
"github.com/go-chi/chi/v5"
)

func (h Handler) listProjects(w http.ResponseWriter, r *http.Request) {
respond.JSON(w, http.StatusOK, projectsResponse{
Projects: h.service.Projects(),
Links: identityLinks{
Self: h.baseURL(r) + "/identity/v3/projects",
},
})
}

func (h Handler) getProject(w http.ResponseWriter, r *http.Request) {
project, ok := h.service.ProjectByID(chi.URLParam(r, "project_id"))
if !ok {
respond.Error(w, http.StatusNotFound, "project not found")
return
}

respond.JSON(w, http.StatusOK, projectResponse{
Project: project,
})
}
18 changes: 18 additions & 0 deletions internal/api/identity/project_dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package identity

import "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects"

type projectsResponse struct {
Projects []projects.Project `json:"projects"`
Links identityLinks `json:"links"`
}

type projectResponse struct {
Project projects.Project `json:"project"`
}

type identityLinks struct {
Self string `json:"self"`
Previous string `json:"previous,omitempty"`
Next string `json:"next,omitempty"`
}
63 changes: 63 additions & 0 deletions internal/api/identity/project_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package identity_test

import (
"net/http/httptest"
"testing"

"github.com/JSYoo5B/SandStack/internal/api/identity"
"github.com/JSYoo5B/SandStack/internal/testhelper"
"github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects"
"github.com/stretchr/testify/suite"
)

type ProjectSuite struct {
suite.Suite
server *httptest.Server
}

func TestProjectSuite(t *testing.T) {
suite.Run(t, new(ProjectSuite))
}

func (s *ProjectSuite) SetupTest() {
s.server = httptest.NewServer(
identity.NewRouter(testhelper.DefaultConfig()),
)
}

func (s *ProjectSuite) TearDownTest() {
s.server.Close()
}

func (s *ProjectSuite) TestListProjects() {
pages, err := projects.List(
testhelper.ServiceClient(s.server.URL),
nil,
).AllPages(s.T().Context())
s.Require().NoError(err)

listed, err := projects.ExtractProjects(pages)
s.Require().NoError(err)
s.Require().Len(listed, 1)

s.Assert().Equal("demo", listed[0].ID)
s.Assert().Equal("demo", listed[0].Name)
s.Assert().Equal("default", listed[0].DomainID)
s.Assert().True(listed[0].Enabled)
}

func (s *ProjectSuite) TestGetProject() {
result := projects.Get(
s.T().Context(),
testhelper.ServiceClient(s.server.URL),
"demo",
)
project, err := result.Extract()
s.Require().NoError(err)
s.Require().NotNil(project)

s.Assert().Equal("demo", project.ID)
s.Assert().Equal("demo", project.Name)
s.Assert().Equal("default", project.DomainID)
s.Assert().True(project.Enabled)
}
29 changes: 29 additions & 0 deletions internal/api/identity/role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package identity

import (
"net/http"

"github.com/JSYoo5B/SandStack/internal/api/respond"
"github.com/go-chi/chi/v5"
)

func (h Handler) listRoles(w http.ResponseWriter, r *http.Request) {
respond.JSON(w, http.StatusOK, rolesResponse{
Roles: h.service.Roles(),
Links: identityLinks{
Self: h.baseURL(r) + "/identity/v3/roles",
},
})
}

func (h Handler) getRole(w http.ResponseWriter, r *http.Request) {
role, ok := h.service.RoleByID(chi.URLParam(r, "role_id"))
if !ok {
respond.Error(w, http.StatusNotFound, "role not found")
return
}

respond.JSON(w, http.StatusOK, roleResponse{
Role: role,
})
}
12 changes: 12 additions & 0 deletions internal/api/identity/role_dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package identity

import "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles"

type rolesResponse struct {
Roles []roles.Role `json:"roles"`
Links identityLinks `json:"links"`
}

type roleResponse struct {
Role roles.Role `json:"role"`
}
61 changes: 61 additions & 0 deletions internal/api/identity/role_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package identity_test

import (
"net/http/httptest"
"testing"

"github.com/JSYoo5B/SandStack/internal/api/identity"
"github.com/JSYoo5B/SandStack/internal/testhelper"
"github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles"
"github.com/stretchr/testify/suite"
)

type RoleSuite struct {
suite.Suite
server *httptest.Server
}

func TestRoleSuite(t *testing.T) {
suite.Run(t, new(RoleSuite))
}

func (s *RoleSuite) SetupTest() {
s.server = httptest.NewServer(
identity.NewRouter(testhelper.DefaultConfig()),
)
}

func (s *RoleSuite) TearDownTest() {
s.server.Close()
}

func (s *RoleSuite) TestListRoles() {
pages, err := roles.List(
testhelper.ServiceClient(s.server.URL),
nil,
).AllPages(s.T().Context())
s.Require().NoError(err)

listed, err := roles.ExtractRoles(pages)
s.Require().NoError(err)
s.Require().Len(listed, 1)

s.Assert().Equal("admin", listed[0].ID)
s.Assert().Equal("admin", listed[0].Name)
s.Assert().Equal("default", listed[0].DomainID)
}

func (s *RoleSuite) TestGetRole() {
result := roles.Get(
s.T().Context(),
testhelper.ServiceClient(s.server.URL),
"admin",
)
role, err := result.Extract()
s.Require().NoError(err)
s.Require().NotNil(role)

s.Assert().Equal("admin", role.ID)
s.Assert().Equal("admin", role.Name)
s.Assert().Equal("default", role.DomainID)
}
36 changes: 35 additions & 1 deletion internal/api/identity/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

appidentity "github.com/JSYoo5B/SandStack/internal/app/identity"
"github.com/JSYoo5B/SandStack/internal/platform/config"
storeidentity "github.com/JSYoo5B/SandStack/internal/store/identity"
"github.com/go-chi/chi/v5"
)

Expand All @@ -19,8 +20,28 @@ func NewRouter(cfg config.Config) http.Handler {
}

func NewHandler(cfg config.Config) Handler {
return NewHandlerWithService(
cfg,
appidentity.NewServiceWithRepositories(
cfg,
appidentity.Repositories{
Users: storeidentity.NewMemoryUserRepository(),
Projects: storeidentity.NewMemoryProjectRepository(),
Roles: storeidentity.NewMemoryRoleRepository(),
Tokens: storeidentity.NewMemoryTokenRepository(),
Services: storeidentity.NewMemoryServiceRepository(),
Endpoints: storeidentity.NewMemoryEndpointRepository(),
},
),
)
}

func NewHandlerWithService(
cfg config.Config,
service appidentity.Service,
) Handler {
return Handler{
service: appidentity.NewService(cfg),
service: service,
config: cfg,
}
}
Expand All @@ -33,6 +54,19 @@ func (h Handler) Router() http.Handler {
router := chi.NewRouter()
router.Get("/", h.version)
router.Post("/auth/tokens", h.createToken)
router.Get("/auth/tokens", h.getToken)
router.Head("/auth/tokens", h.validateToken)
router.Delete("/auth/tokens", h.revokeToken)
router.Get("/projects", h.listProjects)
router.Get("/projects/{project_id}", h.getProject)
router.Get("/users", h.listUsers)
router.Get("/users/{user_id}", h.getUser)
router.Get("/roles", h.listRoles)
router.Get("/roles/{role_id}", h.getRole)
router.Get("/services", h.listServices)
router.Get("/services/{service_id}", h.getService)
router.Get("/endpoints", h.listEndpoints)
router.Get("/endpoints/{endpoint_id}", h.getEndpoint)

return router
}
Expand Down
Loading
Loading