From 140941308f52392194d8614b804786fcc748819b Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Fri, 8 Mar 2024 00:16:58 -0800 Subject: [PATCH] feat: create key CLI command --- server/docs/docs.go | 26 +++--- server/docs/swagger.json | 26 +++--- server/docs/swagger.yaml | 22 ++--- server/keys.go | 27 ++---- server/types/request.go | 11 +++ subatomic-cli/cmd/create-key.go | 87 +++++++++++++++++++ .../cmd/{create.go => create-repo.go} | 10 +-- 7 files changed, 148 insertions(+), 61 deletions(-) create mode 100644 subatomic-cli/cmd/create-key.go rename subatomic-cli/cmd/{create.go => create-repo.go} (86%) diff --git a/server/docs/docs.go b/server/docs/docs.go index 94b2a77..c52dc18 100644 --- a/server/docs/docs.go +++ b/server/docs/docs.go @@ -57,13 +57,13 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/main.createKeyPayload" + "$ref": "#/definitions/types.CreateKeyPayload" } } ], "responses": { - "200": { - "description": "OK" + "201": { + "description": "Created" }, "400": { "description": "Bad Request", @@ -522,13 +522,8 @@ const docTemplate = `{ } }, "definitions": { - "main.createKeyPayload": { + "main.fullKeyResponse": { "type": "object", - "required": [ - "email", - "id", - "name" - ], "properties": { "email": { "type": "string" @@ -538,11 +533,19 @@ const docTemplate = `{ }, "name": { "type": "string" + }, + "public_key": { + "type": "string" } } }, - "main.fullKeyResponse": { + "types.CreateKeyPayload": { "type": "object", + "required": [ + "email", + "id", + "name" + ], "properties": { "email": { "type": "string" @@ -552,9 +555,6 @@ const docTemplate = `{ }, "name": { "type": "string" - }, - "public_key": { - "type": "string" } } }, diff --git a/server/docs/swagger.json b/server/docs/swagger.json index b7495c5..951e1e9 100644 --- a/server/docs/swagger.json +++ b/server/docs/swagger.json @@ -50,13 +50,13 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/main.createKeyPayload" + "$ref": "#/definitions/types.CreateKeyPayload" } } ], "responses": { - "200": { - "description": "OK" + "201": { + "description": "Created" }, "400": { "description": "Bad Request", @@ -515,13 +515,8 @@ } }, "definitions": { - "main.createKeyPayload": { + "main.fullKeyResponse": { "type": "object", - "required": [ - "email", - "id", - "name" - ], "properties": { "email": { "type": "string" @@ -531,11 +526,19 @@ }, "name": { "type": "string" + }, + "public_key": { + "type": "string" } } }, - "main.fullKeyResponse": { + "types.CreateKeyPayload": { "type": "object", + "required": [ + "email", + "id", + "name" + ], "properties": { "email": { "type": "string" @@ -545,9 +548,6 @@ }, "name": { "type": "string" - }, - "public_key": { - "type": "string" } } }, diff --git a/server/docs/swagger.yaml b/server/docs/swagger.yaml index f8c58ae..e3d5288 100644 --- a/server/docs/swagger.yaml +++ b/server/docs/swagger.yaml @@ -1,6 +1,6 @@ basePath: / definitions: - main.createKeyPayload: + main.fullKeyResponse: properties: email: type: string @@ -8,12 +8,10 @@ definitions: type: string name: type: string - required: - - email - - id - - name + public_key: + type: string type: object - main.fullKeyResponse: + types.CreateKeyPayload: properties: email: type: string @@ -21,8 +19,10 @@ definitions: type: string name: type: string - public_key: - type: string + required: + - email + - id + - name type: object types.CreateRepoPayload: properties: @@ -115,10 +115,10 @@ paths: name: body required: true schema: - $ref: '#/definitions/main.createKeyPayload' + $ref: '#/definitions/types.CreateKeyPayload' responses: - "200": - description: OK + "201": + description: Created "400": description: Bad Request schema: diff --git a/server/keys.go b/server/keys.go index 8e75a79..5a6817c 100644 --- a/server/keys.go +++ b/server/keys.go @@ -53,29 +53,19 @@ func (router *keysRouter) getKeys(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, res) } -type createKeyPayload struct { - ID string `json:"id" validate:"required,hostname"` - Name string `json:"name" validate:"required"` - Email string `json:"email" validate:"required,email"` -} - -func (u *createKeyPayload) Bind(r *http.Request) error { - return validate.Struct(u) -} - // createKey godoc // // @Summary Create a new key // @Description create key // @Tags keys // @Accept json -// @Param body body createKeyPayload true "options for the new key" -// @Success 200 +// @Param body body types.CreateKeyPayload true "options for the new key" +// @Success 201 // @Failure 400 {object} types.ErrResponse // @Failure 409 {object} types.ErrResponse // @Router /keys [post] func (router *keysRouter) createKey(w http.ResponseWriter, r *http.Request) { - payload := &createKeyPayload{} + payload := &types.CreateKeyPayload{} if err := render.Bind(r, payload); err != nil { render.Render(w, r, types.ErrInvalidRequest(err)) @@ -96,7 +86,7 @@ func (router *keysRouter) createKey(w http.ResponseWriter, r *http.Request) { panic(err) } - key, err := router.database.SigningKey.Create(). + _, err = router.database.SigningKey.Create(). SetID(payload.ID). SetPublicKey(armoredPublicKey). SetPrivateKey(armoredPrivateKey). @@ -108,11 +98,10 @@ func (router *keysRouter) createKey(w http.ResponseWriter, r *http.Request) { panic(err) } - render.JSON(w, r, &types.KeyResponse{ - ID: key.ID, - Name: key.Name, - Email: key.Email, - }) + w.WriteHeader(http.StatusCreated) + if _, err := w.Write(nil); err != nil { + panic(err) + } } type fullKeyResponse struct { diff --git a/server/types/request.go b/server/types/request.go index 734ba37..170dc79 100644 --- a/server/types/request.go +++ b/server/types/request.go @@ -16,6 +16,17 @@ func (u *CreateRepoPayload) Bind(r *http.Request) error { return validate.Struct(u) } +type CreateKeyPayload struct { + ID string `json:"id" validate:"required,hostname"` + Name string `json:"name" validate:"required"` + Email string `json:"email" validate:"required,email"` +} + +func (u *CreateKeyPayload) Bind(r *http.Request) error { + validate := r.Context().Value(ValidateContextKey{}).(*validator.Validate) + return validate.Struct(u) +} + type SetKeyPayload struct { ID string `json:"id" validate:"required,hostname"` } diff --git a/subatomic-cli/cmd/create-key.go b/subatomic-cli/cmd/create-key.go new file mode 100644 index 0000000..a91772e --- /dev/null +++ b/subatomic-cli/cmd/create-key.go @@ -0,0 +1,87 @@ +/* +Copyright © 2022 NAME HERE +*/ +package cmd + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/FyraLabs/subatomic/server/types" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// keyCreateCmd represents the create command +var keyCreateCmd = &cobra.Command{ + Use: "create [id] [name] [email]", + Short: "Create a new key", + Args: cobra.ExactArgs(3), + + RunE: func(cmd *cobra.Command, args []string) error { + server := viper.GetString("server") + token := viper.GetString("token") + + if server == "" { + return errors.New("server must be defined") + } + + if token == "" { + return errors.New("token must be defined") + } + + payload := types.CreateKeyPayload{ + ID: args[0], + Name: args[1], + Email: args[2], + } + + data, err := json.Marshal(payload) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, server+"/keys", bytes.NewReader(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + req.Header.Add("Authorization", "Bearer "+token) + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return err + } + + if res.StatusCode != http.StatusCreated { + var serverError types.ErrResponse + if err := json.NewDecoder(res.Body).Decode(&serverError); err != nil { + return err + } + + return fmt.Errorf("API returned error: %s", serverError.ErrorText) + } + + return nil + }, +} + +func init() { + keysCmd.AddCommand(keyCreateCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // keyCreateCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // keyCreateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/subatomic-cli/cmd/create.go b/subatomic-cli/cmd/create-repo.go similarity index 86% rename from subatomic-cli/cmd/create.go rename to subatomic-cli/cmd/create-repo.go index 5348120..17a6199 100644 --- a/subatomic-cli/cmd/create.go +++ b/subatomic-cli/cmd/create-repo.go @@ -15,8 +15,8 @@ import ( "github.com/spf13/viper" ) -// createCmd represents the create command -var createCmd = &cobra.Command{ +// repoCreateCmd represents the create command +var repoCreateCmd = &cobra.Command{ Use: "create [id] [type]", Short: "Create a new repo", Args: cobra.ExactArgs(2), @@ -72,15 +72,15 @@ var createCmd = &cobra.Command{ } func init() { - repoCmd.AddCommand(createCmd) + repoCmd.AddCommand(repoCreateCmd) // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: - // createCmd.PersistentFlags().String("foo", "", "A help for foo") + // repoCreateCmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: - // createCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // repoCreateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") }