Skip to content

Commit

Permalink
Merge pull request #267 from juliamcclellan/attribute_builder
Browse files Browse the repository at this point in the history
Attribute spec
  • Loading branch information
pcattori committed Aug 16, 2019
2 parents 319dd28 + 70297af commit 850f09e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [#67](https://github.com/Datatamer/unify-client-python/issues/67) Create a dataset from a pandas `DataFrame`
- [#222](https://github.com/Datatamer/unify-client-python/issues/222) Dataset spec to update an existing dataset
- [#225](https://github.com/Datatamer/unify-client-python/issues/225) Attribute configuration spec to update an existing attribute configuration
- [#223](https://github.com/Datatamer/unify-client-python/issues/223) Update an attribute with an attribute spec

**BUG FIXES**
- [#235](https://github.com/Datatamer/unify-client-python/issues/235) Making `AttributeCollection` retrieve attributes directly instead of by streaming
Expand Down
6 changes: 6 additions & 0 deletions docs/developer-interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ Attribute
.. autoclass:: tamr_unify_client.attribute.resource.Attribute
:members:

Attribute Spec
^^^^^^^^^^^^^^

.. autoclass:: tamr_unify_client.attribute.resource.AttributeSpec
:members:

Attribute Collection
^^^^^^^^^^^^^^^^^^^^

Expand Down
80 changes: 80 additions & 0 deletions tamr_unify_client/attribute/resource.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from copy import deepcopy

from tamr_unify_client.attribute.type import AttributeType
from tamr_unify_client.base_resource import BaseResource

Expand Down Expand Up @@ -52,10 +54,88 @@ def is_nullable(self):
""":type: bool"""
return self._data.get("isNullable")

def spec(self):
"""Returns a spec representation of this attribute.
:return: The attribute spec.
:rtype: :class:`~tamr_unify_client.attribute.resource.AttributeSpec`
"""
return AttributeSpec.of(self)

def __repr__(self):
return (
f"{self.__class__.__module__}."
f"{self.__class__.__qualname__}("
f"relative_id={self.relative_id!r}, "
f"name={self.name!r})"
)


class AttributeSpec:
"""A representation of the server view of an attribute"""

def __init__(self, client, data, api_path):
self._data = data
self.client = client
self.api_path = api_path

@staticmethod
def of(resource):
"""Creates an attribute spec from an attribute.
:param resource: The existing attribute.
:type resource: :class:`~tamr_unify_client.attribute.resource.Attribute`
:return: The corresponding attribute spec.
:rtype: :class:`~tamr_unify_client.attribute.resource.AttributeSpec`
"""
return AttributeSpec(
resource.client, deepcopy(resource._data), resource.api_path
)

def from_data(self, data):
"""Creates a spec with the same client and API path as this one, but new data.
:param data: The data for the new spec.
:type data: dict
:return: The new spec.
:rtype: :class:`~tamr_unify_client.attribute.resource.AttributeSpec`
"""
return AttributeSpec(self.client, data, self.api_path)

def to_dict(self):
"""Returns a version of this spec that conforms to the API representation.
:returns: The spec's dict.
:rtype: dict
"""
return deepcopy(self._data)

def with_description(self, new_description):
"""Creates a new spec with the same properties, updating description.
:param new_description: The new description.
:type new_description: str
:return: The new spec.
:rtype: :class:`~tamr_unify_client.attribute.resource.AttributeSpec`
"""
return self.from_data({**self._data, "description": new_description})

def put(self):
"""Commits the changes and updates the attribute in Tamr.
:return: The updated attribute.
:rtype: :class:`~tamr_unify_client.attribute.resource.Attribute`
"""
updated_attribute = (
self.client.put(self.api_path, json=self._data).successful().json()
)
return Attribute.from_json(self.client, updated_attribute, self.api_path)

def __repr__(self):
return (
f"{self.__class__.__module__}."
f"{self.__class__.__qualname__}("
f"relative_id={self.api_path!r}, "
f"name={self._data['name']!r}, "
f"description={self._data['description']!r})"
)
44 changes: 44 additions & 0 deletions tests/unit/test_attribute.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import partial
import json
from unittest import TestCase

from requests import HTTPError
Expand Down Expand Up @@ -91,6 +93,41 @@ def test_delete_attribute(self):
HTTPError, lambda: dataset.attributes.by_resource_id("RowNum")
)

@responses.activate
def test_update_attribute(self):
def create_callback(request, snoop):
snoop["payload"] = request.body
return 200, {}, json.dumps(self._updated_attribute_json)

relative_id = "dataset/1/attributes/RowNum"
attribute_url = f"http://localhost:9100/api/versioned/v1/{relative_id}"
snoop_dict = {}
responses.add_callback(
responses.PUT, attribute_url, partial(create_callback, snoop=snoop_dict)
)
attribute = Attribute(self.tamr, self._attributes_json[0], relative_id)

temp_spec = attribute.spec()
new_attribute = temp_spec.with_description(
self._updated_attribute_json["description"]
).put()
self.assertEqual(new_attribute.name, self._updated_attribute_json["name"])
self.assertEqual(
new_attribute.description, self._updated_attribute_json["description"]
)

self.assertEqual(
json.loads(snoop_dict["payload"]), self._updated_attribute_json
)

self.assertEqual(attribute.name, self._attributes_json[0]["name"])
self.assertEqual(attribute.description, self._attributes_json[0]["description"])

# checking that intermediate didn't change
self.assertEqual(
temp_spec.to_dict()["description"], self._attributes_json[0]["description"]
)

_dataset_json = {
"id": "unify://unified-data/v1/datasets/1",
"externalId": "number 1",
Expand Down Expand Up @@ -173,3 +210,10 @@ def test_delete_attribute(self):
"isNullable": False,
},
]

_updated_attribute_json = {
"name": "RowNum",
"description": "Synthetic row number updated",
"type": {"baseType": "STRING", "attributes": []},
"isNullable": False,
}

0 comments on commit 850f09e

Please sign in to comment.