Skip to content

Commit

Permalink
Add codex init command, update to new upload API
Browse files Browse the repository at this point in the history
  • Loading branch information
twavv committed Aug 11, 2021
1 parent 1c8bb30 commit c3ddd0b
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 31 deletions.
77 changes: 77 additions & 0 deletions cmd/codex/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package codex

import (
"github.com/pathbird/pbauthor/internal/auth"
"github.com/pathbird/pbauthor/internal/codex"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"path/filepath"
)

var codexInitConfig struct {
systemPackages []string
codexName string
}

var codexInitCmd = &cobra.Command{
Use: "init [<path>]",
Short: "initialize a codex configuration file",

RunE: func(cmd *cobra.Command, args []string) error {
// If no dir is specified, use current directory.
if len(args) == 0 {
args = append(args, ".")
}

if len(args) != 1 {
return cmd.Usage()
}

dir, err := filepath.Abs(args[0])
if err != nil {
return errors.Wrap(err, "invalid codex directory")
}

auth, err := auth.GetAuth()
if err != nil {
return err
}
if auth == nil {
return errors.New("not authenticated")
}

config, err := codex.InitConfig(dir)
if err != nil {
return errors.Wrap(err, "failed to initialize codex config")
}

if len(codexInitConfig.systemPackages) != 0 {
config.Kernel.SystemPackages = codexInitConfig.systemPackages
}

if codexInitConfig.codexName != "" {
config.Upload.Name = codexInitConfig.codexName
}

if err := config.Save(); err != nil {
return errors.Wrap(err, "failed to save codex config")
}
return nil
},
}

func init() {
codexInitCmd.Flags().StringArrayVar(
&codexInitConfig.systemPackages,
"system-packages",
[]string{},
"a list of additional system packages to install",
)
codexInitCmd.Flags().StringVar(
&codexInitConfig.codexName,
"name",
"",
"the name of the codex as displayed in the Pathbird UI",
)
Cmd.AddCommand(codexInitCmd)
}
48 changes: 33 additions & 15 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"encoding/json"
"fmt"
"github.com/pathbird/pbauthor/internal/config"
"github.com/pathbird/pbauthor/internal/version"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"mime/multipart"
"net/http"

"github.com/pkg/errors"
)

type Client struct {
Expand Down Expand Up @@ -157,7 +157,7 @@ func (r *response) UnmarshalJson(target interface{}) error {
return nil
}

const userAgent = `pbauthor`
var userAgent = `pbauthor ` + version.Version

func (c *Client) postJson(r *request) (*response, error) {
reqBody, err := json.Marshal(r.body)
Expand Down Expand Up @@ -185,21 +185,38 @@ func (c *Client) postJson(r *request) (*response, error) {
}

type multipartRequest struct {
route string
fields map[string]string
files []FileRef
// The API route to send the request to
route string
// Serialized to JSON and given to the Pathbird API as request.json
payload interface{}
// An array of files to attach
files []FileRef
}

// Send a multipart form request to the Pathbird API.
// NOTE:
// To ease the annoyance of serializing/deserializing request payloads
// to/from the multipart format (in which it's hard to deal with nested
// objects, arrays, etc.), we always attach the request body as a file
// field named request.json.
func (c *Client) postMultipart(r *multipartRequest) (*response, error) {
body := &bytes.Buffer{}
w := multipart.NewWriter(body)
for fieldname, value := range r.fields {
log.Debugf("setting field: %s", fieldname)
err := w.WriteField(fieldname, value)
if err != nil {
return nil, errors.Wrap(err, "failed to create api request body")
}

// Attach the body as request.json
requestPayload, err := json.Marshal(r.payload)
if err != nil {
return nil, errors.Wrap(err, "failed to serialize request to JSON")
}
f, err := w.CreateFormFile("request.json", "request.json")
if err != nil {
return nil, errors.Wrap(err, "failed to create form file for request.json")
}
if _, err := f.Write(requestPayload); err != nil {
return nil, errors.Wrapf(err, "failed to write body for request.json")
}

// Attach supplemental files
for _, file := range r.files {
// We just always add files underneath the "files" key since that's what every
// Pathbird API endpoint expects
Expand All @@ -209,23 +226,24 @@ func (c *Client) postMultipart(r *multipartRequest) (*response, error) {
return nil, errors.Wrap(err, "failed to create api request body")
}
}
err := w.Close()

// Finalize the writer
err = w.Close()
if err != nil {
return nil, errors.Wrap(err, "failed to create api request body")
}

// Send the request
endpoint := fmt.Sprintf("%s/%s", c.host, r.route)
req, err := http.NewRequest("POST", endpoint, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", w.FormDataContentType())

httpResponse, err := c.do(req)
if err != nil {
return nil, err
}

return &response{
route: r.route,
httpResponse: httpResponse,
Expand Down
21 changes: 11 additions & 10 deletions internal/api/codex_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ type UploadCodexRequest struct {

// required
// the files that comprise the codex bundle
Files []FileRef
Files []FileRef `json:"-"`

// optional
// if specified, replace this codex instead of uploading a new codex
CodexId string `json:"codexId,omitempty"`
CodexId string `json:"replaceCodexId,omitempty"`

KernelOptions KernelOptions `json:"kernelOptions"`
}

type KernelOptions struct {
SystemPackages []string `json:"systemPackages,omitempty"`
}

type UploadCodexResponse struct {
Expand Down Expand Up @@ -50,15 +56,10 @@ var _ error = (*CodexParseFailedError)(nil)
func (c *Client) UploadCodex(
r *UploadCodexRequest,
) (*UploadCodexResponse, *CodexParseFailedError, error) {
fields := make(map[string]string)
fields["codexCategoryId"] = r.CodexCategoryId
if r.CodexId != "" {
fields["replaceCodexId"] = r.CodexId
}
res, err := c.postMultipart(&multipartRequest{
route: "author/upload-codex",
fields: fields,
files: r.Files,
route: "author/upload-codex",
payload: r,
files: r.Files,
})
if err != nil {
return nil, nil, err
Expand Down
21 changes: 16 additions & 5 deletions internal/codex/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,29 @@ const ConfigFileName = "codex.toml"

type Config struct {
Upload UploadConfig `toml:"upload"`
Kernel KernelConfig `toml:"kernel"`

configFile string
}

type UploadConfig struct {
CodexCategory string `toml:"codex_category,omitempty"`
Name string `toml:"name,omitempty"`

// The id of the codex (if being re-uploaded)
// The ID of the codex category where the codex will be uploaded.
CodexCategory string `toml:"codex_category"`
// The name of the codex (as displayed in the Pathbird UI).
Name string `toml:"name"`
// The ID of the codex (if being re-uploaded).
CodexId string `toml:"codex_id,omitempty"`
}

type KernelConfig struct {
// The Docker image to use when running the kernel.
// This overrides all other kernel config options.
Image string `toml:"image,omitempty"`
// An array of additional (usually Debian) packages to install
// (e.g., {"texlive-latex-base"} if the `latex` command is required).
SystemPackages []string `toml:"system_packages"`
}

func (c *Config) Unmarshal(data []byte) error {
err := toml.Unmarshal(data, c)
if err != nil {
Expand Down Expand Up @@ -81,7 +92,7 @@ func GetOrInitCodexConfig(dirname string) (*Config, error) {
configFilePath := filepath.Join(dirname, ConfigFileName)
if _, err := os.Stat(configFilePath); err != nil {
if os.IsNotExist(err) {
return initConfig(dirname)
return InitConfig(dirname)
}
return nil, errors.Wrap(err, "unable to stat codex config file")
}
Expand Down
2 changes: 1 addition & 1 deletion internal/codex/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

// Initialize a new codex config file
func initConfig(dirname string) (*Config, error) {
func InitConfig(dirname string) (*Config, error) {
// Look for a codex file before initializing
files, err := ioutil.ReadDir(dirname)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions internal/codex/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func UploadCodex(
CodexCategoryId: config.Upload.CodexCategory,
Files: files,
CodexId: config.Upload.CodexId,
KernelOptions: api.KernelOptions{
SystemPackages: config.Kernel.SystemPackages,
},
}

res, parseErr, err := client.UploadCodex(req)
Expand Down

0 comments on commit c3ddd0b

Please sign in to comment.