github.com/mssola/todo@v0.0.0-20181029153210-d25348dc3f48/app/topics_api.go (about)

     1  // Copyright (C) 2014-2017 Miquel Sabaté Solà <mikisabate@gmail.com>
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, v. 2.0. If a copy of the MPL was not distributed with this
     5  // file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7  package app
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"log"
    13  	"net/http"
    14  
    15  	"github.com/gorilla/mux"
    16  	"github.com/mssola/todo/lib"
    17  )
    18  
    19  // The parameters that be given through a request body.
    20  type params struct {
    21  	Name     string
    22  	Contents string
    23  }
    24  
    25  // Get the possible parameters from the given request. Note that it will only
    26  // check for the "name" and "contents" parameters.
    27  func getFromBody(req *http.Request) *params {
    28  	var p params
    29  
    30  	if req.Body == nil {
    31  		return nil
    32  	}
    33  
    34  	decoder := json.NewDecoder(req.Body)
    35  	if err := decoder.Decode(&p); err != nil {
    36  		return nil
    37  	}
    38  	return &p
    39  }
    40  
    41  // Safely render and send a JSON response with the given topic. This function
    42  // should be called after performing some operation that might return an error.
    43  // This error from the previous operation is the third parameter. The fourth
    44  // parameter tells this function to generate the Markdown code for this topic.
    45  func renderJSON(res http.ResponseWriter, topic *Topic, err error, md bool) {
    46  	// Try to render the given Topic.
    47  	if err == nil {
    48  		if md {
    49  			topic.RenderMarkdown()
    50  		}
    51  		if b, err := json.Marshal(topic); err == nil {
    52  			fmt.Fprint(res, string(b))
    53  			res.Header().Set("Content-Type", "application/json")
    54  			return
    55  		}
    56  	}
    57  
    58  	// Render a generic error.
    59  	lib.JSONError(res)
    60  }
    61  
    62  // TopicsIndexJSON responds to: GET /topics. This will only be called when the
    63  // user requested a JSON response.
    64  func TopicsIndexJSON(res http.ResponseWriter, req *http.Request) {
    65  	var topics []Topic
    66  	if _, err := Db.Select(&topics, "select * from topics"); err != nil {
    67  		log.Printf("Could not fetch topics: %v", err)
    68  	}
    69  	b, _ := json.Marshal(topics)
    70  	res.Header().Set("Content-Type", "application/json")
    71  	fmt.Fprint(res, string(b))
    72  }
    73  
    74  // TopicsCreateJSON responds to: POST /topics. This will only be called when
    75  // the user requested a JSON response.
    76  func TopicsCreateJSON(res http.ResponseWriter, req *http.Request) {
    77  	if p := getFromBody(req); p == nil {
    78  		lib.JSONError(res)
    79  	} else {
    80  		t, err := createTopic(p.Name)
    81  		renderJSON(res, t, err, false)
    82  	}
    83  }
    84  
    85  // TopicsShowJSON responds to: GET /topics/:id. This will only be called when
    86  // the user requested a JSON response.
    87  func TopicsShowJSON(res http.ResponseWriter, req *http.Request) {
    88  	var t Topic
    89  	p := mux.Vars(req)
    90  	err := Db.SelectOne(&t, "select * from topics where id=$1", p["id"])
    91  	renderJSON(res, &t, err, true)
    92  }
    93  
    94  // TopicsUpdateJSON responds to: PUT/PATCH /topics/:id. This will only be
    95  // called when the user requested a JSON response.
    96  func TopicsUpdateJSON(res http.ResponseWriter, req *http.Request) {
    97  	var str, value string
    98  	var p *params
    99  
   100  	if p = getFromBody(req); p == nil {
   101  		lib.JSONError(res)
   102  		return
   103  	}
   104  
   105  	// Execute the update query. Depending on the given parameters this will be
   106  	// just a plain rename, or a full update.
   107  	if value = p.Name; value == "" {
   108  		str = "contents"
   109  		if value = p.Contents; value == "" {
   110  			lib.JSONError(res)
   111  			return
   112  		}
   113  	} else {
   114  		str = "name"
   115  	}
   116  
   117  	// And finally send the JSON response.
   118  	var t Topic
   119  	str = fmt.Sprintf("update topics set %v=$1 where id=$2 returning *", str)
   120  	err := Db.SelectOne(&t, str, value, mux.Vars(req)["id"])
   121  	renderJSON(res, &t, err, true)
   122  }
   123  
   124  // TopicsDestroyJSON responds to: PUT/PATCH /topics/:id. This will only be
   125  // called when the user requested a JSON response.
   126  func TopicsDestroyJSON(res http.ResponseWriter, req *http.Request) {
   127  	p := mux.Vars(req)
   128  	results, err := Db.Exec("delete from topics where id=$1", p["id"])
   129  
   130  	res.Header().Set("Content-Type", "application/json")
   131  	if err != nil {
   132  		fmt.Fprint(res, lib.Response{Error: "Could not remove topic"})
   133  	} else if count, _ := results.RowsAffected(); count == 0 {
   134  		fmt.Fprint(res, lib.Response{Error: "Could not remove topic"})
   135  	} else {
   136  		fmt.Fprint(res, lib.Response{Message: "Ok"})
   137  	}
   138  }