Skip to content

Commit

Permalink
Implements Atlas' Saved Search REST API
Browse files Browse the repository at this point in the history
* Update code to add GET /v2/search/saved POST /v2/search/saved PUT /v2/search/saved DELETE /v2/search/saved/{guid}  GET /v2/search/saved/{name}

Added code and usage documentation for below -
GET /v2/search/saved
POST /v2/search/saved
PUT /v2/search/saved
DELETE /v2/search/saved/{guid}
GET /v2/search/saved/{name}

I will create separate PR for below and will add test case as well for
all gradually -

GET /v2/search/saved/execute/{name}
GET /v2/search/saved/execute/guid/{guid}

* Added test cases for saved search

* Added few test cases for saved search

* Added all test cases for saved search
  • Loading branch information
pritampan authored and verdan committed Jul 6, 2019
1 parent 2db8406 commit 291de8b
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 0 deletions.
1 change: 1 addition & 0 deletions atlasclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
'search_fulltext': models.SearchFulltext,
'relationship': models.Relationship,
'relationship_guid': models.RelationshipGuid,
'search_saved': models.SearchSaved
}


Expand Down
59 changes: 59 additions & 0 deletions atlasclient/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,3 +846,62 @@ class SearchFulltext(base.QueryableModel):
relationships = {'entities': Entity,
'attributes': AttributeDef,
'fullTextResults': FullTextResult}


class SearchSavedCollection(base.QueryableModelCollection):
"""We can also remove this part but just keeping same structure as above"""
def load(self, response):
for item in response:
model = self.model_class(self,
href=item.get('href'))
model.load(item)
self._models.append(model)


class SearchSaved(base.QueryableModel):
collection_class = SearchSavedCollection
path = 'search/saved'
data_key = 'search_saved'
primary_key = 'guid'
fields = ('name', 'searchParameters', 'ownerName', 'searchType', 'uiParameters')

def inflate(self):
"""Load the resource from the server, if not already loaded. Removed params check for searchParameters"""
if not self._is_inflated:
if self._is_inflating:
# catch infinite recursion when attempting to inflate
# an object that doesn't have enough data to inflate
msg = ("There is not enough data to inflate this object. "
"Need either an href: {} or a {}: {}")
msg = msg.format(self._href, self.primary_key, self._data.get(self.primary_key))
raise exceptions.ClientError(msg)

self._is_inflating = True

try:
params = {}
# To keep the method same as the original request. The default is GET
self.load(self.client.request(self.method, self.url, **params))
except Exception:
self.load(self._data)

self._is_inflated = True
self._is_inflating = False
return self

@events.evented
def create(self, data, **kwargs):
"""This is override method.
"""
self.client.post(self.url, data=data)
return self

@events.evented
def update(self, data, **kwargs):
"""
This is override method
"""
self.method = 'put'
self.load(self.client.put(self.parent.url, data=data))
return self

66 changes: 66 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,72 @@ To retrieve data for the specified DSL::
print(e.classificationNames)
print(e.attributes)

SavedSearchREST
----------

This section explains how to get, create saved search, update or delete them.

Get all saved search for user
~~~~~~~~~~~~~

To retrieve saved search for the Atlas user::

search_saved = client.search_saved()
for s in search_saved:
print(s._data)
print(s.name)


Get saved search by name (for user)
~~~~~~~~~~~~~

To retrieve saved search for the Atlas user by name::

search_saved = client.search_saved(NAME)
print(s.name)
print(s.ownerName)


Create saved search by name (for user)
~~~~~~~~~~~~~

To create saved search for the Atlas user by name::

payload = """{
"name": "trying",
"ownerName": "svc_data_catalog_api",
"searchType": "BASIC",
"searchParameters": {
"typeName": "rdbms_db",
"excludeDeletedEntities": true,
"includeClassificationAttributes": false,
"includeSubTypes": true,
"includeSubClassifications": true,
"limit": 0,
"offset": 0
},
"uiParameters": "Select::0,Name::1,Owner::2,Description::3,Type::4,Classifications::5,Term::6,Db::7"
}"""

response = client.search_saved.create(data=json.loads(payload))


Update saved search by guid (for user)
~~~~~~~~~~~~~

To create saved search for the Atlas user by name::

payload = """{"guid": "fa1f15f0-09fc-403d-8ad7-3bcac379c3f9", "name": "trying2"}"""
response = client.search_saved.update(data=json.loads(payload))


To delete saved search by guid (for user)
~~~~~~~~~~~~~

To delete saved search for the Atlas user by guid::

client.search_saved.delete(guid=GUID)


EntityREST
----------
Expand Down
34 changes: 34 additions & 0 deletions tests/response_json/search_saved_get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[{
"name" : "TESTNAME",
"ownerName" : "...",
"searchParameters" : {
"attributes" : [ "...", "..." ],
"classification" : "...",
"entityFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "OR",
"criterion" : [ { }, { } ],
"operator" : "LIKE"
},
"excludeDeletedEntities" : true,
"includeClassificationAttributes" : true,
"includeSubClassifications" : true,
"includeSubTypes" : true,
"limit" : 12345,
"offset" : 12345,
"query" : "...",
"tagFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "AND",
"criterion" : [ { }, { } ],
"operator" : "ENDS_WITH"
},
"termName" : "...",
"typeName" : "..."
},
"searchType" : "BASIC",
"uiParameters" : "...",
"guid" : "8bbea92b-d98c-4613-ae6e-1a9d0b4f344b"
}]
34 changes: 34 additions & 0 deletions tests/response_json/search_saved_guid_get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name" : "TESTNAME",
"ownerName" : "...",
"searchParameters" : {
"attributes" : [ "...", "..." ],
"classification" : "...",
"entityFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "OR",
"criterion" : [ { }, { } ],
"operator" : "GT"
},
"excludeDeletedEntities" : true,
"includeClassificationAttributes" : true,
"includeSubClassifications" : true,
"includeSubTypes" : true,
"limit" : 12345,
"offset" : 12345,
"query" : "...",
"tagFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "AND",
"criterion" : [ { }, { } ],
"operator" : "GT"
},
"termName" : "...",
"typeName" : "..."
},
"searchType" : "ADVANCED",
"uiParameters" : "...",
"guid" : "8bbea92b-d98c-4613-ae6e-1a9d0b4f344b"
}
34 changes: 34 additions & 0 deletions tests/response_json/search_saved_name_get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name" : "TESTNAME",
"ownerName" : "...",
"searchParameters" : {
"attributes" : [ "...", "..." ],
"classification" : "...",
"entityFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "OR",
"criterion" : [ { }, { } ],
"operator" : "GT"
},
"excludeDeletedEntities" : true,
"includeClassificationAttributes" : true,
"includeSubClassifications" : true,
"includeSubTypes" : true,
"limit" : 12345,
"offset" : 12345,
"query" : "...",
"tagFilters" : {
"attributeName" : "...",
"attributeValue" : "...",
"condition" : "AND",
"criterion" : [ { }, { } ],
"operator" : "GT"
},
"termName" : "...",
"typeName" : "..."
},
"searchType" : "ADVANCED",
"uiParameters" : "...",
"guid" : "..."
}
1 change: 1 addition & 0 deletions tests/response_json/search_saved_update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"guid": "8bbea92b-d98c-4613-ae6e-1a9d0b4f344b", "name": "TESTNAME"}
65 changes: 65 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,32 @@ def search_attribute_response():
response = json.load(json_data)
return(response)

@pytest.fixture(scope='class')
def search_saved_response():
with open('{}/search_saved_get.json'.format(resource_filename(__name__, RESPONSE_JSON_DIR))) as json_data:
response = json.load(json_data)
return(response)

@pytest.fixture(scope='class')
def search_saved_name_response():
with open('{}/search_saved_name_get.json'.format(resource_filename(__name__, RESPONSE_JSON_DIR))) as json_data:
response = json.load(json_data)
return(response)


@pytest.fixture(scope='class')
def search_saved_guid_response():
with open('{}/search_saved_guid_get.json'.format(resource_filename(__name__, RESPONSE_JSON_DIR))) as json_data:
response = json.load(json_data)
return(response)


@pytest.fixture(scope='class')
def search_saved_update():
with open('{}/search_saved_update.json'.format(resource_filename(__name__, RESPONSE_JSON_DIR))) as json_data:
response = json.load(json_data)
return(response)


class TestEntityREST():
def test_entity_post(self, mocker, atlas_client, entity_guid_response, entity_post_response):
Expand Down Expand Up @@ -468,3 +494,42 @@ def test_search_fulltext_get(self, mocker, atlas_client, search_attribute_respon
atlas_client.search_fulltext.client.get.assert_called_with(s.url, params=params)
for e in s.entities:
assert e.attributes['property1'] == {}

def test_search_saved_get(self, mocker, atlas_client, search_saved_response):
mocker.patch.object(atlas_client.search_saved.client, 'get')
atlas_client.search_saved.client.get.return_value = search_saved_response
search_results = atlas_client.search_saved()
for s in search_results:
assert s.name == 'TESTNAME'

def test_search_saved_name_get(self, mocker, atlas_client, search_saved_name_response):
mocker.patch.object(atlas_client.search_saved.client, 'request')
atlas_client.search_saved.client.request.return_value = search_saved_name_response
result = atlas_client.search_saved('TESTNAME')

assert result.name == 'TESTNAME'

def test_search_saved_guid_delete(self, mocker, atlas_client, search_saved_guid_response):
mocker.patch.object(atlas_client.client, 'request')
atlas_client.client.request.return_value = search_saved_guid_response
response = atlas_client.search_saved('TESTNAME')
response.delete(guid=GUID)

def test_search_saved_create(self, mocker, atlas_client, search_saved_name_response):
mocker.patch.object(atlas_client.search_saved.client, 'post')
atlas_client.search_saved.client.post.return_value = search_saved_response
atlas_client.search_saved.create(data=search_saved_response)

def test_search_saved_update(self, mocker, atlas_client, search_saved_guid_response, search_saved_update):
mocker.patch.object(atlas_client.search_saved.client, 'request')
atlas_client.search_saved.client.request.return_value = search_saved_name_response
result = atlas_client.search_saved('TESTNAME')

mocker.patch.object(atlas_client.search_saved.client, 'put')
atlas_client.search_saved.client.put.return_value = search_saved_update

result.update(data=search_saved_update)




0 comments on commit 291de8b

Please sign in to comment.