git.0x0001f346.de/andreas/api integration
This commit is contained in:
parent
7f0cb3d1ce
commit
16eaba69c4
@ -5,7 +5,7 @@ As a fan of [TimeAPI.io](https://www.timeapi.io/swagger/index.html), but not thi
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
neo@matrix:~$ curl -s -H "Accept: application/json" "http://localhost:9100/Europe/Berlin" | jq
|
neo@matrix:~$ curl -s -H "Accept: application/json" "http://localhost:9100/Time/current/zone?timeZone=Europe/Berlin" | jq
|
||||||
{
|
{
|
||||||
"year": 2023,
|
"year": 2023,
|
||||||
"month": 6,
|
"month": 6,
|
||||||
|
140
apiTime/api.go
Normal file
140
apiTime/api.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package apiTime
|
||||||
|
|
||||||
|
import "git.0x0001f346.de/andreas/api"
|
||||||
|
|
||||||
|
var tags []string = []string{"Time"}
|
||||||
|
|
||||||
|
func GetComponents() *api.Components {
|
||||||
|
components := &api.Components{
|
||||||
|
Schemas: map[string]*api.ComponentsSchema{
|
||||||
|
"CurrentTime": {
|
||||||
|
Properties: map[string]*api.ComponentsProperty{
|
||||||
|
"year": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Year",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 2020,
|
||||||
|
},
|
||||||
|
"month": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Month",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 12,
|
||||||
|
},
|
||||||
|
"day": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Day",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 13,
|
||||||
|
},
|
||||||
|
"hour": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Hour of the day in range 0-24",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 9,
|
||||||
|
},
|
||||||
|
"minute": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Minute",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 30,
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Second",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 30,
|
||||||
|
},
|
||||||
|
"milliSeconds": {
|
||||||
|
Type: "integer",
|
||||||
|
Description: "Milliseconds",
|
||||||
|
Format: "int64",
|
||||||
|
Example: 123,
|
||||||
|
},
|
||||||
|
"dateTime": {
|
||||||
|
Type: "string",
|
||||||
|
Description: "Full date time",
|
||||||
|
Format: "date-time",
|
||||||
|
Example: "2020-12-13T09:30:30+01:00",
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
Type: "string",
|
||||||
|
Description: "Date string",
|
||||||
|
Example: "13/12/2020",
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
Type: "string",
|
||||||
|
Description: "Time string",
|
||||||
|
Example: "09:30",
|
||||||
|
},
|
||||||
|
"timeZone": {
|
||||||
|
Type: "string",
|
||||||
|
Description: "TimeZone of the result",
|
||||||
|
Example: "Europe/Berlin",
|
||||||
|
},
|
||||||
|
"dayOfWeek": {
|
||||||
|
Type: "string",
|
||||||
|
Description: "The day of the week",
|
||||||
|
Example: "Monday",
|
||||||
|
},
|
||||||
|
"dstActive": {
|
||||||
|
Type: "boolean",
|
||||||
|
Description: "Boolean describing whether DST is applied and active in that timezone",
|
||||||
|
Example: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AdditionalProperties: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaths() map[string]*api.Path {
|
||||||
|
paths := map[string]*api.Path{
|
||||||
|
"/Time/current/zone": {
|
||||||
|
GET: &api.Method{
|
||||||
|
Function: TimeCurrentZone,
|
||||||
|
Tags: tags,
|
||||||
|
Summary: "Gets the current time of a time zone.",
|
||||||
|
Parameters: []*api.Parameters{
|
||||||
|
{
|
||||||
|
Name: "timeZone",
|
||||||
|
In: "query",
|
||||||
|
Description: "Full IANA time zone names.",
|
||||||
|
Schema: &api.ParametersSchema{
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
Example: "Europe/Berlin",
|
||||||
|
AllowReserved: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Responses: map[int]*api.Response{
|
||||||
|
200: {
|
||||||
|
Description: "Current time",
|
||||||
|
Content: map[string]*api.ResponseContent{
|
||||||
|
"application/json": {
|
||||||
|
Schema: &api.ResponseSchema{
|
||||||
|
Ref: "#/components/schemas/CurrentTime",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
Description: "Error message",
|
||||||
|
Content: map[string]*api.ResponseContent{
|
||||||
|
"application/json": {
|
||||||
|
Schema: &api.ResponseSchema{
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}
|
81
apiTime/time.go
Normal file
81
apiTime/time.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package apiTime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dateTimeResponse struct {
|
||||||
|
Year int `json:"year,omitempty"`
|
||||||
|
Month int `json:"month,omitempty"`
|
||||||
|
Day int `json:"day,omitempty"`
|
||||||
|
Hour int `json:"hour,omitempty"`
|
||||||
|
Minute int `json:"minute,omitempty"`
|
||||||
|
Seconds int `json:"seconds,omitempty"`
|
||||||
|
MilliSeconds int `json:"milliSeconds,omitempty"`
|
||||||
|
DateTime string `json:"dateTime,omitempty"`
|
||||||
|
Date string `json:"date,omitempty"`
|
||||||
|
Time string `json:"time,omitempty"`
|
||||||
|
TimeZone string `json:"timeZone,omitempty"`
|
||||||
|
DayOfWeek string `json:"dayOfWeek,omitempty"`
|
||||||
|
DstActive bool `json:"dstActive,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeCurrentZone(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Language", "en")
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
timeZone := ""
|
||||||
|
|
||||||
|
vals := r.URL.Query()
|
||||||
|
parameters, ok := vals["timeZone"]
|
||||||
|
if ok && len(parameters) == 1 {
|
||||||
|
timeZone = parameters[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeZone == "" {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
fmt.Fprint(w, "{\"error\": \"invalid value for timeZone\"}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loc, err := time.LoadLocation(timeZone)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
fmt.Fprint(w, "{\"error\": \"invalid value for timeZone\"}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%q %q\n", loc.String(), r.RemoteAddr)
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
fmt.Fprint(w, getDateTimeResponseAsJSONString(loc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDateTimeResponse(loc *time.Location) dateTimeResponse {
|
||||||
|
t := time.Now().In(loc)
|
||||||
|
return dateTimeResponse{
|
||||||
|
Year: t.Year(),
|
||||||
|
Month: int(t.Month()),
|
||||||
|
Day: t.Day(),
|
||||||
|
Hour: t.Hour(),
|
||||||
|
Minute: t.Minute(),
|
||||||
|
Seconds: t.Second(),
|
||||||
|
MilliSeconds: func(i int64, err error) int { return int(i) }(strconv.ParseInt(strconv.Itoa(t.Nanosecond())[:3], 10, 0)),
|
||||||
|
DateTime: t.Format(time.RFC3339),
|
||||||
|
Date: fmt.Sprintf("%02d/%02d/%04d", t.Month(), t.Day(), t.Year()),
|
||||||
|
Time: fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()),
|
||||||
|
TimeZone: loc.String(),
|
||||||
|
DayOfWeek: t.Weekday().String(),
|
||||||
|
DstActive: t.IsDST(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDateTimeResponseAsJSONString(loc *time.Location) string {
|
||||||
|
r, _ := json.Marshal(getDateTimeResponse(loc))
|
||||||
|
return string(r)
|
||||||
|
}
|
138
main.go
138
main.go
@ -1,34 +1,83 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
"git.0x0001f346.de/andreas/Datetime-API/apiTime"
|
||||||
|
"git.0x0001f346.de/andreas/api"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
var portToListenOn int = 9100
|
const apiTitle string = "Datetime-API"
|
||||||
var defaultTZ string = "Europe/Berlin"
|
const apiDescription string = "A simple API to get the current time for a given time zone."
|
||||||
|
const apiVersion string = "0.1"
|
||||||
type DateTimeResponse struct {
|
const portToListenOn int = 9100
|
||||||
Year int `json:"year,omitempty"`
|
|
||||||
Month int `json:"month,omitempty"`
|
|
||||||
Day int `json:"day,omitempty"`
|
|
||||||
Hour int `json:"hour,omitempty"`
|
|
||||||
Minute int `json:"minute,omitempty"`
|
|
||||||
Seconds int `json:"seconds,omitempty"`
|
|
||||||
MilliSeconds int `json:"milliSeconds,omitempty"`
|
|
||||||
DateTime string `json:"dateTime,omitempty"`
|
|
||||||
Date string `json:"date,omitempty"`
|
|
||||||
Time string `json:"time,omitempty"`
|
|
||||||
TimeZone string `json:"timeZone,omitempty"`
|
|
||||||
DayOfWeek string `json:"dayOfWeek,omitempty"`
|
|
||||||
DstActive bool `json:"dstActive,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
API := buildAPI()
|
||||||
|
router := mux.NewRouter()
|
||||||
|
|
||||||
|
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Language", "en")
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
fmt.Fprint(w, API.ToJSONString())
|
||||||
|
}).Methods("GET")
|
||||||
|
|
||||||
|
for route, path := range API.Paths {
|
||||||
|
if path.DELETE != nil {
|
||||||
|
if path.DELETE.Function == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
router.HandleFunc(route, path.DELETE.Function).Methods("DELETE")
|
||||||
|
}
|
||||||
|
if path.GET != nil {
|
||||||
|
if path.GET.Function == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
router.HandleFunc(route, path.GET.Function).Methods("GET")
|
||||||
|
}
|
||||||
|
if path.PATCH != nil {
|
||||||
|
if path.PATCH.Function == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
router.HandleFunc(route, path.PATCH.Function).Methods("PATCH")
|
||||||
|
}
|
||||||
|
if path.POST != nil {
|
||||||
|
if path.POST.Function == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
router.HandleFunc(route, path.POST.Function).Methods("POST")
|
||||||
|
}
|
||||||
|
if path.PUT != nil {
|
||||||
|
if path.PUT.Function == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
router.HandleFunc(route, path.PUT.Function).Methods("PUT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printStartupBanner()
|
||||||
|
|
||||||
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", portToListenOn), router))
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAPI() api.API {
|
||||||
|
fullAPI := api.GetAPIPrototype(apiTitle, apiDescription, apiVersion)
|
||||||
|
|
||||||
|
for route, path := range apiTime.GetPaths() {
|
||||||
|
fullAPI.Paths[route] = path
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, schema := range apiTime.GetComponents().Schemas {
|
||||||
|
fullAPI.Components.Schemas[name] = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func printStartupBanner() {
|
||||||
fmt.Println("********************************************")
|
fmt.Println("********************************************")
|
||||||
fmt.Println("* *")
|
fmt.Println("* *")
|
||||||
fmt.Println("* git.0x0001f346.de/andreas/Datetime-API *")
|
fmt.Println("* git.0x0001f346.de/andreas/Datetime-API *")
|
||||||
@ -37,51 +86,4 @@ func main() {
|
|||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Printf("Listening: http://localhost:%d\n", portToListenOn)
|
fmt.Printf("Listening: http://localhost:%d\n", portToListenOn)
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
loc, _ := time.LoadLocation(defaultTZ)
|
|
||||||
if r.URL.String() != "/" {
|
|
||||||
newloc, err := time.LoadLocation(r.URL.String()[1:])
|
|
||||||
if err == nil {
|
|
||||||
loc = newloc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(
|
|
||||||
"%q %q\n",
|
|
||||||
loc.String(),
|
|
||||||
r.RemoteAddr,
|
|
||||||
)
|
|
||||||
|
|
||||||
w.Header().Add("Content-Language", "en")
|
|
||||||
w.Header().Add("Content-Type", "application/json; charset=utf-8")
|
|
||||||
w.Header().Add("Server", "git.0x0001f346.de/andreas/Datetime-API")
|
|
||||||
fmt.Fprint(w, GetDateTimeResponseAsJSONString(loc))
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", portToListenOn), nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDateTimeResponse(loc *time.Location) DateTimeResponse {
|
|
||||||
t := time.Now().In(loc)
|
|
||||||
return DateTimeResponse{
|
|
||||||
Year: t.Year(),
|
|
||||||
Month: int(t.Month()),
|
|
||||||
Day: t.Day(),
|
|
||||||
Hour: t.Hour(),
|
|
||||||
Minute: t.Minute(),
|
|
||||||
Seconds: t.Second(),
|
|
||||||
MilliSeconds: func(i int64, err error) int { return int(i) }(strconv.ParseInt(strconv.Itoa(t.Nanosecond())[:3], 10, 0)),
|
|
||||||
DateTime: t.Format(time.RFC3339),
|
|
||||||
Date: fmt.Sprintf("%02d/%02d/%04d", t.Month(), t.Day(), t.Year()),
|
|
||||||
Time: fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()),
|
|
||||||
TimeZone: loc.String(),
|
|
||||||
DayOfWeek: t.Weekday().String(),
|
|
||||||
DstActive: t.IsDST(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDateTimeResponseAsJSONString(loc *time.Location) string {
|
|
||||||
r, _ := json.Marshal(GetDateTimeResponse(loc))
|
|
||||||
return string(r)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user