restgohttphandlergo-echo

Appending to an existing list via http request


I'm in the process of making a simple REST API using Echo. I have a variable which is the following map, based on this struct I've made:

    type Checklist struct {
        ID         int      `json:"id"`
        Title      string   `json:"title"`
        Lines      []string `json:"lines"`
        AuthorName string   `json:"authorName"`
        AuthorID   int      `json:"authorID"`
        Tags       []Tag    `json:"tags"`
    }

    var (
        checklists    = map[int]*Checklist{}
        checklistSeq  = 1
        checklistLock = sync.Mutex{}
    )

After creating a new checklist and appending it to the checklists variable, how can I send a request which appends a new line in the Lines field of the new checklist?

The solution I thought up was this:

    func createChecklist(c echo.Context) error {
        checklistLock.Lock()
        defer checklistLock.Unlock()
        newChecklist := &Checklist{
            ID:    checklistSeq,
            Lines: make([]string, 0),
            Tags:  make([]Tag, 0),
        }
        if err := c.Bind(newChecklist); err != nil {
            return err
        }
        checklists[newChecklist.ID] = newChecklist
        checklistSeq++
        return c.JSON(http.StatusOK, newChecklist)
    }
    func addLine(c echo.Context) error {
        checklistLock.Lock()
        defer checklistLock.Unlock()
        id, _ := strconv.Atoi(c.Param("id"))
        checklist := *checklists[id]
        line := []string{""}
        if err := c.Bind(line); err != nil {
            return err
        }
        checklist.Lines = line
        return c.JSON(http.StatusCreated, checklists)
    }

However, when I test this handler, it gives me the following results:

    // 1: Creating a new checklist

    $ curl -X POST -H 'Content-Type: application/json' -d '{"title": "test"}' localhost:1234/checklist

    >> {"id":1,"title":"test","lines":[],"authorName":"","authorID":0,"tags":[]}

    // 2: Check to see the checklist has been created.
 
    $ curl -X GET localhost:1234/checklist

    >> {"1":{"id":1,"title":"test","lines":[],"authorName":"","authorID":0,"tags":[]}}
    // 3: Attempting to create a new line
    $ curl -X POST -H 'Content-Type: application/json' -d '{"lines": "test123"}' localhost:1234/checklist/1

    >> curl: (52) Empty reply from server

    // 4: Confirming it hasn't been created.

    $ curl -X GET localhost:1234/checklist

    >> {"1":{"id":1,"title":"test","lines":[],"authorName":"","authorID":0,"tags":[]}}

So the function doesn't actually work, since neither the expected response comes back from pinging the POST to the appropriate route or the line is actually added to the field.


Solution

  • func addLine(c echo.Context) error {
        checklistLock.Lock()
        defer checklistLock.Unlock()
    
        id, err := strconv.Atoi(c.Param("id"))
        if err != nil {
            return err
        }
    
        // do not deref *Checklist
        cl, ok := checklists[id]
        if !ok {
            return echo.ErrNotFound
        }
    
        // use same structure as the expected JSON
        var input struct { Lines []string `json:"lines"` }
    
        // always pass a pointer to c.Bind
        if err := c.Bind(&input); err != nil {
            return err
        }
    
        // do not overwrite previously written lines, use append
        cl.Lines = append(cl.Lines, input.Lines...)
    
        return c.JSON(http.StatusOK, cl)
    }
    

    Now try:

    $ curl -X POST -H 'Content-Type: application/json' -d '{"lines": ["test123"]}' localhost:1234/checklist/1
    

    (notice that "test123" is enclosed in brackets)