gateway api: improve response handling

* Always return a json message also on error. For internal errors return a
generic "internal server error" message to not leak the real internal error to
clients
* Return 201 Created on resource creation
* Return 204 No Content on resource deletion and other action with no json
output
This commit is contained in:
Simone Gotti 2019-04-08 11:35:45 +02:00
parent 763d77d899
commit 56d903d4db
12 changed files with 171 additions and 199 deletions

View File

@ -15,6 +15,7 @@
package api
import (
"encoding/json"
"net/http"
"net/url"
@ -24,12 +25,33 @@ import (
"github.com/sorintlab/agola/internal/util"
)
type ErrorResponse struct {
Message string `json:"message"`
}
func ErrorResponseFromError(err error) *ErrorResponse {
if util.IsErrBadRequest(err) {
return &ErrorResponse{Message: err.Error()}
}
// on generic error return an generic message to not leak the real error
return &ErrorResponse{Message: "internal server error"}
}
func httpError(w http.ResponseWriter, err error) bool {
if err != nil {
response := ErrorResponseFromError(err)
resj, merr := json.Marshal(response)
if merr != nil {
w.WriteHeader(http.StatusInternalServerError)
return true
}
if util.IsErrBadRequest(err) {
http.Error(w, err.Error(), http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
w.Write(resj)
} else {
http.Error(w, "", http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Write(resj)
}
return true
}
@ -37,6 +59,24 @@ func httpError(w http.ResponseWriter, err error) bool {
return false
}
func httpResponse(w http.ResponseWriter, code int, res interface{}) error {
w.Header().Set("Content-Type", "application/json")
if res != nil {
resj, err := json.Marshal(res)
if err != nil {
httpError(w, err)
return err
}
w.WriteHeader(code)
_, err = w.Write(resj)
return err
}
w.WriteHeader(code)
return nil
}
func GetConfigTypeRef(r *http.Request) (types.ConfigType, string, error) {
vars := mux.Vars(r)
projectRef, err := url.PathUnescape(vars["projectref"])

View File

@ -15,7 +15,6 @@
package api
import (
"encoding/json"
"net/http"
csapi "github.com/sorintlab/agola/internal/services/configstore/api"
@ -78,13 +77,11 @@ func (h *OAuth2CallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
response = &RegisterUserResponse{}
}
resp := RemoteSourceAuthResult{
res := RemoteSourceAuthResult{
RequestType: string(cresp.RequestType),
Response: response,
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -15,18 +15,16 @@
package api
import (
"context"
"encoding/json"
"net/http"
"strconv"
csapi "github.com/sorintlab/agola/internal/services/configstore/api"
"github.com/sorintlab/agola/internal/services/gateway/command"
"github.com/sorintlab/agola/internal/services/types"
"github.com/sorintlab/agola/internal/util"
"go.uber.org/zap"
"github.com/gorilla/mux"
"github.com/pkg/errors"
)
type CreateOrgRequest struct {
@ -34,12 +32,12 @@ type CreateOrgRequest struct {
}
type CreateOrgHandler struct {
log *zap.SugaredLogger
configstoreClient *csapi.Client
log *zap.SugaredLogger
ch *command.CommandHandler
}
func NewCreateOrgHandler(logger *zap.Logger, configstoreClient *csapi.Client) *CreateOrgHandler {
return &CreateOrgHandler{log: logger.Sugar(), configstoreClient: configstoreClient}
func NewCreateOrgHandler(logger *zap.Logger, ch *command.CommandHandler) *CreateOrgHandler {
return &CreateOrgHandler{log: logger.Sugar(), ch: ch}
}
func (h *CreateOrgHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -52,39 +50,20 @@ func (h *CreateOrgHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
org, err := h.createOrg(ctx, &req)
if err != nil {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(org); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
func (h *CreateOrgHandler) createOrg(ctx context.Context, req *CreateOrgRequest) (*OrgResponse, error) {
if !util.ValidateName(req.Name) {
return nil, errors.Errorf("invalid org name %q", req.Name)
}
u := &types.Organization{
creq := &command.CreateOrgRequest{
Name: req.Name,
}
h.log.Infof("creating org")
u, _, err := h.configstoreClient.CreateOrg(ctx, u)
if err != nil {
return nil, errors.Wrapf(err, "failed to create org")
org, err := h.ch.CreateOrg(ctx, creq)
if httpError(w, err) {
h.log.Errorf("err: %+v", err)
return
}
h.log.Infof("org %s created, ID: %s", u.Name, u.ID)
res := createOrgResponse(u)
return res, nil
res := createOrgResponse(org)
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
}
}
type DeleteOrgHandler struct {
@ -111,43 +90,9 @@ func (h *DeleteOrgHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
httpError(w, err)
return
}
}
type CurrentOrgHandler struct {
log *zap.SugaredLogger
configstoreClient *csapi.Client
}
func NewCurrentOrgHandler(logger *zap.Logger, configstoreClient *csapi.Client) *CurrentOrgHandler {
return &CurrentOrgHandler{log: logger.Sugar(), configstoreClient: configstoreClient}
}
func (h *CurrentOrgHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
orgIDVal := ctx.Value("orgid")
if orgIDVal == nil {
http.Error(w, "", http.StatusBadRequest)
return
}
orgID := orgIDVal.(string)
org, resp, err := h.configstoreClient.GetOrg(ctx, orgID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
res := createOrgResponse(org)
if err := json.NewEncoder(w).Encode(res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -177,10 +122,8 @@ func (h *OrgHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createOrgResponse(org)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -210,26 +153,20 @@ func (h *OrgByNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createOrgResponse(org)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
type OrgsResponse struct {
Orgs []*OrgResponse `json:"orgs"`
}
type OrgResponse struct {
ID string `json:"id"`
Name string `json:"name"`
}
func createOrgResponse(r *types.Organization) *OrgResponse {
func createOrgResponse(o *types.Organization) *OrgResponse {
org := &OrgResponse{
ID: r.ID,
Name: r.Name,
ID: o.ID,
Name: o.Name,
}
return org
}
@ -287,13 +224,7 @@ func (h *OrgsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for i, p := range csorgs {
orgs[i] = createOrgResponse(p)
}
orgsResponse := &OrgsResponse{
Orgs: orgs,
}
if err := json.NewEncoder(w).Encode(orgsResponse); err != nil {
if err := httpResponse(w, http.StatusOK, orgs); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -75,17 +75,14 @@ func (h *CreateProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
}
project, err := h.ch.CreateProject(ctx, creq)
if err != nil {
if httpError(w, err) {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
res := createProjectResponse(project)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -110,10 +107,12 @@ func (h *ProjectReconfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
}
if err := h.ch.ReconfigProject(ctx, projectRef); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}
type DeleteProjectHandler struct {
@ -155,6 +154,10 @@ func (h *DeleteProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}
type ProjectHandler struct {
@ -187,10 +190,8 @@ func (h *ProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createProjectResponse(project)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -74,10 +74,9 @@ func (h *CreateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
return
}
if err := json.NewEncoder(w).Encode(projectGroup); err != nil {
res := createProjectGroupResponse(projectGroup)
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -111,10 +110,8 @@ func (h *ProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
}
res := createProjectGroupResponse(projectGroup)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -152,10 +149,8 @@ func (h *ProjectGroupProjectsHandler) ServeHTTP(w http.ResponseWriter, r *http.R
projects[i] = createProjectResponse(p)
}
if err := json.NewEncoder(w).Encode(projects); err != nil {
if err := httpResponse(w, http.StatusOK, projects); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -193,10 +188,8 @@ func (h *ProjectGroupSubgroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
subgroups[i] = createProjectGroupResponse(g)
}
if err := json.NewEncoder(w).Encode(subgroups); err != nil {
if err := httpResponse(w, http.StatusOK, subgroups); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -65,12 +65,9 @@ func (h *CreateRemoteSourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
}
res := createRemoteSourceResponse(rs)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
func (h *CreateRemoteSourceHandler) createRemoteSource(ctx context.Context, req *CreateRemoteSourceRequest) (*types.RemoteSource, error) {
@ -165,10 +162,8 @@ func (h *RemoteSourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
}
res := createRemoteSourceResponse(rs)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -226,9 +221,7 @@ func (h *RemoteSourcesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
remoteSources[i] = createRemoteSourceResponse(rs)
}
if err := json.NewEncoder(w).Encode(remoteSources); err != nil {
if err := httpResponse(w, http.StatusOK, remoteSources); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -213,10 +213,8 @@ func (h *RunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createRunResponse(runResp.Run, runResp.RunConfig)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -257,11 +255,8 @@ func (h *RuntaskHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rct := rc.Tasks[rt.ID]
res := createRunTaskResponse(rt, rct)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -353,11 +348,8 @@ func (h *RunsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for i, r := range runsResp.Runs {
runs[i] = createRunsResponse(r)
}
if err := json.NewEncoder(w).Encode(runs); err != nil {
if err := httpResponse(w, http.StatusOK, runs); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}

View File

@ -78,10 +78,8 @@ func (h *SecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
secrets[i] = createSecretResponse(s)
}
if err := json.NewEncoder(w).Encode(secrets); err != nil {
if err := httpResponse(w, http.StatusOK, secrets); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -148,11 +146,8 @@ func (h *CreateSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
h.log.Infof("secret %s created, ID: %s", s.Name, s.ID)
res := createSecretResponse(s)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -193,4 +188,7 @@ func (h *DeleteSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}

View File

@ -64,11 +64,8 @@ func (h *CreateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createUserResponse(u)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -129,10 +126,8 @@ func (h *CurrentUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createUserResponse(user)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -162,10 +157,8 @@ func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createUserResponse(user)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -195,10 +188,8 @@ func (h *UserByNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
res := createUserResponse(user)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusOK, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -276,10 +267,8 @@ func (h *UsersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
users[i] = createUserResponse(p)
}
if err := json.NewEncoder(w).Encode(users); err != nil {
if err := httpResponse(w, http.StatusOK, users); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -315,19 +304,16 @@ func (h *CreateUserLAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
return
}
resp, err := h.createUserLA(ctx, userName, req)
res, err := h.createUserLA(ctx, userName, req)
if err != nil {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
func (h *CreateUserLAHandler) createUserLA(ctx context.Context, userName string, req *CreateUserLARequest) (*CreateUserLAResponse, error) {
@ -376,6 +362,10 @@ func (h *DeleteUserLAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}
type CreateUserTokenRequest struct {
@ -418,13 +408,12 @@ func (h *CreateUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
return
}
resp := &CreateUserTokenResponse{
res := &CreateUserTokenResponse{
Token: token,
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -450,6 +439,10 @@ func (h *DeleteUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}
type RegisterUserRequest struct {
@ -480,17 +473,15 @@ func (h *RegisterUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
return
}
resp, err := h.registerUser(ctx, req)
res, err := h.registerUser(ctx, req)
if err != nil {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -540,19 +531,16 @@ func (h *AuthorizeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
resp, err := h.authorize(ctx, req)
res, err := h.authorize(ctx, req)
if err != nil {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
func (h *AuthorizeHandler) authorize(ctx context.Context, req *LoginUserRequest) (*AuthorizeResponse, error) {
@ -609,23 +597,19 @@ func (h *LoginUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
resp, err := h.loginUser(ctx, req)
res, err := h.loginUser(ctx, req)
if err != nil {
h.log.Errorf("err: %+v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
func (h *LoginUserHandler) loginUser(ctx context.Context, req *LoginUserRequest) (*LoginUserResponse, error) {
creq := &command.LoginUserRequest{
RemoteSourceName: req.RemoteSourceName,
}
@ -648,8 +632,3 @@ func (h *LoginUserHandler) loginUser(ctx context.Context, req *LoginUserRequest)
}
return resp, nil
}
type RemoteSourceAuthResponse struct {
Oauth2Redirect string `json:"oauth_2_redirect"`
Response interface{} `json:"response"`
}

View File

@ -132,10 +132,8 @@ func (h *VariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
variables[i] = createVariableResponse(v, cssecrets)
}
if err := json.NewEncoder(w).Encode(variables); err != nil {
if err := httpResponse(w, http.StatusOK, variables); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -218,11 +216,8 @@ func (h *CreateVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
h.log.Infof("variable %s created, ID: %s", v.Name, v.ID)
res := createVariableResponse(v, cssecrets)
if err := json.NewEncoder(w).Encode(res); err != nil {
if err := httpResponse(w, http.StatusCreated, res); err != nil {
h.log.Errorf("err: %+v", err)
httpError(w, err)
return
}
}
@ -264,4 +259,7 @@ func (h *DeleteVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
httpError(w, err)
return
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}

View File

@ -0,0 +1,50 @@
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"context"
"github.com/sorintlab/agola/internal/services/types"
"github.com/sorintlab/agola/internal/util"
"github.com/pkg/errors"
)
type CreateOrgRequest struct {
Name string
}
func (c *CommandHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (*types.Organization, error) {
if req.Name == "" {
return nil, util.NewErrBadRequest(errors.Errorf("organization name required"))
}
if !util.ValidateName(req.Name) {
return nil, errors.Errorf("invalid organization name %q", req.Name)
}
org := &types.Organization{
Name: req.Name,
}
c.log.Infof("creating organization")
org, _, err := c.configstoreClient.CreateOrg(ctx, org)
if err != nil {
return nil, errors.Wrapf(err, "failed to create organization")
}
c.log.Infof("organization %s created, ID: %s", org.Name, org.ID)
return org, nil
}

View File

@ -184,7 +184,7 @@ func (g *Gateway) Run(ctx context.Context) error {
orgHandler := api.NewOrgHandler(logger, g.configstoreClient)
orgByNameHandler := api.NewOrgByNameHandler(logger, g.configstoreClient)
orgsHandler := api.NewOrgsHandler(logger, g.configstoreClient)
createOrgHandler := api.NewCreateOrgHandler(logger, g.configstoreClient)
createOrgHandler := api.NewCreateOrgHandler(logger, g.ch)
deleteOrgHandler := api.NewDeleteOrgHandler(logger, g.configstoreClient)
runHandler := api.NewRunHandler(logger, g.runserviceClient)