Added Node.create_asset_from_file() utility function

This also adds some unittests using the Responses library.
This commit is contained in:
2016-07-05 16:47:51 +02:00
parent 6ad9b5f7bd
commit 8dfb0a193b
4 changed files with 313 additions and 6 deletions

View File

@@ -1,3 +1,7 @@
import json
import os.path
from .resource import List
from .resource import Find
from .resource import Create
@@ -70,6 +74,49 @@ class Node(List, Find, Create, Post, Update, Delete, Replace):
utils.convert_datetime(item)
return cls.list_class(response)
@classmethod
def create_asset_from_file(cls, project_id, parent_node_id, asset_type, filename,
always_create_new_node=False, api=None):
"""Uploads the file to the Cloud and creates an asset node."""
api = api or Api.Default()
# Upload the file.
with open(filename, mode='rb') as infile:
file_upload_resp = api.post('storage/stream/%s' % project_id,
files={'file': infile})
if file_upload_resp['status'] != 'ok':
raise ValueError('Received bad status %s from Pillar: %s' %
(file_upload_resp['status'], json.dumps(file_upload_resp)))
file_id = file_upload_resp['file_id']
# Create or update the node.
basic_properties = {
'project': project_id,
'node_type': 'asset',
'parent': parent_node_id,
'name': os.path.basename(filename)
}
if not always_create_new_node:
# Try to find an existing one to see if there is anything to update.
existing_node = cls.find_first({'where': basic_properties}, api=api)
if existing_node:
# Just update the file ID and we're done.
existing_node.properties.content_type = asset_type
existing_node.properties.file = file_id
existing_node.update(api=api)
return existing_node
basic_properties.update({
'properties': {'content_type': asset_type,
'file': file_id},
})
node = cls(basic_properties)
node.create(api=api)
return node
class NodeType(List, Find, Create, Post, Delete):
"""NodeType class wrapping the REST node_types endpoint

View File

@@ -1,9 +1,12 @@
# Primary requirements
pyOpenSSL==0.15.1
requests==2.9.1
tox>=2.3.1
wheel>=0.29.0
coverage>=3.5
PyTest
pytest-xdist
pytest-cov
# Development requirements
tox==2.3.1
wheel==0.29.0
coverage==4.0.3
pytest==2.9.1
pytest-xdist==1.14
pytest-cov==2.2.1
responses==0.5.1

256
tests/test_nodes.py Normal file
View File

@@ -0,0 +1,256 @@
import unittest
import responses
import pillarsdk
import pillarsdk.exceptions as sdk_exceptions
mock = responses.RequestsMock(assert_all_requests_are_fired=True)
class AssetNodesTests(unittest.TestCase):
def setUp(self):
self.endpoint = 'http://localhost:12345'
self.api = pillarsdk.Api(
endpoint=self.endpoint,
username='',
password='',
token='jemoeder',
)
self.project_id = 1234
@mock.activate
def test_create_asset_from_file__always_new_node(self):
parent_node_id = 24 * 'a'
mock.add(responses.POST,
'%s/nodes' % self.endpoint,
json={
'_id': 24 * 'b',
'parent': parent_node_id,
'name': 'test_nodes.py',
'project': self.project_id,
},
status=201)
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'ok',
'file_id': 24 * 'c',
},
status=201)
node = pillarsdk.Node.create_asset_from_file(
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
always_create_new_node=True,
api=self.api)
self.assertEqual(node['_id'], 24 * 'b')
self.assertEqual(node['parent'], parent_node_id)
self.assertEqual(node['name'], 'test_nodes.py')
self.assertEqual(node['node_type'], 'asset')
self.assertEqual(node['properties']['content_type'], 'image')
self.assertEqual(node['properties']['file'], 24 * 'c')
@mock.activate
def test_create_asset_from_file__update_existing_node(self):
parent_node_id = 24 * 'a'
asset_node_id = 24 * 'b'
# Uploading the file
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'ok',
'file_id': 24 * 'c',
},
status=201)
# Finding the existing node
mock.add(responses.GET,
'%s/nodes' % self.endpoint,
json={'_items': [{
'_id': asset_node_id,
'_etag': 'awesome-etag',
'name': 'test_nodes.py',
'node_type': 'asset',
'project': self.project_id,
'parent': parent_node_id,
'properties': {
'content_type': 'video',
'file': 24 * 'e',
}}
]
})
# Updating the node
mock.add(responses.PUT,
'%s/nodes/%s' % (self.endpoint, asset_node_id),
json={'_created': 'Wed, 29 Jun 2016 14:45:35 GMT',
'_deleted': False,
'_etag': 'df983fb8834802be83f0f657201cbf7a3d177a9c',
'_id': asset_node_id,
'_status': 'OK',
'_updated': 'Tue, 05 Jul 2016 14:12:27 GMT'},
status=200)
node = pillarsdk.Node.create_asset_from_file(
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
api=self.api)
self.assertEqual(node['_id'], asset_node_id)
self.assertEqual(node['parent'], parent_node_id)
self.assertEqual(node['name'], 'test_nodes.py')
self.assertEqual(node['node_type'], 'asset')
self.assertEqual(node['properties']['content_type'], 'image')
self.assertEqual(node['properties']['file'], 24 * 'c')
@mock.activate
def test_create_asset_from_file__create_new_node(self):
parent_node_id = 24 * 'a'
asset_node_id = 24 * 'b'
# Upload the file
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'ok',
'file_id': 24 * 'c',
},
status=201)
# Try to find whether the node exists (it doesn't).
mock.add(responses.GET,
'%s/nodes' % self.endpoint,
json={'_items': []})
# Create a new node
mock.add(responses.POST,
'%s/nodes' % self.endpoint,
json={
'_id': asset_node_id,
'parent': parent_node_id,
'name': 'test_nodes.py',
'project': self.project_id,
},
status=201)
node = pillarsdk.Node.create_asset_from_file(
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
api=self.api)
self.assertEqual(node['_id'], asset_node_id)
self.assertEqual(node['parent'], parent_node_id)
self.assertEqual(node['name'], 'test_nodes.py')
self.assertEqual(node['node_type'], 'asset')
self.assertEqual(node['properties']['content_type'], 'image')
self.assertEqual(node['properties']['file'], 24 * 'c')
@mock.activate
def test_create_asset_from_file__upload_fails(self):
parent_node_id = 24 * 'a'
# Upload the file
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'error',
'error': 'Internal server error'
},
status=500)
self.assertRaises(
sdk_exceptions.ServerError,
pillarsdk.Node.create_asset_from_file,
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
api=self.api)
@mock.activate
def test_create_asset_from_file__create_new_node_fails(self):
parent_node_id = 24 * 'a'
asset_node_id = 24 * 'b'
# Upload the file
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'ok',
'file_id': 24 * 'c',
},
status=201)
# Try to find whether the node exists (it doesn't).
mock.add(responses.GET,
'%s/nodes' % self.endpoint,
json={'_items': []})
# Create a new node, which fails
mock.add(responses.POST,
'%s/nodes' % self.endpoint,
status=500)
self.assertRaises(
sdk_exceptions.ServerError,
pillarsdk.Node.create_asset_from_file,
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
api=self.api)
@mock.activate
def test_create_asset_from_file__update_existing_node_fails(self):
parent_node_id = 24 * 'a'
asset_node_id = 24 * 'b'
# Uploading the file
mock.add(responses.POST,
'%s/storage/stream/%s' % (self.endpoint, self.project_id),
json={
'status': 'ok',
'file_id': 24 * 'c',
},
status=201)
# Finding the existing node
mock.add(responses.GET,
'%s/nodes' % self.endpoint,
json={'_items': [{
'_id': asset_node_id,
'_etag': 'awesome-etag',
'name': 'test_nodes.py',
'node_type': 'asset',
'project': self.project_id,
'parent': parent_node_id,
'properties': {
'content_type': 'video',
'file': 24 * 'e',
}}
]
})
# Updating the node fails
mock.add(responses.PUT,
'%s/nodes/%s' % (self.endpoint, asset_node_id),
status=500)
self.assertRaises(
sdk_exceptions.ServerError,
pillarsdk.Node.create_asset_from_file,
project_id=self.project_id,
parent_node_id=parent_node_id,
asset_type='image',
filename=__file__,
api=self.api)

View File

@@ -12,6 +12,7 @@ deps=coverage >=3.5
PyTest
pytest-xdist
pytest-cov
responses
; For now we skip doctests.
;[testenv:py35]