Skip to content

Commit

Permalink
Add api automation (#77)
Browse files Browse the repository at this point in the history
* Add api automation

* Fix api automation and add api

* Fix client

* Change CURL to env variable

* Clean up env variables

* Removing the old api clients

* Change chunk search interaction

* internalise delete_collection

* Fix delete_collection function name

* move collection creation to the beginning

* Add override

* Change decorator

* Fix arguments in light of API change

* Fix default parameter kwargs in post requests

* Fix syntax for semi local-global variable

* Fix min score

* Change collection name to kwarg

* Fix search_by_id arg

* Change API Automation tool

* fix fields to search_fields

* Fix the document_id

* Push fix for edit_document

* Add typecheck to colelction name in SDK

* Try fix collectionclient

* Deprecate collection client

* Add python pytest

* Update API automation

* Add error import

* Update CONTRIBUTING

* Add openapi to sdk to dev requirement

* move testing requirement
  • Loading branch information
boba-and-beer committed Feb 24, 2021
1 parent aa11d99 commit 3dfd32b
Show file tree
Hide file tree
Showing 18 changed files with 2,873 additions and 135 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-pytest.yml
Expand Up @@ -49,4 +49,4 @@ jobs:
API_KEY: ${{ secrets.API_KEY }}
VI_USERNAME: ${{ secrets.VI_USERNAME }}
VI_API_KEY: ${{ secrets.VI_API_KEY }}
run: python -m pytest ${{ matrix.test-path }} --use_client
run: python -m pytest ${{ matrix.test-path }} --use_client --reruns 5 --reruns-delay 1
10 changes: 8 additions & 2 deletions CONTRIBUTING.md
Expand Up @@ -103,9 +103,15 @@ try to re-run the notebooks to ensure that your submission is not causing errors

### Style guide

For documentation strings, `Vector AI` follows the [google style](https://google.github.io/styleguide/pyguide.html).
For documentation strings, `Vector AI` aims to follows the [google style](https://google.github.io/styleguide/pyguide.html) as closely as possible.
For internal attributes, we use _ in front of the name of the attribute.
We also prefer being as explicit as possible and try to avoid args and kwargs as much as we can.
However, we will not reject PRs if they do not follow this style.

### Running Automated API creation

To run the automated APi creation, simply run:
```
python utils/automate_api.py
```

#### This guide was inspired by Transformers [transformers guide to contributing](https://github.com/huggingface/transformers/blob/master/CONTRIBUTING.md) which was influenced by Scikit-learn [scikit-learn guide to contributing](https://github.com/scikit-learn/scikit-learn/blob/master/CONTRIBUTING.md).
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -6,8 +6,8 @@

core_req = ["requests", "numpy", "pandas", "appdirs>=1.4.4", "tqdm>=4.27.0", "plotly>=4.0.0"]
extras_req = {
"dev" : ["twine", "black", "pytest", "pytest-cov", "vectorai"],
"test" : ["pytest", "pytest-cov"],
"dev" : ["twine", "black", "pytest", "pytest-cov", "vectorai", "openapi-to-sdk"],
"test" : ["pytest", "pytest-cov", "pytest-rerunfailures"],
"docs" : ["sphinx-rtd-theme>=0.5.0", "nbsphinx>=0.7.1"]
}
extras_req["all"] = [p for r in extras_req.values() for p in r]
Expand Down
14 changes: 7 additions & 7 deletions tests/conftest.py
Expand Up @@ -3,7 +3,7 @@
"""
import pytest
import os
from vectorai.client import ViClient, ViCollectionClient
from vectorai.client import ViClient
from vectorai.analytics.client import ViAnalyticsClient
from vectorai.models.deployed import ViText2Vec
import random
Expand Down Expand Up @@ -54,12 +54,12 @@ def test_client(test_username, test_api_key):
def test_collection_name():
return "test_colour_col_" + str(get_random_string(3))

@pytest.fixture
def test_collection_client(test_username, test_api_key, test_collection_name):
"""Testing for the client login.
"""
client = ViCollectionClient(username=test_username, api_key=test_api_key, collection_name=test_collection_name)
return client
# @pytest.fixture
# def test_collection_client(test_username, test_api_key, test_collection_name):
# """Testing for the client login.
# """
# client = ViCollectionClient(username=test_username, api_key=test_api_key, collection_name=test_collection_name)
# return client

@pytest.fixture
def test_analytics_client(test_username, test_api_key):
Expand Down
5 changes: 3 additions & 2 deletions tests/test_read.py
Expand Up @@ -34,7 +34,7 @@ def test_advanced_search_by_id(self, test_client, test_collection_name):
]
results = test_client.advanced_search_by_id(test_collection_name,
document_id=test_client.random_documents(test_collection_name)['documents'][0]['_id'],
fields={'color_vector_':1}, filters=filter_query)
search_fields={'color_vector_':1}, filters=filter_query)
assert len(results) > 0

@pytest.mark.use_client
Expand Down Expand Up @@ -102,6 +102,7 @@ def test_search_collections(test_client):
cn = 'example_collection_123y8io'
if cn not in test_client.list_collections():
test_client.create_collection(cn)
time.sleep(2)
assert len(test_client.search_collections('123y8io')) > 0, "Not searching collections properly."
test_client.delete_collection(cn)

Expand All @@ -114,7 +115,7 @@ def test_random_recommendation_smoke_test(test_client, test_collection_name):
time.sleep(2)
results = test_client.random_recommendation(
test_collection_name,
field='color_vector_')
search_field='color_vector_')
assert len(results['results']) > 0, "Random recommendation fails."

@pytest.mark.use_client
Expand Down
9 changes: 6 additions & 3 deletions tests/test_search.py
Expand Up @@ -5,6 +5,7 @@
import pytest
import time

@pytest.mark.skip(reason="Chunk Search being altered.")
@pytest.mark.use_client
def test_chunk_search(test_client, test_collection_name):
if test_collection_name in test_client.list_collections():
Expand All @@ -13,7 +14,9 @@ def test_chunk_search(test_client, test_collection_name):
test_client.create_sample_documents(10, include_chunks=True))
time.sleep(5)
vec = np.random.rand(1, 30).tolist()[0]
results = test_client.chunk_search(test_collection_name,
vector=vec,
search_fields=['chunk.color_chunkvector_'])
results = test_client.chunk_search(
test_collection_name,
vector=vec,
search_fields=['chunk.color_chunkvector_'],
)
assert 'error' not in results.keys()
165 changes: 84 additions & 81 deletions tests/test_write.py
Expand Up @@ -54,7 +54,8 @@ def assert_json_serializable(document, temp_json_file="test.json"):

class TestInsert:
@pytest.mark.use_client
def test_insert_documents_simple_and_collection_stats_match(self, test_client, test_collection_name):
def test_insert_documents_simple_and_collection_stats_match(self, test_client,
test_collection_name):
"""
Testing for simple document insertion
"""
Expand Down Expand Up @@ -84,7 +85,8 @@ def test_inserting_documents_without_id_fields(self, test_client, test_collectio
time.sleep(3)

@pytest.mark.use_client
def test_inserting_documents_without_id_fields_with_overwrite(self, test_client, test_collection_name):
def test_inserting_documents_without_id_fields_with_overwrite(self, test_client,
test_collection_name):
"""
Test inserting documents if they do not have an ID field.
"""
Expand Down Expand Up @@ -117,7 +119,8 @@ def test_inserting_documents_when_id_is_not_a_string(self, test_client, test_col
time.sleep(3)

@pytest.mark.use_client
def test_inserting_documents_when_id_is_not_a_string_with_overwrite(self, test_client, test_collection_name):
def test_inserting_documents_when_id_is_not_a_string_with_overwrite(self, test_client,
test_collection_name):
"""
Test inserting documents when ID is not a string
"""
Expand Down Expand Up @@ -204,7 +207,7 @@ def test_edit_document(self, test_client, test_collection_name):
collection_name=test_collection_name, edits=edits
)
time.sleep(2)
doc = client.id(test_collection_name, document_id="1")
doc = client.id(collection_name=test_collection_name, document_id="1")
assert doc["location"] == "Paris"

@pytest.mark.use_client
Expand Down Expand Up @@ -256,9 +259,9 @@ def test_edit_documents(self, test_client, test_collection_name):
{"_id": "1", "location": "New York",},
]
test_client.edit_documents(test_collection_name, edits)
doc = test_client.id(test_collection_name, document_id="2")
doc = test_client.id(collection_name=test_collection_name, document_id="2")
assert doc["location"] == "Sydney"
doc = test_client.id(test_collection_name, document_id="1")
doc = test_client.id(collection_name=test_collection_name, document_id="1")
assert doc['location'] == 'New York'

def test__write_document_nested_field():
Expand Down Expand Up @@ -350,37 +353,37 @@ def test_multiprocess_insert_with_error_with_overwrite(test_client, test_collect
assert test_client.collection_stats(test_collection_name)['number_of_documents'] == NUM_OF_DOCUMENTS_INSERTED
test_client.delete_collection(test_collection_name)

@pytest.mark.use_client
def test_multiprocess_with_collection_client(test_collection_client, test_collection_name):
NUM_OF_DOCUMENTS_INSERTED = 100
if test_collection_client.collection_name in test_collection_client.list_collections():
test_collection_client.delete_collection()
time.sleep(5)
documents = test_collection_client.create_sample_documents(NUM_OF_DOCUMENTS_INSERTED)
results = test_collection_client.insert_documents(documents, workers=5)
time.sleep(10)
assert len(results['failed_document_ids']) == 0
assert test_collection_client.collection_name in test_collection_client.list_collections()
assert test_collection_client.collection_stats()['number_of_documents'] == NUM_OF_DOCUMENTS_INSERTED
test_collection_client.delete_collection()

@pytest.mark.use_client
def test_multiprocess__with_error_with_collection_client(test_collection_client):
NUM_OF_DOCUMENTS_INSERTED = 100
if test_collection_client.collection_name in test_collection_client.list_collections():
test_collection_client.delete_collection()
time.sleep(5)
documents = test_collection_client.create_sample_documents(NUM_OF_DOCUMENTS_INSERTED)
documents.append({
'_id': 9993,
'color': np.nan
})
# This should result in 1 failure
results = test_collection_client.insert_documents(documents, workers=5, overwrite=True)
time.sleep(10)
assert len(results['failed_document_ids']) == 1
assert test_collection_client.collection_name in test_collection_client.list_collections()
assert test_collection_client.collection_stats()['number_of_documents'] == NUM_OF_DOCUMENTS_INSERTED
# @pytest.mark.use_client
# def test_multiprocess_with_collection_client(test_collection_client, test_collection_name):
# NUM_OF_DOCUMENTS_INSERTED = 100
# if test_collection_client.collection_name in test_collection_client.list_collections():
# test_collection_client.delete_collection()
# time.sleep(5)
# documents = test_collection_client.create_sample_documents(NUM_OF_DOCUMENTS_INSERTED)
# results = test_collection_client.insert_documents(documents, workers=5)
# time.sleep(10)
# assert len(results['failed_document_ids']) == 0
# assert test_collection_client.collection_name in test_collection_client.list_collections()
# assert test_collection_client.collection_stats()['number_of_documents'] == NUM_OF_DOCUMENTS_INSERTED
# test_collection_client.delete_collection()

# @pytest.mark.use_client
# def test_multiprocess__with_error_with_collection_client(test_collection_client):
# NUM_OF_DOCUMENTS_INSERTED = 100
# if test_collection_client.collection_name in test_collection_client.list_collections():
# test_collection_client.delete_collection()
# time.sleep(5)
# documents = test_collection_client.create_sample_documents(NUM_OF_DOCUMENTS_INSERTED)
# documents.append({
# '_id': 9993,
# 'color': np.nan
# })
# # This should result in 1 failure
# results = test_collection_client.insert_documents(documents, workers=5, overwrite=True)
# time.sleep(10)
# assert len(results['failed_document_ids']) == 1
# assert test_collection_client.collection_name in test_collection_client.list_collections()
# assert test_collection_client.collection_stats()['number_of_documents'] == NUM_OF_DOCUMENTS_INSERTED

@pytest.mark.use_client
def test_multiprocess_with_overwrite(test_client, test_collection_name):
Expand Down Expand Up @@ -409,21 +412,21 @@ def test_multiprocess_with_overwrite_insert(test_client, test_collection_name):
@pytest.mark.use_client
def test_multiprocess_overwrite(test_client, test_collection_name):
if test_collection_name in test_client.list_collections():
test_client.delete_collection()
test_client.delete_collection(test_collection_name)
time.sleep(5)
NUM_OF_DOCS = 100
docs = test_client.create_sample_documents(NUM_OF_DOCS)
test_client.insert_documents(test_collection_name, docs[0:5], workers=1, overwrite=False)
# For document with id '3'
TEST_ID = '3'
id_document = test_client.id(test_collection_name, TEST_ID)
id_document = test_client.id(collection_name=test_collection_name, document_id=TEST_ID)
test_client.set_field('test.field', id_document, 'stranger')
docs[3] = id_document
print(docs[3])
docs[3].update({'_id': '3'})
response = test_client.insert_documents(test_collection_name, docs[3:5], workers=1,
overwrite=True)
id_document = test_client.id(test_collection_name, TEST_ID)
id_document = test_client.id(collection_name=test_collection_name, document_id=TEST_ID)
assert test_client.get_field('test.field', id_document) == 'stranger'
time.sleep(5)
test_client.delete_collection(test_collection_name)
Expand All @@ -438,57 +441,57 @@ def test_multiprocess_not_overwrite(test_client, test_collection_name):
test_client.insert_documents(test_collection_name, docs[0:5], workers=1, overwrite=False)
# For document with id '3'
TEST_ID = '3'
id_document = test_client.id(test_collection_name, TEST_ID)
id_document = test_client.id(collection_name=test_collection_name, document_id=TEST_ID)
test_client.set_field('test.field', id_document, 'stranger')
docs[3] = id_document
docs[3].update({'_id': '3'})
response = test_client.insert_documents(test_collection_name, docs[3:5], workers=1,
overwrite=False)
id_document = test_client.id(test_collection_name, TEST_ID)
id_document = test_client.id(collection_name=test_collection_name, document_id=TEST_ID)
with pytest.raises(MissingFieldError):
test_client.get_field('test.field', id_document)
time.sleep(5)
test_client.delete_collection(test_collection_name)

@pytest.mark.use_client
def test_multiprocess_overwrite_collection_client(test_collection_client, test_collection_name):
if test_collection_client.collection_name in test_collection_client.list_collections():
test_collection_client.delete_collection()
time.sleep(5)
NUM_OF_DOCS = 10
docs = test_collection_client.create_sample_documents(NUM_OF_DOCS)
test_collection_client.insert_documents(docs[0:5], workers=1, overwrite=False)
# For document with id '3'
TEST_ID = '3'
id_document = test_collection_client.id(TEST_ID)
test_collection_client.set_field('test.field', id_document, 'stranger')
docs[3] = id_document
docs[3].update({'_id': '3'})
response = test_collection_client.insert_documents(docs[3:5], workers=1,
overwrite=True)
id_document = test_collection_client.id(TEST_ID)
assert test_collection_client.get_field('test.field', id_document) == 'stranger'
time.sleep(5)
test_collection_client.delete_collection()

@pytest.mark.use_client
def test_multiprocess_not_overwrite_collection_client(test_collection_client, test_collection_name):
NUM_OF_DOCS = 10
docs = test_collection_client.create_sample_documents(NUM_OF_DOCS)
test_collection_client.insert_documents(docs[0:5], workers=1, overwrite=False)
# For document with id '3'
TEST_ID = '3'
id_document = test_collection_client.id(TEST_ID)
test_collection_client.set_field('test.field', id_document, 'stranger')
docs[3] = id_document
docs[3].update({'_id': '3'})
response = test_collection_client.insert_documents(docs[3:5], workers=1,
overwrite=False)
id_document = test_collection_client.id(TEST_ID)
with pytest.raises(MissingFieldError):
test_collection_client.get_field('test.field', id_document)
time.sleep(5)
test_collection_client.delete_collection()
# @pytest.mark.use_client
# def test_multiprocess_overwrite_collection_client(test_collection_client, test_collection_name):
# if test_collection_client.collection_name in test_collection_client.list_collections():
# test_collection_client.delete_collection()
# time.sleep(5)
# NUM_OF_DOCS = 10
# docs = test_collection_client.create_sample_documents(NUM_OF_DOCS)
# test_collection_client.insert_documents(docs[0:5], workers=1, overwrite=False)
# # For document with id '3'
# TEST_ID = '3'
# id_document = test_collection_client.id(document_id=TEST_ID)
# test_collection_client.set_field('test.field', id_document, 'stranger')
# docs[3] = id_document
# docs[3].update({'_id': '3'})
# response = test_collection_client.insert_documents(docs[3:5], workers=1,
# overwrite=True)
# id_document = test_collection_client.id(document_id=TEST_ID)
# assert test_collection_client.get_field('test.field', id_document) == 'stranger'
# time.sleep(5)
# test_collection_client.delete_collection()

# @pytest.mark.use_client
# def test_multiprocess_not_overwrite_collection_client(test_collection_client, test_collection_name):
# NUM_OF_DOCS = 10
# docs = test_collection_client.create_sample_documents(NUM_OF_DOCS)
# test_collection_client.insert_documents(docs[0:5], workers=1, overwrite=False)
# # For document with id '3'
# TEST_ID = '3'
# id_document = test_collection_client.id(document_id=TEST_ID)
# test_collection_client.set_field('test.field', id_document, 'stranger')
# docs[3] = id_document
# docs[3].update({'_id': '3'})
# response = test_collection_client.insert_documents(docs[3:5], workers=1,
# overwrite=False)
# id_document = test_collection_client.id(document_id=TEST_ID)
# with pytest.raises(MissingFieldError):
# test_collection_client.get_field('test.field', id_document)
# time.sleep(5)
# test_collection_client.delete_collection()

def test_dummy_vector(test_client):
"""
Expand Down
15 changes: 9 additions & 6 deletions tests/utils.py
@@ -1,15 +1,15 @@
import time
import random
import string
from vectorai import ViClient, ViCollectionClient
from vectorai import ViClient

class TempClient:
def __init__(self, client, collection_name: str=None):
self.client = client
if isinstance(client, ViClient):
self.collection_name = collection_name
elif isinstance(client, ViCollectionClient):
self.collection_name = self.client.collection_name
# elif isinstance(client, ViCollectionClient):
# self.collection_name = self.client.collection_name

def teardown_collection(self):
if self.collection_name in self.client.list_collections():
Expand Down Expand Up @@ -40,6 +40,9 @@ def __init__(self, client, collection_name: str=None, num_of_docs: int=10):
self.collection_name = collection_name
self.client.collection_name = collection_name
self.num_of_docs = num_of_docs
self.teardown_collection()
self.client.insert_documents(self.collection_name,
self.client.create_sample_documents(self.num_of_docs))

def generate_random_collection_name(self):
return self.generate_random_string(20)
Expand All @@ -49,7 +52,7 @@ def generate_random_string(self, num_of_letters):
return ''.join(random.choice(letters) for i in range(num_of_letters))

def __enter__(self):
self.teardown_collection()
self.client.insert_documents(self.collection_name,
self.client.create_sample_documents(self.num_of_docs))
# self.teardown_collection()
# self.client.insert_documents(self.collection_name,
# self.client.create_sample_documents(self.num_of_docs))
return self.client

0 comments on commit 3dfd32b

Please sign in to comment.