From 645155ad109d7d5b9a124b4f64c9d0f1a2b393ab Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Fri, 5 Jan 2024 11:26:43 -0600 Subject: [PATCH] api endpoint for currencies --- backend/app/api/handlers/v1/controller.go | 15 +++- backend/app/api/main.go | 26 ++++++ backend/app/api/routes.go | 2 + backend/app/api/static/docs/docs.go | 81 ++++++++++++++----- backend/app/api/static/docs/swagger.json | 81 ++++++++++++++----- backend/app/api/static/docs/swagger.yaml | 55 ++++++++++--- backend/go.sum | 8 ++ .../internal/core/currencies/currencies.go | 2 +- backend/internal/core/services/all.go | 18 +++++ backend/internal/sys/config/conf.go | 17 ++-- docs/docs/api/openapi-2.0.json | 81 ++++++++++++++----- frontend/lib/api/types/data-contracts.ts | 21 +++-- 12 files changed, 322 insertions(+), 85 deletions(-) diff --git a/backend/app/api/handlers/v1/controller.go b/backend/app/api/handlers/v1/controller.go index 36df742..ab2ff96 100644 --- a/backend/app/api/handlers/v1/controller.go +++ b/backend/app/api/handlers/v1/controller.go @@ -112,7 +112,7 @@ func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, bus *event // @Summary Application Info // @Tags Base // @Produce json -// @Success 200 {object} ApiSummary +// @Success 200 {object} APISummary // @Router /v1/status [GET] func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) error { @@ -127,6 +127,19 @@ func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.Hand } } +// HandleCurrency godoc +// +// @Summary Currency +// @Tags Base +// @Produce json +// @Success 200 {object} currencies.Currency +// @Router /v1/currency [GET] +func (ctrl *V1Controller) HandleCurrency() errchain.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + return server.JSON(w, http.StatusOK, ctrl.svc.Currencies.Slice()) + } +} + func (ctrl *V1Controller) HandleCacheWS() errchain.HandlerFunc { m := melody.New() diff --git a/backend/app/api/main.go b/backend/app/api/main.go index 2c27017..07b8e8d 100644 --- a/backend/app/api/main.go +++ b/backend/app/api/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "fmt" "net/http" @@ -13,6 +14,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + "github.com/hay-kot/homebox/backend/internal/core/currencies" "github.com/hay-kot/homebox/backend/internal/core/services" "github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus" "github.com/hay-kot/homebox/backend/internal/data/ent" @@ -126,12 +128,36 @@ func run(cfg *config.Config) error { return err } + collectFuncs := []currencies.CollectorFunc{ + currencies.CollectDefaults(), + } + + if cfg.CurrencyConfig != "" { + content, err := os.ReadFile(cfg.CurrencyConfig) + if err != nil { + log.Fatal(). + Err(err). + Str("path", cfg.CurrencyConfig). + Msg("failed to read currency config file") + } + + collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content))) + } + + currencies, err := currencies.CollectionCurrencies(collectFuncs...) + if err != nil { + log.Fatal(). + Err(err). + Msg("failed to collect currencies") + } + app.bus = eventbus.New() app.db = c app.repos = repo.New(c, app.bus, cfg.Storage.Data) app.services = services.New( app.repos, services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID), + services.WithCurrencies(currencies), ) // ========================================================================= diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 62a260b..7ffc313 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -64,6 +64,8 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR BuildTime: buildTime, }))) + r.Get(v1Base("/currencies"), chain.ToHandlerFunc(v1Ctrl.HandleCurrency())) + providers := []v1.AuthProvider{ providers.NewLocalProvider(a.services.User), } diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index bbbe376..5be95b3 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -150,6 +150,25 @@ const docTemplate = `{ } } }, + "/v1/currency": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "Currency", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/currencies.Currency" + } + } + } + } + }, "/v1/groups": { "get": { "security": [ @@ -410,6 +429,16 @@ const docTemplate = `{ "description": "location Ids", "name": "locations", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "parent Ids", + "name": "parentIds", + "in": "query" } ], "responses": { @@ -1574,7 +1603,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1.ApiSummary" + "$ref": "#/definitions/v1.APISummary" } } } @@ -1645,6 +1674,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/v1.LoginForm" } + }, + { + "type": "string", + "description": "auth provider", + "name": "provider", + "in": "query" } ], "responses": { @@ -1823,6 +1858,23 @@ const docTemplate = `{ } }, "definitions": { + "currencies.Currency": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "local": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + } + }, "repo.DocumentOut": { "type": "object", "properties": { @@ -1998,12 +2050,6 @@ const docTemplate = `{ "$ref": "#/definitions/repo.ItemAttachment" } }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/repo.ItemSummary" - } - }, "createdAt": { "type": "string" }, @@ -2181,8 +2227,7 @@ const docTemplate = `{ "type": "boolean" }, "assetId": { - "type": "string", - "example": "0" + "type": "integer" }, "description": { "type": "string" @@ -2736,15 +2781,7 @@ const docTemplate = `{ } } }, - "v1.ActionAmountResult": { - "type": "object", - "properties": { - "completed": { - "type": "integer" - } - } - }, - "v1.ApiSummary": { + "v1.APISummary": { "type": "object", "properties": { "allowRegistration": { @@ -2773,6 +2810,14 @@ const docTemplate = `{ } } }, + "v1.ActionAmountResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, "v1.Build": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index e2d98fe..dbbdba7 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -143,6 +143,25 @@ } } }, + "/v1/currency": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "Currency", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/currencies.Currency" + } + } + } + } + }, "/v1/groups": { "get": { "security": [ @@ -403,6 +422,16 @@ "description": "location Ids", "name": "locations", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "parent Ids", + "name": "parentIds", + "in": "query" } ], "responses": { @@ -1567,7 +1596,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1.ApiSummary" + "$ref": "#/definitions/v1.APISummary" } } } @@ -1638,6 +1667,12 @@ "schema": { "$ref": "#/definitions/v1.LoginForm" } + }, + { + "type": "string", + "description": "auth provider", + "name": "provider", + "in": "query" } ], "responses": { @@ -1816,6 +1851,23 @@ } }, "definitions": { + "currencies.Currency": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "local": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + } + }, "repo.DocumentOut": { "type": "object", "properties": { @@ -1991,12 +2043,6 @@ "$ref": "#/definitions/repo.ItemAttachment" } }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/repo.ItemSummary" - } - }, "createdAt": { "type": "string" }, @@ -2174,8 +2220,7 @@ "type": "boolean" }, "assetId": { - "type": "string", - "example": "0" + "type": "integer" }, "description": { "type": "string" @@ -2729,15 +2774,7 @@ } } }, - "v1.ActionAmountResult": { - "type": "object", - "properties": { - "completed": { - "type": "integer" - } - } - }, - "v1.ApiSummary": { + "v1.APISummary": { "type": "object", "properties": { "allowRegistration": { @@ -2766,6 +2803,14 @@ } } }, + "v1.ActionAmountResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, "v1.Build": { "type": "object", "properties": { diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index 2f16660..53a3b70 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -1,5 +1,16 @@ basePath: /api definitions: + currencies.Currency: + properties: + code: + type: string + local: + type: string + name: + type: string + symbol: + type: string + type: object repo.DocumentOut: properties: id: @@ -116,10 +127,6 @@ definitions: items: $ref: '#/definitions/repo.ItemAttachment' type: array - children: - items: - $ref: '#/definitions/repo.ItemSummary' - type: array createdAt: type: string description: @@ -238,8 +245,7 @@ definitions: archived: type: boolean assetId: - example: "0" - type: string + type: integer description: type: string fields: @@ -608,12 +614,7 @@ definitions: token: type: string type: object - v1.ActionAmountResult: - properties: - completed: - type: integer - type: object - v1.ApiSummary: + v1.APISummary: properties: allowRegistration: type: boolean @@ -632,6 +633,11 @@ definitions: type: string type: array type: object + v1.ActionAmountResult: + properties: + completed: + type: integer + type: object v1.Build: properties: buildTime: @@ -789,6 +795,18 @@ paths: summary: Get Item by Asset ID tags: - Items + /v1/currency: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/currencies.Currency' + summary: Currency + tags: + - Base /v1/groups: get: produces: @@ -942,6 +960,13 @@ paths: type: string name: locations type: array + - collectionFormat: multi + description: parent Ids + in: query + items: + type: string + name: parentIds + type: array produces: - application/json responses: @@ -1656,7 +1681,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/v1.ApiSummary' + $ref: '#/definitions/v1.APISummary' summary: Application Info tags: - Base @@ -1699,6 +1724,10 @@ paths: required: true schema: $ref: '#/definitions/v1.LoginForm' + - description: auth provider + in: query + name: provider + type: string produces: - application/json responses: diff --git a/backend/go.sum b/backend/go.sum index fc7cdd2..4cb487e 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -115,6 +115,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -122,6 +124,8 @@ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTS github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olahol/melody v1.1.4 h1:RQHfKZkQmDxI0+SLZRNBCn4LiXdqxLKRGSkT8Dyoe/E= github.com/olahol/melody v1.1.4/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= @@ -139,6 +143,10 @@ github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/backend/internal/core/currencies/currencies.go b/backend/internal/core/currencies/currencies.go index fa255ca..28073a9 100644 --- a/backend/internal/core/currencies/currencies.go +++ b/backend/internal/core/currencies/currencies.go @@ -68,7 +68,7 @@ func NewCurrencyService(currencies []Currency) *CurrencyRegistry { } } -func (cs *CurrencyRegistry) GetCurrencies() []Currency { +func (cs *CurrencyRegistry) Slice() []Currency { cs.mu.RLock() defer cs.mu.RUnlock() out := make([]Currency, 0, len(cs.registry)) diff --git a/backend/internal/core/services/all.go b/backend/internal/core/services/all.go index 9b5e127..3c03a4e 100644 --- a/backend/internal/core/services/all.go +++ b/backend/internal/core/services/all.go @@ -2,6 +2,7 @@ package services import ( + "github.com/hay-kot/homebox/backend/internal/core/currencies" "github.com/hay-kot/homebox/backend/internal/data/repo" ) @@ -10,12 +11,14 @@ type AllServices struct { Group *GroupService Items *ItemService BackgroundService *BackgroundService + Currencies *currencies.CurrencyRegistry } type OptionsFunc func(*options) type options struct { autoIncrementAssetID bool + currencies []currencies.Currency } func WithAutoIncrementAssetID(v bool) func(*options) { @@ -24,13 +27,27 @@ func WithAutoIncrementAssetID(v bool) func(*options) { } } +func WithCurrencies(v []currencies.Currency) func(*options) { + return func(o *options) { + o.currencies = v + } +} + func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices { if repos == nil { panic("repos cannot be nil") } + defaultCurrencies, err := currencies.CollectionCurrencies( + currencies.CollectDefaults(), + ) + if err != nil { + panic("failed to collect default currencies") + } + options := &options{ autoIncrementAssetID: true, + currencies: defaultCurrencies, } for _, opt := range opts { @@ -45,5 +62,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices { autoIncrementAssetID: options.autoIncrementAssetID, }, BackgroundService: &BackgroundService{repos}, + Currencies: currencies.NewCurrencyService(options.currencies), } } diff --git a/backend/internal/sys/config/conf.go b/backend/internal/sys/config/conf.go index e2d04d4..2602bc7 100644 --- a/backend/internal/sys/config/conf.go +++ b/backend/internal/sys/config/conf.go @@ -17,14 +17,15 @@ const ( type Config struct { conf.Version - Mode string `yaml:"mode" conf:"default:development"` // development or production - Web WebConfig `yaml:"web"` - Storage Storage `yaml:"storage"` - Log LoggerConf `yaml:"logger"` - Mailer MailerConf `yaml:"mailer"` - Demo bool `yaml:"demo"` - Debug DebugConf `yaml:"debug"` - Options Options `yaml:"options"` + Mode string `yaml:"mode" conf:"default:development"` // development or production + CurrencyConfig string `yaml:"currencies"` + Web WebConfig `yaml:"web"` + Storage Storage `yaml:"storage"` + Log LoggerConf `yaml:"logger"` + Mailer MailerConf `yaml:"mailer"` + Demo bool `yaml:"demo"` + Debug DebugConf `yaml:"debug"` + Options Options `yaml:"options"` } type Options struct { diff --git a/docs/docs/api/openapi-2.0.json b/docs/docs/api/openapi-2.0.json index e2d98fe..dbbdba7 100644 --- a/docs/docs/api/openapi-2.0.json +++ b/docs/docs/api/openapi-2.0.json @@ -143,6 +143,25 @@ } } }, + "/v1/currency": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Base" + ], + "summary": "Currency", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/currencies.Currency" + } + } + } + } + }, "/v1/groups": { "get": { "security": [ @@ -403,6 +422,16 @@ "description": "location Ids", "name": "locations", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "parent Ids", + "name": "parentIds", + "in": "query" } ], "responses": { @@ -1567,7 +1596,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/v1.ApiSummary" + "$ref": "#/definitions/v1.APISummary" } } } @@ -1638,6 +1667,12 @@ "schema": { "$ref": "#/definitions/v1.LoginForm" } + }, + { + "type": "string", + "description": "auth provider", + "name": "provider", + "in": "query" } ], "responses": { @@ -1816,6 +1851,23 @@ } }, "definitions": { + "currencies.Currency": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "local": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + } + }, "repo.DocumentOut": { "type": "object", "properties": { @@ -1991,12 +2043,6 @@ "$ref": "#/definitions/repo.ItemAttachment" } }, - "children": { - "type": "array", - "items": { - "$ref": "#/definitions/repo.ItemSummary" - } - }, "createdAt": { "type": "string" }, @@ -2174,8 +2220,7 @@ "type": "boolean" }, "assetId": { - "type": "string", - "example": "0" + "type": "integer" }, "description": { "type": "string" @@ -2729,15 +2774,7 @@ } } }, - "v1.ActionAmountResult": { - "type": "object", - "properties": { - "completed": { - "type": "integer" - } - } - }, - "v1.ApiSummary": { + "v1.APISummary": { "type": "object", "properties": { "allowRegistration": { @@ -2766,6 +2803,14 @@ } } }, + "v1.ActionAmountResult": { + "type": "object", + "properties": { + "completed": { + "type": "integer" + } + } + }, "v1.Build": { "type": "object", "properties": { diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index e5457bb..c264d03 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -10,6 +10,13 @@ * --------------------------------------------------------------- */ +export interface CurrenciesCurrency { + code: string; + local: string; + name: string; + symbol: string; +} + export interface DocumentOut { id: string; path: string; @@ -81,7 +88,6 @@ export interface ItemOut { /** @example "0" */ assetId: string; attachments: ItemAttachment[]; - children: ItemSummary[]; createdAt: Date | string; description: string; fields: ItemField[]; @@ -141,8 +147,7 @@ export interface ItemSummary { export interface ItemUpdate { archived: boolean; - /** @example "0" */ - assetId: string; + assetId: number; description: string; fields: ItemField[]; id: string; @@ -364,11 +369,7 @@ export interface UserRegistration { token: string; } -export interface ActionAmountResult { - completed: number; -} - -export interface ApiSummary { +export interface APISummary { allowRegistration: boolean; build: Build; demo: boolean; @@ -378,6 +379,10 @@ export interface ApiSummary { versions: string[]; } +export interface ActionAmountResult { + completed: number; +} + export interface Build { buildTime: string; commit: string;