feat: transition to grpc

master
ALI Hamza 2021-12-19 22:30:21 +07:00
parent f24d6b6bda
commit 23174ccf82
Signed by: hamza
GPG Key ID: 22473A32291F8CB6
31 changed files with 4352 additions and 1077 deletions

@ -1,35 +1,29 @@
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.8.2 --package=api --generate types -o types.gen.go ../schema/schema.yaml
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.8.2 --package=api --generate chi-server -o routes.gen.go ../schema/schema.yaml
package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hhhapz/hackathon/models"
codequestpb "github.com/hhhapz/codequest/api/v1"
"github.com/hhhapz/codequest/models"
"golang.org/x/oauth2"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection"
)
// inProd tells the server whether it is in prod.
// It should be set using ldflags on build time.
var inProd bool
type Server struct {
*AuthService
*UserService
}
type OAuthStore interface {
Create(callback string) (code string)
Validate(code string) (string, bool)
Exchange(ctx context.Context, code string) (*oauth2.Token, error)
Remove(code string)
}
type UserStore interface {
ConsumeToken(ctx context.Context, token *oauth2.Token) (*models.Token, bool, error)
CreateToken(ctx context.Context, user *models.User) (*models.Token, error)
RevokeToken(ctx context.Context, token string) error
RevokeUserTokens(ctx context.Context, user *models.User) (int64, error)
@ -37,50 +31,43 @@ type UserStore interface {
User(ctx context.Context, email string) (*models.User, error)
Users(ctx context.Context) ([]*models.User, error)
DecodeUser(ctx context.Context, buf []byte) (*models.User, error)
UserByToken(ctx context.Context, token string) (*models.User, error)
UpdateUser(ctx context.Context, user *models.User) error
DeleteUser(ctx context.Context, user *models.User) error
}
var _ ServerInterface = (*Server)(nil)
type Server struct {
*AuthService
*UserService
}
func NewServer(oaStore OAuthStore, userStore UserStore) *Server {
return &Server{
func NewServer(os OAuthStore, us UserStore) (*grpc.Server, error) {
s := &Server{
AuthService: &AuthService{
oauthStore: oaStore,
userStore: userStore,
oauthStore: os,
userStore: us,
},
UserService: &UserService{
userStore: userStore,
userStore: us,
},
}
srv := grpc.NewServer(
grpc.UnaryInterceptor(AuthInterceptor(s.AuthService.defaultAuthFunc)),
)
reflection.Register(srv)
codequestpb.RegisterAuthServiceServer(srv, s.AuthService)
codequestpb.RegisterUserServiceServer(srv, s.UserService)
return srv, nil
}
func serverError(w http.ResponseWriter, code int, message string) {
w.WriteHeader(code)
json.NewEncoder(w).Encode(Error{
Code: code,
Message: message,
})
}
type parseError struct {
typ string
value string
reason string
message string
}
func newParseError(typ, value, reason string) error {
return parseError{
typ: typ,
value: value,
reason: reason,
message: "invalid %s(%q): %s",
}
}
// List of commonly used error values
var (
ErrInvalidArgument = grpc.Errorf(
codes.InvalidArgument,
"Invalid argument",
)
)
func (err parseError) Error() string {
return fmt.Sprintf(err.message, err.typ, err.value, err.reason)
func err() {
}

@ -2,169 +2,86 @@ package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strconv"
"strings"
"time"
"github.com/hhhapz/hackathon/models"
"golang.org/x/oauth2"
codequestpb "github.com/hhhapz/codequest/api/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
type AuthService struct {
codequestpb.UnimplementedAuthServiceServer
oauthStore OAuthStore
userStore UserStore
}
func (as *AuthService) GenOauth(w http.ResponseWriter, r *http.Request, params GenOauthParams) {
// We enforce callback field in production.
if inProd && params.Callback != "https://hackathon.teamortix.com" {
serverError(w, http.StatusUnprocessableEntity, "invalid callback provided")
}
page := as.oauthStore.Create(params.Callback)
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(ConsentPage{page})
}
var couldNotValidate = "Unable to authorize. Please try again.\nIf this issue persists, please contact an admin."
func (as *AuthService) AuthorizeCallback(w http.ResponseWriter, r *http.Request, params AuthorizeCallbackParams) {
callback, ok := as.oauthStore.Validate(params.State)
if !ok {
serverError(w, http.StatusUnprocessableEntity, "Invalid state provided")
return
}
token, err := as.oauthStore.Exchange(r.Context(), params.Code)
if err != nil {
log.Printf("error: could not perform token exchange: %v", err)
serverError(w, http.StatusInternalServerError, couldNotValidate)
return
}
accessToken, err := as.consumeToken(r.Context(), token)
reason := "serverError"
if errors.As(err, &parseError{}) {
reason = "email"
}
const (
MDHost = "x-forwarded-host"
UIHost = "codequest.teamortix.com"
)
if err != nil {
log.Printf("error: could not consume oauth token: %v", err)
uri := fmt.Sprintf("%s/fail?reason=%s", callback, reason)
http.Redirect(w, r, uri, http.StatusFound)
return
func (as *AuthService) AuthFuncOverride(ctx context.Context, method string) (context.Context, error) {
switch method {
case "DeleteToken":
return BearerAuth(ctx, as.userStore)
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: accessToken,
Path: "/",
Expires: time.Now().Add(time.Hour * 24 * 14), // 2 weeks
SameSite: http.SameSiteNoneMode, // handled via CORS
Secure: inProd,
})
http.Redirect(w, r, callback+"/success", http.StatusFound)
return ctx, nil
}
const userinfoEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
func (as *AuthService) consumeToken(ctx context.Context, token *oauth2.Token) (string, error) {
endpoint := userinfoEndpoint + token.AccessToken
client := http.Client{Timeout: time.Second * 5}
res, err := client.Get(endpoint)
if err != nil {
return "", fmt.Errorf("could not get userinfo: %w", err)
}
defer res.Body.Close()
buf, err := io.ReadAll(res.Body)
if err != nil {
return "", fmt.Errorf("could not read request body: %w", err)
}
func (as *AuthService) OAuthCode(ctx context.Context, req *codequestpb.OAuthCodeRequest) (*codequestpb.OAuthCodeResponse, error) {
md := RequestMetadata(ctx)
host := md.Get(MDHost)
user := &models.User{
CreatedAt: models.NewTime(time.Now()),
}
if err := json.Unmarshal(buf, &user); err != nil {
return "", fmt.Errorf("could not decode userinfo json: %w", err)
if host != UIHost && inProd {
return nil, status.Errorf(codes.InvalidArgument, "invalid host: %v", host)
}
if u, err := as.userStore.User(ctx, user.Email); err == nil {
var tk *models.Token
if tk, err = as.userStore.CreateToken(ctx, u); err != nil {
return "", fmt.Errorf("could not create user token: %w", err)
}
return tk.Token, nil
}
page := as.oauthStore.Create(host)
return &codequestpb.OAuthCodeResponse{
RedirectURI: page,
}, nil
}
student, valid := parseEmail(user.Email)
if !valid {
return "", newParseError("email", user.Email, "invalid email domain")
func (as *AuthService) Token(ctx context.Context, req *codequestpb.TokenRequest) (*codequestpb.Token, error) {
_, ok := as.oauthStore.Validate(req.State)
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid state %q", req.State)
}
user.Teacher = !student
err = as.userStore.UpdateUser(ctx, user)
token, err := as.oauthStore.Exchange(ctx, req.Code)
if err != nil {
return "", fmt.Errorf("could not create user: %w", err)
log.Printf("could not perform token exchange: %v", err)
return nil, status.Errorf(codes.InvalidArgument, "invalid code %q: %v", req.Code, err)
}
tk, err := as.userStore.CreateToken(ctx, user)
tk, ok, err := as.userStore.ConsumeToken(ctx, token)
if err != nil {
return "", fmt.Errorf("could not create user token: %w", err)
if ok {
return nil, status.Errorf(codes.InvalidArgument, "could not authorize: %v", err)
}
log.Printf("could not authorize request: %v", err)
return nil, status.Errorf(codes.Internal, "could not authorize: internal error")
}
return tk.Token, nil
return &codequestpb.Token{
Token: tk.Token,
Expires: nil,
}, nil
}
func (as *AuthService) DeleteToken(w http.ResponseWriter, r *http.Request, params DeleteTokenParams) {
var all bool
if params.All != nil {
all = *params.All
}
func (as *AuthService) DeleteToken(ctx context.Context, req *codequestpb.DeleteTokenRequest) (*emptypb.Empty, error) {
u := UserCtx(ctx)
var err error
if all {
user, err := as.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided.")
return
}
_, err = as.userStore.RevokeUserTokens(r.Context(), user)
if req.All {
_, err = as.userStore.RevokeUserTokens(ctx, u)
} else {
err = as.userStore.RevokeToken(r.Context(), params.Token)
err = as.userStore.RevokeToken(ctx, TokenCtx(ctx))
}
if err != nil {
log.Printf("Could not revoke user token %#v: %v", params, err)
serverError(w, http.StatusInternalServerError, "Could not revoke token.")
return
}
w.WriteHeader(http.StatusNoContent)
}
const domain = "jisedu.or.id"
// parseEmail parses the provided email, and returns two bools;
// 1. if the email belongs to a student: it conforms to `\d+@[domain]`, and
// 2. if it's a valid internal email (ends with `domain`).
func parseEmail(email string) (bool, bool) {
parts := strings.Split(email, "@")
if len(parts) != 2 || parts[1] != domain {
return false, false
log.Printf("could not revoke token: %v", err)
return nil, status.Errorf(codes.Internal, "could not revoke token")
}
_, err := strconv.Atoi(parts[1])
return err == nil, true
return &emptypb.Empty{}, nil
}

@ -0,0 +1,77 @@
package api
import (
"context"
"strings"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"github.com/hhhapz/codequest/models"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
UserKey = "ctxuser"
TokenKey = "ctxtoken"
)
func AuthInterceptor(authFunc grpc_auth.AuthFunc) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
var newCtx context.Context
var err error
if overrideSrv, ok := info.Server.(grpc_auth.ServiceAuthFuncOverride); ok {
method := strings.LastIndex(info.FullMethod, "/")
newCtx, err = overrideSrv.AuthFuncOverride(ctx, info.FullMethod[method+1:])
} else {
newCtx, err = authFunc(ctx)
}
if err != nil {
return nil, err
}
return handler(newCtx, req)
}
}
func (as AuthService) defaultAuthFunc(ctx context.Context) (context.Context, error) {
return BearerAuth(ctx, as.userStore)
}
func BearerAuth(ctx context.Context, us UserStore) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
return nil, err
}
u, err := us.UserByToken(ctx, token)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid auth token: %v", token)
}
ctx = context.WithValue(ctx, UserKey, u)
ctx = context.WithValue(ctx, TokenKey, token)
return ctx, nil
}
func AdminOnly(ctx context.Context) (context.Context, error) {
u := UserCtx(ctx)
if u == nil {
return nil, status.Errorf(codes.Unauthenticated, "user not found")
}
if !u.Admin {
return nil, status.Errorf(codes.PermissionDenied, "access denied: user is not admin")
}
return ctx, nil
}
func UserCtx(ctx context.Context) *models.User {
u, _ := ctx.Value(UserKey).(*models.User)
return u
}
func TokenCtx(ctx context.Context) string {
t, _ := ctx.Value(TokenKey).(string)
return t
}

@ -0,0 +1,30 @@
package api
import (
"context"
"strings"
"google.golang.org/grpc/metadata"
)
type MD metadata.MD
func RequestMetadata(ctx context.Context) MD {
m, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil
}
return MD(m)
}
func (md MD) Get(k string) string {
if md == nil {
return ""
}
k = strings.ToLower(k)
if res := md[k]; len(res) > 0 {
return res[0]
}
return ""
}

@ -1,450 +0,0 @@
// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.8.2 DO NOT EDIT.
package api
import (
"fmt"
"net/http"
"github.com/deepmap/oapi-codegen/pkg/runtime"
"github.com/go-chi/chi/v5"
)
// ServerInterface represents all server handlers.
type ServerInterface interface {
// (GET /auth/authorize)
AuthorizeCallback(w http.ResponseWriter, r *http.Request, params AuthorizeCallbackParams)
// (GET /auth/code)
GenOauth(w http.ResponseWriter, r *http.Request, params GenOauthParams)
// (DELETE /auth/token)
DeleteToken(w http.ResponseWriter, r *http.Request, params DeleteTokenParams)
// (GET /users/all)
GetAllUsers(w http.ResponseWriter, r *http.Request, params GetAllUsersParams)
// (GET /users/email)
GetUserByEmail(w http.ResponseWriter, r *http.Request, params GetUserByEmailParams)
// (PATCH /users/email)
ModifyOtherUser(w http.ResponseWriter, r *http.Request, params ModifyOtherUserParams)
// (GET /users/me)
GetMe(w http.ResponseWriter, r *http.Request, params GetMeParams)
// (PATCH /users/me)
ModifyUser(w http.ResponseWriter, r *http.Request, params ModifyUserParams)
}
// ServerInterfaceWrapper converts contexts to parameters.
type ServerInterfaceWrapper struct {
Handler ServerInterface
HandlerMiddlewares []MiddlewareFunc
}
type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
// AuthorizeCallback operation middleware
func (siw *ServerInterfaceWrapper) AuthorizeCallback(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params AuthorizeCallbackParams
// ------------- Required query parameter "state" -------------
if paramValue := r.URL.Query().Get("state"); paramValue != "" {
} else {
http.Error(w, "Query argument state is required, but not found", http.StatusBadRequest)
return
}
err = runtime.BindQueryParameter("form", true, true, "state", r.URL.Query(), &params.State)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter state: %s", err), http.StatusBadRequest)
return
}
// ------------- Required query parameter "code" -------------
if paramValue := r.URL.Query().Get("code"); paramValue != "" {
} else {
http.Error(w, "Query argument code is required, but not found", http.StatusBadRequest)
return
}
err = runtime.BindQueryParameter("form", true, true, "code", r.URL.Query(), &params.Code)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter code: %s", err), http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.AuthorizeCallback(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// GenOauth operation middleware
func (siw *ServerInterfaceWrapper) GenOauth(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params GenOauthParams
// ------------- Required query parameter "callback" -------------
if paramValue := r.URL.Query().Get("callback"); paramValue != "" {
} else {
http.Error(w, "Query argument callback is required, but not found", http.StatusBadRequest)
return
}
err = runtime.BindQueryParameter("form", true, true, "callback", r.URL.Query(), &params.Callback)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter callback: %s", err), http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GenOauth(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// DeleteToken operation middleware
func (siw *ServerInterfaceWrapper) DeleteToken(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params DeleteTokenParams
// ------------- Optional query parameter "all" -------------
if paramValue := r.URL.Query().Get("all"); paramValue != "" {
}
err = runtime.BindQueryParameter("form", true, false, "all", r.URL.Query(), &params.All)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter all: %s", err), http.StatusBadRequest)
return
}
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.DeleteToken(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// GetAllUsers operation middleware
func (siw *ServerInterfaceWrapper) GetAllUsers(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params GetAllUsersParams
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GetAllUsers(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// GetUserByEmail operation middleware
func (siw *ServerInterfaceWrapper) GetUserByEmail(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params GetUserByEmailParams
// ------------- Required query parameter "email" -------------
if paramValue := r.URL.Query().Get("email"); paramValue != "" {
} else {
http.Error(w, "Query argument email is required, but not found", http.StatusBadRequest)
return
}
err = runtime.BindQueryParameter("form", true, true, "email", r.URL.Query(), &params.Email)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter email: %s", err), http.StatusBadRequest)
return
}
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GetUserByEmail(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// ModifyOtherUser operation middleware
func (siw *ServerInterfaceWrapper) ModifyOtherUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params ModifyOtherUserParams
// ------------- Required query parameter "email" -------------
if paramValue := r.URL.Query().Get("email"); paramValue != "" {
} else {
http.Error(w, "Query argument email is required, but not found", http.StatusBadRequest)
return
}
err = runtime.BindQueryParameter("form", true, true, "email", r.URL.Query(), &params.Email)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid format for parameter email: %s", err), http.StatusBadRequest)
return
}
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.ModifyOtherUser(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// GetMe operation middleware
func (siw *ServerInterfaceWrapper) GetMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params GetMeParams
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GetMe(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// ModifyUser operation middleware
func (siw *ServerInterfaceWrapper) ModifyUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
// Parameter object where we will unmarshal all parameters from the context
var params ModifyUserParams
var cookie *http.Cookie
if cookie, err = r.Cookie("token"); err == nil {
var value string
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
if err != nil {
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
return
}
params.Token = value
} else {
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
return
}
var handler = func(w http.ResponseWriter, r *http.Request) {
siw.Handler.ModifyUser(w, r, params)
}
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler(w, r.WithContext(ctx))
}
// Handler creates http.Handler with routing matching OpenAPI spec.
func Handler(si ServerInterface) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{})
}
type ChiServerOptions struct {
BaseURL string
BaseRouter chi.Router
Middlewares []MiddlewareFunc
}
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{
BaseRouter: r,
})
}
func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
return HandlerWithOptions(si, ChiServerOptions{
BaseURL: baseURL,
BaseRouter: r,
})
}
// HandlerWithOptions creates http.Handler with additional options
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
r := options.BaseRouter
if r == nil {
r = chi.NewRouter()
}
wrapper := ServerInterfaceWrapper{
Handler: si,
HandlerMiddlewares: options.Middlewares,
}
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/auth/authorize", wrapper.AuthorizeCallback)
})
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/auth/code", wrapper.GenOauth)
})
r.Group(func(r chi.Router) {
r.Delete(options.BaseURL+"/auth/token", wrapper.DeleteToken)
})
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/users/all", wrapper.GetAllUsers)
})
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/users/email", wrapper.GetUserByEmail)
})
r.Group(func(r chi.Router) {
r.Patch(options.BaseURL+"/users/email", wrapper.ModifyOtherUser)
})
r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/users/me", wrapper.GetMe)
})
r.Group(func(r chi.Router) {
r.Patch(options.BaseURL+"/users/me", wrapper.ModifyUser)
})
return r
}

@ -1,115 +0,0 @@
// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.8.2 DO NOT EDIT.
package api
import (
"time"
openapi_types "github.com/deepmap/oapi-codegen/pkg/types"
)
// ConsentPage defines model for ConsentPage.
type ConsentPage struct {
Url string `json:"url"`
}
// Error defines model for Error.
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
// User defines model for User.
type User struct {
Admin bool `json:"admin"`
CreatedAt time.Time `json:"created_at"`
Email openapi_types.Email `json:"email"`
// GradeLevel is only present if teacher is false.
GradeLevel *int `json:"grade_level,omitempty"`
Id string `json:"id"`
Name string `json:"name"`
Picture string `json:"picture"`
Teacher bool `json:"teacher"`
}
// DefaultResponse defines model for DefaultResponse.
type DefaultResponse Error
// AuthorizeCallbackParams defines parameters for AuthorizeCallback.
type AuthorizeCallbackParams struct {
State string `json:"state"`
Code string `json:"code"`
}
// GenOauthParams defines parameters for GenOauth.
type GenOauthParams struct {
Callback string `json:"callback"`
}
// DeleteTokenParams defines parameters for DeleteToken.
type DeleteTokenParams struct {
All *bool `json:"all,omitempty"`
// User authentication token.
Token string `json:"token"`
}
// GetAllUsersParams defines parameters for GetAllUsers.
type GetAllUsersParams struct {
// User authentication token.
Token string `json:"token"`
}
// GetUserByEmailParams defines parameters for GetUserByEmail.
type GetUserByEmailParams struct {
// User email.
Email openapi_types.Email `json:"email"`
// User authentication token.
Token string `json:"token"`
}
// ModifyOtherUserJSONBody defines parameters for ModifyOtherUser.
type ModifyOtherUserJSONBody struct {
Admin bool `json:"admin"`
GradeLevel int `json:"grade_level"`
Name string `json:"name"`
NewEmail *openapi_types.Email `json:"new_email,omitempty"`
Picture string `json:"picture"`
Teacher bool `json:"teacher"`
}
// ModifyOtherUserParams defines parameters for ModifyOtherUser.
type ModifyOtherUserParams struct {
// User email.
Email openapi_types.Email `json:"email"`
// User authentication token.
Token string `json:"token"`
}
// GetMeParams defines parameters for GetMe.
type GetMeParams struct {
// User authentication token.
Token string `json:"token"`
}
// ModifyUserJSONBody defines parameters for ModifyUser.
type ModifyUserJSONBody struct {
GradeLevel int `json:"grade_level"`
Name string `json:"name"`
}
// ModifyUserParams defines parameters for ModifyUser.
type ModifyUserParams struct {
// User authentication token.
Token string `json:"token"`
}
// ModifyOtherUserJSONRequestBody defines body for ModifyOtherUser for application/json ContentType.
type ModifyOtherUserJSONRequestBody ModifyOtherUserJSONBody
// ModifyUserJSONRequestBody defines body for ModifyUser for application/json ContentType.
type ModifyUserJSONRequestBody ModifyUserJSONBody

@ -1,210 +1,150 @@
package api
import (
"context"
"database/sql"
"encoding/json"
"log"
"net/http"
"strings"
"github.com/deepmap/oapi-codegen/pkg/types"
"github.com/hhhapz/hackathon/models"
codequestpb "github.com/hhhapz/codequest/api/v1"
"github.com/hhhapz/codequest/models"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
)
type UserService struct {
codequestpb.UnimplementedUserServiceServer
userStore UserStore
}
func (us *UserService) GetMe(w http.ResponseWriter, r *http.Request, params GetMeParams) {
u, err := us.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided")
return
}
writeUser(w, u)
func (us *UserService) User(ctx context.Context, req *codequestpb.UserRequest) (*codequestpb.User, error) {
u := UserCtx(ctx)
return convertUser(u), nil
}
func (us *UserService) ModifyUser(w http.ResponseWriter, r *http.Request, params ModifyUserParams) {
u, err := us.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided")
return
func (us *UserService) UserByEmail(ctx context.Context, req *codequestpb.UserByEmailRequest) (*codequestpb.User, error) {
u := UserCtx(ctx)
if u.Email == req.Email {
return convertUser(u), nil
}
var body ModifyUserJSONBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
serverError(w, http.StatusBadRequest, "Invalid JSON body for ModifyUser")
return
var err error
if ctx, err = AdminOnly(ctx); err != nil {
return nil, err
}
u.Name = strings.TrimSpace(body.Name)
if len(u.Name) < 3 || len(u.Name) > 30 {
serverError(w, http.StatusUnprocessableEntity, "Name must be between 3 and 30 characters long")
return
user, err := us.userStore.User(ctx, req.Email)
if err != nil {
log.Printf("Could not fetch user(%q): %v", req.Email, err)
return nil, status.Errorf(codes.NotFound, "email not found: %q", req.Email)
}
if !u.Teacher {
if body.GradeLevel < 9 || body.GradeLevel > 12 {
serverError(w, http.StatusUnprocessableEntity, "Grade Level must be between 9 and 12")
return
}
u.GradeLevel = sql.NullInt64{Int64: int64(body.GradeLevel), Valid: true}
return convertUser(user), nil
}
func (us *UserService) AllUsers(ctx context.Context, req *codequestpb.AllUsersRequest) (*codequestpb.AllUsersResponse, error) {
var err error
if ctx, err = AdminOnly(ctx); err != nil {
return nil, err
}
err = us.userStore.UpdateUser(r.Context(), u)
users, err := us.userStore.Users(ctx)
if err != nil {
log.Printf("Could not modify user %s: %v", u.Email, err)
serverError(w, http.StatusInternalServerError, "Could not update user")
return
log.Printf("Could not fetch all users: %v", err)
return nil, status.Errorf(codes.Internal, "unable to fetch users")
}
writeUser(w, u)
var userList []*codequestpb.User
for _, u := range users {
userList = append(userList, convertUser(u))
}
return &codequestpb.AllUsersResponse{Users: userList}, nil
}
func (us *UserService) GetUserByEmail(w http.ResponseWriter, r *http.Request, params GetUserByEmailParams) {
u, err := us.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided")
return
}
func (us *UserService) UpdateUser(ctx context.Context, req *codequestpb.UpdateUserRequest) (*codequestpb.User, error) {
u := UserCtx(ctx)
email := string(params.Email)
if u.Email == email {
writeUser(w, u)
return
u.Name = req.Body.Name
if len(u.Name) < 3 || len(u.Name) > 20 {
return nil, status.Errorf(codes.InvalidArgument, "name must be between 3 and 20 characters")
}
if !u.Admin {
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
serverError(w, http.StatusForbidden, "You are not authorized to do this")
return
gl := req.Body.GradeLevel
u.GradeLevel = sql.NullInt64{Int64: int64(gl), Valid: true}
if gl < 9 || gl > 12 {
return nil, status.Errorf(codes.InvalidArgument, "grade level must be between 9 and 12")
}
user, err := us.userStore.User(r.Context(), email)
if err != nil {
log.Printf("Could not fetch user(%q): %v", email, err)
serverError(w, http.StatusNotFound, "Could not find user with the specified email")
return
if err := us.userStore.UpdateUser(ctx, u); err != nil {
log.Printf("could not update user: %v", err)
return nil, status.Errorf(codes.Internal, "unable to update user")
}
writeUser(w, user)
return convertUser(u), nil
}
func (us *UserService) ModifyOtherUser(w http.ResponseWriter, r *http.Request, params ModifyOtherUserParams) {
u, err := us.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided")
return
func (us *UserService) AdminUpdateUser(ctx context.Context, req *codequestpb.UpdateUserRequest) (*codequestpb.User, error) {
var err error
if ctx, err = AdminOnly(ctx); err != nil {
return nil, err
}
if !u.Admin {
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
serverError(w, http.StatusForbidden, "You are not authorized to do this")
return
user, err := us.userStore.User(ctx, req.Email)
if err != nil {
log.Printf("Could not fetch user(%q): %v", req.Email, err)
return nil, status.Errorf(codes.NotFound, "email not found: %q", req.Email)
}
user, err := us.userStore.User(r.Context(), string(params.Email))
if err != nil {
log.Printf("Could not fetch user(%q): %v", params.Email, err)
serverError(w, http.StatusNotFound, "Could not find user with the specified email")
return
user.Name = req.Body.Name
if len(user.Name) < 3 || len(user.Name) > 20 {
return nil, status.Errorf(codes.InvalidArgument, "name must be between 3 and 20 characters")
}
var body ModifyOtherUserJSONBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
serverError(w, http.StatusBadRequest, "Invalid JSON body for ModifyOtherUser")
return
gl := req.Body.GradeLevel
user.GradeLevel = sql.NullInt64{Int64: int64(gl), Valid: true}
if gl < 9 || gl > 12 {
return nil, status.Errorf(codes.InvalidArgument, "grade level must be between 9 and 12")
}
user.Name = strings.TrimSpace(body.Name)
user.Email = string(*body.NewEmail)
user.Picture = body.Picture
user.Teacher = body.Teacher
user.Admin = body.Admin
user.Admin = req.Body.Admin
if len(user.Name) < 3 || len(user.Name) > 30 {
serverError(w, http.StatusUnprocessableEntity, "Name must be between 3 and 30 characters long")
return
}
user.GradeLevel = sql.NullInt64{}
if !user.Teacher {
if body.GradeLevel < 9 || body.GradeLevel > 12 {
serverError(w, http.StatusUnprocessableEntity, "Grade Level must be between 9 and 12")
return
}
user.GradeLevel = sql.NullInt64{Int64: int64(body.GradeLevel), Valid: true}
if err := us.userStore.UpdateUser(ctx, user); err != nil {
log.Printf("could not update user: %v", err)
return nil, status.Errorf(codes.Internal, "unable to update user")
}
writeUser(w, user)
return convertUser(user), nil
}
func (us *UserService) GetAllUsers(w http.ResponseWriter, r *http.Request, params GetAllUsersParams) {
u, err := us.userStore.UserByToken(r.Context(), params.Token)
if err != nil {
serverError(w, http.StatusUnauthorized, "Invalid token provided")
return
func (us *UserService) DeleteUser(ctx context.Context, req *codequestpb.DeleteUserRequest) (*codequestpb.User, error) {
var err error
if ctx, err = AdminOnly(ctx); err != nil {
return nil, err
}
if !u.Admin {
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
serverError(w, http.StatusForbidden, "You are not authorized to do this")
return
user, err := us.userStore.User(ctx, req.Email)
if err != nil {
log.Printf("Could not fetch user(%q): %v", req.Email, err)
return nil, status.Errorf(codes.NotFound, "email not found: %q", req.Email)
}
users, err := us.userStore.Users(r.Context())
if err != nil {
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
serverError(w, http.StatusInternalServerError, "Could not fetch users")
return
if err := us.userStore.DeleteUser(ctx, user); err != nil {
log.Printf("could not delete user: %v", err)
return nil, status.Errorf(codes.Internal, "unable to delete user")
}
writeUsers(w, users)
return convertUser(user), nil
}
func writeUser(w http.ResponseWriter, u *models.User) {
var grade *int
if u.GradeLevel.Valid {
g := int(u.GradeLevel.Int64)
grade = &g
}
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(User{
func convertUser(u *models.User) *codequestpb.User {
return &codequestpb.User{
Admin: u.Admin,
CreatedAt: u.CreatedAt.Time(),
Email: types.Email(u.Email),
GradeLevel: grade,
Id: u.ID,
Email: u.Email,
GradeLevel: int32(u.GradeLevel.Int64),
ID: u.ID,
Name: u.Name,
Picture: u.Picture,
Teacher: u.Teacher,
})
}
func writeUsers(w http.ResponseWriter, us []*models.User) {
var users []User
for _, u := range us {
var grade *int
if u.GradeLevel.Valid {
g := int(u.GradeLevel.Int64)
grade = &g
}
users = append(users, User{
Admin: u.Admin,
CreatedAt: u.CreatedAt.Time(),
Email: types.Email(u.Email),
GradeLevel: grade,
Id: u.ID,
Name: u.Name,
Picture: u.Picture,
Teacher: u.Teacher,
})
}
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
CreatedAt: timestamppb.New(u.CreatedAt.Time()),
}
}

@ -0,0 +1,19 @@
; pin protoc version
[protoc]
version=v3.19.1
; go
[generate go]
json_tag_postproc=true
[generate go-grpc]
[generate grpc-gateway]
; swagger/openapiv2
[generate openapiv2]
out=./gen/json/{{ .Package }}
[generate ts] ; typescript
out=./gen/ts/{{ .Package }}
[generate grpc-gateway-ts] ; typescript
out=./gen/ts-gateway/{{ .Package }}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,913 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: github.com/hhhapz/codequest/api/v1/all.proto
/*
Package codequest is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package codequest
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
func request_AuthService_OAuthCode_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OAuthCodeRequest
var metadata runtime.ServerMetadata
msg, err := client.OAuthCode(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_AuthService_OAuthCode_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OAuthCodeRequest
var metadata runtime.ServerMetadata
msg, err := server.OAuthCode(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_AuthService_Token_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_AuthService_Token_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokenRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_Token_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Token(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_AuthService_Token_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokenRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_Token_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Token(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_AuthService_DeleteToken_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_AuthService_DeleteToken_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteTokenRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeleteToken_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.DeleteToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_AuthService_DeleteToken_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteTokenRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeleteToken_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DeleteToken(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_User_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserRequest
var metadata runtime.ServerMetadata
msg, err := client.User(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_User_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserRequest
var metadata runtime.ServerMetadata
msg, err := server.User(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_UserByEmail_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserByEmailRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := client.UserByEmail(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_UserByEmail_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserByEmailRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := server.UserByEmail(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_AllUsers_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AllUsersRequest
var metadata runtime.ServerMetadata
msg, err := client.AllUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_AllUsers_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AllUsersRequest
var metadata runtime.ServerMetadata
msg, err := server.AllUsers(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_UserService_UpdateUser_0 = &utilities.DoubleArray{Encoding: map[string]int{"Body": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
func request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUser_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdateUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUser_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdateUser(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_AdminUpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := client.AdminUpdateUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_AdminUpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := server.AdminUpdateUser(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_DeleteUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteUserRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := client.DeleteUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_DeleteUser_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteUserRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["Email"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Email")
}
protoReq.Email, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Email", err)
}
msg, err := server.DeleteUser(ctx, &protoReq)
return msg, metadata, err
}
// RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux".
// UnaryRPC :call AuthServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAuthServiceHandlerFromEndpoint instead.
func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer) error {
mux.Handle("GET", pattern_AuthService_OAuthCode_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/OAuthCode", runtime.WithHTTPPathPattern("/v1/auth/code"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_AuthService_OAuthCode_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_OAuthCode_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_AuthService_Token_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/Token", runtime.WithHTTPPathPattern("/v1/auth/token"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_AuthService_Token_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_Token_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_AuthService_DeleteToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/DeleteToken", runtime.WithHTTPPathPattern("/v1/auth/token"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_AuthService_DeleteToken_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_DeleteToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterUserServiceHandlerServer registers the http handlers for service UserService to "mux".
// UnaryRPC :call UserServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterUserServiceHandlerFromEndpoint instead.
func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server UserServiceServer) error {
mux.Handle("GET", pattern_UserService_User_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/User", runtime.WithHTTPPathPattern("/v1/users/me"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_User_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_User_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_UserByEmail_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/UserByEmail", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_UserByEmail_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UserByEmail_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_AllUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/AllUsers", runtime.WithHTTPPathPattern("/v1/admin/users"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_AllUsers_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_AllUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/UpdateUser", runtime.WithHTTPPathPattern("/v1/users/me"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_UpdateUser_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UpdateUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_UserService_AdminUpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/AdminUpdateUser", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_AdminUpdateUser_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_AdminUpdateUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_UserService_DeleteUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/DeleteUser", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_DeleteUser_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_DeleteUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterAuthServiceHandlerFromEndpoint is same as RegisterAuthServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterAuthServiceHandler(ctx, mux, conn)
}
// RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn grpc.ClientConnInterface) error {
return RegisterAuthServiceHandlerClient(ctx, mux, NewAuthServiceClient(conn))
}
// RegisterAuthServiceHandlerClient registers the http handlers for service AuthService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AuthServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "AuthServiceClient" to call the correct interceptors.
func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error {
mux.Handle("GET", pattern_AuthService_OAuthCode_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/OAuthCode", runtime.WithHTTPPathPattern("/v1/auth/code"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AuthService_OAuthCode_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_OAuthCode_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_AuthService_Token_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/Token", runtime.WithHTTPPathPattern("/v1/auth/token"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AuthService_Token_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_Token_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_AuthService_DeleteToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.AuthService/DeleteToken", runtime.WithHTTPPathPattern("/v1/auth/token"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AuthService_DeleteToken_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_DeleteToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_AuthService_OAuthCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "auth", "code"}, ""))
pattern_AuthService_Token_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "auth", "token"}, ""))
pattern_AuthService_DeleteToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "auth", "token"}, ""))
)
var (
forward_AuthService_OAuthCode_0 = runtime.ForwardResponseMessage
forward_AuthService_Token_0 = runtime.ForwardResponseMessage
forward_AuthService_DeleteToken_0 = runtime.ForwardResponseMessage
)
// RegisterUserServiceHandlerFromEndpoint is same as RegisterUserServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterUserServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterUserServiceHandler(ctx, mux, conn)
}
// RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn grpc.ClientConnInterface) error {
return RegisterUserServiceHandlerClient(ctx, mux, NewUserServiceClient(conn))
}
// RegisterUserServiceHandlerClient registers the http handlers for service UserService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "UserServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UserServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "UserServiceClient" to call the correct interceptors.
func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error {
mux.Handle("GET", pattern_UserService_User_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/User", runtime.WithHTTPPathPattern("/v1/users/me"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_User_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_User_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_UserByEmail_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/UserByEmail", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_UserByEmail_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UserByEmail_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_AllUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/AllUsers", runtime.WithHTTPPathPattern("/v1/admin/users"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_AllUsers_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_AllUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/UpdateUser", runtime.WithHTTPPathPattern("/v1/users/me"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_UpdateUser_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UpdateUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_UserService_AdminUpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/AdminUpdateUser", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_AdminUpdateUser_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_AdminUpdateUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_UserService_DeleteUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/hhhapz.codequest.v1.UserService/DeleteUser", runtime.WithHTTPPathPattern("/v1/admin/users/{Email}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_DeleteUser_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_DeleteUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_UserService_User_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "users", "me"}, ""))
pattern_UserService_UserByEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "admin", "users", "Email"}, ""))
pattern_UserService_AllUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "admin", "users"}, ""))
pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "users", "me"}, ""))
pattern_UserService_AdminUpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "admin", "users", "Email"}, ""))
pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "admin", "users", "Email"}, ""))
)
var (
forward_UserService_User_0 = runtime.ForwardResponseMessage
forward_UserService_UserByEmail_0 = runtime.ForwardResponseMessage
forward_UserService_AllUsers_0 = runtime.ForwardResponseMessage
forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage
forward_UserService_AdminUpdateUser_0 = runtime.ForwardResponseMessage
forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage
)

@ -0,0 +1,440 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package codequest
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// AuthServiceClient is the client API for AuthService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AuthServiceClient interface {
OAuthCode(ctx context.Context, in *OAuthCodeRequest, opts ...grpc.CallOption) (*OAuthCodeResponse, error)
Token(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*Token, error)
DeleteToken(ctx context.Context, in *DeleteTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
type authServiceClient struct {
cc grpc.ClientConnInterface
}
func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
return &authServiceClient{cc}
}
func (c *authServiceClient) OAuthCode(ctx context.Context, in *OAuthCodeRequest, opts ...grpc.CallOption) (*OAuthCodeResponse, error) {
out := new(OAuthCodeResponse)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.AuthService/OAuthCode", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) Token(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*Token, error) {
out := new(Token)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.AuthService/Token", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) DeleteToken(ctx context.Context, in *DeleteTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.AuthService/DeleteToken", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility
type AuthServiceServer interface {
OAuthCode(context.Context, *OAuthCodeRequest) (*OAuthCodeResponse, error)
Token(context.Context, *TokenRequest) (*Token, error)
DeleteToken(context.Context, *DeleteTokenRequest) (*emptypb.Empty, error)
mustEmbedUnimplementedAuthServiceServer()
}
// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations.
type UnimplementedAuthServiceServer struct {
}
func (UnimplementedAuthServiceServer) OAuthCode(context.Context, *OAuthCodeRequest) (*OAuthCodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method OAuthCode not implemented")
}
func (UnimplementedAuthServiceServer) Token(context.Context, *TokenRequest) (*Token, error) {
return nil, status.Errorf(codes.Unimplemented, "method Token not implemented")
}
func (UnimplementedAuthServiceServer) DeleteToken(context.Context, *DeleteTokenRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteToken not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthServiceServer will
// result in compilation errors.
type UnsafeAuthServiceServer interface {
mustEmbedUnimplementedAuthServiceServer()
}
func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
s.RegisterService(&AuthService_ServiceDesc, srv)
}
func _AuthService_OAuthCode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(OAuthCodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).OAuthCode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.AuthService/OAuthCode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).OAuthCode(ctx, req.(*OAuthCodeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_Token_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TokenRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Token(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.AuthService/Token",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Token(ctx, req.(*TokenRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_DeleteToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteTokenRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).DeleteToken(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.AuthService/DeleteToken",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).DeleteToken(ctx, req.(*DeleteTokenRequest))
}
return interceptor(ctx, in, info, handler)
}
// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var AuthService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "hhhapz.codequest.v1.AuthService",
HandlerType: (*AuthServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "OAuthCode",
Handler: _AuthService_OAuthCode_Handler,
},
{
MethodName: "Token",
Handler: _AuthService_Token_Handler,
},
{
MethodName: "DeleteToken",
Handler: _AuthService_DeleteToken_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "github.com/hhhapz/codequest/api/v1/all.proto",
}
// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServiceClient interface {
User(ctx context.Context, in *UserRequest, opts ...grpc.CallOption) (*User, error)
UserByEmail(ctx context.Context, in *UserByEmailRequest, opts ...grpc.CallOption) (*User, error)
AllUsers(ctx context.Context, in *AllUsersRequest, opts ...grpc.CallOption) (*AllUsersResponse, error)
UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error)
AdminUpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error)
DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*User, error)
}
type userServiceClient struct {
cc grpc.ClientConnInterface
}
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
return &userServiceClient{cc}
}
func (c *userServiceClient) User(ctx context.Context, in *UserRequest, opts ...grpc.CallOption) (*User, error) {
out := new(User)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/User", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) UserByEmail(ctx context.Context, in *UserByEmailRequest, opts ...grpc.CallOption) (*User, error) {
out := new(User)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/UserByEmail", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) AllUsers(ctx context.Context, in *AllUsersRequest, opts ...grpc.CallOption) (*AllUsersResponse, error) {
out := new(AllUsersResponse)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/AllUsers", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error) {
out := new(User)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/UpdateUser", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) AdminUpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error) {
out := new(User)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/AdminUpdateUser", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*User, error) {
out := new(User)
err := c.cc.Invoke(ctx, "/hhhapz.codequest.v1.UserService/DeleteUser", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer
// for forward compatibility
type UserServiceServer interface {
User(context.Context, *UserRequest) (*User, error)
UserByEmail(context.Context, *UserByEmailRequest) (*User, error)
AllUsers(context.Context, *AllUsersRequest) (*AllUsersResponse, error)
UpdateUser(context.Context, *UpdateUserRequest) (*User, error)
AdminUpdateUser(context.Context, *UpdateUserRequest) (*User, error)
DeleteUser(context.Context, *DeleteUserRequest) (*User, error)
mustEmbedUnimplementedUserServiceServer()
}
// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
type UnimplementedUserServiceServer struct {
}
func (UnimplementedUserServiceServer) User(context.Context, *UserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method User not implemented")
}
func (UnimplementedUserServiceServer) UserByEmail(context.Context, *UserByEmailRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method UserByEmail not implemented")
}
func (UnimplementedUserServiceServer) AllUsers(context.Context, *AllUsersRequest) (*AllUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AllUsers not implemented")
}
func (UnimplementedUserServiceServer) UpdateUser(context.Context, *UpdateUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented")
}
func (UnimplementedUserServiceServer) AdminUpdateUser(context.Context, *UpdateUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method AdminUpdateUser not implemented")
}
func (UnimplementedUserServiceServer) DeleteUser(context.Context, *DeleteUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to UserServiceServer will
// result in compilation errors.
type UnsafeUserServiceServer interface {
mustEmbedUnimplementedUserServiceServer()
}
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
s.RegisterService(&UserService_ServiceDesc, srv)
}
func _UserService_User_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).User(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/User",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).User(ctx, req.(*UserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_UserByEmail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UserByEmailRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).UserByEmail(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/UserByEmail",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).UserByEmail(ctx, req.(*UserByEmailRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_AllUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AllUsersRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).AllUsers(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/AllUsers",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).AllUsers(ctx, req.(*AllUsersRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_UpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).UpdateUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/UpdateUser",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).UpdateUser(ctx, req.(*UpdateUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_AdminUpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).AdminUpdateUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/AdminUpdateUser",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).AdminUpdateUser(ctx, req.(*UpdateUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_DeleteUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).DeleteUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hhhapz.codequest.v1.UserService/DeleteUser",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).DeleteUser(ctx, req.(*DeleteUserRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var UserService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "hhhapz.codequest.v1.UserService",
HandlerType: (*UserServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "User",
Handler: _UserService_User_Handler,
},
{
MethodName: "UserByEmail",
Handler: _UserService_UserByEmail_Handler,
},
{
MethodName: "AllUsers",
Handler: _UserService_AllUsers_Handler,
},
{
MethodName: "UpdateUser",
Handler: _UserService_UpdateUser_Handler,
},
{
MethodName: "AdminUpdateUser",
Handler: _UserService_AdminUpdateUser_Handler,
},
{
MethodName: "DeleteUser",
Handler: _UserService_DeleteUser_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "github.com/hhhapz/codequest/api/v1/all.proto",
}

@ -0,0 +1,50 @@
package codequest
import (
"github.com/gunk/opt/file/java"
"github.com/gunk/opt/http"
"github.com/gunk/opt/openapiv2"
"github.com/gunk/opt/proto"
"time"
)
type AuthService interface {
// +gunk http.Match{
// Method: "GET",
// Path: "/v1/auth/code",
// }
OAuthCode(OAuthCodeRequest) OAuthCodeResponse
// +gunk http.Match{
// Method: "GET",
// Path: "/v1/auth/token",
// }
Token(TokenRequest) Token
// +gunk http.Match{
// Method: "DELETE",
// Path: "/v1/auth/token",
// }
DeleteToken(DeleteTokenRequest)
}
type Token struct {
Token string `pb:"1" json:"token"`
Expires time.Time `pb:"2" json:"expires"`
}
type OAuthCodeRequest struct{}
type OAuthCodeResponse struct {
RedirectURI string `pb:"1" json:"redirect_uri"`
}
type TokenRequest struct {
Code string `pb:"1" json:"code"`
State string `pb:"2" json:"state"`
}
type DeleteTokenRequest struct {
All bool `pb:"1" json:"all"`
Token Token `pb:"2" json:"token"`
}

@ -0,0 +1,28 @@
// +gunk proto.Package("hhhapz.codequest.v1")
package codequest
import (
"github.com/gunk/opt/file/java"
"github.com/gunk/opt/http"
"github.com/gunk/opt/openapiv2"
"github.com/gunk/opt/proto"
"time"
)
// User is a contestant in the competition.
type User struct {
// ID of the user. Received via Google's OAuth2 API.
ID string `pb:"1" json:"id"`
// Name of the user.
Name string `pb:"2" json:"name"`
// Email of the user.
Email string `pb:"3" json:"email"`
// Picture is the URL of the user's profile picture.
Picture string `pb:"4" json:"picture"`
// GradeLevel of the user.
GradeLevel int `pb:"5" json:"grade_level"`
// Admin is true if the user is an administrator.
Admin bool `pb:"6" json:"admin"`
// CreatedAt is the time the user was created.
CreatedAt time.Time `pb:"7" json:"created_at"`
}

@ -0,0 +1,410 @@
{
"swagger": "2.0",
"info": {
"title": "github.com/hhhapz/codequest/api/v1/all.proto",
"version": "version not set"
},
"tags": [
{
"name": "AuthService"
},
{
"name": "UserService"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1/admin/users": {
"get": {
"operationId": "UserService_AllUsers",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1AllUsersResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"UserService"
]
}
},
"/v1/admin/users/{email}": {
"get": {
"operationId": "UserService_UserByEmail",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "email",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"UserService"
]
},
"delete": {
"operationId": "UserService_DeleteUser",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "email",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"UserService"
]
},
"put": {
"operationId": "UserService_AdminUpdateUser",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "email",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1UpdateFields"
}
}
],
"tags": [
"UserService"
]
}
},
"/v1/auth/code": {
"get": {
"operationId": "AuthService_OAuthCode",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1OAuthCodeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"AuthService"
]
}
},
"/v1/auth/token": {
"get": {
"operationId": "AuthService_Token",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1Token"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "code",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "state",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"AuthService"
]
},
"delete": {
"operationId": "AuthService_DeleteToken",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "all",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "token.token",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "token.expires",
"in": "query",
"required": false,
"type": "string",
"format": "date-time"
}
],
"tags": [
"AuthService"
]
}
},
"/v1/users/me": {
"get": {
"operationId": "UserService_User",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"UserService"
]
},
"put": {
"operationId": "UserService_UpdateUser",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1UpdateFields"
}
},
{
"name": "email",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"UserService"
]
}
}
},
"definitions": {
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"$ref": "#/definitions/protobufAny"
}
}
}
},
"v1AllUsersResponse": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "#/definitions/v1User"
}
}
}
},
"v1OAuthCodeResponse": {
"type": "object",
"properties": {
"redirect_uri": {
"type": "string"
}
}
},
"v1Token": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"expires": {
"type": "string",
"format": "date-time"
}
}
},
"v1UpdateFields": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"grade_level": {
"type": "integer",
"format": "int32"
},
"admin": {
"type": "boolean"
}
}
},
"v1User": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the user. Received via Google's OAuth2 API."
},
"name": {
"type": "string",
"description": "Name of the user."
},
"email": {
"type": "string",
"description": "Email of the user."
},
"picture": {
"type": "string",
"description": "Picture is the URL of the user's profile picture."
},
"grade_level": {
"type": "integer",
"format": "int32",
"description": "GradeLevel of the user."
},
"admin": {
"type": "boolean",
"description": "Admin is true if the user is an administrator."
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "CreatedAt is the time the user was created."
}
},
"description": "User is a contestant in the competition."
}
}
}

@ -0,0 +1,102 @@
/* eslint-disable */
// @ts-nocheck
/*
* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY
*/
import * as fm from "../../../../../fetch.pb"
import * as GoogleProtobufEmpty from "../../../../../google/protobuf/empty.pb"
import * as GoogleProtobufTimestamp from "../../../../../google/protobuf/timestamp.pb"
export type Token = {
token?: string
expires?: GoogleProtobufTimestamp.Timestamp
}
export type OAuthCodeRequest = {
}
export type OAuthCodeResponse = {
redirectURI?: string
}
export type TokenRequest = {
code?: string
state?: string
}
export type DeleteTokenRequest = {
all?: boolean
token?: Token
}
export type User = {
id?: string
name?: string
email?: string
picture?: string
gradeLevel?: number
admin?: boolean
createdAt?: GoogleProtobufTimestamp.Timestamp
}
export type UpdateFields = {
name?: string
email?: string
gradeLevel?: number
admin?: boolean
}
export type UserRequest = {
}
export type AllUsersRequest = {
}
export type UpdateUserRequest = {
email?: string
body?: UpdateFields
}
export type UserByEmailRequest = {
email?: string
}
export type DeleteUserRequest = {
email?: string
}
export type AllUsersResponse = {
users?: User[]
}
export class AuthService {
static OAuthCode(req: OAuthCodeRequest, initReq?: fm.InitReq): Promise<OAuthCodeResponse> {
return fm.fetchReq<OAuthCodeRequest, OAuthCodeResponse>(`/v1/auth/code?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
}
static Token(req: TokenRequest, initReq?: fm.InitReq): Promise<Token> {
return fm.fetchReq<TokenRequest, Token>(`/v1/auth/token?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
}
static DeleteToken(req: DeleteTokenRequest, initReq?: fm.InitReq): Promise<GoogleProtobufEmpty.Empty> {
return fm.fetchReq<DeleteTokenRequest, GoogleProtobufEmpty.Empty>(`/v1/auth/token`, {...initReq, method: "DELETE"})
}
}
export class UserService {
static User(req: UserRequest, initReq?: fm.InitReq): Promise<User> {
return fm.fetchReq<UserRequest, User>(`/v1/users/me?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
}
static UserByEmail(req: UserByEmailRequest, initReq?: fm.InitReq): Promise<User> {
return fm.fetchReq<UserByEmailRequest, User>(`/v1/admin/users/${req["email"]}?${fm.renderURLSearchParams(req, ["email"])}`, {...initReq, method: "GET"})
}
static AllUsers(req: AllUsersRequest, initReq?: fm.InitReq): Promise<AllUsersResponse> {
return fm.fetchReq<AllUsersRequest, AllUsersResponse>(`/v1/admin/users?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
}
static UpdateUser(req: UpdateUserRequest, initReq?: fm.InitReq): Promise<User> {
return fm.fetchReq<UpdateUserRequest, User>(`/v1/users/me`, {...initReq, method: "PUT", body: JSON.stringify(req["Body"])})
}
static AdminUpdateUser(req: UpdateUserRequest, initReq?: fm.InitReq): Promise<User> {
return fm.fetchReq<UpdateUserRequest, User>(`/v1/admin/users/${req["email"]}`, {...initReq, method: "PUT", body: JSON.stringify(req["Body"])})
}
static DeleteUser(req: DeleteUserRequest, initReq?: fm.InitReq): Promise<User> {
return fm.fetchReq<DeleteUserRequest, User>(`/v1/admin/users/${req["email"]}`, {...initReq, method: "DELETE"})
}
}

@ -0,0 +1,232 @@
/* eslint-disable */
// @ts-nocheck
/*
* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY
*/
export interface InitReq extends RequestInit {
pathPrefix?: string
}
export function fetchReq<I, O>(path: string, init?: InitReq): Promise<O> {
const {pathPrefix, ...req} = init || {}
const url = pathPrefix ? `${pathPrefix}${path}` : path
return fetch(url, req).then(r => r.json().then((body: O) => {
if (!r.ok) { throw body; }
return body;
})) as Promise<O>
}
// NotifyStreamEntityArrival is a callback that will be called on streaming entity arrival
export type NotifyStreamEntityArrival<T> = (resp: T) => void
/**
* fetchStreamingRequest is able to handle grpc-gateway server side streaming call
* it takes NotifyStreamEntityArrival that lets users respond to entity arrival during the call
* all entities will be returned as an array after the call finishes.
**/
export async function fetchStreamingRequest<S, R>(path: string, callback?: NotifyStreamEntityArrival<R>, init?: InitReq) {
const {pathPrefix, ...req} = init || {}
const url = pathPrefix ?`${pathPrefix}${path}` : path
const result = await fetch(url, req)
// needs to use the .ok to check the status of HTTP status code
// http other than 200 will not throw an error, instead the .ok will become false.
// see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#
if (!result.ok) {
const resp = await result.json()
const errMsg = resp.error && resp.error.message ? resp.error.message : ""
throw new Error(errMsg)
}
if (!result.body) {
throw new Error("response doesnt have a body")
}
await result.body
.pipeThrough(new TextDecoderStream())
.pipeThrough<R>(getNewLineDelimitedJSONDecodingStream<R>())
.pipeTo(getNotifyEntityArrivalSink((e: R) => {
if (callback) {
callback(e)
}
}))
// wait for the streaming to finish and return the success respond
return
}
/**
* JSONStringStreamController represents the transform controller that's able to transform the incoming
* new line delimited json content stream into entities and able to push the entity to the down stream
*/
interface JSONStringStreamController<T> extends TransformStreamDefaultController {
buf?: string
pos?: number
enqueue: (s: T) => void
}
/**
* getNewLineDelimitedJSONDecodingStream returns a TransformStream that's able to handle new line delimited json stream content into parsed entities
*/
function getNewLineDelimitedJSONDecodingStream<T>(): TransformStream<string, T> {
return new TransformStream({
start(controller: JSONStringStreamController<T>) {
controller.buf = ''
controller.pos = 0
},
transform(chunk: string, controller: JSONStringStreamController<T>) {
if (controller.buf === undefined) {
controller.buf = ''
}
if (controller.pos === undefined) {
controller.pos = 0
}
controller.buf += chunk
while (controller.pos < controller.buf.length) {
if (controller.buf[controller.pos] === '\n') {
const line = controller.buf.substring(0, controller.pos)
const response = JSON.parse(line)
controller.enqueue(response.result)
controller.buf = controller.buf.substring(controller.pos + 1)
controller.pos = 0
} else {
++controller.pos
}
}
}
})
}
/**
* getNotifyEntityArrivalSink takes the NotifyStreamEntityArrival callback and return
* a sink that will call the callback on entity arrival
* @param notifyCallback
*/
function getNotifyEntityArrivalSink<T>(notifyCallback: NotifyStreamEntityArrival<T>) {
return new WritableStream<T>({
write(entity: T) {
notifyCallback(entity)
}
})
}
type Primitive = string | boolean | number;
type RequestPayload = Record<string, unknown>;
type FlattenedRequestPayload = Record<string, Primitive | Array<Primitive>>;
/**
* Checks if given value is a plain object
* Logic copied and adapted from below source:
* https://github.com/char0n/ramda-adjunct/blob/master/src/isPlainObj.js
* @param {unknown} value
* @return {boolean}
*/
function isPlainObject(value: unknown): boolean {
const isObject =
Object.prototype.toString.call(value).slice(8, -1) === "Object";
const isObjLike = value !== null && isObject;
if (!isObjLike || !isObject) {
return false;
}
const proto = Object.getPrototypeOf(value);
const hasObjectConstructor =
typeof proto === "object" &&
proto.constructor === Object.prototype.constructor;
return hasObjectConstructor;
}
/**
* Checks if given value is of a primitive type
* @param {unknown} value
* @return {boolean}
*/
function isPrimitive(value: unknown): boolean {
return ["string", "number", "boolean"].some(t => typeof value === t);
}
/**
* Checks if given primitive is zero-value
* @param {Primitive} value
* @return {boolean}
*/
function isZeroValuePrimitive(value: Primitive): boolean {
return value === false || value === 0 || value === "";
}
/**
* Flattens a deeply nested request payload and returns an object
* with only primitive values and non-empty array of primitive values
* as per https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
* @param {RequestPayload} requestPayload
* @param {String} path
* @return {FlattenedRequestPayload>}
*/
function flattenRequestPayload<T extends RequestPayload>(
requestPayload: T,
path: string = ""
): FlattenedRequestPayload {
return Object.keys(requestPayload).reduce(
(acc: T, key: string): T => {
const value = requestPayload[key];
const newPath = path ? [path, key].join(".") : key;
const isNonEmptyPrimitiveArray =
Array.isArray(value) &&
value.every(v => isPrimitive(v)) &&
value.length > 0;
const isNonZeroValuePrimitive =
isPrimitive(value) && !isZeroValuePrimitive(value as Primitive);
let objectToMerge = {};
if (isPlainObject(value)) {
objectToMerge = flattenRequestPayload(value as RequestPayload, newPath);
} else if (isNonZeroValuePrimitive || isNonEmptyPrimitiveArray) {
objectToMerge = { [newPath]: value };
}
return { ...acc, ...objectToMerge };
},
{} as T
) as FlattenedRequestPayload;
}
/**
* Renders a deeply nested request payload into a string of URL search
* parameters by first flattening the request payload and then removing keys
* which are already present in the URL path.
* @param {RequestPayload} requestPayload
* @param {string[]} urlPathParams
* @return {string}
*/
export function renderURLSearchParams<T extends RequestPayload>(
requestPayload: T,
urlPathParams: string[] = []
): string {
const flattenedRequestPayload = flattenRequestPayload(requestPayload);
const urlSearchParams = Object.keys(flattenedRequestPayload).reduce(
(acc: string[][], key: string): string[][] => {
// key should not be present in the url path as a parameter
const value = flattenedRequestPayload[key];
if (urlPathParams.find(f => f === key)) {
return acc;
}
return Array.isArray(value)
? [...acc, ...value.map(m => [key, m.toString()])]
: (acc = [...acc, [key, value.toString()]]);
},
[] as string[][]
);
return new URLSearchParams(urlSearchParams).toString();
}

@ -0,0 +1,318 @@
// package: hhhapz.codequest.v1
// file: github.com/hhhapz/codequest/api/v1/all.proto
import * as jspb from "google-protobuf";
import * as google_api_annotations_pb from "../../../../../google/api/annotations_pb";
import * as google_protobuf_empty_pb from "google-protobuf/google/protobuf/empty_pb";
import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/timestamp_pb";
export class Token extends jspb.Message {
getToken(): string;
setToken(value: string): void;
hasExpires(): boolean;
clearExpires(): void;
getExpires(): google_protobuf_timestamp_pb.Timestamp | undefined;
setExpires(value?: google_protobuf_timestamp_pb.Timestamp): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Token.AsObject;
static toObject(includeInstance: boolean, msg: Token): Token.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Token, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Token;
static deserializeBinaryFromReader(message: Token, reader: jspb.BinaryReader): Token;
}
export namespace Token {
export type AsObject = {
token: string,
expires?: google_protobuf_timestamp_pb.Timestamp.AsObject,
}
}
export class OAuthCodeRequest extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): OAuthCodeRequest.AsObject;
static toObject(includeInstance: boolean, msg: OAuthCodeRequest): OAuthCodeRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: OAuthCodeRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): OAuthCodeRequest;
static deserializeBinaryFromReader(message: OAuthCodeRequest, reader: jspb.BinaryReader): OAuthCodeRequest;
}
export namespace OAuthCodeRequest {
export type AsObject = {
}
}
export class OAuthCodeResponse extends jspb.Message {
getRedirecturi(): string;
setRedirecturi(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): OAuthCodeResponse.AsObject;
static toObject(includeInstance: boolean, msg: OAuthCodeResponse): OAuthCodeResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: OAuthCodeResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): OAuthCodeResponse;
static deserializeBinaryFromReader(message: OAuthCodeResponse, reader: jspb.BinaryReader): OAuthCodeResponse;
}
export namespace OAuthCodeResponse {
export type AsObject = {
redirecturi: string,
}
}
export class TokenRequest extends jspb.Message {
getCode(): string;
setCode(value: string): void;
getState(): string;
setState(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): TokenRequest.AsObject;
static toObject(includeInstance: boolean, msg: TokenRequest): TokenRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: TokenRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): TokenRequest;
static deserializeBinaryFromReader(message: TokenRequest, reader: jspb.BinaryReader): TokenRequest;
}
export namespace TokenRequest {
export type AsObject = {
code: string,
state: string,
}
}
export class DeleteTokenRequest extends jspb.Message {
getAll(): boolean;
setAll(value: boolean): void;
hasToken(): boolean;
clearToken(): void;
getToken(): Token | undefined;
setToken(value?: Token): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DeleteTokenRequest.AsObject;
static toObject(includeInstance: boolean, msg: DeleteTokenRequest): DeleteTokenRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: DeleteTokenRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): DeleteTokenRequest;
static deserializeBinaryFromReader(message: DeleteTokenRequest, reader: jspb.BinaryReader): DeleteTokenRequest;
}
export namespace DeleteTokenRequest {
export type AsObject = {
all: boolean,
token?: Token.AsObject,
}
}
export class User extends jspb.Message {
getId(): string;
setId(value: string): void;
getName(): string;
setName(value: string): void;
getEmail(): string;
setEmail(value: string): void;
getPicture(): string;
setPicture(value: string): void;
getGradelevel(): number;
setGradelevel(value: number): void;
getAdmin(): boolean;
setAdmin(value: boolean): void;
hasCreatedat(): boolean;
clearCreatedat(): void;
getCreatedat(): google_protobuf_timestamp_pb.Timestamp | undefined;
setCreatedat(value?: google_protobuf_timestamp_pb.Timestamp): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): User.AsObject;
static toObject(includeInstance: boolean, msg: User): User.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: User, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): User;
static deserializeBinaryFromReader(message: User, reader: jspb.BinaryReader): User;
}
export namespace User {
export type AsObject = {
id: string,
name: string,
email: string,
picture: string,
gradelevel: number,
admin: boolean,
createdat?: google_protobuf_timestamp_pb.Timestamp.AsObject,
}
}
export class UpdateFields extends jspb.Message {
getName(): string;
setName(value: string): void;
getEmail(): string;
setEmail(value: string): void;
getGradelevel(): number;
setGradelevel(value: number): void;
getAdmin(): boolean;
setAdmin(value: boolean): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateFields.AsObject;
static toObject(includeInstance: boolean, msg: UpdateFields): UpdateFields.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UpdateFields, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UpdateFields;
static deserializeBinaryFromReader(message: UpdateFields, reader: jspb.BinaryReader): UpdateFields;
}
export namespace UpdateFields {
export type AsObject = {
name: string,
email: string,
gradelevel: number,
admin: boolean,
}
}
export class UserRequest extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UserRequest.AsObject;
static toObject(includeInstance: boolean, msg: UserRequest): UserRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UserRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UserRequest;
static deserializeBinaryFromReader(message: UserRequest, reader: jspb.BinaryReader): UserRequest;
}
export namespace UserRequest {
export type AsObject = {
}
}
export class AllUsersRequest extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AllUsersRequest.AsObject;
static toObject(includeInstance: boolean, msg: AllUsersRequest): AllUsersRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: AllUsersRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AllUsersRequest;
static deserializeBinaryFromReader(message: AllUsersRequest, reader: jspb.BinaryReader): AllUsersRequest;
}
export namespace AllUsersRequest {
export type AsObject = {
}
}
export class UpdateUserRequest extends jspb.Message {
getEmail(): string;
setEmail(value: string): void;
hasBody(): boolean;
clearBody(): void;
getBody(): UpdateFields | undefined;
setBody(value?: UpdateFields): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateUserRequest.AsObject;
static toObject(includeInstance: boolean, msg: UpdateUserRequest): UpdateUserRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UpdateUserRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UpdateUserRequest;
static deserializeBinaryFromReader(message: UpdateUserRequest, reader: jspb.BinaryReader): UpdateUserRequest;
}
export namespace UpdateUserRequest {
export type AsObject = {
email: string,
body?: UpdateFields.AsObject,
}
}
export class UserByEmailRequest extends jspb.Message {
getEmail(): string;
setEmail(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UserByEmailRequest.AsObject;
static toObject(includeInstance: boolean, msg: UserByEmailRequest): UserByEmailRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UserByEmailRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UserByEmailRequest;
static deserializeBinaryFromReader(message: UserByEmailRequest, reader: jspb.BinaryReader): UserByEmailRequest;
}
export namespace UserByEmailRequest {
export type AsObject = {
email: string,
}
}
export class DeleteUserRequest extends jspb.Message {
getEmail(): string;
setEmail(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DeleteUserRequest.AsObject;
static toObject(includeInstance: boolean, msg: DeleteUserRequest): DeleteUserRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: DeleteUserRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): DeleteUserRequest;
static deserializeBinaryFromReader(message: DeleteUserRequest, reader: jspb.BinaryReader): DeleteUserRequest;
}
export namespace DeleteUserRequest {
export type AsObject = {
email: string,
}
}
export class AllUsersResponse extends jspb.Message {
clearUsersList(): void;
getUsersList(): Array<User>;
setUsersList(value: Array<User>): void;
addUsers(value?: User, index?: number): User;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AllUsersResponse.AsObject;
static toObject(includeInstance: boolean, msg: AllUsersResponse): AllUsersResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: AllUsersResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AllUsersResponse;
static deserializeBinaryFromReader(message: AllUsersResponse, reader: jspb.BinaryReader): AllUsersResponse;
}
export namespace AllUsersResponse {
export type AsObject = {
usersList: Array<User.AsObject>,
}
}

@ -0,0 +1,75 @@
package codequest
import (
"github.com/gunk/opt/file/java"
"github.com/gunk/opt/http"
"github.com/gunk/opt/openapiv2"
"github.com/gunk/opt/proto"
"time"
)
type UserService interface {
// +gunk http.Match{
// Method: "GET",
// Path: "/v1/users/me",
// }
User(UserRequest) User
// +gunk http.Match{
// Method: "GET",
// Path: "/v1/admin/users/{Email}",
// }
UserByEmail(UserByEmailRequest) User
// +gunk http.Match{
// Method: "GET",
// Path: "/v1/admin/users",
// }
AllUsers(AllUsersRequest) AllUsersResponse
// +gunk http.Match{
// Method: "PUT",
// Path: "/v1/users/me",
// Body: "Body",
// }
UpdateUser(UpdateUserRequest) User
// +gunk http.Match{
// Method: "PUT",
// Path: "/v1/admin/users/{Email}",
// Body: "Body",
// }
AdminUpdateUser(UpdateUserRequest) User
// +gunk http.Match{
// Method: "DELETE",
// Path: "/v1/admin/users/{Email}",
// }
DeleteUser(DeleteUserRequest) User
}
type UpdateFields struct {
Name string `pb:"1" json:"name"`
GradeLevel int `pb:"3" json:"grade_level"`
Admin bool `pb:"4" json:"admin"`
}
type UserRequest struct{}
type AllUsersRequest struct{}
type UpdateUserRequest struct {
Email string `pb:"1" json:"email"`
Body UpdateFields `pb:"2" json:"fields"`
}
type UserByEmailRequest struct {
Email string `pb:"1" json:"email"`
}
type DeleteUserRequest struct {
Email string `pb:"1" json:"email"`
}
type AllUsersResponse struct {
Users []User `pb:"1" json:"users"`
}

@ -3,21 +3,19 @@ package main
import (
"flag"
"fmt"
"log"
"net/http"
"io"
"net"
"os"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/hhhapz/hackathon/api"
"github.com/hhhapz/hackathon/db"
"github.com/hhhapz/codequest/api"
"github.com/hhhapz/codequest/db"
"github.com/peterbourgon/ff/v3"
"google.golang.org/grpc/grpclog"
)
func run() error {
fs := flag.NewFlagSet("hackathon", flag.ExitOnError)
port := fs.Int("port", 8080, "HTTP Server Port")
fs := flag.NewFlagSet("codequest", flag.ExitOnError)
port := fs.Int("port", 10000, "GRPC Server Port")
secretFile := fs.String("secret", "client.secret.json", "Path to google oauth2 secret credentials JSON file.")
dbFile := fs.String("db", "db.sqlite", "Path to sqlite3 database file")
if err := ff.Parse(fs, os.Args[1:], ff.WithEnvVarPrefix("HK")); err != nil {
@ -38,33 +36,19 @@ func run() error {
return fmt.Errorf("could not create oauth config: %w", err)
}
server := api.NewServer(oaStore, database)
server, err := api.NewServer(oaStore, database)
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.RealIP)
r.Use(middleware.CleanPath)
log := grpclog.NewLoggerV2(os.Stderr, io.Discard, os.Stderr)
grpclog.SetLoggerV2(log)
r.Route("/api", func(r chi.Router) {
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://hackathon.teamortix.com", "http://localhost:8081"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowedHeaders: []string{"Authorization", "Content-Type", "*"},
AllowCredentials: true,
MaxAge: 300,
}))
api.HandlerFromMux(server, r)
})
s := &http.Server{
Handler: r,
Addr: fmt.Sprintf("0.0.0.0:%d", *port),
addr := fmt.Sprintf("0.0.0.0:%d", *port)
lis, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("failed to listen: %v", err)
}
log.Println("Starting Web server on", s.Addr)
return s.ListenAndServe()
log.Info("Serving gRPC on http://", addr)
return server.Serve(lis)
}
func main() {

@ -73,6 +73,7 @@ func (o *OAuthState) Validate(state string) (string, bool) {
if !ok {
return "", false
}
delete(o.states, state)
return entry.callback, true
}
@ -81,13 +82,6 @@ func (o *OAuthState) Exchange(ctx context.Context, code string) (*oauth2.Token,
return tk, err
}
func (o *OAuthState) Remove(code string) {
o.m.Lock()
defer o.m.Unlock()
delete(o.states, code)
}
func (o *OAuthState) GarbageCycle(period time.Duration, duration time.Duration) {
tick := time.NewTicker(period)

@ -10,7 +10,7 @@ import (
"log"
"time"
"github.com/hhhapz/hackathon/models"
"github.com/hhhapz/codequest/models"
"github.com/mattn/go-sqlite3"
)

@ -2,9 +2,14 @@ package db
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/hhhapz/hackathon/models"
"github.com/hhhapz/codequest/models"
"golang.org/x/oauth2"
)
func (db *DB) Users(ctx context.Context) ([]*models.User, error) {
@ -21,3 +26,57 @@ func (db *DB) UpdateUser(ctx context.Context, user *models.User) error {
}
return nil
}
func (db *DB) DeleteUser(ctx context.Context, user *models.User) error {
if err := user.Delete(ctx, db.DB); err != nil {
return fmt.Errorf("could not delete user: %w", err)
}
return nil
}
const (
userinfoEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
emailDomain = "@jisedu.or.id"
)
func (db *DB) ConsumeToken(ctx context.Context, token *oauth2.Token) (*models.Token, bool, error) {
endpoint := userinfoEndpoint + token.AccessToken
client := http.Client{Timeout: time.Second * 5}
res, err := client.Get(endpoint)
if err != nil {
return nil, false, fmt.Errorf("could not get userinfo: %w", err)
}
defer res.Body.Close()
user := &models.User{
CreatedAt: models.NewTime(time.Now()),
}
if err := json.NewDecoder(res.Body).Decode(&user); err != nil {
return nil, false, fmt.Errorf("could not decode userinfo json: %w", err)
}
if u, err := db.User(ctx, user.Email); err == nil {
var tk *models.Token
if tk, err = db.CreateToken(ctx, u); err != nil {
return nil, false, fmt.Errorf("could not create user token: %w", err)
}
return tk, true, nil
}
if !strings.HasSuffix(user.Email, emailDomain) {
return nil, true, fmt.Errorf("invalid registration email %q: must end with %q", user.Email, emailDomain)
}
err = db.UpdateUser(ctx, user)
if err != nil {
return nil, false, fmt.Errorf("could not create user: %w", err)
}
tk, err := db.CreateToken(ctx, user)
if err != nil {
return nil, false, fmt.Errorf("could not create user token: %w", err)
}
return tk, true, nil
}

@ -1,36 +1,27 @@
module github.com/hhhapz/hackathon
module github.com/hhhapz/codequest
go 1.17
require (
github.com/deepmap/oapi-codegen v1.8.2
github.com/getkin/kin-openapi v0.61.0
github.com/go-chi/chi/v5 v5.0.4
github.com/go-chi/cors v1.2.0
github.com/k0kubun/pp v3.0.1+incompatible
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0
github.com/mattn/go-sqlite3 v1.14.8
github.com/peterbourgon/ff/v3 v3.1.0
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5
google.golang.org/genproto v0.0.0-20211116182654-e63d96a377c4
google.golang.org/grpc v1.40.0
google.golang.org/protobuf v1.27.1
)
require (
cloud.google.com/go v0.94.1 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/googleapis/gax-go/v2 v2.1.0 // indirect
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/pkg/errors v0.8.1 // indirect
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365 // indirect
golang.org/x/text v0.3.6 // indirect
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/api v0.57.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect
google.golang.org/grpc v1.40.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

103
go.sum

@ -57,15 +57,9 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepmap/oapi-codegen v1.7.1 h1:fBWh5YlHQbH0lCY+CPVlEV1fmqqPU0kBBVN71QiOyCI=
github.com/deepmap/oapi-codegen v1.7.1/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -74,23 +68,17 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/getkin/kin-openapi v0.61.0 h1:6awGqF5nG5zkVpMsAih1QH4VgzS8phTxECUWIFo7zko=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo=
github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -122,7 +110,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -162,58 +149,47 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/peterbourgon/ff/v3 v3.1.0 h1:5JAeDK5j/zhKFjyHEZQXwXBoDijERaos10RE+xamOsY=
github.com/peterbourgon/ff/v3 v3.1.0/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -227,13 +203,14 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -303,8 +280,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -319,8 +297,9 @@ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -334,21 +313,18 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -361,14 +337,12 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -382,9 +356,9 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365 h1:6wSTsvPddg9gc/mVEEyk9oOAoxn+bT4Z9q1zx+4RwA4=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -393,13 +367,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -437,6 +410,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@ -445,6 +419,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@ -514,6 +489,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
@ -546,8 +522,9 @@ google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwy
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20211116182654-e63d96a377c4 h1:nPiLDJ9/wsay2NDshdJ1B24frx+butTxmaVaCxDBChY=
google.golang.org/genproto v0.0.0-20211116182654-e63d96a377c4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -590,16 +567,14 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -0,0 +1,67 @@
# No Time For Directions!
__Hermes__ is the Greek god, and amongst others, he is the god of travel, trade,
and athletes. Hermes has placed his hope in you.
You're dropped to a random location near Mount Cyllene in Arcadia, widely
considered the birthplace of Hermes. After years of training, **You** are now
set out on a quest. You must steal a key from __Hecate__, hidden near Mount
Cyllene.
Unfortunately, "near", is as close as you know where you are. The instructions
on the parchment Hermes gave begin from here, however, he never had the time to
tell you how to follow them, or where they lead to.
The document has different markings that appear to tell you which direction to
travel in. They indicade a direction, (`N`, `S`, `E` or `W`), and the number of
steps you must take to find the hiding location.
The problem is, that there's over a 100 different directions, and there's no
time following these directions one by one. It will take far too long! You take
a moment and work out the final destination, so you can get more quickly. Given
that you can only walk in the cardinal directions, what is the shortest path to
the destination?
### Example
- Given the following input
```
N5
E2
S9
W3
```
Instructs you to to travel `5` steps North, `2` steps East, `9` steps South,
and `3` steps West. Simplifying it, means `4` steps South, and `1` step West,
or `5` steps away.
- Given the following input
```
N6
E5
N5
W3
N4
S9
E4
S1
W6
E3
```
Leaves you `5` steps North, and `3` steps East, or `8` steps away.
Each line will have 2 characters of input, the first being the direction, and
the second being the number of steps. The number of steps on each line will
always be between 1 and 9, inclusive, steps.
**How many steps away** is the key?
{{ if ge .Step 2 }}
## Part 2
{{ end }}

@ -0,0 +1,13 @@
package question
import "github.com/hhhapz/codequest/models"
func init() {
Register(&Question{
ID: "directions",
Text: ``,
Level: 0,
Generate: func(*models.User) string { panic("not implemented") },
Validate: func(*models.User, string) bool { panic("not implemented") },
})
}

@ -0,0 +1,13 @@
package question
import "github.com/hhhapz/codequest/models"
func init() {
Register(&Question{
ID: "",
Text: "",
Level: 0,
Generate: func(*models.User) string { panic("not implemented") },
Validate: func(*models.User, string) bool { panic("not implemented") },
})
}

@ -0,0 +1,51 @@
package question
import (
"math/rand"
"github.com/hhhapz/codequest/models"
)
var bank Bank
// Bank is a map of different questions registered.
// The key of the map is the ID of the question.
//
// A custom type was created for convenience for picking random questions based
// on difficulties, and for registration.
type Bank [][]*Question
func Register(q *Question) {
bank[q.Level] = append(bank[q.Level], q)
}
func Questions(user *models.User, level Level) []*Question {
qs := make([]*Question, len(bank))
r := rand.New(rand.NewSource(user.CreatedAt.Time().Unix()))
for level := range bank {
idx := r.Intn(len(bank[level]))
qs[level] = bank[level][idx]
}
return qs
}
type Question struct {
ID string
Text string
Level Level
Generate func(user *models.User) string
Validate func(user *models.User, solution string) bool
}
// Level represents the difficulty of each question.
// As the level gets higher, the difficulty also gets higher.
type Level int
// Allowed difficulty levels.
const (
Level1 Level = iota
Level2
Level3
)

@ -64,19 +64,14 @@ paths:
delete:
tags: ["Auth"]
operationId: delete token
x-go-middlewares: ["token"]
parameters:
- $ref: "#/components/parameters/Token"
- name: all
in: query
required: false
schema:
type: boolean
default: false
- name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
type: boolean
responses:
'204':
description: User successfully logged out.
@ -88,13 +83,9 @@ paths:
description: Get self user information.
tags: ["Users"]
operationId: get me
x-go-middlewares: ["token"]
parameters:
- name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
- $ref: "#/components/parameters/Token"
responses:
'200':
description: User information.
@ -104,17 +95,13 @@ paths:
$ref: "#/components/schemas/User"
default:
$ref: "#/components/responses/DefaultResponse"
patch:
put:
description: Update self user.
tags: ["Users"]
operationId: modify user
x-go-middlewares: ["token"]
parameters:
- name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
- $ref: "#/components/parameters/Token"
requestBody:
description: Modified user information.
content:
@ -146,13 +133,9 @@ paths:
Requires admin to get user info not equal to the owner of the token.
tags: ["Users"]
operationId: get user by email
x-go-middlewares: ["token"]
parameters:
- name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
- $ref: "#/components/parameters/Token"
- name: email
in: query
description: User email.
@ -169,10 +152,11 @@ paths:
$ref: "#/components/schemas/User"
default:
$ref: "#/components/responses/DefaultResponse"
patch:
put:
description: Update another user. Requires admin.
tags: ["Users"]
operationId: modify other user
x-go-middlewares: ["token", "admin_token"]
parameters:
- name: token
in: cookie
@ -228,27 +212,34 @@ paths:
/users/all:
get:
description: Get all users. Requires admin.
description: Get all users. Requires admin.
tags: ["Users"]
operationId: get all users
x-go-middlewares: ["token", "admin_token"]
parameters:
- name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
- $ref: "#/components/parameters/Token"
responses:
'200':
description: All user information.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
type: array
items:
$ref: "#/components/schemas/User"
default:
$ref: "#/components/responses/DefaultResponse"
components:
parameters:
Token:
name: token
in: cookie
description: User authentication token.
required: true
schema:
type: string
responses:
DefaultResponse:
description: Unexpected server error or invalid user input.
@ -301,11 +292,7 @@ components:
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
default: 400
message:
type: string

@ -7,31 +7,30 @@ const config = {
plugins: [
require('daisyui'),
],
daisyui: {
themes: [
{
'hackathon': {
'neutral': '#9C6B91',
'neutral-focus': '#702860',
'neutral-content': '#EAE7E4',
'secondary': '#703529',
'secondary-focus': '#A8351E',
'primary': '#9BA77A',
'primary-focus': '#5E6536',
'primary-content': '#EAE7E4',
'secondary': '#A8351E',
'secondary-focus': '#703529',
'secondary-content': '#EAE7E4',
'accent': '#283D70',
'accent-focus': '#2E3546',
'accent-content': '#EAE7E4',
'neutral': '#1F1B12',
'neutral-focus': '#221F17',
'neutral-focus': '#4F4630',
'neutral-content': '#EAE7E4',
'primary': '#283D70',
'primary-focus': '#2E3546',
'primary-content': '#EAE7E4',
'base-100': '#FFEBC4',
'base-200': '#FAF2DE',
'base-300': '#B98976',
'base-100': '#EBDCB3',
'base-200': '#E0C98A',
'base-300': '#B89876',
'base-content': '#3F270E',
'info': '#2094f3',
'success': '#009485',
'warning': '#ff9900',
@ -39,7 +38,41 @@ const config = {
},
},
],
}
},
// daisyui: {
// themes: [
// {
// 'hackathon': {
// 'primary': '#8B4D96',
// 'primary-focus': '#702860',
// 'primary-content': '#EAE7E4',
// 'neutral': '#972812',
// 'neutral-focus': '#A8351E',
// 'neutral-content': '#EAE7E4',
// 'neutral': '#1F1B12',
// 'neutral-focus': '#221F17',
// 'neutral-content': '#EAE7E4',
// 'secondary': '#283D70',
// 'secondary-focus': '#2E3546',
// 'secondary-content': '#EAE7E4',
// 'base-100': '#967E74',
// 'base-200': '#E4E0D2',
// 'base-300': '#B98976',
// 'base-content': '#D1AA59',
// 'info': '#2094f3',
// 'success': '#009485',
// 'warning': '#ff9900',
// 'error': '#ff5724',
// },
// },
// ],
// }
};
module.exports = config;