diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index c8475415..6c4fe769 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -67,8 +67,16 @@ jobs: tests-v4: name: Tests v4 runs-on: ubuntu-latest + strategy: + matrix: + auth_integration: [ "auth_enabled", "auth_disabled" ] env: EXTERNAL_WEAVIATE_RUNNING: false + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + OKTA_CLIENT_SECRET: ${{ secrets.OKTA_CLIENT_SECRET }} + WCS_DUMMY_CI_PW: ${{ secrets.WCS_DUMMY_CI_PW }} + OKTA_DUMMY_CI_PW: ${{ secrets.OKTA_DUMMY_CI_PW }} + INTEGRATION_TESTS_AUTH: ${{ matrix.auth_integration }} steps: - uses: actions/checkout@v3 - name: Login to Docker Hub @@ -82,9 +90,12 @@ jobs: with: go-version: 1.19 cache: true + - name: Start Weaviate + run: ./v4/test/start_containers.sh - name: Run tests run: | cd v4 - docker-compose -f test/docker-compose.yaml up -d go test -v ./weaviate/... ( for pkg in $(go list ./... | grep 'weaviate-go-client/v4/test'); do if ! go test -v -count 1 -race "$pkg"; then echo "Test for $pkg failed" >&2; false; exit; fi; done) + - name: Stop Weaviate + run: ./v4/test/stop_containers.sh diff --git a/v4/go.mod b/v4/go.mod index 182078be..b68f9a28 100644 --- a/v4/go.mod +++ b/v4/go.mod @@ -6,4 +6,5 @@ require ( github.com/go-openapi/strfmt v0.21.3 github.com/stretchr/testify v1.8.0 github.com/weaviate/weaviate v1.17.2-0.20230118094121-abf30eac8656 + golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 ) diff --git a/v4/go.sum b/v4/go.sum index ecb93917..5b9e19f3 100644 --- a/v4/go.sum +++ b/v4/go.sum @@ -1061,6 +1061,7 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1341,6 +1342,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/v4/test/auth/auth_mock_test.go b/v4/test/auth/auth_mock_test.go new file mode 100644 index 00000000..a5365f3f --- /dev/null +++ b/v4/test/auth/auth_mock_test.go @@ -0,0 +1,267 @@ +package test + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/weaviate/weaviate-go-client/v4/weaviate" + "github.com/weaviate/weaviate-go-client/v4/weaviate/auth" +) + +const ( + AccessToken = "HELLO!IamAnAccessToken" + RefreshToken = "IAmARefreshToken" +) + +// Test that the client warns when no refresh token is provided by the authentication provider +func TestAuthMock_NoRefreshToken(t *testing.T) { + tests := []struct { + name string + authConfig auth.Config + scope []string + }{ + {name: "User/PW", authConfig: auth.ResourceOwnerPasswordFlow{Username: "SomeUsername", Password: "IamWrong"}}, + {name: "Bearer token", authConfig: auth.BearerToken{AccessToken: "NotAToken"}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // write log to buffer + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + // endpoint for access tokens + muxToken := http.NewServeMux() + muxToken.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(fmt.Sprint(`{"access_token": "` + AccessToken + `", "expires_in": "5"}`))) + }) + sToken := httptest.NewServer(muxToken) + defer sToken.Close() + + // provides all endpoints + muxEndpoints := http.NewServeMux() + muxEndpoints.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(`{"token_endpoint": "` + sToken.URL + `/auth"}`))) + }) + sEndpoints := httptest.NewServer(muxEndpoints) + defer sEndpoints.Close() + + // Returns the address of the auth server + mux := http.NewServeMux() + mux.HandleFunc("/v1/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"href": "` + sEndpoints.URL + `/endpoints", "clientId": "DoesNotMatter"}`)) + }) + mux.HandleFunc("/v1/schema", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{}`)) + }) + s := httptest.NewServer(mux) + defer s.Close() + + cfg, err := weaviate.NewConfig(strings.TrimPrefix(s.URL, "http://"), "http", tc.authConfig, nil) + assert.Nil(t, err) + assert.True(t, strings.Contains(buf.String(), "Auth002")) + + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + }) + } +} + +// Test that client using CC automatically get a new token after expiration +func TestAuthMock_RefreshCC(t *testing.T) { + i := 0 + // endpoint for access tokens + muxToken := http.NewServeMux() + muxToken.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) { + i += 1 // record how often this was called + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(fmt.Sprint(`{"access_token": "` + AccessToken + `", "expires_in": "1"}`))) + }) + sToken := httptest.NewServer(muxToken) + defer sToken.Close() + + // provides all endpoints + muxEndpoints := http.NewServeMux() + muxEndpoints.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(`{"token_endpoint": "` + sToken.URL + `/auth"}`))) + }) + sEndpoints := httptest.NewServer(muxEndpoints) + defer sEndpoints.Close() + + // Returns the address of the auth server + mux := http.NewServeMux() + mux.HandleFunc("/v1/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"href": "` + sEndpoints.URL + `/endpoints", "clientId": "DoesNotMatter"}`)) + }) + mux.HandleFunc("/v1/schema", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{}`)) + }) + s := httptest.NewServer(mux) + defer s.Close() + + cfg, err := weaviate.NewConfig(strings.TrimPrefix(s.URL, "http://"), "http", auth.ClientCredentials{ClientSecret: "SecretValue"}, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + assert.Equal(t, i, 3) // client does 3 initial calls to token endpoint + + time.Sleep(time.Second * 5) + // current token expires, so the oauth client needs to get a new one + AuthErr2 := client.Schema().AllDeleter().Do(context.TODO()) + assert.Equal(t, i, 4) + assert.Nil(t, AuthErr2) +} + +// Test that client uses refresh tokens to get new access/refresh tokens before their expiration, including during idle +// times. +func TestAuthMock_RefreshUserPWAndToken(t *testing.T) { + expirationTimeRefreshToken := 3 + expirationTimeToken := uint(2) + tests := []struct { + name string + authConfig auth.Config + scope []string + }{ + {name: "User/PW", authConfig: auth.ResourceOwnerPasswordFlow{Username: "SomeUsername", Password: "IamWrong"}}, + {name: "Bearer token", authConfig: auth.BearerToken{ + AccessToken: AccessToken, ExpiresIn: expirationTimeToken, RefreshToken: RefreshToken, + }}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tokenRefreshTime := time.Now() + // endpoint for access tokens + muxToken := http.NewServeMux() + muxToken.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) { + // refresh token cannot be expired + assert.True(t, time.Now().Sub(tokenRefreshTime).Seconds() < float64(expirationTimeRefreshToken)) + + tokenRefreshTime = time.Now() // update time when the tokens where refreshed the last time + w.Header().Set("Content-Type", "application/json") + w.Write([]byte( + fmt.Sprintf(`{"access_token": "%v", "expires_in": %v, "refresh_token": "%v", "refresh_expires_in" : %v}`, + AccessToken, expirationTimeToken, RefreshToken, expirationTimeRefreshToken))) + }) + sToken := httptest.NewServer(muxToken) + defer sToken.Close() + + // provides all endpoints + muxEndpoints := http.NewServeMux() + muxEndpoints.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(`{"token_endpoint": "` + sToken.URL + `/auth"}`))) + }) + sEndpoints := httptest.NewServer(muxEndpoints) + defer sEndpoints.Close() + + // Returns the address of the auth server + mux := http.NewServeMux() + mux.HandleFunc("/v1/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"href": "` + sEndpoints.URL + `/endpoints", "clientId": "DoesNotMatter"}`)) + }) + mux.HandleFunc("/v1/schema", func(w http.ResponseWriter, r *http.Request) { + // Access Token cannot be expired + assert.True(t, time.Now().Sub(tokenRefreshTime).Seconds() < float64(expirationTimeToken)) + w.Write([]byte(`{}`)) + }) + s := httptest.NewServer(mux) + defer s.Close() + + cfg, err := weaviate.NewConfig(strings.TrimPrefix(s.URL, "http://"), "http", tc.authConfig, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + + // access and refresh token expired, so the client needs to refresh automatically in the background + time.Sleep(time.Second * 5) + AuthErr2 := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr2) + }) + } +} + +// Test that the client can handle situations in which a proxy returns a catchall page for all requests +func TestAuthMock_CatchAllProxy(t *testing.T) { + // write log to buffer + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + // Simulate a proxy that returns something if a page is not available => no valid json + mux := http.NewServeMux() + mux.HandleFunc("/v1/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`NotAValidJsonResponse`)) + }) + mux.HandleFunc("/v1/schema", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{}`)) + }) + s := httptest.NewServer(mux) + defer s.Close() + + cfg, err := weaviate.NewConfig(strings.TrimPrefix(s.URL, "http://"), "http", nil, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) +} + +// Test that client using CC automatically get a new token after expiration +func TestAuthMock_CheckDefaultScopes(t *testing.T) { + // endpoint for access tokens + muxToken := http.NewServeMux() + muxToken.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + body, _ := io.ReadAll(r.Body) + bodyS := string(body) + assert.Equal(t, bodyS[len(bodyS)-15:], "something+extra") // scopes are in the body + + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(fmt.Sprint(`{"access_token": "` + AccessToken + `", "expires_in": "1"}`))) + }) + sToken := httptest.NewServer(muxToken) + defer sToken.Close() + + // provides all endpoints + muxEndpoints := http.NewServeMux() + muxEndpoints.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(fmt.Sprintf(`{"token_endpoint": "` + sToken.URL + `/auth"}`))) + }) + sEndpoints := httptest.NewServer(muxEndpoints) + defer sEndpoints.Close() + + // Returns the address of the auth server + mux := http.NewServeMux() + mux.HandleFunc("/v1/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"href": "` + sEndpoints.URL + `/endpoints", "clientId": "DoesNotMatter", "scopes": ["something", "extra"]}`)) + }) + mux.HandleFunc("/v1/schema", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{}`)) + }) + s := httptest.NewServer(mux) + defer s.Close() + + cfg, err := weaviate.NewConfig(strings.TrimPrefix(s.URL, "http://"), "http", auth.ClientCredentials{ClientSecret: "SecretValue"}, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) +} diff --git a/v4/test/auth/auth_test.go b/v4/test/auth/auth_test.go new file mode 100644 index 00000000..fe1612d4 --- /dev/null +++ b/v4/test/auth/auth_test.go @@ -0,0 +1,261 @@ +package test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/weaviate/weaviate-go-client/v4/test/testsuit" + "github.com/weaviate/weaviate-go-client/v4/weaviate" + "github.com/weaviate/weaviate-go-client/v4/weaviate/auth" +) + +const ( + oktaScope = "some_scope" + wcsUser = "ms_2d0e007e7136de11d5f29fce7a53dae219a51458@existiert.net" + oktaUser = "test@test.de" +) + +func TestAuth_clientCredential(t *testing.T) { + tests := []struct { + name string + envVar string + scope []string + port int + }{ + {name: "Okta", envVar: "OKTA_CLIENT_SECRET", scope: []string{oktaScope}, port: testsuit.OktaCCPort}, + {name: "Azure", envVar: "AZURE_CLIENT_SECRET", scope: []string{"4706508f-30c2-469b-8b12-ad272b3de864/.default"}, port: testsuit.AzurePort}, + {name: "Azure (hardcoded scope)", envVar: "AZURE_CLIENT_SECRET", scope: nil, port: testsuit.AzurePort}, + } + + for _, tc := range tests { + clientSecret := os.Getenv(tc.envVar) + if clientSecret == "" { + t.Skip("No client secret supplied for ", tc.name) + } + + clientCredentialConf := auth.ClientCredentials{ClientSecret: clientSecret, Scopes: tc.scope} + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", tc.port), "http", clientCredentialConf, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + } +} + +func TestAuth_clientCredential_WrongParameters(t *testing.T) { + clientSecret := os.Getenv("OKTA_CLIENT_SECRET") + if clientSecret == "" { + t.Skip("No client secret supplied for okta") + } + + tests := []struct { + name string + secret string + scope []string + }{ + {name: "Wrong credential", secret: "ImNotaRealSecret", scope: []string{"OktaScope"}}, + {name: "Wrong scope", secret: clientSecret, scope: []string{"MadeUpScope"}}, + } + + for _, tc := range tests { + t.Run(t.Name(), func(t *testing.T) { + clientCredentialConf := auth.ClientCredentials{ClientSecret: tc.secret, Scopes: tc.scope} + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", testsuit.OktaCCPort), "http", clientCredentialConf, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.NotNil(t, AuthErr) + }) + } +} + +func TestAuth_UserPW(t *testing.T) { + tests := []struct { + name string + user string + envVar string + scope []string + port int + warning bool + }{ + {name: "WCS", user: wcsUser, envVar: "WCS_DUMMY_CI_PW", port: testsuit.WCSPort, warning: false}, + {name: "Okta (no scope)", user: oktaUser, envVar: "OKTA_DUMMY_CI_PW", port: testsuit.OktaUsersPort, warning: false}, + {name: "Okta", user: oktaUser, envVar: "OKTA_DUMMY_CI_PW", port: testsuit.OktaUsersPort, scope: []string{"offline_access"}, warning: false}, + {name: "Okta (scope without refresh)", user: oktaUser, envVar: "OKTA_DUMMY_CI_PW", port: testsuit.OktaUsersPort, scope: []string{"offline_access"}, warning: true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // write log to buffer + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + pw := os.Getenv(tc.envVar) + if pw == "" { + t.Skip("No password supplied for " + tc.name) + } else { + // This should be in a branch, so the GC can collect the client and with that shut down the background + // routine that writes to the log. Otherwise, we'd have a data race between the this goroutine and the + // test accessing the buffer. + clientCredentialConf := auth.ResourceOwnerPasswordFlow{Username: tc.user, Password: pw, Scopes: tc.scope} + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", tc.port), "http", clientCredentialConf, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + } + runtime.GC() + + if tc.warning { + assert.True(t, strings.Contains(buf.String(), "Auth002")) + } + }) + } +} + +func TestAuth_UserPW_wrongPW(t *testing.T) { + clientCredentialConf := auth.ResourceOwnerPasswordFlow{Username: "SomeUsername", Password: "IamWrong"} + _, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", testsuit.WCSPort), "http", clientCredentialConf, nil) + assert.NotNil(t, err) +} + +func TestNoAuthOnWeaviateWithoutAuth(t *testing.T) { + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", testsuit.NoAuthPort), "http", nil, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) +} + +func TestNoAuthOnWeaviateWithAuth(t *testing.T) { + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", testsuit.WCSPort), "http", nil, nil) + assert.Nil(t, err) + client := weaviate.New(*cfg) + + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.NotNil(t, AuthErr) +} + +// Test that log contains a warning when configuring the client with authentication, but weaviate is configured without +// authentication. Otherwise, the client is working normally +func TestAuthOnWeaviateWithoutAuth(t *testing.T) { + tests := []struct { + name string + authConfig auth.Config + scope []string + }{ + {name: "User/PW", authConfig: auth.ResourceOwnerPasswordFlow{Username: "SomeUsername", Password: "IamWrong"}}, + {name: "Client credentials", authConfig: auth.ClientCredentials{ClientSecret: "NotASecret", Scopes: []string{"No scope"}}}, + {name: "Bearer token", authConfig: auth.BearerToken{AccessToken: "NotAToken"}}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + cfg, err := weaviate.NewConfig(fmt.Sprintf("localhost:%v", testsuit.NoAuthPort), "http", tc.authConfig, nil) + assert.Nil(t, err) + assert.True(t, strings.Contains(buf.String(), "The client was configured to use authentication")) + + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + }) + } +} + +func TestAuthNoWeaviateOnPort(t *testing.T) { + _, err := weaviate.NewConfig("localhost:"+fmt.Sprint(testsuit.NoWeaviatePort), "http", auth.ResourceOwnerPasswordFlow{Username: "SomeUsername", Password: "IamWrong"}, nil) + assert.NotNil(t, err) +} + +func TestAuthBearerToken(t *testing.T) { + tests := []struct { + name string + user string + envVar string + port int + }{ + {name: "WCS", user: wcsUser, envVar: "WCS_DUMMY_CI_PW", port: testsuit.WCSPort}, + {name: "Okta", user: oktaUser, envVar: "OKTA_DUMMY_CI_PW", port: testsuit.OktaUsersPort}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + pw := os.Getenv(tc.envVar) + if pw == "" { + t.Skip("No password supplied for " + tc.name) + } + url := fmt.Sprintf("localhost:%v", tc.port) + + accessToken, refreshToken := getAccessToken(t, url, tc.user, pw) + cfg, err := weaviate.NewConfig(url, "http", auth.BearerToken{AccessToken: accessToken, RefreshToken: refreshToken}, nil) + assert.Nil(t, err) + + client := weaviate.New(*cfg) + AuthErr := client.Schema().AllDeleter().Do(context.TODO()) + assert.Nil(t, AuthErr) + }) + } +} + +func getAccessToken(t *testing.T, weaviateUrl, user, pw string) (string, string) { + resp, err := http.Get(fmt.Sprintf("http://%s/v1/.well-known/openid-configuration", weaviateUrl)) + require.Nil(t, err) + body, err := io.ReadAll(resp.Body) + require.Nil(t, err) + cfg := struct { + Href string `json:"href"` + ClientID string `json:"clientId"` + }{} + err = json.Unmarshal(body, &cfg) + require.Nil(t, err) + if err := resp.Body.Close(); err != nil { + t.Error(err) + } + respAuth, err := http.Get(cfg.Href) + require.Nil(t, err) + bodyAuth, err := io.ReadAll(respAuth.Body) + require.Nil(t, err) + endpoint := struct { + TokenEndpoint string `json:"token_endpoint"` + }{} + err = json.Unmarshal(bodyAuth, &endpoint) + require.Nil(t, err) + err = respAuth.Body.Close() + require.Nil(t, err) + respToken, err := http.PostForm(endpoint.TokenEndpoint, url.Values{ + "grant_type": []string{"password"}, "client_id": []string{cfg.ClientID}, "username": []string{user}, "password": []string{pw}, + }) + require.Nil(t, err) + bodyTokens, err := io.ReadAll(respToken.Body) + require.Nil(t, err) + err = respToken.Body.Close() + require.Nil(t, err) + // get tokens + tokens := struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + }{} + err = json.Unmarshal(bodyTokens, &tokens) + require.Nil(t, err) + return tokens.AccessToken, tokens.RefreshToken +} diff --git a/v4/test/backup/backup_test.go b/v4/test/backup/backup_test.go index 993a848d..b4b5a953 100644 --- a/v4/test/backup/backup_test.go +++ b/v4/test/backup/backup_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate" "github.com/weaviate/weaviate-go-client/v4/weaviate/backup" "github.com/weaviate/weaviate-go-client/v4/weaviate/graphql" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestBackups_integration(t *testing.T) { @@ -29,7 +29,7 @@ func TestBackups_integration(t *testing.T) { const dockerComposeBackupDir = "/tmp/backups" random := rand.New(rand.NewSource(time.Now().UnixNano())) - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CleanUpWeaviate(t, client) t.Run("create and restore backup with waiting", func(t *testing.T) { @@ -174,10 +174,12 @@ func TestBackups_integration(t *testing.T) { assert.Equal(t, dockerComposeBackupDir+"/"+backupID, createStatusResponse.Path) assert.Equal(t, backup.BACKEND_FILESYSTEM, createStatusResponse.Backend) assert.Empty(t, createStatusResponse.Error) - assert.Contains(t, []string{models.BackupCreateStatusResponseStatusSTARTED, + assert.Contains(t, []string{ + models.BackupCreateStatusResponseStatusSTARTED, models.BackupCreateStatusResponseStatusTRANSFERRING, models.BackupCreateStatusResponseStatusTRANSFERRED, - models.BackupCreateStatusResponseStatusSUCCESS}, *createStatusResponse.Status) + models.BackupCreateStatusResponseStatusSUCCESS, + }, *createStatusResponse.Status) if models.BackupCreateStatusResponseStatusSUCCESS == *createStatusResponse.Status { break @@ -230,10 +232,12 @@ func TestBackups_integration(t *testing.T) { assert.Equal(t, dockerComposeBackupDir+"/"+backupID, restoreStatusResponse.Path) assert.Equal(t, backup.BACKEND_FILESYSTEM, restoreStatusResponse.Backend) assert.Empty(t, restoreStatusResponse.Error) - assert.Contains(t, []string{models.BackupRestoreStatusResponseStatusSTARTED, + assert.Contains(t, []string{ + models.BackupRestoreStatusResponseStatusSTARTED, models.BackupRestoreStatusResponseStatusTRANSFERRING, models.BackupRestoreStatusResponseStatusTRANSFERRED, - models.BackupRestoreStatusResponseStatusSUCCESS}, *restoreStatusResponse.Status) + models.BackupRestoreStatusResponseStatusSUCCESS, + }, *restoreStatusResponse.Status) if models.BackupRestoreStatusResponseStatusSUCCESS == *restoreStatusResponse.Status { break diff --git a/v4/test/batch/batchCreate_test.go b/v4/test/batch/batchCreate_test.go index 02a3a01e..b0fe0772 100644 --- a/v4/test/batch/batchCreate_test.go +++ b/v4/test/batch/batchCreate_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" ) func TestBatchCreate_integration(t *testing.T) { @@ -19,7 +19,7 @@ func TestBatchCreate_integration(t *testing.T) { }) t.Run("POST /batch/{type}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) // Create some classes to add in a batch @@ -51,13 +51,13 @@ func TestBatchCreate_integration(t *testing.T) { "description": "Putting the game of letter soups to a whole new level.", }, } - classASlice := []*models.Object{classA1,classA2} + classASlice := []*models.Object{classA1, classA2} batchResultT, batchErrT := client.Batch().ObjectsBatcher().WithObject(classT1).WithObject(classT2).Do(context.Background()) assert.Nil(t, batchErrT) assert.NotNil(t, batchResultT) assert.Equal(t, 2, len(batchResultT)) - batchResultA, batchErrA := client.Batch().ObjectsBatcher().WithObjects(classA1,classA2).Do(context.Background()) + batchResultA, batchErrA := client.Batch().ObjectsBatcher().WithObjects(classA1, classA2).Do(context.Background()) assert.Nil(t, batchErrA) assert.NotNil(t, batchResultA) assert.Equal(t, 2, len(batchResultA)) @@ -67,7 +67,6 @@ func TestBatchCreate_integration(t *testing.T) { assert.NotNil(t, batchResultSlice) assert.Equal(t, 2, len(batchResultSlice)) - objectT1, objErrT1 := client.Data().ObjectsGetter().WithClassName("Pizza").WithID("abefd256-8574-442b-9293-9205193737ee").Do(context.Background()) assert.Nil(t, objErrT1) assert.NotNil(t, objectT1) @@ -85,7 +84,7 @@ func TestBatchCreate_integration(t *testing.T) { }) t.Run("POST /batch/references", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create some objects diff --git a/v4/test/batch/batchDelete_test.go b/v4/test/batch/batchDelete_test.go index 54f59669..9ca99443 100644 --- a/v4/test/batch/batchDelete_test.go +++ b/v4/test/batch/batchDelete_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/helpers" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestBatchDelete_integration(t *testing.T) { @@ -24,7 +24,7 @@ func TestBatchDelete_integration(t *testing.T) { }) t.Run("batch delete dry run", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) where := filters.Where(). @@ -61,7 +61,7 @@ func TestBatchDelete_integration(t *testing.T) { }) t.Run("batch delete", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) nowString := fmt.Sprint(time.Now().UnixNano() / int64(time.Millisecond)) @@ -103,7 +103,7 @@ func TestBatchDelete_integration(t *testing.T) { }) t.Run("batch delete no matches", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) where := filters.Where(). diff --git a/v4/test/batch/batchReferences_deprecated_test.go b/v4/test/batch/batchReferences_deprecated_test.go index e07378d0..10147524 100644 --- a/v4/test/batch/batchReferences_deprecated_test.go +++ b/v4/test/batch/batchReferences_deprecated_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" ) func TestBatchCreate_integration_deprecated(t *testing.T) { @@ -19,7 +19,7 @@ func TestBatchCreate_integration_deprecated(t *testing.T) { }) t.Run("POST /batch/references", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create some objects diff --git a/v4/test/classifications/classification_test.go b/v4/test/classifications/classification_test.go index 6752993b..b36dbbf9 100644 --- a/v4/test/classifications/classification_test.go +++ b/v4/test/classifications/classification_test.go @@ -5,17 +5,16 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate" "github.com/weaviate/weaviate-go-client/v4/weaviate/classifications" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" "github.com/weaviate/weaviate/usecases/classification" - "github.com/stretchr/testify/assert" ) func TestClassifications_integration(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviate() if err != nil { @@ -25,7 +24,7 @@ func TestClassifications_integration(t *testing.T) { }) t.Run("POST /classifications", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) createClassificationClasses(t, client) classifyProperties := []string{"tagged"} @@ -43,7 +42,7 @@ func TestClassifications_integration(t *testing.T) { }) t.Run("GET /classifications/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) createClassificationClasses(t, client) var k int32 = 3 diff --git a/v4/test/classifications/classification_with_where_test.go b/v4/test/classifications/classification_with_where_test.go index 7fdf45ef..dc06a484 100644 --- a/v4/test/classifications/classification_with_where_test.go +++ b/v4/test/classifications/classification_with_where_test.go @@ -5,16 +5,15 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/classifications" "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/usecases/classification" - "github.com/stretchr/testify/assert" ) func TestClassifications_With_Where_Filters_integration(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviate() if err != nil { @@ -24,7 +23,7 @@ func TestClassifications_With_Where_Filters_integration(t *testing.T) { }) t.Run("POST /classifications", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) createClassificationClasses(t, client) sourceWhere := filters.Where(). @@ -59,7 +58,7 @@ func TestClassifications_With_Where_Filters_integration(t *testing.T) { }) t.Run("GET /classifications/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) createClassificationClasses(t, client) sourceWhere := filters.Where(). diff --git a/v4/test/cluster/nodes_test.go b/v4/test/cluster/nodes_test.go index 90f131ce..931c9ec9 100644 --- a/v4/test/cluster/nodes_test.go +++ b/v4/test/cluster/nodes_test.go @@ -5,18 +5,17 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestClusterNodes_integration(t *testing.T) { - const ( - expectedWeaviateVersion = "1.17.0-prealpha" - expectedWeaviateGitHash = "29e987d" + expectedWeaviateVersion = "1.17.0" + expectedWeaviateGitHash = "37d3b17" ) t.Run("up", func(t *testing.T) { @@ -28,7 +27,7 @@ func TestClusterNodes_integration(t *testing.T) { }) t.Run("GET /nodes without data", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) nodesStatus, err := client.Cluster().NodesStatusGetter().Do(context.Background()) require.Nil(t, err) @@ -47,7 +46,7 @@ func TestClusterNodes_integration(t *testing.T) { }) t.Run("GET /nodes with data", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) diff --git a/v4/test/contextionary/contextionary_test.go b/v4/test/contextionary/contextionary_test.go index 6edea4d3..782347b2 100644 --- a/v4/test/contextionary/contextionary_test.go +++ b/v4/test/contextionary/contextionary_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" - "github.com/stretchr/testify/assert" ) func TestContextionary_integration(t *testing.T) { @@ -18,7 +18,7 @@ func TestContextionary_integration(t *testing.T) { }) t.Run("GET /modules/text2vec-contextionary/concepts/{concept}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) concepts, err := client.C11y().ConceptsGetter().WithConcept("pizzaHawaii").Do(context.Background()) assert.Nil(t, err) @@ -29,7 +29,7 @@ func TestContextionary_integration(t *testing.T) { }) t.Run("POST /modules/text2vec-contextionary/extensions", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) err1 := client.C11y().ExtensionCreator().WithConcept("xoxo").WithDefinition("Hugs and kisses").WithWeight(1.0).Do(context.Background()) assert.Nil(t, err1) diff --git a/v4/test/data/data_deprecated_test.go b/v4/test/data/data_deprecated_test.go index 1650a65a..ef2e6a21 100644 --- a/v4/test/data/data_deprecated_test.go +++ b/v4/test/data/data_deprecated_test.go @@ -5,11 +5,11 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/fault" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestData_integration_deprecated(t *testing.T) { @@ -22,7 +22,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("POST /{semanticType}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -70,7 +70,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("POST vectorizorless class", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaWithVectorizorlessClass(t, client) @@ -116,7 +116,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("GET /actions /things", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) _, errCreate := client.Data().Creator().WithClassName("Pizza").WithProperties(map[string]string{ @@ -155,7 +155,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("GET underscore properties", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -249,7 +249,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("DELETE /{type}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -298,7 +298,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("HEAD /{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -357,7 +357,7 @@ func TestData_integration_deprecated(t *testing.T) { t.Run("PUT /{type}/{id}", func(t *testing.T) { // PUT replaces the object fully - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -425,7 +425,7 @@ func TestData_integration_deprecated(t *testing.T) { t.Run("PATCH(merge) /{type}/{id}", func(t *testing.T) { // PATCH merges the new object with the existing object - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -492,7 +492,7 @@ func TestData_integration_deprecated(t *testing.T) { }) t.Run("POST /{type}/validate", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) diff --git a/v4/test/data/data_test.go b/v4/test/data/data_test.go index 05c1fe1c..a24dddf1 100644 --- a/v4/test/data/data_test.go +++ b/v4/test/data/data_test.go @@ -5,11 +5,11 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/fault" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestData_integration(t *testing.T) { @@ -22,7 +22,7 @@ func TestData_integration(t *testing.T) { }) t.Run("POST /{semanticType}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -72,7 +72,7 @@ func TestData_integration(t *testing.T) { }) t.Run("POST vectorizorless class", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaWithVectorizorlessClass(t, client) @@ -119,7 +119,7 @@ func TestData_integration(t *testing.T) { }) t.Run("GET /actions /things", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) _, errCreate := client.Data().Creator().WithClassName("Pizza").WithProperties(map[string]string{ @@ -161,7 +161,7 @@ func TestData_integration(t *testing.T) { }) t.Run("GET underscore properties", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -261,7 +261,7 @@ func TestData_integration(t *testing.T) { }) t.Run("GET /objects/{className}/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) resp, err := client.Data().ObjectsGetter(). @@ -276,7 +276,7 @@ func TestData_integration(t *testing.T) { }) t.Run("DELETE /{type}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -329,7 +329,7 @@ func TestData_integration(t *testing.T) { }) t.Run("DELETE /objects/{className}/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) err := client.Data().Deleter(). @@ -342,7 +342,7 @@ func TestData_integration(t *testing.T) { }) t.Run("HEAD /{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -406,7 +406,7 @@ func TestData_integration(t *testing.T) { }) t.Run("HEAD /objects/{className}/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) exists, err := client.Data().Checker(). @@ -421,7 +421,7 @@ func TestData_integration(t *testing.T) { t.Run("PUT /{type}/{id}", func(t *testing.T) { // PUT replaces the object fully - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -490,7 +490,7 @@ func TestData_integration(t *testing.T) { }) t.Run("PUT /objects/{className}/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) id := "5b6a08ba-1d46-43aa-89cc-8b070790c6f2" @@ -520,7 +520,7 @@ func TestData_integration(t *testing.T) { t.Run("PATCH(merge) /{type}/{id}", func(t *testing.T) { // PATCH merges the new object with the existing object - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) @@ -589,7 +589,7 @@ func TestData_integration(t *testing.T) { }) t.Run("PATCH /objects/{className}/{id}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) id := "5b6a08ba-1d46-43aa-89cc-8b070790c6f2" @@ -622,7 +622,7 @@ func TestData_integration(t *testing.T) { }) t.Run("POST /{type}/validate", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFood(t, client) diff --git a/v4/test/data/reference_deprecated_test.go b/v4/test/data/reference_deprecated_test.go index 70f1fe4a..6714dc60 100644 --- a/v4/test/data/reference_deprecated_test.go +++ b/v4/test/data/reference_deprecated_test.go @@ -6,14 +6,13 @@ import ( "testing" "github.com/go-openapi/strfmt" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" ) func TestData_reference_integration_deprecated(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviateDeprecated() if err != nil { @@ -23,7 +22,7 @@ func TestData_reference_integration_deprecated(t *testing.T) { }) t.Run("POST /{type}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) propertySchemaT := map[string]string{ @@ -71,7 +70,7 @@ func TestData_reference_integration_deprecated(t *testing.T) { }) t.Run("PUT /{type}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create things with references @@ -135,7 +134,7 @@ func TestData_reference_integration_deprecated(t *testing.T) { }) t.Run("DELETE /{type}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create things with references diff --git a/v4/test/data/reference_test.go b/v4/test/data/reference_test.go index 2317fd35..d46318f2 100644 --- a/v4/test/data/reference_test.go +++ b/v4/test/data/reference_test.go @@ -6,14 +6,13 @@ import ( "testing" "github.com/go-openapi/strfmt" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" ) func TestData_reference_integration(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviate() if err != nil { @@ -23,7 +22,7 @@ func TestData_reference_integration(t *testing.T) { }) t.Run("POST /{type}/{className}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) propertySchemaT := map[string]string{ @@ -71,7 +70,7 @@ func TestData_reference_integration(t *testing.T) { }) t.Run("PUT /{type}/{className}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create things with references @@ -135,7 +134,7 @@ func TestData_reference_integration(t *testing.T) { }) t.Run("DELETE /{type}/{className}/{id}/references/{propertyName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateWeaviateTestSchemaFoodWithReferenceProperty(t, client) // Create things with references diff --git a/v4/test/docker-compose-azure.yml b/v4/test/docker-compose-azure.yml new file mode 100644 index 00000000..38d40df4 --- /dev/null +++ b/v4/test/docker-compose-azure.yml @@ -0,0 +1,29 @@ +--- +version: '3.4' +services: + weaviate-auth-azure: + command: + - --host + - 0.0.0.0 + - --port + - '8081' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d + ports: + - 8081:8081 + restart: on-failure:0 + environment: + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false' + AUTHENTICATION_OIDC_ENABLED: 'true' + AUTHENTICATION_OIDC_CLIENT_ID: '4706508f-30c2-469b-8b12-ad272b3de864' + AUTHENTICATION_OIDC_ISSUER: 'https://login.microsoftonline.com/36c47fb4-f57c-4e1c-8760-d42293932cc2/v2.0' + AUTHENTICATION_OIDC_USERNAME_CLAIM: 'oid' + AUTHENTICATION_OIDC_GROUPS_CLAIM: 'groups' + AUTHORIZATION_ADMINLIST_ENABLED: 'true' + AUTHORIZATION_ADMINLIST_USERS: 'b6bf8e1d-d398-4e5d-8f1b-50fda9146a64' + AUTHENTICATION_OIDC_SCOPES: 'openid,email' +... + diff --git a/v4/test/docker-compose-deprecated-api-test.yml b/v4/test/docker-compose-deprecated-api-test.yml index 85ab9103..6c322220 100644 --- a/v4/test/docker-compose-deprecated-api-test.yml +++ b/v4/test/docker-compose-deprecated-api-test.yml @@ -3,16 +3,16 @@ version: '3.4' services: weaviate: command: - - --host - - 0.0.0.0 - - --port - - '8080' - - --scheme - - http + - --host + - 0.0.0.0 + - --port + - '8080' + - --scheme + - http # TODO: temporarily use this version until next release image: semitechnologies/weaviate:1.13.2 ports: - - 8080:8080 + - 8080:8080 restart: on-failure:0 environment: LOG_LEVEL: "debug" @@ -31,4 +31,4 @@ services: EXTENSIONS_STORAGE_MODE: weaviate EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 - ENABLE_COMPOUND_SPLITTING: 'false' + ENABLE_COMPOUND_SPLITTING: 'false' \ No newline at end of file diff --git a/v4/test/docker-compose-okta-cc.yml b/v4/test/docker-compose-okta-cc.yml new file mode 100644 index 00000000..938a9f74 --- /dev/null +++ b/v4/test/docker-compose-okta-cc.yml @@ -0,0 +1,29 @@ +--- +version: '3.4' +services: + weaviate-auth-okta-cc: + command: + - --host + - 0.0.0.0 + - --port + - '8082' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d + ports: + - 8082:8082 + restart: on-failure:0 + environment: + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false' + AUTHENTICATION_OIDC_ENABLED: 'true' + AUTHENTICATION_OIDC_CLIENT_ID: '0oa7e9ipdkVZRUcxo5d7' + AUTHENTICATION_OIDC_ISSUER: 'https://dev-32300990.okta.com/oauth2/aus7e9kxbwYQB0eht5d7' + AUTHENTICATION_OIDC_USERNAME_CLAIM: 'cid' + AUTHENTICATION_OIDC_GROUPS_CLAIM: 'groups' + AUTHORIZATION_ADMINLIST_ENABLED: 'true' + AUTHORIZATION_ADMINLIST_USERS: '0oa7e9ipdkVZRUcxo5d7' + AUTHENTICATION_OIDC_SCOPES: 'openid,email' +... + diff --git a/v4/test/docker-compose-okta-users.yml b/v4/test/docker-compose-okta-users.yml new file mode 100644 index 00000000..eb27a64f --- /dev/null +++ b/v4/test/docker-compose-okta-users.yml @@ -0,0 +1,29 @@ +--- +version: '3.4' +services: + weaviate-auth-okta-users: + command: + - --host + - 0.0.0.0 + - --port + - '8083' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d + ports: + - 8083:8083 + restart: on-failure:0 + environment: + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false' + AUTHENTICATION_OIDC_ENABLED: 'true' + AUTHENTICATION_OIDC_CLIENT_ID: '0oa7iz2g41rNxv95B5d7' + AUTHENTICATION_OIDC_ISSUER: 'https://dev-32300990.okta.com/oauth2/aus7iz3tna3kckRWS5d7' + AUTHENTICATION_OIDC_USERNAME_CLAIM: 'sub' + AUTHENTICATION_OIDC_GROUPS_CLAIM: 'groups' + AUTHORIZATION_ADMINLIST_ENABLED: 'true' + AUTHORIZATION_ADMINLIST_USERS: 'test@test.de' + AUTHENTICATION_OIDC_SCOPES: 'openid,email' +... + diff --git a/v4/test/docker-compose-wcs.yml b/v4/test/docker-compose-wcs.yml new file mode 100644 index 00000000..17400ff7 --- /dev/null +++ b/v4/test/docker-compose-wcs.yml @@ -0,0 +1,46 @@ +--- +version: '3.4' +services: + weaviate-auth-wcs: + command: + - --host + - 0.0.0.0 + - --port + - '8085' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d + ports: + - 8085:8085 + restart: on-failure:0 + environment: + LOG_LEVEL: "debug" + CONTEXTIONARY_URL: contextionary:9999 + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false' + AUTHENTICATION_OIDC_ENABLED: 'true' + AUTHENTICATION_OIDC_CLIENT_ID: 'wcs' + AUTHENTICATION_OIDC_ISSUER: 'https://auth.wcs.api.semi.technology/auth/realms/SeMI' + AUTHENTICATION_OIDC_USERNAME_CLAIM: 'email' + AUTHENTICATION_OIDC_GROUPS_CLAIM: 'groups' + AUTHORIZATION_ADMINLIST_ENABLED: 'true' + AUTHORIZATION_ADMINLIST_USERS: 'ms_2d0e007e7136de11d5f29fce7a53dae219a51458@existiert.net' + AUTHENTICATION_OIDC_SCOPES: 'openid,email' + PERSISTENCE_DATA_PATH: "./data" + DEFAULT_VECTORIZER_MODULE: text2vec-contextionary + ENABLE_MODULES: text2vec-contextionary,backup-filesystem + BACKUP_FILESYSTEM_PATH: "/tmp/backups" + CLUSTER_GOSSIP_BIND_PORT: "7100" + CLUSTER_DATA_BIND_PORT: "7101" + contextionary: + image: semitechnologies/contextionary:en0.16.0-v1.1.0 + ports: + - "9999:9999" + environment: + OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 + EXTENSIONS_STORAGE_MODE: weaviate + EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 + NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 + ENABLE_COMPOUND_SPLITTING: 'false' +... diff --git a/v4/test/docker-compose.yaml b/v4/test/docker-compose.yml similarity index 90% rename from v4/test/docker-compose.yaml rename to v4/test/docker-compose.yml index 632ae1cd..96bbbedb 100644 --- a/v4/test/docker-compose.yaml +++ b/v4/test/docker-compose.yml @@ -9,7 +9,7 @@ services: - '8080' - --scheme - http - image: semitechnologies/weaviate:preview-replace-shardingconfig-replicas-with-replication-factor-29e987d + image: semitechnologies/weaviate:1.17.0 ports: - 8080:8080 restart: on-failure:0 diff --git a/v4/test/graphql/graphql_helper_test.go b/v4/test/graphql/graphql_helper_test.go index 6948119d..c3fb16ef 100644 --- a/v4/test/graphql/graphql_helper_test.go +++ b/v4/test/graphql/graphql_helper_test.go @@ -5,9 +5,9 @@ import ( "encoding/json" "testing" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/weaviate" "github.com/weaviate/weaviate-go-client/v4/weaviate/graphql" - "github.com/stretchr/testify/require" ) type GetPizzaResponse struct { diff --git a/v4/test/graphql/graphql_test.go b/v4/test/graphql/graphql_test.go index 7b092788..b034d432 100644 --- a/v4/test/graphql/graphql_test.go +++ b/v4/test/graphql/graphql_test.go @@ -6,17 +6,16 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" "github.com/weaviate/weaviate-go-client/v4/weaviate/graphql" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGraphQL_integration(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviate() if err != nil { @@ -25,7 +24,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -222,7 +221,7 @@ func TestGraphQL_integration(t *testing.T) { t.Run("Explore", func(t *testing.T) { t.Run("with certainty", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -263,7 +262,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("with distance", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -305,7 +304,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Aggregate", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -746,7 +745,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get with group filter", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -770,7 +769,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get with creationTimeUnix filters", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -811,7 +810,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get with lastUpdateTimeUnix filters", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -852,7 +851,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get bm25 filter", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -887,7 +886,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("Get hybrid filter", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -922,7 +921,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("MultiClass Get", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -1197,7 +1196,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("MultiClassGet with group filter", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -1226,7 +1225,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("MultiClassGet with creationTimeUnix filters", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) @@ -1283,7 +1282,7 @@ func TestGraphQL_integration(t *testing.T) { }) t.Run("MultiClassGet with lastUpdateTimeUnix filters", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) testsuit.CreateTestSchemaAndData(t, client) defer testsuit.CleanUpWeaviate(t, client) diff --git a/v4/test/misc/misc_test.go b/v4/test/misc/misc_test.go index 5a1329fd..866b6a99 100644 --- a/v4/test/misc/misc_test.go +++ b/v4/test/misc/misc_test.go @@ -5,14 +5,13 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" - "github.com/stretchr/testify/assert" ) func TestMisc_integration(t *testing.T) { - t.Run("up", func(t *testing.T) { err := testenv.SetupLocalWeaviate() if err != nil { @@ -22,8 +21,7 @@ func TestMisc_integration(t *testing.T) { }) t.Run("GET /.well-known/ready", func(t *testing.T) { - - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) isReady, err := client.Misc().ReadyChecker().Do(context.Background()) assert.Nil(t, err) @@ -31,8 +29,7 @@ func TestMisc_integration(t *testing.T) { }) t.Run("GET /.well-known/live", func(t *testing.T) { - - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) isLive, err := client.Misc().LiveChecker().Do(context.Background()) assert.Nil(t, err) @@ -40,8 +37,7 @@ func TestMisc_integration(t *testing.T) { }) t.Run("GET /.well-known/openid-configuration", func(t *testing.T) { - - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) openIDconfig, err := client.Misc().OpenIDConfigurationGetter().Do(context.Background()) assert.Nil(t, err) @@ -49,7 +45,7 @@ func TestMisc_integration(t *testing.T) { }) t.Run("GET /meta", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) meta, err := client.Misc().MetaGetter().Do(context.Background()) assert.Nil(t, err) assert.NotEmpty(t, meta.Version) diff --git a/v4/test/misc/misc_version_check_test.go b/v4/test/misc/misc_version_check_test.go index 9c07064d..81ef3096 100644 --- a/v4/test/misc/misc_version_check_test.go +++ b/v4/test/misc/misc_version_check_test.go @@ -5,14 +5,14 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" - "github.com/stretchr/testify/assert" ) func TestMisc_version_check(t *testing.T) { // needs to be the same client for the whole test - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) t.Run("Weaviate is not live, perform live check", func(t *testing.T) { isReady, err := client.Misc().ReadyChecker().Do(context.Background()) @@ -71,7 +71,7 @@ func TestMisc_version_check(t *testing.T) { func TestMisc_empty_version_check(t *testing.T) { // needs to be the same client for the whole test - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) t.Run("Weaviate is not live, perform live check", func(t *testing.T) { isReady, err := client.Misc().ReadyChecker().Do(context.Background()) diff --git a/v4/test/schema/schema_test.go b/v4/test/schema/schema_test.go index e3d3f8ed..7d45f5f7 100644 --- a/v4/test/schema/schema_test.go +++ b/v4/test/schema/schema_test.go @@ -4,12 +4,12 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/weaviate/weaviate-go-client/v4/test/testsuit" "github.com/weaviate/weaviate-go-client/v4/weaviate/testenv" "github.com/weaviate/weaviate/entities/models" "github.com/weaviate/weaviate/entities/storagestate" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestSchema_integration(t *testing.T) { @@ -21,7 +21,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("POST /schema", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "Band", @@ -52,7 +52,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("POST /schema", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "Run", @@ -80,7 +80,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Delete /schema/{type}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClassThing := &models.Class{ Class: "Pizza", @@ -112,7 +112,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Delete All schema", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClassThing := &models.Class{ Class: "Pizza", @@ -137,7 +137,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("POST /schema/{type}/{className}/properties", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClassThing := &models.Class{ Class: "Pizza", @@ -178,7 +178,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("GET /schema/{className}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "Band", @@ -216,7 +216,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("GET /schema/{className}/shards", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) class := &models.Class{ Class: "Article", @@ -247,7 +247,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("PUT /schema/{className}/shards/{shardName}", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) class := &models.Class{ Class: "ClassOne", @@ -298,7 +298,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Update all shards convenience method", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) shardCount := 3 @@ -368,7 +368,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Create class with BM25 config", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "Run", @@ -405,7 +405,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Create class with Stopword config", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "SpaceThings", @@ -443,7 +443,7 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Create class with BM25 and Stopword config", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) schemaClass := &models.Class{ Class: "SpaceThings", @@ -486,11 +486,11 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Create class with explicit replication config", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) className := "ReplicationClass" schemaClass := &models.Class{ - Class: className, + Class: className, ReplicationConfig: &models.ReplicationConfig{ Factor: 3, }, @@ -514,11 +514,11 @@ func TestSchema_integration(t *testing.T) { }) t.Run("Create class with implicit replication config", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) className := "ReplicationClass" schemaClass := &models.Class{ - Class: className, + Class: className, } err := client. @@ -555,14 +555,14 @@ func TestSchema_errors(t *testing.T) { }) t.Run("Run Do without setting a class", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) err := client.Schema().ClassCreator().Do(context.Background()) assert.NotNil(t, err) }) t.Run("Fail to add class with property having not supported tokenization", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) pizzaClass := &models.Class{ Class: "Pizza", @@ -582,7 +582,7 @@ func TestSchema_errors(t *testing.T) { }) t.Run("Fail to add property having not supported tokenization", func(t *testing.T) { - client := testsuit.CreateTestClient() + client := testsuit.CreateTestClient(8080, nil) pizzaClass := &models.Class{ Class: "Pizza", diff --git a/v4/test/start_containers.sh b/v4/test/start_containers.sh new file mode 100755 index 00000000..d37220d4 --- /dev/null +++ b/v4/test/start_containers.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +docker-compose -f v4/test/docker-compose-azure.yml up -d +docker-compose -f v4/test/docker-compose-okta-cc.yml up -d +docker-compose -f v4/test/docker-compose-okta-users.yml up -d +docker-compose -f v4/test/docker-compose-wcs.yml up -d +docker-compose -f v4/test/docker-compose.yml up -d diff --git a/v4/test/stop_containers.sh b/v4/test/stop_containers.sh new file mode 100755 index 00000000..4bafdd8a --- /dev/null +++ b/v4/test/stop_containers.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +docker-compose -f v4/test/docker-compose.yml down --remove-orphans +docker-compose -f v4/test/docker-compose-azure.yml down --remove-orphans +docker-compose -f v4/test/docker-compose-okta-cc.yml down --remove-orphans +docker-compose -f v4/test/docker-compose-okta-users.yml down --remove-orphans +docker-compose -f v4/test/docker-compose-wcs.yml down --remove-orphans \ No newline at end of file diff --git a/v4/test/testsuit/generics.go b/v4/test/testsuit/generics.go index e26bda3c..92151647 100644 --- a/v4/test/testsuit/generics.go +++ b/v4/test/testsuit/generics.go @@ -2,12 +2,25 @@ package testsuit import ( "context" + "fmt" + "net/http" + "os" "testing" "github.com/go-openapi/strfmt" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/weaviate" + "github.com/weaviate/weaviate-go-client/v4/weaviate/auth" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" +) + +const ( + NoAuthPort = 8080 + AzurePort = 8081 + OktaCCPort = 8082 + OktaUsersPort = 8083 + WCSPort = 8085 + NoWeaviatePort = 8888 ) // CreateWeaviateTestSchemaFood creates a class for each semantic type (Pizza and Soup) @@ -135,12 +148,25 @@ func CleanUpWeaviate(t *testing.T, client *weaviate.Client) { } // CreateTestClient running on local host 8080 -func CreateTestClient() *weaviate.Client { - cfg := weaviate.Config{ - Host: "localhost:8080", - Scheme: "http", +func CreateTestClient(port int, connectionClient *http.Client) *weaviate.Client { + integrationTestsWithAuth := os.Getenv("INTEGRATION_TESTS_AUTH") + var cfg *weaviate.Config + wcsPw := os.Getenv("WCS_DUMMY_CI_PW") + if connectionClient == nil && integrationTestsWithAuth == "auth_enabled" && wcsPw != "" { + clientCredentialConf := auth.ResourceOwnerPasswordFlow{Username: "ms_2d0e007e7136de11d5f29fce7a53dae219a51458@existiert.net", Password: wcsPw} + var err error + cfg, err = weaviate.NewConfig("localhost:"+fmt.Sprint(WCSPort), "http", clientCredentialConf, nil) + if err != nil { + cfg = &weaviate.Config{Host: "localhost:" + fmt.Sprint(port), Scheme: "http"} + } + } else { + cfg = &weaviate.Config{ + Host: "localhost:" + fmt.Sprint(port), + Scheme: "http", + ConnectionClient: connectionClient, + } } - client := weaviate.New(cfg) + client := weaviate.New(*cfg) return client } diff --git a/v4/weaviate/auth/auth.go b/v4/weaviate/auth/auth.go new file mode 100644 index 00000000..54cd1c84 --- /dev/null +++ b/v4/weaviate/auth/auth.go @@ -0,0 +1,189 @@ +package auth + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/weaviate/weaviate-go-client/v4/weaviate/connection" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" +) + +const oidcConfigURL = "/.well-known/openid-configuration" + +type Config interface { + GetAuthClient(con *connection.Connection) (*http.Client, error) +} + +type authBase struct { + ClientId string + WeaviateScopes []string + TokenEndpoint string +} + +func (ab *authBase) getIdAndTokenEndpoint(ctx context.Context, con *connection.Connection) error { + rest, err := con.RunREST(ctx, oidcConfigURL, http.MethodGet, nil) + if err != nil { + return err + } + cfg := struct { + Href string `json:"href"` + ClientID string `json:"clientId"` + Scopes []string `json:"scopes"` + }{} + + switch status := rest.StatusCode; status { + case 404: + log.Println("Auth001: The client was configured to use authentication, but weaviate is configured without" + + "authentication. Are you sure this is correct?") + return nil + case 200: // status code is ok + decodeErr := rest.DecodeBodyIntoTarget(&cfg) + if decodeErr != nil { + // Some setups are behind proxies that return some default page - for example a login - for all requests. + // If the response is not json, we assume that this is the case and try unauthenticated access. + log.Printf("Auth005: Could not parse Weaviates OIDC configuration, using unauthenticated access. If "+ + "you added an authorization header yourself it will be unaffected. This can happen if weaviate is "+ + "miss-configured or you have a proxy inbetween the client and weaviate. You can test this by visiting %v.", + oidcConfigURL) + + return nil + } + default: + return fmt.Errorf("OIDC configuration url %s returned status code %v", oidcConfigURL, rest.StatusCode) + } + + endpoints, err := con.RunRESTExternal(context.TODO(), cfg.Href, http.MethodGet, nil) + if err != nil { + return err + } + var resultEndpoints map[string]interface{} + decodeEndpointErr := endpoints.DecodeBodyIntoTarget(&resultEndpoints) + if decodeEndpointErr != nil { + return err + } + + tokenEndpoint, ok := resultEndpoints["token_endpoint"].(string) + if !ok { + return errors.New("could not parse token_endpoint from OIDC response") + } + ab.ClientId = cfg.ClientID + ab.WeaviateScopes = cfg.Scopes + ab.TokenEndpoint = tokenEndpoint + return nil +} + +type ClientCredentials struct { + ClientSecret string + Scopes []string + authBase +} + +func (cc ClientCredentials) GetAuthClient(con *connection.Connection) (*http.Client, error) { + err := cc.getIdAndTokenEndpoint(context.Background(), con) + if err != nil { + return nil, err + } else if cc.ClientId == "" && cc.TokenEndpoint == "" { + return nil, nil // not configured with authentication + } + + // remove openid scopes from the scopes returned by weaviate (these are returned by default). These are not accepted + // by some providers for client credentials + for j := len(cc.WeaviateScopes) - 1; j >= 0; j-- { + if cc.WeaviateScopes[j] == "openid" || cc.WeaviateScopes[j] == "email" { + if j != len(cc.WeaviateScopes) { + cc.WeaviateScopes[j] = cc.WeaviateScopes[len(cc.WeaviateScopes)-1] + } + cc.WeaviateScopes = cc.WeaviateScopes[:len(cc.WeaviateScopes)-1] + } + } + + if cc.Scopes == nil { + if strings.HasPrefix(cc.TokenEndpoint, "https://login.microsoftonline.com") { + cc.Scopes = []string{cc.ClientId + "/.default"} + } + } + cc.Scopes = append(cc.Scopes, cc.WeaviateScopes...) + + config := clientcredentials.Config{ + ClientID: cc.ClientId, + ClientSecret: cc.ClientSecret, + TokenURL: cc.TokenEndpoint, + Scopes: cc.Scopes, + } + return config.Client(context.TODO()), nil +} + +type ResourceOwnerPasswordFlow struct { + Username string + Password string + Scopes []string + authBase +} + +func (ro ResourceOwnerPasswordFlow) GetAuthClient(con *connection.Connection) (*http.Client, error) { + err := ro.getIdAndTokenEndpoint(context.Background(), con) + if err != nil { + return nil, err + } else if ro.ClientId == "" && ro.TokenEndpoint == "" { + return nil, nil // not configured with authentication + } + + if ro.Scopes == nil || len(ro.Scopes) == 0 { + ro.Scopes = []string{"offline_access"} + } + ro.Scopes = append(ro.Scopes, ro.WeaviateScopes...) + + conf := oauth2.Config{ClientID: ro.ClientId, Endpoint: oauth2.Endpoint{TokenURL: ro.TokenEndpoint}} + token, err := conf.PasswordCredentialsToken(context.TODO(), ro.Username, ro.Password) + if err != nil { + return nil, err + } + // username + password are not saved by the client, so there is no possibility of refreshing the token with a + // refresh_token. + if token.RefreshToken == "" { + log.Printf("Auth002: Your access token is valid for %v and no refresh token was provided.", token.Expiry.Sub(time.Now())) + return oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(token)), nil + } + + // creat oauth configuration that includes the endpoint and client id as a token source with a refresh token + // (if available), then the client can auto refresh the token + confRefresh := oauth2.Config{ClientID: ro.ClientId, Endpoint: oauth2.Endpoint{TokenURL: ro.TokenEndpoint}} + tokenSource := confRefresh.TokenSource(context.TODO(), &oauth2.Token{ + AccessToken: token.AccessToken, RefreshToken: token.RefreshToken, Expiry: token.Expiry, + }) + + return oauth2.NewClient(context.TODO(), tokenSource), nil +} + +type BearerToken struct { + AccessToken string + RefreshToken string + ExpiresIn uint + authBase +} + +func (bt BearerToken) GetAuthClient(con *connection.Connection) (*http.Client, error) { + // we don't need these values, but we can check if weaviate is configured with authentication enabled + err := bt.getIdAndTokenEndpoint(context.Background(), con) + if err == nil && bt.ClientId == "" && len(bt.WeaviateScopes) == 0 && bt.TokenEndpoint == "" { + return nil, nil + } + + // there is no possibility of refreshing the token without a refresh_token. + if bt.RefreshToken == "" { + log.Printf("Auth002: Your access token is valid for %v and no refresh token was provided.", time.Now().Add(time.Second*time.Duration(bt.ExpiresIn))) + return oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: bt.AccessToken})), nil + } + conf := oauth2.Config{ClientID: bt.ClientId, Endpoint: oauth2.Endpoint{TokenURL: bt.TokenEndpoint}} + tokenSource := conf.TokenSource(context.TODO(), &oauth2.Token{ + AccessToken: bt.AccessToken, RefreshToken: bt.RefreshToken, Expiry: time.Now().Add(time.Second * time.Duration(bt.ExpiresIn)), + }) + + return oauth2.NewClient(context.TODO(), tokenSource), nil +} diff --git a/v4/weaviate/backup/backup.go b/v4/weaviate/backup/backup.go index 95e0589b..55ee4cf9 100644 --- a/v4/weaviate/backup/backup.go +++ b/v4/weaviate/backup/backup.go @@ -21,8 +21,8 @@ func New(connection *connection.Connection) *API { // Creator creates backup creator builder func (s *API) Creator() *BackupCreator { return &BackupCreator{ - connection: s.connection, - statusGetter: s.CreateStatusGetter(), + connection: s.connection, + statusGetter: s.CreateStatusGetter(), } } diff --git a/v4/weaviate/batch/objectsCreate.go b/v4/weaviate/batch/objectsCreate.go index f1bb188b..c1c43c7e 100644 --- a/v4/weaviate/batch/objectsCreate.go +++ b/v4/weaviate/batch/objectsCreate.go @@ -22,7 +22,7 @@ type ObjectsBatcher struct { } // WithObjects adds objects to the batch -func (ob *ObjectsBatcher) WithObjects(object... *models.Object) *ObjectsBatcher { +func (ob *ObjectsBatcher) WithObjects(object ...*models.Object) *ObjectsBatcher { ob.objects = append(ob.objects, object...) return ob } diff --git a/v4/weaviate/connection/rest.go b/v4/weaviate/connection/rest.go index 16a6c90b..68f1b7aa 100644 --- a/v4/weaviate/connection/rest.go +++ b/v4/weaviate/connection/rest.go @@ -5,10 +5,13 @@ import ( "context" "encoding/json" "io" - "io/ioutil" + "log" "net/http" + "runtime" + "time" "github.com/weaviate/weaviate-go-client/v4/weaviate/fault" + "golang.org/x/oauth2" ) const apiVersion = "v1" @@ -18,6 +21,11 @@ type Connection struct { basePath string httpClient *http.Client headers map[string]string + doneCh chan bool +} + +func finalizer(c *Connection) { + c.doneCh <- true } // NewConnection based on scheme://host @@ -27,12 +35,59 @@ func NewConnection(scheme string, host string, httpClient *http.Client, headers if client == nil { client = &http.Client{} } - - return &Connection{ + connection := &Connection{ basePath: scheme + "://" + host + "/" + apiVersion, httpClient: client, headers: headers, + doneCh: make(chan bool), + } + + // shutdown goroutine when connections is cleaned up + runtime.SetFinalizer(connection, finalizer) + + transport, ok := connection.httpClient.Transport.(*oauth2.Transport) + if ok { + connection.startRefreshGoroutine(transport) } + + return connection +} + +// startRefreshGoroutine starts a background goroutine that periodically refreshes the auth token. +// The oauth2 package only refreshes the Tokens on new http requests => if there is no request for the lifetime of +// the refresh token the client will become de-authenticated without this. +func (con *Connection) startRefreshGoroutine(transport *oauth2.Transport) { + token, err := transport.Source.Token() + if err != nil { + log.Printf("Error during token refresh, getting token: %v", err) + return + } + // there is no point in manual refreshing if there is no refresh token. Note that this is the default with client + // credentials + if token.RefreshToken == "" { + return + } + + timeToSleep := token.Expiry.Sub(time.Now()) - time.Second/10 + if timeToSleep <= 0 { + return + } + ticker := time.NewTicker(timeToSleep) + go func() { + for { + select { + case <-con.doneCh: + return + case <-ticker.C: + _, err = con.RunREST(context.TODO(), "/meta", http.MethodGet, nil) + if err != nil { + log.Printf("Error during token refresh, rest request: %v", err) + return + } + } + } + }() + } func (con *Connection) addHeaderToRequest(request *http.Request) { @@ -41,15 +96,6 @@ func (con *Connection) addHeaderToRequest(request *http.Request) { } request.Header.Set("Content-Type", "application/json") request.Header.Set("Accept", "application/json") - -} - -func (con *Connection) addURLParametersToRequest(request *http.Request, parameters map[string]string) { - q := request.URL.Query() - for key, value := range parameters { - q.Add(key, value) - } - request.URL.RawQuery = q.Encode() } func (con *Connection) marshalBody(body interface{}) (io.Reader, error) { @@ -64,7 +110,8 @@ func (con *Connection) marshalBody(body interface{}) (io.Reader, error) { } func (con *Connection) createRequest(ctx context.Context, path string, - restMethod string, body interface{}) (*http.Request, error) { + restMethod string, body interface{}, +) (*http.Request, error) { url := con.basePath + path // Create the URL jsonBody, err := con.marshalBody(body) @@ -77,7 +124,6 @@ func (con *Connection) createRequest(ctx context.Context, path string, return nil, err } con.addHeaderToRequest(request) - //con.addURLParametersToRequest(request, urlParameters) request.WithContext(ctx) return request, nil } @@ -90,7 +136,8 @@ func (con *Connection) createRequest(ctx context.Context, path string, // a response that may be parsed into a struct after the fact // error if there was a network issue func (con *Connection) RunREST(ctx context.Context, path string, - restMethod string, requestBody interface{}) (*ResponseData, error) { + restMethod string, requestBody interface{}, +) (*ResponseData, error) { request, requestErr := con.createRequest(ctx, path, restMethod, requestBody) if requestErr != nil { return nil, requestErr @@ -101,7 +148,36 @@ func (con *Connection) RunREST(ctx context.Context, path string, } defer response.Body.Close() - body, bodyErr := ioutil.ReadAll(response.Body) + body, bodyErr := io.ReadAll(response.Body) + if bodyErr != nil { + return nil, bodyErr + } + + return &ResponseData{ + Body: body, + StatusCode: response.StatusCode, + }, nil +} + +func (con *Connection) RunRESTExternal(ctx context.Context, hostAndPath string, restMethod string, requestBody interface{}) (*ResponseData, error) { + jsonBody, err := con.marshalBody(requestBody) + if err != nil { + return nil, err + } + + request, err := http.NewRequest(restMethod, hostAndPath, jsonBody) + if err != nil { + return nil, err + } + con.addHeaderToRequest(request) + request.WithContext(ctx) + response, responseErr := con.httpClient.Do(request) + if responseErr != nil { + return nil, responseErr + } + + defer response.Body.Close() + body, bodyErr := io.ReadAll(response.Body) if bodyErr != nil { return nil, bodyErr } diff --git a/v4/weaviate/data/path_test.go b/v4/weaviate/data/path_test.go index cebbcd69..896acc39 100644 --- a/v4/weaviate/data/path_test.go +++ b/v4/weaviate/data/path_test.go @@ -3,9 +3,9 @@ package data import ( "testing" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/weaviate/data/replication" "github.com/weaviate/weaviate-go-client/v4/weaviate/util" - "github.com/stretchr/testify/assert" ) func Test_buildObjectPath(t *testing.T) { diff --git a/v4/weaviate/except/handling.go b/v4/weaviate/except/handling.go index 864acfc1..c496e72a 100644 --- a/v4/weaviate/except/handling.go +++ b/v4/weaviate/except/handling.go @@ -32,7 +32,8 @@ func NewUnexpectedStatusCodeErrorFromRESTResponse(responseData *connection.Respo } // CheckResponseDataErrorAndStatusCode returns the response error if it is not nil, -// and an WeaviateClientError if the status code is not matching +// +// and an WeaviateClientError if the status code is not matching func CheckResponseDataErrorAndStatusCode(responseData *connection.ResponseData, responseErr error, expectedStatusCodes ...int) error { if responseErr != nil { return NewDerivedWeaviateClientError(responseErr) diff --git a/v4/weaviate/fault/error.go b/v4/weaviate/fault/error.go index 9fc2f3b5..3c1625ec 100644 --- a/v4/weaviate/fault/error.go +++ b/v4/weaviate/fault/error.go @@ -3,9 +3,10 @@ package fault import "fmt" // WeaviateClientError is returned if the client experienced an error. -// If the error is due to weaviate returning an unexpected status code the IsUnexpectedStatusCode field will be true -// and the StatusCode field will be set -// If the error occurred for another reason the DerivedFromError will be set and IsUnexpectedStatusCode will be false +// +// If the error is due to weaviate returning an unexpected status code the IsUnexpectedStatusCode field will be true +// and the StatusCode field will be set +// If the error occurred for another reason the DerivedFromError will be set and IsUnexpectedStatusCode will be false type WeaviateClientError struct { IsUnexpectedStatusCode bool StatusCode int diff --git a/v4/weaviate/filters/where_test.go b/v4/weaviate/filters/where_test.go index bf313215..690048f7 100644 --- a/v4/weaviate/filters/where_test.go +++ b/v4/weaviate/filters/where_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/weaviate/weaviate-go-client/v4/test/helpers" "github.com/weaviate/weaviate/entities/models" - "github.com/stretchr/testify/assert" ) func TestWhereBuilder_BuildOperandsRecursively(t *testing.T) { diff --git a/v4/weaviate/graphql/aggregate.go b/v4/weaviate/graphql/aggregate.go index 2325c4bc..f28c2ed0 100644 --- a/v4/weaviate/graphql/aggregate.go +++ b/v4/weaviate/graphql/aggregate.go @@ -48,7 +48,8 @@ func (ab *AggregateBuilder) WithWhere(where *filters.WhereBuilder) *AggregateBui } // WithGroupBy adds the group by property clause as the filter. -// The group by value/path clause still needs to be set in the WithFields field. +// +// The group by value/path clause still needs to be set in the WithFields field. func (ab *AggregateBuilder) WithGroupBy(propertyName string) *AggregateBuilder { ab.includesFilterClause = true ab.groupByClausePropertyName = propertyName diff --git a/v4/weaviate/graphql/aggregate_test.go b/v4/weaviate/graphql/aggregate_test.go index c90b791b..598c6314 100644 --- a/v4/weaviate/graphql/aggregate_test.go +++ b/v4/weaviate/graphql/aggregate_test.go @@ -3,8 +3,8 @@ package graphql import ( "testing" - "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" "github.com/stretchr/testify/assert" + "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" ) func TestAggregateBuilder(t *testing.T) { diff --git a/v4/weaviate/graphql/bm25builder.go b/v4/weaviate/graphql/bm25builder.go index 5434d77f..b5973d74 100644 --- a/v4/weaviate/graphql/bm25builder.go +++ b/v4/weaviate/graphql/bm25builder.go @@ -1,10 +1,9 @@ package graphql import ( + "encoding/json" "fmt" "strings" - - "encoding/json" ) type BM25ArgumentBuilder struct { diff --git a/v4/weaviate/graphql/explore_test.go b/v4/weaviate/graphql/explore_test.go index 6ff8546a..37a41b17 100644 --- a/v4/weaviate/graphql/explore_test.go +++ b/v4/weaviate/graphql/explore_test.go @@ -11,7 +11,6 @@ import ( ) func TestExploreBuilder(t *testing.T) { - t.Run("Simple Explore", func(t *testing.T) { conMock := &MockRunREST{} diff --git a/v4/weaviate/graphql/get_test.go b/v4/weaviate/graphql/get_test.go index a0a668f9..9d99d295 100644 --- a/v4/weaviate/graphql/get_test.go +++ b/v4/weaviate/graphql/get_test.go @@ -7,10 +7,10 @@ import ( "os" "testing" - "github.com/weaviate/weaviate-go-client/v4/weaviate/connection" - "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/weaviate/weaviate-go-client/v4/weaviate/connection" + "github.com/weaviate/weaviate-go-client/v4/weaviate/filters" ) // MockRunREST for testing @@ -31,7 +31,6 @@ func (mrr *MockRunREST) RunREST(ctx context.Context, path string, restMethod str } func TestQueryBuilder(t *testing.T) { - t.Run("Simple Get", func(t *testing.T) { conMock := &MockRunREST{} @@ -795,7 +794,6 @@ func TestBM25Builder(t *testing.T) { expected := `{Get {Pizza (bm25:{query: "good", properties: ["name","description"]}) {}}}` assert.Equal(t, expected, query) - } func TestHybridBuilder(t *testing.T) { diff --git a/v4/weaviate/schema/shardUpdater.go b/v4/weaviate/schema/shardUpdater.go index 9a32121c..b940fdd8 100644 --- a/v4/weaviate/schema/shardUpdater.go +++ b/v4/weaviate/schema/shardUpdater.go @@ -42,7 +42,8 @@ func (s *ShardUpdater) Do(ctx context.Context) (*models.ShardStatus, error) { } func updateShard(ctx context.Context, conn *connection.Connection, className, shardName string, - status models.ShardStatus) (*models.ShardStatus, error) { + status models.ShardStatus, +) (*models.ShardStatus, error) { responseData, err := conn.RunREST( ctx, fmt.Sprintf("/schema/%s/shards/%s", className, shardName), http.MethodPut, status) if err != nil { diff --git a/v4/weaviate/testenv/environment.go b/v4/weaviate/testenv/environment.go index 8471543a..759a0a09 100644 --- a/v4/weaviate/testenv/environment.go +++ b/v4/weaviate/testenv/environment.go @@ -13,9 +13,10 @@ import ( // SetupLocalWeaviate creates a local weaviate running on 8080 using docker compose // Will only wait for it to be reachable if env `EXTERNAL_WEAVIATE_RUNNING` is set to True. -// `EXTERNAL_WEAVIATE_RUNNING` should be set if all tests are supposed to be run in a test suit. -// This prevents unnecessary starting and stopping of the docker-compose which prevents errors -// due to syncing issues and speeds up the process +// +// `EXTERNAL_WEAVIATE_RUNNING` should be set if all tests are supposed to be run in a test suit. +// This prevents unnecessary starting and stopping of the docker-compose which prevents errors +// due to syncing issues and speeds up the process func SetupLocalWeaviate() error { if !isExternalWeaviateRunning() { err := test.SetupWeaviate() @@ -29,9 +30,10 @@ func SetupLocalWeaviate() error { // SetupLocalWeaviateDeprecated creates a local weaviate running on 8080 using docker compose // for pre-v1.14 backwards compatibility tests. // Will only wait for it to be reachable if env `EXTERNAL_WEAVIATE_RUNNING` is set to True. -// `EXTERNAL_WEAVIATE_RUNNING` should be set if all tests are supposed to be run in a test suit. -// This prevents unnecessary starting and stopping of the docker-compose which prevents errors -// due to syncing issues and speeds up the process +// +// `EXTERNAL_WEAVIATE_RUNNING` should be set if all tests are supposed to be run in a test suit. +// This prevents unnecessary starting and stopping of the docker-compose which prevents errors +// due to syncing issues and speeds up the process func SetupLocalWeaviateDeprecated() error { if !isExternalWeaviateRunning() { err := test.SetupWeaviateDeprecated() @@ -75,7 +77,8 @@ func WaitForWeaviate() error { // TearDownLocalWeaviate shuts down the locally started weaviate docker compose // If `EXTERNAL_WEAVIATE_RUNNING` this function will not do anything -// see SetupLocalWeaviate for more info. +// +// see SetupLocalWeaviate for more info. func TearDownLocalWeaviate() error { if isExternalWeaviateRunning() { return nil @@ -88,7 +91,8 @@ func TearDownLocalWeaviate() error { // TearDownLocalWeaviateDeprecated shuts down the locally started weaviate docker compose // used for pre-v1.14 backwards compatibility tests. // If `EXTERNAL_WEAVIATE_RUNNING` this function will not do anything -// see SetupLocalWeaviate for more info. +// +// see SetupLocalWeaviate for more info. func TearDownLocalWeaviateDeprecated() error { if isExternalWeaviateRunning() { return nil diff --git a/v4/weaviate/weaviate_client.go b/v4/weaviate/weaviate_client.go index aba8ef9d..af5f2d81 100644 --- a/v4/weaviate/weaviate_client.go +++ b/v4/weaviate/weaviate_client.go @@ -2,6 +2,9 @@ package weaviate import ( "context" + "net/http" + + "github.com/weaviate/weaviate-go-client/v4/weaviate/auth" "github.com/weaviate/weaviate-go-client/v4/weaviate/backup" "github.com/weaviate/weaviate-go-client/v4/weaviate/batch" "github.com/weaviate/weaviate-go-client/v4/weaviate/classifications" @@ -13,7 +16,6 @@ import ( "github.com/weaviate/weaviate-go-client/v4/weaviate/misc" "github.com/weaviate/weaviate-go-client/v4/weaviate/schema" "github.com/weaviate/weaviate-go-client/v4/weaviate/util" - "net/http" ) // Config of the client endpoint @@ -33,6 +35,19 @@ type Config struct { Headers map[string]string } +func NewConfig(host string, scheme string, authConfig auth.Config, headers map[string]string) (*Config, error) { + var client *http.Client + var err error + if authConfig != nil { + tmpCon := connection.NewConnection(scheme, host, nil, headers) + client, err = authConfig.GetAuthClient(tmpCon) + if err != nil { + return nil, err + } + } + return &Config{Host: host, Scheme: scheme, Headers: headers, ConnectionClient: client}, nil +} + // Client implementing the weaviate API // Every function represents one API group of weaviate and provides a set of functions and builders to interact with them. //