Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions v2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Add missing endpoints from cluster to v2
- Add missing endpoints from security to v2
- Add missing endpoints from authentication to v2
- Add missing endpoints from general-request-handling to v2

## [2.1.5](https://github.com/arangodb/go-driver/tree/v2.1.5) (2025-08-31)
- Add tasks endpoints to v2
Expand Down
9 changes: 7 additions & 2 deletions v2/arangodb/client_admin_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,19 @@ func (c *clientAdmin) GetSystemTime(ctx context.Context, dbName string) (float64

// GetServerStatus returns status information about the server
func (c *clientAdmin) GetServerStatus(ctx context.Context, dbName string) (ServerStatusResponse, error) {
url := connection.NewUrl("_db", url.PathEscape(dbName), "_admin", "status")
var endPoint string
if dbName == "" {
endPoint = connection.NewUrl("_admin", "status")
} else {
endPoint = connection.NewUrl("_db", url.PathEscape(dbName), "_admin", "status")
}

var response struct {
shared.ResponseStruct `json:",inline"`
ServerStatusResponse `json:",inline"`
}

resp, err := connection.CallGet(ctx, c.client.connection, url, &response)
resp, err := connection.CallGet(ctx, c.client.connection, endPoint, &response)
if err != nil {
return ServerStatusResponse{}, errors.WithStack(err)
}
Expand Down
4 changes: 4 additions & 0 deletions v2/arangodb/client_server_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type ClientServerInfo interface {
// ServerID Gets the ID of this server in the cluster.
// An error is returned when calling this to a server that is not part of a cluster.
ServerID(ctx context.Context) (string, error)

// HandleAdminVersion retrieves the ArangoDB server version information
// This endpoint is an alias for `GET /_api/version`.
HandleAdminVersion(ctx context.Context, opts *GetVersionOptions) (VersionInfo, error)
}

// VersionInfo describes the version of a database server.
Expand Down
29 changes: 29 additions & 0 deletions v2/arangodb/client_server_info_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,32 @@ func (o *GetVersionOptions) modifyRequest(r connection.Request) error {
}
return nil
}

// HandleAdminVersion retrieves the ArangoDB server version information
// This endpoint is an alias for `GET /_api/version`.
func (c clientServerInfo) HandleAdminVersion(ctx context.Context, opts *GetVersionOptions) (VersionInfo, error) {
url := connection.NewUrl("_admin", "version")

var response struct {
shared.ResponseStruct `json:",inline"`
VersionInfo
}

// Always provide a non-nil modifier
modifier := func(r connection.Request) error { return nil }
if opts != nil {
modifier = opts.modifyRequest
}

resp, err := connection.CallGet(ctx, c.client.connection, url, &response, modifier)
if err != nil {
return VersionInfo{}, errors.WithStack(err)
}

switch code := resp.Code(); code {
case http.StatusOK:
return response.VersionInfo, nil
default:
return VersionInfo{}, response.AsArangoErrorWithCode(code)
}
}
49 changes: 42 additions & 7 deletions v2/tests/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,26 @@ func Test_GetSystemTime(t *testing.T) {

func Test_GetServerStatus(t *testing.T) {
Wrap(t, func(t *testing.T, client arangodb.Client) {
withContextT(t, time.Minute, func(ctx context.Context, t testing.TB) {
db, err := client.GetDatabase(context.Background(), "_system", nil)
require.NoError(t, err)
require.NotEmpty(t, db)
withContextT(t, time.Minute, func(ctx context.Context, tb testing.TB) {
t.Run("WithoutDBName", func(t *testing.T) {
resp, err := client.GetServerStatus(context.Background(), "")
require.NoError(t, err)
require.NotEmpty(t, resp)
})
t.Run("WithDBName", func(t *testing.T) {
db, err := client.GetDatabase(context.Background(), "_system", nil)
require.NoError(t, err)
require.NotEmpty(t, db)

resp, err := client.GetServerStatus(context.Background(), db.Name())
require.NoError(t, err)
require.NotEmpty(t, resp)
resp, err := client.GetServerStatus(context.Background(), db.Name())
require.NoError(t, err)
require.NotEmpty(t, resp)
})
t.Run("InvalidDBName", func(t *testing.T) {
_, err := client.GetServerStatus(context.Background(), "invalid/db/name")
t.Logf("error :%v\n", err)
require.Error(t, err)
})
})
})
}
Expand Down Expand Up @@ -555,3 +567,26 @@ func validateJWTSecretsResponse(t testing.TB, resp arangodb.JWTSecretsResult, op

t.Logf("%s JWT secrets validation completed successfully with %d total secrets", operation, len(resp.Passive)+1)
}
func Test_HandleAdminVersion(t *testing.T) {
Wrap(t, func(t *testing.T, client arangodb.Client) {
withContextT(t, time.Minute, func(ctx context.Context, tb testing.TB) {
t.Run("With Options", func(t *testing.T) {
resp, err := client.HandleAdminVersion(context.Background(), &arangodb.GetVersionOptions{
Details: utils.NewType(true),
})
require.NoError(t, err)
require.NotEmpty(t, resp.Version)
require.NotEmpty(t, resp.Server)
require.NotEmpty(t, resp.License)
require.NotEmpty(t, resp.Details)
})
t.Run("Without options", func(t *testing.T) {
resp, err := client.HandleAdminVersion(context.Background(), nil)
require.NoError(t, err)
require.NotEmpty(t, resp.Version)
require.NotEmpty(t, resp.Server)
require.NotEmpty(t, resp.License)
})
})
})
}
63 changes: 51 additions & 12 deletions v2/tests/client_access_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,39 @@ func Test_AccessTokens(t *testing.T) {
user := "root"

t.Run("Create Access Token With All valid data", func(t *testing.T) {
tokenName := fmt.Sprintf("Token-%d-%d", time.Now().UnixNano(), rand.Int())
t.Logf("Create Access Token With All valid data - Creating token with name: %s\n", tokenName)
req := arangodb.AccessTokenRequest{
Name: utils.NewType(tokenName),
ValidUntil: utils.NewType(expiresAt),
}
var err error
resp, err := client.CreateAccessToken(ctx, &user, req)
maxRetries := 3

for i := 0; i < maxRetries; i++ {
tokenName := fmt.Sprintf("Token-%d-%d-%d", time.Now().UnixNano(), rand.Int(), i)
cleanupToken(ctx, client, user, tokenName) // optional, safe to call repeatedly

req := arangodb.AccessTokenRequest{
Name: utils.NewType(tokenName),
ValidUntil: utils.NewType(expiresAt),
}

resp, err := client.CreateAccessToken(ctx, &user, req)
if err == nil {
tokenResp = &resp
break // success
}

// if conflict, retry; else fail immediately
var arangoErr shared.ArangoError
if errors.As(err, &arangoErr) && arangoErr.Code == 409 {
t.Logf("Conflict detected, retrying token creation... attempt %d\n", i+1)
continue
} else {
break
}
}

require.NoError(t, err)
require.NotNil(t, resp)
tokenResp = &resp
require.NotNil(t, tokenResp)
require.NotNil(t, tokenResp.Id)
require.NotNil(t, tokenResp.Token)
require.NotNil(t, tokenResp.Fingerprint)
require.Equal(t, tokenName, *tokenResp.Name)
require.Equal(t, true, *tokenResp.Active)
require.Equal(t, expiresAt, *tokenResp.ValidUntil)
})
Expand Down Expand Up @@ -86,7 +104,7 @@ func Test_AccessTokens(t *testing.T) {
})

t.Run("Client try to create duplicate access token name", func(t *testing.T) {
if tokenResp.Name == nil {
if tokenResp == nil || tokenResp.Name == nil {
t.Skip("Skipping delete test because token creation failed")
}
t.Logf("Client try to create duplicate access token name - token name: %s\n", *tokenResp.Name)
Expand All @@ -106,7 +124,7 @@ func Test_AccessTokens(t *testing.T) {
})

t.Run("Delete Access Token", func(t *testing.T) {
if tokenResp.Id == nil {
if tokenResp == nil || tokenResp.Id == nil {
t.Skip("Skipping delete test because token creation failed")
}
err := client.DeleteAccessToken(ctx, &user, tokenResp.Id)
Expand Down Expand Up @@ -174,3 +192,24 @@ func Test_AccessTokens(t *testing.T) {
})
})
}

// Cleanup tokens with the same name
func cleanupToken(ctx context.Context, client arangodb.Client, user string, tokenName string) {
tokens, err := client.GetAllAccessToken(ctx, &user)
if err != nil {
// log and continue
fmt.Printf("Failed to list tokens for cleanup: %v\n", err)
return
}

for _, token := range tokens.Tokens {
if token.Name != nil && *token.Name == tokenName {
if token.Id != nil {
err := client.DeleteAccessToken(ctx, &user, token.Id)
if err != nil {
fmt.Printf("Failed to delete token %s: %v\n", *token.Name, err)
}
}
}
}
}