working commit

This commit is contained in:
2026-02-12 14:51:16 +02:00
parent a9526f73a2
commit e4d9a6d161
7 changed files with 281 additions and 93 deletions
+57 -57
View File
@@ -77,6 +77,63 @@ func (oper *Operator) CreateAccount(ctx context.Context, params *CreateAccountPa
return res, err return res, err
} }
type GetAccountParams struct {
Username string `json:"username"`
AccountID string `json:"accountId"`
}
type GetAccountResult struct {
Account *descr.AccountShort `json:"account"`
}
func (oper *Operator) GetAccount(ctx context.Context, params *GetAccountParams) (*GetAccountResult, error) {
var err error
res := &GetAccountResult{}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
}
accountShort := &descr.AccountShort{
Username: accountDescr.Username,
Disabled: accountDescr.Disabled,
CreatedAt: accountDescr.CreatedAt,
UpdatedAt: accountDescr.UpdatedAt,
Grants: make([]descr.GrantShort, 0),
}
grantDescrs, err := oper.mdb.ListGrantsByAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
for _, grantDescrs := range grantDescrs {
grantShorts := descr.GrantShort{
Operation: grantDescrs.Operation,
CreatedAt: grantDescrs.CreatedAt,
}
accountShort.Grants = append(accountShort.Grants, grantShorts)
}
res.Account = accountShort
return res, err
}
type UpdateAccountParams struct { type UpdateAccountParams struct {
Username string `json:"username"` Username string `json:"username"`
AccountID string `json:"accountId"` AccountID string `json:"accountId"`
@@ -216,60 +273,3 @@ func (oper *Operator) ListAccounts(ctx context.Context, params *ListAccountsPara
} }
return res, err return res, err
} }
type GetAccountParams struct {
Username string `json:"username"`
AccountID string `json:"accountId"`
}
type GetAccountResult struct {
Account *descr.AccountShort `json:"account"`
}
func (oper *Operator) GetAccount(ctx context.Context, params *GetAccountParams) (*GetAccountResult, error) {
var err error
res := &GetAccountResult{}
var accountDescr *descr.Account
var accountExists bool
switch {
case params.AccountID != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with ID %s dont exists", params.AccountID)
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = oper.mdb.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if !accountExists {
err := fmt.Errorf("Account with name %s dont exists", params.Username)
return res, err
}
}
accountShort := &descr.AccountShort{
Username: accountDescr.Username,
Disabled: accountDescr.Disabled,
CreatedAt: accountDescr.CreatedAt,
UpdatedAt: accountDescr.UpdatedAt,
Grants: make([]descr.GrantShort, 0),
}
grantDescrs, err := oper.mdb.ListGrantsByAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
for _, grantDescrs := range grantDescrs {
grantShorts := descr.GrantShort{
Operation: grantDescrs.Operation,
CreatedAt: grantDescrs.CreatedAt,
}
accountShort.Grants = append(accountShort.Grants, grantShorts)
}
res.Account = accountShort
return res, err
}
+10 -10
View File
@@ -99,17 +99,17 @@ func (svc *Service) Build() error {
svc.rout.Get(`/v2/{name}/tags/list`, svc.hand.GetTags) svc.rout.Get(`/v2/{name}/tags/list`, svc.hand.GetTags)
svc.rout.Get(`/v2/{name}/referrers/{digest}`, svc.hand.GetReferer) svc.rout.Get(`/v2/{name}/referrers/{digest}`, svc.hand.GetReferer)
svc.rout.Post(`/v3/account/create`, svc.hand.CreateAccount) svc.rout.Post(`/v3/api/account/create`, svc.hand.CreateAccount)
svc.rout.Post(`/v3/account/get`, svc.hand.GetAccount) svc.rout.Post(`/v3/api/account/get`, svc.hand.GetAccount)
svc.rout.Post(`/v3/account/update`, svc.hand.UpdateAccount) svc.rout.Post(`/v3/api/account/update`, svc.hand.UpdateAccount)
svc.rout.Post(`/v3/account/delete`, svc.hand.DeleteAccount) svc.rout.Post(`/v3/api/account/delete`, svc.hand.DeleteAccount)
svc.rout.Post(`/v3/accounts/list`, svc.hand.ListAccounts) svc.rout.Post(`/v3/api/accounts/list`, svc.hand.ListAccounts)
svc.rout.Post(`/v3/grant/create`, svc.hand.CreateGrant) svc.rout.Post(`/v3/api/grant/create`, svc.hand.CreateGrant)
svc.rout.Post(`/v3/grant/get`, svc.hand.GetGrant) svc.rout.Post(`/v3/api/grant/get`, svc.hand.GetGrant)
svc.rout.Post(`/v3/grant/update`, svc.hand.UpdateGrant) svc.rout.Post(`/v3/api/grant/update`, svc.hand.UpdateGrant)
svc.rout.Post(`/v3/grant/delete`, svc.hand.DeleteGrant) svc.rout.Post(`/v3/api/grant/delete`, svc.hand.DeleteGrant)
svc.rout.Post(`/v3/grants/list`, svc.hand.ListGrants) svc.rout.Post(`/v3/api/grants/list`, svc.hand.ListGrants)
svc.rout.NotFound(svc.hand.NotFound) svc.rout.NotFound(svc.hand.NotFound)
+49 -14
View File
@@ -15,16 +15,18 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"mstore/app/descr"
"mstore/app/handler" "mstore/app/handler"
"mstore/app/operator" "mstore/app/operator"
) )
func (cli *Client) CreateAccount(ctx context.Context, hosturi, username, password string) error { func (cli *Client) CreateAccount(ctx context.Context, hosturi, username, password string) (string, error) {
var err error var err error
var res string
apiuri, err := url.JoinPath(hosturi, "/v3/api/account/create") apiuri, err := setApiPath(hosturi, "/v3/api/account/create")
if err != nil { if err != nil {
return err return res, err
} }
operParams := operator.CreateAccountParams{ operParams := operator.CreateAccountParams{
Username: username, Username: username,
@@ -32,26 +34,60 @@ func (cli *Client) CreateAccount(ctx context.Context, hosturi, username, passwor
} }
paramsJson, err := json.Marshal(operParams) paramsJson, err := json.Marshal(operParams)
if err != nil { if err != nil {
return err return res, err
} }
respBytes, err := doHTTPCall(ctx, apiuri, paramsJson) respBytes, err := doHTTPCall(ctx, apiuri, paramsJson)
if err != nil { if err != nil {
return err return res, err
} }
operRes := handler.NewResponse[operator.CreateAccountResult]() operRes := handler.NewResponse[operator.CreateAccountResult]()
err = json.Unmarshal(respBytes, operRes) err = json.Unmarshal(respBytes, operRes)
if err != nil { if err != nil {
return err return res, err
} }
if !operRes.Error { if operRes.Error {
err = fmt.Errorf("%s", operRes.Message) err = fmt.Errorf("%s", operRes.Message)
return err return res, err
} }
return err res = operRes.Result.AccountID
return res, err
} }
func (cli *Client) GetAccount(ctx context.Context, hosturi, id, username string) error { func (cli *Client) GetAccountByID(ctx context.Context, hosturi, id string) (descr.AccountShort, error) {
var err error
var res descr.AccountShort
apipath, err := setApiPath(hosturi, "/v3/api/account/get")
if err != nil {
return res, err
}
operParams := operator.GetAccountParams{
AccountID: id,
}
paramsJson, err := json.Marshal(operParams)
if err != nil {
return res, err
}
respBytes, err := doHTTPCall(ctx, apipath, paramsJson)
if err != nil {
return res, err
}
operRes := handler.NewResponse[operator.GetAccountResult]()
err = json.Unmarshal(respBytes, operRes)
if err != nil {
return res, err
}
if operRes.Error {
err = fmt.Errorf("%s", operRes.Message)
return res, err
}
return res, err
}
func (cli *Client) GetAccountByName(ctx context.Context, hosturi, username string) error {
var err error var err error
apipath, err := url.JoinPath(hosturi, "/v3/api/account/get") apipath, err := url.JoinPath(hosturi, "/v3/api/account/get")
@@ -60,7 +96,6 @@ func (cli *Client) GetAccount(ctx context.Context, hosturi, id, username string)
} }
operParams := operator.GetAccountParams{ operParams := operator.GetAccountParams{
Username: username, Username: username,
AccountID: id,
} }
paramsJson, err := json.Marshal(operParams) paramsJson, err := json.Marshal(operParams)
if err != nil { if err != nil {
@@ -76,7 +111,7 @@ func (cli *Client) GetAccount(ctx context.Context, hosturi, id, username string)
if err != nil { if err != nil {
return err return err
} }
if !operRes.Error { if operRes.Error {
err = fmt.Errorf("%s", operRes.Message) err = fmt.Errorf("%s", operRes.Message)
return err return err
} }
@@ -109,7 +144,7 @@ func (cli *Client) UpdateAccount(ctx context.Context, hosturi, id, username, new
if err != nil { if err != nil {
return err return err
} }
if !operRes.Error { if operRes.Error {
err = fmt.Errorf("%s", operRes.Message) err = fmt.Errorf("%s", operRes.Message)
return err return err
} }
@@ -141,7 +176,7 @@ func (cli *Client) DeleteAccount(ctx context.Context, hosturi, id, username stri
if err != nil { if err != nil {
return err return err
} }
if !operRes.Error { if operRes.Error {
err = fmt.Errorf("%s", operRes.Message) err = fmt.Errorf("%s", operRes.Message)
return err return err
} }
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package client
import (
"context"
"fmt"
//"math/rand"
//"os"
//"path/filepath"
"sync"
"testing"
"time"
"mstore/app/server"
"github.com/stretchr/testify/require"
)
func TestAccountLife(t *testing.T) {
var srvport int64 = 10250
srvdir := t.TempDir()
srvaddr := fmt.Sprintf("foouser:foopass@127.0.0.1:%d", srvport)
srv, err := server.NewServer()
require.NoError(t, err)
{
err = srv.Configure()
require.NoError(t, err)
srv.SetDatadir(srvdir)
srv.SetLogdir(srvdir)
srv.SetRundir(srvdir)
srv.SetPort(srvport)
err = srv.Build()
require.NoError(t, err)
var svcWG sync.WaitGroup
errPipe := make(chan error, 5)
startFunc := func() {
err := srv.Service().Run()
errPipe <- err
svcWG.Done()
}
stopFunc := func() {
srv.Service().Stop()
svcWG.Wait()
err = <-errPipe
require.NoError(t, err)
}
defer stopFunc()
svcWG.Add(1)
go startFunc()
time.Sleep(1 * time.Second)
}
{
// ServiceHello
fmt.Printf("=== ServiceHello ===\n")
cli := NewClient()
ctx := context.Background()
helloRes, err := cli.ServiceHello(ctx, srvaddr+"/hello", 1*time.Second)
require.NoError(t, err)
require.True(t, helloRes)
}
username := "testuser"
password := "testpass"
var accountID string
{
// CreateAccount
fmt.Printf("=== CreateAccount ===\n")
cli := NewClient()
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
accountID, err = cli.CreateAccount(ctx, srvaddr, username, password)
require.NoError(t, err)
}
{
// GetAccount
fmt.Printf("=== GetAccount ===\n")
cli := NewClient()
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
_, err = cli.GetAccountByID(ctx, srvaddr, accountID)
require.NoError(t, err)
}
/*
{
// ListAccounts
fmt.Printf("=== ListAccounts ===\n")
cli := NewClient()
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
files, err := cli.ListAccounts(ctx, srvaddr+"/")
require.NoError(t, err)
require.NotZero(t, len(files))
}
{
// DeleteAccount
fmt.Printf("=== DeleteAccount ===\n")
cli := NewClient()
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
err = cli.DeleteAccount(ctx, srvaddr+"/foo.bin")
require.NoError(t, err)
}
{
// !AccountExists
fmt.Printf("=== AccountExists ===\n")
cli := NewClient()
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
exists, _, err := cli.AccountInfo(ctx, srvaddr+"/foo.bin")
require.NoError(t, err)
require.False(t, exists)
}
*/
}
@@ -24,7 +24,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestFileLife(t *testing.T) { func xxxTestFileLife(t *testing.T) {
var srvport int64 = 10250 var srvport int64 = 10250
srvdir := t.TempDir() srvdir := t.TempDir()
srvaddr := fmt.Sprintf("testuser:testpass@127.0.0.1:%d", srvport) srvaddr := fmt.Sprintf("testuser:testpass@127.0.0.1:%d", srvport)
@@ -74,8 +74,6 @@ func TestFileLife(t *testing.T) {
require.True(t, helloRes) require.True(t, helloRes)
} }
return
filesize := 32 filesize := 32
{ {
// PutFile // PutFile
@@ -98,8 +96,8 @@ func TestFileLife(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
{ {
// FileExists // FileInfo
fmt.Printf("=== FileExists ===\n") fmt.Printf("=== FileInfo ===\n")
cli := NewClient() cli := NewClient()
ctx := context.Background() ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second) ctx, _ = context.WithTimeout(ctx, 1*time.Second)
@@ -146,8 +144,8 @@ func TestFileLife(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
{ {
// !FileExists // !FileInfo
fmt.Printf("=== FileExists ===\n") fmt.Printf("=== FileInfo ===\n")
cli := NewClient() cli := NewClient()
ctx := context.Background() ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 1*time.Second) ctx, _ = context.WithTimeout(ctx, 1*time.Second)
+21 -4
View File
@@ -13,17 +13,34 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
//"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
//"net/url" "net/url"
"strings"
//"mstore/app/handler"
//"mstore/app/operator"
"mstore/pkg/auxhttp" "mstore/pkg/auxhttp"
) )
func setApiPath(base, apipath string) (string, error) {
var res string
if !strings.Contains(base, "://") {
base = "https://" + base
}
uri, err := url.Parse(base)
if err != nil {
return res, err
}
uri.Path = "/"
uri.Path, err = url.JoinPath(uri.Path, apipath)
if err != nil {
return res, err
}
res = uri.String()
return res, nil
}
func doHTTPCall(ctx context.Context, apiuri string, reqBytes []byte) ([]byte, error) { func doHTTPCall(ctx context.Context, apiuri string, reqBytes []byte) ([]byte, error) {
var err error var err error
respBytes := make([]byte, 0) respBytes := make([]byte, 0)