Skip to content

Commit

Permalink
Merge pull request #2676 from ActiveState/DX-1944
Browse files Browse the repository at this point in the history
  • Loading branch information
Naatan authored Aug 1, 2023
2 parents 21da0b3 + 2bf6e9a commit 346aa0e
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 207 deletions.
2 changes: 1 addition & 1 deletion cmd/state/internal/cmdtree/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func newPublish(prime *primer.Values) *captain.Command {
Name: locale.Tl("filepath", "filepath"),
Description: locale.Tl("author_upload_filepath_description", "A tar.gz or zip archive containing the source files of the ingredient."),
Value: &params.Filepath,
Required: false,
Required: true,
},
},
func(_ *captain.Command, _ []string) error {
Expand Down
83 changes: 55 additions & 28 deletions internal/gqlclient/gqlclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (c *Client) RunWithContext(ctx context.Context, request Request, response i
name := strutils.Summarize(request.Query(), 25)
defer profile.Measure(fmt.Sprintf("gqlclient:RunWithContext:(%s)", name), time.Now())

if fileRequest, ok := request.(RequestWithFiles); ok && len(fileRequest.Files()) > 0 {
if fileRequest, ok := request.(RequestWithFiles); ok {
return c.runWithFiles(ctx, fileRequest, response)
}

Expand Down Expand Up @@ -247,30 +247,31 @@ func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, resp
}

// Map
mapField, err := mw.CreateFormField("map")
if err != nil {
reqErrChan <- errs.Wrap(err, "Could not create form field map")
return
}
for n, f := range gqlReq.Files() {
if _, err := mapField.Write([]byte(fmt.Sprintf(`{"%d": ["%s"]}`, n, f.Field))); err != nil {
reqErrChan <- errs.Wrap(err, "Could not write map field")
return
}
}

// File upload
for n, file := range gqlReq.Files() {
part, err := mw.CreateFormFile(fmt.Sprintf("%d", n), file.Name)
if len(gqlReq.Files()) > 0 {
mapField, err := mw.CreateFormField("map")
if err != nil {
reqErrChan <- errs.Wrap(err, "Could not create form file")
reqErrChan <- errs.Wrap(err, "Could not create form field map")
return
}

_, err = io.Copy(part, file.R)
if err != nil {
reqErrChan <- errs.Wrap(err, "Could not read file")
return
for n, f := range gqlReq.Files() {
if _, err := mapField.Write([]byte(fmt.Sprintf(`{"%d": ["%s"]}`, n, f.Field))); err != nil {
reqErrChan <- errs.Wrap(err, "Could not write map field")
return
}
}
// File upload
for n, file := range gqlReq.Files() {
part, err := mw.CreateFormFile(fmt.Sprintf("%d", n), file.Name)
if err != nil {
reqErrChan <- errs.Wrap(err, "Could not create form file")
return
}

_, err = io.Copy(part, file.R)
if err != nil {
reqErrChan <- errs.Wrap(err, "Could not read file")
return
}
}
}
}()
Expand Down Expand Up @@ -304,20 +305,46 @@ func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, resp
}
req = req.WithContext(ctx)
c.Log(fmt.Sprintf(">> Raw Request: %s\n", req.URL.String()))
res, resErr := http.DefaultClient.Do(req)
if reqErr := <-reqErrChan; reqErr != nil {
return reqErr

var res *http.Response
resErrChan := make(chan error)
go func() {
var err error
res, err = http.DefaultClient.Do(req)
resErrChan <- err
}()

// Due to the streaming uploads the request error can happen both before and after the http request itself, hence
// the creative select case you see before you.
wait := true
for wait {
select {
case err := <-reqErrChan:
if err != nil {
c.Log(fmt.Sprintf("Request Error: %s", err))
return err
}
case err := <-resErrChan:
wait = false
if err != nil {
c.Log(fmt.Sprintf("Response Error: %s", err))
return err
}
}
}
if resErr != nil {
return resErr

if res == nil {
return errs.New("Received empty response")
}

defer res.Body.Close()
var buf bytes.Buffer
if _, err := io.Copy(&buf, res.Body); err != nil {
c.Log(fmt.Sprintf("Read Error: %s", err))
return errors.Wrap(err, "reading body")
}
resp := buf.Bytes()
c.Log(fmt.Sprintf("<< %s\n", string(resp)))
c.Log(fmt.Sprintf("<< Response code: %d, body: %s\n", res.StatusCode, string(resp)))

// Work around API's that don't follow the graphql standard
// https://activestatef.atlassian.net/browse/PB-4291
Expand Down
2 changes: 1 addition & 1 deletion internal/locale/locales/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@ uploadingredient_editor_opening:
Opening editor to edit ingredient meta information. Alternatively you may manually edit the following file: [ACTIONABLE]{{.V0}}[/RESET].
uploadingredient_success:
other: |
Successfully uploaded as:
Successfully published as:
Ingredient ID: [[ACTIONABLE]{{.V0}}[/RESET]
Ingredient Version ID: [ACTIONABLE]{{.V1}}[/RESET]
Revision: [ACTIONABLE]{{.V2}}[/RESET]
25 changes: 16 additions & 9 deletions internal/runners/packages/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ func NewSearch(prime primeable) *Search {
func (s *Search) Run(params SearchRunParams, nstype model.NamespaceType) error {
logging.Debug("ExecuteSearch")

language, err := targetedLanguage(params.Language, s.proj)
if err != nil {
return locale.WrapError(err, fmt.Sprintf("%s_err_cannot_obtain_language", nstype))
}
var ns model.Namespace
if params.Ingredient.Namespace == "" {
language, err := targetedLanguage(params.Language, s.proj)
if err != nil {
return locale.WrapError(err, fmt.Sprintf("%s_err_cannot_obtain_language", nstype))
}

ns := model.NewNamespacePkgOrBundle(language, nstype)
if params.Ingredient.Namespace != "" {
ns = model.NewNamespacePkgOrBundle(language, nstype)
} else {
ns = model.NewRawNamespace(params.Ingredient.Namespace)
}

var err error
var packages []*model.IngredientAndVersion
if params.ExactTerm {
packages, err = model.SearchIngredientsStrict(ns.String(), params.Ingredient.Name, true, true, params.Timestamp.Time)
Expand All @@ -67,7 +70,7 @@ func (s *Search) Run(params SearchRunParams, nstype model.NamespaceType) error {
)
}

s.out.Print(output.Prepare(formatSearchResults(packages), packages))
s.out.Print(output.Prepare(formatSearchResults(packages, params.Ingredient.Namespace != ""), packages))

return nil
}
Expand Down Expand Up @@ -142,7 +145,7 @@ type searchPackageRow struct {

type searchOutput []searchPackageRow

func formatSearchResults(packages []*model.IngredientAndVersion) *searchOutput {
func formatSearchResults(packages []*model.IngredientAndVersion, showNamespace bool) *searchOutput {
rows := make(searchOutput, len(packages))

filterNilStr := func(s *string) string {
Expand All @@ -153,8 +156,12 @@ func formatSearchResults(packages []*model.IngredientAndVersion) *searchOutput {
}

for i, pack := range packages {
name := filterNilStr(pack.Ingredient.Name)
if showNamespace {
name = fmt.Sprintf("%s/%s", *pack.Ingredient.PrimaryNamespace, name)
}
row := searchPackageRow{
Pkg: filterNilStr(pack.Ingredient.Name),
Pkg: name,
Version: pack.Version,
versions: len(pack.Versions),
Modules: makeModules(pack.Ingredient.NormalizedName, pack),
Expand Down
12 changes: 6 additions & 6 deletions internal/runners/publish/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (r *Runner) Run(params *Params) error {
!strings.HasSuffix(strings.ToLower(params.Filepath), ".tar.gz") {
return locale.NewInputError("err_uploadingredient_file_not_supported", "Expected file extension to be either .zip or .tar.gz: '{{.V0}}'", params.Filepath)
}
} else if !params.Editor {
} else if !params.Edit {
return locale.NewInputError("err_uploadingredient_file_required", "You have to supply the source archive unless editing.")
}

Expand Down Expand Up @@ -188,7 +188,7 @@ func (r *Runner) Run(params *Params) error {

cont, err := r.prompt.Confirm(
"",
locale.Tl("uploadingredient_confirm", `Upload following ingredient?
locale.Tl("uploadingredient_confirm", `Publish following ingredient?
{{.V0}}
`, string(b)),
Expand All @@ -198,11 +198,11 @@ func (r *Runner) Run(params *Params) error {
return errs.Wrap(err, "Confirmation failed")
}
if !cont {
r.out.Print(locale.Tl("uploadingredient_cancel", "Upload cancelled"))
r.out.Print(locale.Tl("uploadingredient_cancel", "Publish cancelled"))
return nil
}

r.out.Notice(locale.Tl("uploadingredient_uploading", "Uploading ingredient..."))
r.out.Notice(locale.Tl("uploadingredient_uploading", "Publishing ingredient..."))

pr, err := request.Publish(reqVars, params.Filepath)
if err != nil {
Expand All @@ -214,8 +214,8 @@ func (r *Runner) Run(params *Params) error {
return locale.WrapError(err, "err_uploadingredient_publish", "Could not publish ingredient")
}

if result.Error != "" {
return locale.NewError("err_uploadingredient_publish_api", "API responded with error: {{.V0}}", result.Message)
if result.Publish.Error != "" {
return locale.NewError("err_uploadingredient_publish_api", "API responded with error: {{.V0}}", result.Publish.Error)
}

r.out.Print(output.Prepare(
Expand Down
2 changes: 1 addition & 1 deletion pkg/platform/api/graphql/model/publish.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package model

type PublishResult struct {
ErrorResponse
Publish struct {
ErrorResponse
IngredientID string `json:"ingredientID"`
IngredientVersionID string `json:"ingredientVersionID"`
Revision int `json:"revision"`
Expand Down
33 changes: 22 additions & 11 deletions pkg/platform/api/graphql/request/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@ import (
)

func Publish(vars PublishVariables, filepath string) (*PublishInput, error) {
f, err := os.Open(filepath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, locale.WrapInputError(err, "err_upload_file_not_found", "Could not find file at {{.V0}}", filepath)
var f *os.File
if filepath != "" {
var err error
f, err = os.Open(filepath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, locale.WrapInputError(err, "err_upload_file_not_found", "Could not find file at {{.V0}}", filepath)
}
return nil, errs.Wrap(err, "Could not open file %s", filepath)
}
return nil, errs.Wrap(err, "Could not open file %s", filepath)
}

checksum, err := fileutils.Sha256Hash(filepath)
if err != nil {
return nil, locale.WrapError(err, "err_upload_file_checksum", "Could not calculate checksum for file")
}
checksum, err := fileutils.Sha256Hash(filepath)
if err != nil {
return nil, locale.WrapError(err, "err_upload_file_checksum", "Could not calculate checksum for file")
}

vars.FileChecksum = checksum
vars.FileChecksum = checksum
}

return &PublishInput{
Variables: vars,
Expand Down Expand Up @@ -148,6 +152,9 @@ func (p *PublishInput) Close() error {
}

func (p *PublishInput) Files() []gqlclient.File {
if p.file == nil {
return []gqlclient.File{}
}
return []gqlclient.File{
{
Field: "variables.input.file", // this needs to map to the graphql input, eg. variables.input.file
Expand All @@ -166,6 +173,10 @@ func (p *PublishInput) Query() string {
ingredientVersionID
revision
}
... on Error{
__typename
error: message
}
}
}
`
Expand Down
Loading

0 comments on commit 346aa0e

Please sign in to comment.