Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 75 additions & 3 deletions ci/apiv2/test_http_methods.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import requests
import json
import time

from hashtopolis import HashtopolisConnector, HashtopolisConfig
from utils import BaseTest



class HttpMethodsTest(BaseTest):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.config = HashtopolisConfig()

def test_empty_body(self):
config = HashtopolisConfig()
conn = HashtopolisConnector('/ui/users', config)
conn = HashtopolisConnector('/ui/users', self.config)
conn.authenticate()

headers = conn._headers
Expand All @@ -19,4 +26,69 @@ def test_empty_body(self):
self.assertGreaterEqual(len(values), 1)

# TODO: Test for non-empty body which should fail
# TODO: Test for invalid parameters
# TODO: Test for invalid parameters

def test_get_one_response_should_not_duplicate_self_reference_in_data(self):
conn = HashtopolisConnector('/ui/users', self.config)
conn.authenticate()
resource_path = conn._model_uri + "/1"
uri = conn._api_endpoint + resource_path

response = requests.get(uri, headers=conn._headers)
r = response.json()

self.assertIsNotNone(r['links']['self'], "Top level self reference should be present in all responses.")
self.assertIn(response.request.path_url, r['links']['self'], "Self reference for a single resource should be its path.")
self.assertTrue(isinstance(r['data'], dict), "A single resource should be represented as an object.")
self.assertNotIn('self', r['data'].get('links', {}), "A single resource should not include a self reference.")

def test_get_many_response_should_include_self_reference_for_every_resource(self):
conn = HashtopolisConnector('/ui/hashtypes', self.config)
conn.authenticate()
resource_path = conn._model_uri
uri = conn._api_endpoint + resource_path

response = requests.get(uri, headers=conn._headers)
r = response.json()

self.assertIsNotNone(r['links']['self'])
self.assertIn(response.request.path_url, r['links']['self'], "Self reference for a resource collection should be its path.")

resources = r.get('data')
self.assertIsInstance(resources, list)
self.assertGreater(len(resources), 0)

for resource in resources:
self.assertIsInstance(resource, dict)
self.assertIn('self', resource.get('links', {}), "A resource in a collection should contain a self refrence")
self.assertEqual(f"{response.request.path_url}/{resource.get('id')}", resource['links']['self'])

def test_post_user_should_return_getone_uri_in_location(self):
conn = HashtopolisConnector('/ui/users', self.config)
conn.authenticate()
uri = f"{conn._api_endpoint}{conn._model_uri}"
stamp = int(time.time() * 1000)

payload = {
"data": {
"type": "user",
"attributes": {
"name": f"test-{stamp}",
"email": f"test-{stamp}@example.com",
"globalPermissionGroupId": 1,
"isValid": True,
"sessionLifetime": 3600,
},
},
}

headers = dict(conn._headers)
headers["Content-Type"] = "application/json"
response = requests.post(uri, headers=headers, data=json.dumps(payload))

self.assertEqual(response.status_code, 201)
self.assertIsNotNone(response.headers.get('Location'), "Missing Location header in created resource")

resource_response = requests.get(f"{conn._hashtopolis_uri}{response.headers.get('Location')}", headers=conn._headers)
self.assertEqual(resource_response.status_code, 200, "Unable to find the created resource")

10 changes: 4 additions & 6 deletions src/inc/apiv2/common/AbstractBaseAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -760,9 +760,6 @@ protected function obj2Resource(object $obj, array &$expandResult = [], ?array $
"type" => $this->getObjectTypeName($obj),
"id" => $obj->getId(),
"attributes" => $attributes,
"links" => [
"self" => $linkSelf,
],
];

if (sizeof($relationships) > 0) {
Expand Down Expand Up @@ -1657,6 +1654,9 @@ protected static function getOneResource(object $apiClass, object $object, Reque

$linksSelf = $request->getUri()->getPath() . ((!empty($linksQuery)) ? '?' . $linksQuery : '');
$links = ["self" => $linksSelf];

$resourceApiClass = $apiClass->container->get('classMapper')->get(get_class($object));
$resourceLocation = $apiClass->routeParser->urlFor($resourceApiClass . ':getOne', ['id' => $object->getId()]);

$metaData = [];
if ($apiClass->permissionErrors !== null) {
Expand All @@ -1669,10 +1669,8 @@ protected static function getOneResource(object $apiClass, object $object, Reque
$body->write($apiClass->ret2json($ret));

return $response->withHeader("Content-Type", "application/vnd.api+json")
->withHeader("Location", $dataResources[0]["links"]["self"])
->withHeader("Location", $resourceLocation)
->withStatus($statusCode);
//for location we use links value from $dataresources because if we use $linksSelf, the wrong location gets returned in
//case of a POST request
}

//Meta response for helper functions that do not respond with resource records
Expand Down
4 changes: 4 additions & 0 deletions src/inc/apiv2/common/AbstractModelAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,10 @@ public static function getManyResources(object $apiClass, Request $request, Resp
foreach ($objects as $object) {
// Create object
$newObject = $apiClass->obj2Resource($object, $expandResult, $request->getQueryParams()['fields'] ?? null, $request->getQueryParams()['aggregate'] ?? null);
// Resource path
$resourceApiClass = $apiClass->container->get('classMapper')->get(get_class($object));
$resourceLocation = $apiClass->routeParser->urlFor($resourceApiClass . ':getOne', ['id' => $object->getId()]);
$newObject["links"]["self"] = $resourceLocation;
$includedResources = $apiClass->processExpands($apiClass, $expands, $object, $expandResult, $includedResources, $request->getQueryParams()['fields'] ?? null, $request->getQueryParams()['aggregate'] ?? null);

// Add to result output
Expand Down
Loading