diff options
| author | Shulhan <m.shulhan@gmail.com> | 2020-03-31 01:11:26 +0700 |
|---|---|---|
| committer | Shulhan <m.shulhan@gmail.com> | 2020-04-01 06:28:03 +0700 |
| commit | 8a141995aabdee289d2b096bd77a10b52b08a1bf (patch) | |
| tree | 98af569d4c3e2620809ce591c2733184cd77db8f /server.go | |
| parent | 84fdfdb6ae4175a125fc67a6aed377476d31ee0e (diff) | |
| download | kamusku-8a141995aabdee289d2b096bd77a10b52b08a1bf.tar.xz | |
all: implement server and client for dictionary API
Currently the server and client can onyl handle API for looking up
definitions of the words through "/api/definisi" URL.
Diffstat (limited to 'server.go')
| -rw-r--r-- | server.go | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/server.go b/server.go new file mode 100644 index 0000000..a241f05 --- /dev/null +++ b/server.go @@ -0,0 +1,167 @@ +// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package kbbi + +import ( + "fmt" + "log" + stdhttp "net/http" + "strings" + "time" + + "github.com/shuLhan/share/lib/http" +) + +const ( + defListen = ":3394" + emptyResponse = "{}" +) + +// +// Server for KBBI with caching and spell checking functionalities. +// +type Server struct { + http *http.Server + kamus *kamusCache + + // The client that forward request to official KBBI server. + forwardc *directClient + + // If offline is true and the word definition is not found on cache, + // the server will not forward request to official KBBI server. + offline bool +} + +// +// NewServer create and initialize the server. +// +func NewServer() (server *Server, err error) { + opts := &http.ServerOptions{ + Address: defListen, + } + + server = &Server{} + + server.kamus, err = newKamusCache("") + if err != nil { + return nil, fmt.Errorf("http.NewServer: %w", err) + } + + server.http, err = http.NewServer(opts) + if err != nil { + return nil, fmt.Errorf("http.NewServer: %w", err) + } + + server.forwardc, err = newDirectClient(nil) + if err != nil { + return nil, fmt.Errorf("http.NewServer: %w", err) + } + + err = server.registerEndpoints() + if err != nil { + return nil, fmt.Errorf("http.NewServer: %w", err) + } + + return server, nil +} + +// +// Start the HTTP server. +// +func (server *Server) Start() (err error) { + go server.dumpCache() + + return server.http.Start() +} + +// +// dumpCache periodically dump the cache to file to be loaded later. +// +func (server *Server) dumpCache() { + ticker := time.NewTicker(1 * time.Hour) + + for range ticker.C { + if !server.kamus.isChanging() { + continue + } + err := server.kamus.store() + if err != nil { + log.Println("server.dumpCache: ", err) + } + } +} + +func (server *Server) handleDefinisi( + httpRes stdhttp.ResponseWriter, + httpReq *stdhttp.Request, + reqBody []byte, +) (resBody []byte, err error) { + paramKata := httpReq.Form.Get(paramNameKata) + if len(paramKata) == 0 { + return []byte(emptyResponse), nil + } + + inputs := strings.Split(paramKata, ",") + res := make(DefinisiResponse, len(inputs)) + + for _, in := range inputs { + in = strings.TrimSpace(in) + if len(in) == 0 { + continue + } + + kata := server.kamus.get(in) + if kata != nil { + res[in] = kata + continue + } + + if server.offline { + continue + } + + // The word does not exist in cache, retrieve it from official + // website. + fwRes, err := server.forwardc.CariDefinisi([]string{in}) + if err != nil { + kata.err = err + continue + } + + for k, v := range fwRes { + if k == in { + res[in] = v + server.kamus.set(k, v) + delete(fwRes, k) + break + } + } + } + + if len(res) == 0 { + return []byte(emptyResponse), nil + } + + resBody, err = res.pack() + if err != nil { + return nil, err + } + + return resBody, nil +} + +// +// registerEndpoints register the API endpoints. +// +func (server *Server) registerEndpoints() (err error) { + epDefinisi := &http.Endpoint{ + Method: http.RequestMethodGet, + Path: pathAPIDefinisi, + RequestType: http.RequestTypeQuery, + ResponseType: http.ResponseTypeJSON, + Call: server.handleDefinisi, + } + return server.http.RegisterEndpoint(epDefinisi) +} |
