<commit_before>package main

import (
	"encoding/json"
	"net/http"

	"fmt"
	"strconv"

	"github.com/julienschmidt/httprouter"
)

// addDynamicRoutes will dynamically add RESTful routes to the router. Routes are added based off of the keys that
// are present in the parsed JSON file. For instance, if a JSON file is set up like:
//
//    {
//        "posts": [ { "id": 1, "title": "Foo" } ]
//    }
//
// The following routes will be created:
//
//    POST /posts (creates a new post record)
//    GET /posts (returns all post records)
//    GET /posts/:id (returns a specific record)
//    PUT /posts/:id (creates or updates a record with the specified ID)
//    PATCH /posts/:id (updates a record with the specified ID)
//    DELETE /posts/:id (deletes the specified record)
//
//
func addDynamicRoutes(router *httprouter.Router) {
	// set up our routes
	for _, itemType := range serverData.ItemTypes() {
		// Shadow these variables. If this isn't done, then the closures below will see
		// `value` and `key` as whatever they were in the last(?) iteration of the above for loop
		itemType := itemType

		// POST /type
		router.POST(fmt.Sprintf("/%s", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			data, err := readRequestData(r)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			dataMutex.Lock()

			// The idea with grabbing the record with ID 1 is to see if any records even exist. If none exist, the loop
			// should not execute at all, giving the first record id 1
			id := int64(1)
			_, err = serverData.RecordWithId(itemType, id)
			for id = maxIds[itemType]; err != ErrorNotFound; _, err = serverData.RecordWithId(itemType, id) {
				id++
			}

			data["id"] = json.Number(strconv.FormatInt(id, 10))

			dirty = true
			serverData.AddRecord(itemType, data)

			maxIds[itemType] = id

			dataMutex.Unlock()

			w.WriteHeader(http.StatusCreated)
		})

		// GET /type
		router.GET(fmt.Sprintf("/%s", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			items, _ := serverData.ItemType(itemType)
			genericJsonResponse(w, r, items)
		})

		// (GET,PATCH,PUT,DELETE) /type/id
		for _, method := range []string{"GET", "PATCH", "PUT", "DELETE"} {
			method := method
			router.Handle(method, fmt.Sprintf("/%s/:id", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
				idParam, _ := strconv.ParseInt(ps.ByName("id"), 10, 64)

				record, err := serverData.RecordWithId(itemType, idParam)

				if err != nil {
					if err == ErrorNotFound {
						w.WriteHeader(http.StatusNotFound)
					} else {
						w.WriteHeader(http.StatusInternalServerError)
					}
				}

				// The method type determines how we respond
				switch method {
				case "GET":
					genericJsonResponse(w, r, record)
					return
				case "PATCH":
					updatedData, err := readRequestData(r)
					if err != nil {
						w.WriteHeader(http.StatusBadRequest)
						return
					}

					dataMutex.Lock()
					for key, value := range updatedData {
						record[key] = value
					}

					dirty = true

					dataMutex.Unlock()

					return
				case "PUT":
					updatedData, err := readRequestData(r)
					if err != nil {
						w.WriteHeader(http.StatusBadRequest)
						return
					}

					dataMutex.Lock()

					for key, _ := range record {
						record[key] = nil
					}

					for key, value := range updatedData {
						record[key] = value
					}

					dirty = true

					dataMutex.Unlock()

					w.WriteHeader(http.StatusOK)
					return
				case "DELETE":
					dataMutex.Lock()

					dirty = true
					serverData.DeleteRecord(itemType, idParam)

					dataMutex.Unlock()

					w.WriteHeader(http.StatusOK)
					return
				}

				// If it's not found, then this request acts as a POST
				if method == "PUT" {
					newData, err := readRequestData(r)
					if err != nil {
						w.WriteHeader(http.StatusBadRequest)
						return
					}

					if _, hasId := newData["id"]; !hasId {
						newData["id"] = idParam
					}

					dataMutex.Lock()
					dirty = true

					serverData.AddRecord(itemType, newData)

					dataMutex.Unlock()

					w.WriteHeader(http.StatusCreated)
					return
				}
			})
		}
	}
}

// addStaticRoutes adds all routes which are present regardless of the JSON file's data. These include
//<commit_msg>Fix PUT requests when record not found<commit_after>package main

import (
	"encoding/json"
	"net/http"

	"fmt"
	"strconv"

	"github.com/julienschmidt/httprouter"
)

// addDynamicRoutes will dynamically add RESTful routes to the router. Routes are added based off of the keys that
// are present in the parsed JSON file. For instance, if a JSON file is set up like:
//
//    {
//        "posts": [ { "id": 1, "title": "Foo" } ]
//    }
//
// The following routes will be created:
//
//    POST /posts (creates a new post record)
//    GET /posts (returns all post records)
//    GET /posts/:id (returns a specific record)
//    PUT /posts/:id (creates or updates a record with the specified ID)
//    PATCH /posts/:id (updates a record with the specified ID)
//    DELETE /posts/:id (deletes the specified record)
//
//
func addDynamicRoutes(router *httprouter.Router) {
	// set up our routes
	for _, itemType := range serverData.ItemTypes() {
		// Shadow these variables. If this isn't done, then the closures below will see
		// `value` and `key` as whatever they were in the last(?) iteration of the above for loop
		itemType := itemType

		// POST /type
		router.POST(fmt.Sprintf("/%s", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			data, err := readRequestData(r)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			dataMutex.Lock()

			// The idea with grabbing the record with ID 1 is to see if any records even exist. If none exist, the loop
			// should not execute at all, giving the first record id 1
			id := int64(1)
			_, err = serverData.RecordWithId(itemType, id)
			for id = maxIds[itemType]; err != ErrorNotFound; _, err = serverData.RecordWithId(itemType, id) {
				id++
			}

			data["id"] = json.Number(strconv.FormatInt(id, 10))

			dirty = true
			serverData.AddRecord(itemType, data)

			maxIds[itemType] = id

			dataMutex.Unlock()

			w.WriteHeader(http.StatusCreated)
		})

		// GET /type
		router.GET(fmt.Sprintf("/%s", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			items, _ := serverData.ItemType(itemType)
			genericJsonResponse(w, r, items)
		})

		// (GET,PATCH,PUT,DELETE) /type/id
		for _, method := range []string{"GET", "PATCH", "PUT", "DELETE"} {
			method := method
			router.Handle(method, fmt.Sprintf("/%s/:id", itemType), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
				idParam, _ := strconv.ParseInt(ps.ByName("id"), 10, 64)

				record, err := serverData.RecordWithId(itemType, idParam)

				if err != nil {
					if err == ErrorNotFound {
						// If it's not found, then this request acts as a POST
						if method == "PUT" {
							newData, err := readRequestData(r)
							if err != nil {
								w.WriteHeader(http.StatusBadRequest)
								return
							}

							if _, hasId := newData["id"]; !hasId {
								newData["id"] = idParam
							}

							dataMutex.Lock()
							dirty = true

							serverData.AddRecord(itemType, newData)

							dataMutex.Unlock()

							w.WriteHeader(http.StatusCreated)
						} else {
							w.WriteHeader(http.StatusNotFound)
						}
					} else {
						w.WriteHeader(http.StatusInternalServerError)
					}

					return
				}

				// The method type determines how we respond
				switch method {
				case "GET":
					genericJsonResponse(w, r, record)
					return
				case "PATCH":
					updatedData, err := readRequestData(r)
					if err != nil {
						w.WriteHeader(http.StatusBadRequest)
						return
					}

					dataMutex.Lock()
					for key, value := range updatedData {
						record[key] = value
					}

					dirty = true

					dataMutex.Unlock()

					return
				case "PUT":
					updatedData, err := readRequestData(r)
					if err != nil {
						w.WriteHeader(http.StatusBadRequest)
						return
					}

					dataMutex.Lock()

					for key, _ := range record {
						record[key] = nil
					}

					for key, value := range updatedData {
						record[key] = value
					}

					dirty = true

					dataMutex.Unlock()

					w.WriteHeader(http.StatusOK)
					return
				case "DELETE":
					dataMutex.Lock()

					dirty = true
					serverData.DeleteRecord(itemType, idParam)

					dataMutex.Unlock()

					w.WriteHeader(http.StatusOK)
					return
				}
			})
		}
	}
}

// addStaticRoutes adds all routes which are present regardless of the JSON file's data. These include
//
{'fraction_non_alphanumeric': 0.12188110842819364, 'fraction_numerical': 0.0027595722662987236, 'mean_word_length': 2.607631688096226, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4278558', 'n_tokens_mistral': 3519, 'n_tokens_neox': 2737, 'n_words': 801}