diff --git a/v2/arangodb/client.go b/v2/arangodb/client.go index 21a48270..30d00246 100644 --- a/v2/arangodb/client.go +++ b/v2/arangodb/client.go @@ -20,7 +20,9 @@ package arangodb -import "github.com/arangodb/go-driver/v2/connection" +import ( + "github.com/arangodb/go-driver/v2/connection" +) type Client interface { // Connection returns current Driver Connection @@ -33,4 +35,5 @@ type Client interface { ClientServerInfo ClientAdmin ClientAsyncJob + ClientFoxx } diff --git a/v2/arangodb/client_foxx.go b/v2/arangodb/client_foxx.go new file mode 100644 index 00000000..87561f86 --- /dev/null +++ b/v2/arangodb/client_foxx.go @@ -0,0 +1,79 @@ +// +// DISCLAIMER +// +// Copyright 2025 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany + +package arangodb + +import ( + "context" + "strconv" + + "github.com/arangodb/go-driver/v2/connection" +) + +type ClientFoxx interface { + ClientFoxxService + //ClientFoxxDependencies +} + +type ClientFoxxService interface { + // InstallFoxxService installs a new service at a given mount path. + InstallFoxxService(ctx context.Context, dbName string, zipFile string, options *FoxxCreateOptions) error + // UninstallFoxxService uninstalls service at a given mount path. + UninstallFoxxService(ctx context.Context, dbName string, options *FoxxDeleteOptions) error +} + +type FoxxCreateOptions struct { + Mount *string +} + +type FoxxDeleteOptions struct { + Mount *string + Teardown *bool +} + +// ImportDocumentRequest holds Query parameters for /import. +type InstallFoxxServiceRequest struct { + FoxxCreateOptions `json:",inline"` +} + +type UninstallFoxxServiceRequest struct { + FoxxDeleteOptions `json:",inline"` +} + +func (c *InstallFoxxServiceRequest) modifyRequest(r connection.Request) error { + if c == nil { + return nil + } + + r.AddHeader(connection.ContentType, "application/zip") + r.AddQuery("mount", *c.Mount) + + return nil +} + +func (c *UninstallFoxxServiceRequest) modifyRequest(r connection.Request) error { + if c == nil { + return nil + } + + r.AddQuery("mount", *c.Mount) + r.AddQuery("teardown", strconv.FormatBool(*c.Teardown)) + return nil + +} diff --git a/v2/arangodb/client_foxx_impl.go b/v2/arangodb/client_foxx_impl.go new file mode 100644 index 00000000..668d1aa4 --- /dev/null +++ b/v2/arangodb/client_foxx_impl.go @@ -0,0 +1,100 @@ +// +// DISCLAIMER +// +// Copyright 2025 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany + +package arangodb + +import ( + "context" + "net/http" + "os" + + "github.com/arangodb/go-driver/v2/arangodb/shared" + "github.com/arangodb/go-driver/v2/connection" + "github.com/pkg/errors" +) + +var _ ClientFoxx = &clientFoxx{} + +type clientFoxx struct { + client *client +} + +func newClientFoxx(client *client) *clientFoxx { + return &clientFoxx{ + client: client, + } +} + +// InstallFoxxService installs a new service at a given mount path. +func (c *clientFoxx) InstallFoxxService(ctx context.Context, dbName string, zipFile string, opts *FoxxCreateOptions) error { + + url := connection.NewUrl("_db", dbName, "_api/foxx") + var response struct { + shared.ResponseStruct `json:",inline"` + } + + request := &InstallFoxxServiceRequest{} + if opts != nil { + request.FoxxCreateOptions = *opts + } + + bytes, err := os.ReadFile(zipFile) + if err != nil { + return errors.WithStack(err) + } + + resp, err := connection.CallPost(ctx, c.client.connection, url, &response, bytes, request.modifyRequest) + if err != nil { + return errors.WithStack(err) + } + + switch code := resp.Code(); code { + case http.StatusOK: + return nil + default: + return response.AsArangoErrorWithCode(code) + } + +} + +// UninstallFoxxService uninstalls service at a given mount path. +func (c *clientFoxx) UninstallFoxxService(ctx context.Context, dbName string, opts *FoxxDeleteOptions) error { + url := connection.NewUrl("_db", dbName, "_api/foxx/service") + + var response struct { + shared.ResponseStruct `json:",inline"` + } + + request := &UninstallFoxxServiceRequest{} + if opts != nil { + request.FoxxDeleteOptions = *opts + } + + resp, err := connection.CallPost(ctx, c.client.connection, url, &response, nil, request.modifyRequest) + if err != nil { + return errors.WithStack(err) + } + + switch code := resp.Code(); code { + case http.StatusOK: + return nil + default: + return response.AsArangoErrorWithCode(code) + } +} diff --git a/v2/arangodb/client_impl.go b/v2/arangodb/client_impl.go index 9d94d305..6f474d95 100644 --- a/v2/arangodb/client_impl.go +++ b/v2/arangodb/client_impl.go @@ -38,6 +38,7 @@ func newClient(connection connection.Connection) *client { c.clientServerInfo = newClientServerInfo(c) c.clientAdmin = newClientAdmin(c) c.clientAsyncJob = newClientAsyncJob(c) + c.clientFoxx = newClientFoxx(c) c.Requests = NewRequests(connection) @@ -54,6 +55,7 @@ type client struct { *clientServerInfo *clientAdmin *clientAsyncJob + *clientFoxx Requests } diff --git a/v2/tests/foxx_test.go b/v2/tests/foxx_test.go new file mode 100644 index 00000000..fba09999 --- /dev/null +++ b/v2/tests/foxx_test.go @@ -0,0 +1,83 @@ +// +// DISCLAIMER +// +// Copyright 2025 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package tests + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/arangodb/go-driver/v2/arangodb" + "github.com/arangodb/go-driver/v2/connection" + "github.com/arangodb/go-driver/v2/utils" +) + +func Test_FoxxItzpapalotlService(t *testing.T) { + Wrap(t, func(t *testing.T, client arangodb.Client) { + WithDatabase(t, client, nil, func(db arangodb.Database) { + t.Run("Install and uninstall Foxx", func(t *testing.T) { + withContextT(t, defaultTestTimeout, func(ctx context.Context, t testing.TB) { + + if os.Getenv("TEST_CONNECTION") == "vst" { + skipBelowVersion(client, ctx, "3.6", t) + } + + // /tmp/resources/ directory is provided by .travis.yml + zipFilePath := "/tmp/resources/itzpapalotl-v1.2.0.zip" + if _, err := os.Stat(zipFilePath); os.IsNotExist(err) { + // Test works only via travis pipeline unless the above file exists locally + t.Skipf("file %s does not exist", zipFilePath) + } + + timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Minute*30) + mountName := "test" + options := &arangodb.FoxxCreateOptions{ + Mount: utils.NewType[string]("/" + mountName), + } + err := client.InstallFoxxService(timeoutCtx, db.Name(), zipFilePath, options) + cancel() + require.NoError(t, err) + + timeoutCtx, cancel = context.WithTimeout(context.Background(), time.Second*30) + resp, err := connection.CallGet(ctx, client.Connection(), "_db/_system/"+mountName+"/random", nil, nil, nil) + require.NotNil(t, resp) + + value, ok := resp, true + require.Equal(t, true, ok) + require.NotEmpty(t, value) + cancel() + + timeoutCtx, cancel = context.WithTimeout(context.Background(), time.Second*30) + deleteOptions := &arangodb.FoxxDeleteOptions{ + Mount: utils.NewType[string]("/" + mountName), + Teardown: utils.NewType[bool](true), + } + err = client.UninstallFoxxService(timeoutCtx, db.Name(), deleteOptions) + cancel() + require.NoError(t, err) + }) + }) + }) + }) +}