Restructure the go package

This commit is contained in:
2021-12-16 21:57:05 +01:00
parent a9b5ac4df2
commit 39ef200967
26 changed files with 111 additions and 98 deletions
+44
View File
@@ -0,0 +1,44 @@
package data
//go:generate $GOPATH/bin/easyjson -pkg -no_std_marshalers
import "html/template"
// Pages are ghost pages data
//easyjson:json
type Pages struct {
Pages []Post `json:"pages"`
Meta Meta `json:"meta"`
}
// Post contains ghost post data
//easyjson:json
type Post struct {
ID string `json:"id"`
UUID string `json:"uuid"`
Title string `json:"title"`
HTML template.HTML `json:"html"`
FImage template.URL `json:"feature_image"`
}
// Meta contains ghost result metadata
//easyjson:json
type Meta struct {
Pagination Pagination `json:"pagination"`
}
// Pagination contains ghost pagination data
//easyjson:json
type Pagination struct {
Page int `json:"page"`
Limit int `json:"limit"`
Pages int `json:"pages"`
Total int `json:"total"`
}
// Posts are ghost posts data
//easyjson:json
type Posts struct {
Posts []Post `json:"posts"`
Meta Meta `json:"meta"`
}
+407
View File
@@ -0,0 +1,407 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package data
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
template "html/template"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData(in *jlexer.Lexer, out *Posts) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "posts":
if in.IsNull() {
in.Skip()
out.Posts = nil
} else {
in.Delim('[')
if out.Posts == nil {
if !in.IsDelim(']') {
out.Posts = make([]Post, 0, 0)
} else {
out.Posts = []Post{}
}
} else {
out.Posts = (out.Posts)[:0]
}
for !in.IsDelim(']') {
var v1 Post
(v1).UnmarshalEasyJSON(in)
out.Posts = append(out.Posts, v1)
in.WantComma()
}
in.Delim(']')
}
case "meta":
(out.Meta).UnmarshalEasyJSON(in)
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData(out *jwriter.Writer, in Posts) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"posts\":"
out.RawString(prefix[1:])
if in.Posts == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in.Posts {
if v2 > 0 {
out.RawByte(',')
}
(v3).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"meta\":"
out.RawString(prefix)
(in.Meta).MarshalEasyJSON(out)
}
out.RawByte('}')
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Posts) MarshalEasyJSON(w *jwriter.Writer) {
easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData(w, v)
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Posts) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData(l, v)
}
func easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData1(in *jlexer.Lexer, out *Post) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "id":
out.ID = string(in.String())
case "uuid":
out.UUID = string(in.String())
case "title":
out.Title = string(in.String())
case "html":
out.HTML = template.HTML(in.String())
case "feature_image":
out.FImage = template.URL(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData1(out *jwriter.Writer, in Post) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"id\":"
out.RawString(prefix[1:])
out.String(string(in.ID))
}
{
const prefix string = ",\"uuid\":"
out.RawString(prefix)
out.String(string(in.UUID))
}
{
const prefix string = ",\"title\":"
out.RawString(prefix)
out.String(string(in.Title))
}
{
const prefix string = ",\"html\":"
out.RawString(prefix)
out.String(string(in.HTML))
}
{
const prefix string = ",\"feature_image\":"
out.RawString(prefix)
out.String(string(in.FImage))
}
out.RawByte('}')
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Post) MarshalEasyJSON(w *jwriter.Writer) {
easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData1(w, v)
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Post) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData1(l, v)
}
func easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData2(in *jlexer.Lexer, out *Pagination) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "page":
out.Page = int(in.Int())
case "limit":
out.Limit = int(in.Int())
case "pages":
out.Pages = int(in.Int())
case "total":
out.Total = int(in.Int())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData2(out *jwriter.Writer, in Pagination) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"page\":"
out.RawString(prefix[1:])
out.Int(int(in.Page))
}
{
const prefix string = ",\"limit\":"
out.RawString(prefix)
out.Int(int(in.Limit))
}
{
const prefix string = ",\"pages\":"
out.RawString(prefix)
out.Int(int(in.Pages))
}
{
const prefix string = ",\"total\":"
out.RawString(prefix)
out.Int(int(in.Total))
}
out.RawByte('}')
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Pagination) MarshalEasyJSON(w *jwriter.Writer) {
easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData2(w, v)
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Pagination) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData2(l, v)
}
func easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData3(in *jlexer.Lexer, out *Pages) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "pages":
if in.IsNull() {
in.Skip()
out.Pages = nil
} else {
in.Delim('[')
if out.Pages == nil {
if !in.IsDelim(']') {
out.Pages = make([]Post, 0, 0)
} else {
out.Pages = []Post{}
}
} else {
out.Pages = (out.Pages)[:0]
}
for !in.IsDelim(']') {
var v4 Post
(v4).UnmarshalEasyJSON(in)
out.Pages = append(out.Pages, v4)
in.WantComma()
}
in.Delim(']')
}
case "meta":
(out.Meta).UnmarshalEasyJSON(in)
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData3(out *jwriter.Writer, in Pages) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"pages\":"
out.RawString(prefix[1:])
if in.Pages == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v5, v6 := range in.Pages {
if v5 > 0 {
out.RawByte(',')
}
(v6).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"meta\":"
out.RawString(prefix)
(in.Meta).MarshalEasyJSON(out)
}
out.RawByte('}')
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Pages) MarshalEasyJSON(w *jwriter.Writer) {
easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData3(w, v)
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Pages) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData3(l, v)
}
func easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData4(in *jlexer.Lexer, out *Meta) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "pagination":
(out.Pagination).UnmarshalEasyJSON(in)
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData4(out *jwriter.Writer, in Meta) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"pagination\":"
out.RawString(prefix[1:])
(in.Pagination).MarshalEasyJSON(out)
}
out.RawByte('}')
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Meta) MarshalEasyJSON(w *jwriter.Writer) {
easyjson794297d0EncodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData4(w, v)
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Meta) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson794297d0DecodeCodeTokarchUkMainnikaNikitaTokarchUkFrontendGhostData4(l, v)
}
+16
View File
@@ -0,0 +1,16 @@
package ghost
import (
"code.tokarch.uk/mainnika/nikita-tokarch-uk/pkg/ghost/data"
"code.tokarch.uk/mainnika/nikita-tokarch-uk/pkg/ghost/params"
)
// Client is the ghost backend client
type Client interface {
// GetPosts returns blog posts according to query params
GetPosts(queryParams ...params.Modifier) (posts *data.Posts, err error)
// GetPostBySlug returns a single post by its slug title and query params
GetPostBySlug(slug string, queryParams ...params.Modifier) (posts *data.Posts, err error)
// GetPageBySlug returns a single page by its slug title and query params
GetPageBySlug(slug string, queryParams ...params.Modifier) (pages *data.Pages, err error)
}
+39
View File
@@ -0,0 +1,39 @@
package params
// Params are generics query argument
type Params struct {
Limit int
Page int
}
// Modifier function takes params and makes some changes
type Modifier func(params Params) Params
// Modifiers is a list of modifier
type Modifiers []Modifier
// Apply function modifies params
func (ms Modifiers) Apply(params Params) Params {
for _, m := range ms {
params = m(params)
}
return params
}
// WithLimit modifier setups the limit
func WithLimit(limit int) Modifier {
return func(params Params) Params {
params.Limit = limit
return params
}
}
// WithPage modifier setups the page
func WithPage(page int) Modifier {
return func(params Params) Params {
params.Page = page
return params
}
}
+171
View File
@@ -0,0 +1,171 @@
package httpclient
import (
"fmt"
"strconv"
"sync"
"time"
"github.com/mailru/easyjson"
"github.com/valyala/fasthttp"
"code.tokarch.uk/mainnika/nikita-tokarch-uk/pkg/ghost"
"code.tokarch.uk/mainnika/nikita-tokarch-uk/pkg/ghost/data"
"code.tokarch.uk/mainnika/nikita-tokarch-uk/pkg/ghost/params"
)
var _ ghost.Client = (*HTTPClient)(nil)
// Ghost content data URIs:
const (
ghostAPIPrefix = "/ghost/api/v3/"
ghostAPIGetPosts = ghostAPIPrefix + "content/posts/"
ghostAPIGetPostBySlug = ghostAPIPrefix + "content/posts/slug/%s/"
ghostAPIGetPageBySlug = ghostAPIPrefix + "content/pages/slug/%s/"
)
// HTTPClient implements the ghost http client
type HTTPClient struct {
QueryTimeout time.Duration
ContentKey string
Addr string
Secured bool
Headers map[string]string
client *fasthttp.HostClient
setupClientOnce sync.Once
}
// setupClient creates the default http client
func (g *HTTPClient) setupClient() {
g.client = &fasthttp.HostClient{
Addr: g.Addr,
IsTLS: g.Secured,
DisableHeaderNamesNormalizing: true,
DisablePathNormalizing: true,
}
}
// doQuery does the method and unmarshals the result into the easyjson Unmarshaler
func (g *HTTPClient) doQuery(path string, v easyjson.Unmarshaler, params params.Params) (err error) {
g.setupClientOnce.Do(g.setupClient)
req := fasthttp.AcquireRequest()
res := fasthttp.AcquireResponse()
defer func() {
fasthttp.ReleaseResponse(res)
fasthttp.ReleaseRequest(req)
}()
g.setupRequest(path, req)
g.applyParams(params, req)
err = g.client.DoTimeout(req, res, g.QueryTimeout)
if err != nil {
return
}
if res.StatusCode() != fasthttp.StatusOK {
return fmt.Errorf("non OK status code: %d", res.StatusCode())
}
resBytes := res.Body()
if resBytes == nil && v == nil {
return fmt.Errorf("nothing to unmarshal")
}
if resBytes == nil {
return
}
err = easyjson.Unmarshal(resBytes, v)
return
}
// setupRequest does the necessary initial configuration to the http request
func (g *HTTPClient) setupRequest(path string, req *fasthttp.Request) {
uri := req.URI()
scheme := "http"
if g.Secured {
scheme = "https"
}
uri.SetHost(g.Addr)
uri.SetPath(path)
uri.SetScheme(scheme)
uri.QueryArgs().Add("key", g.ContentKey)
for hKey, hValue := range g.Headers {
req.Header.Add(hKey, hValue)
}
}
// applyParams function additionally configure the http request using params
func (g *HTTPClient) applyParams(p params.Params, req *fasthttp.Request) (err error) {
uri := req.URI()
limit := p.Limit
if limit > 0 {
uri.QueryArgs().Add("limit", strconv.Itoa(limit))
}
page := p.Page
if page > 1 {
uri.QueryArgs().Add("page", strconv.Itoa(page))
}
return
}
// GetPageBySlug returns the only one page using slug filter
func (g *HTTPClient) GetPageBySlug(slug string, queryModifiers ...params.Modifier) (pages *data.Pages, err error) {
pages = &data.Pages{}
defaultParams := params.Params{}
method := fmt.Sprintf(ghostAPIGetPageBySlug, slug)
err = g.doQuery(method, pages, defaultParams)
if err != nil {
pages = nil
}
return
}
// GetPosts returns posts
func (g *HTTPClient) GetPosts(queryModifiers ...params.Modifier) (posts *data.Posts, err error) {
posts = &data.Posts{}
defaultParams := params.Params{}
combinedParams := params.Modifiers(queryModifiers).Apply(defaultParams)
err = g.doQuery(ghostAPIGetPosts, posts, combinedParams)
if err != nil {
posts = nil
}
return
}
// GetPostBySlug returns the only one post using slug filter
func (g *HTTPClient) GetPostBySlug(slug string, queryModifiers ...params.Modifier) (posts *data.Posts, err error) {
posts = &data.Posts{}
defaultParams := params.Params{}
combinedParams := params.Modifiers(queryModifiers).Apply(defaultParams)
method := fmt.Sprintf(ghostAPIGetPostBySlug, slug)
err = g.doQuery(method, posts, combinedParams)
if err != nil {
posts = nil
}
return
}