diff --git a/djangoldp/migrations/0002_auto_20190905_1542.py b/djangoldp/migrations/0002_auto_20190906_0642.py similarity index 60% rename from djangoldp/migrations/0002_auto_20190905_1542.py rename to djangoldp/migrations/0002_auto_20190906_0642.py index ff3b2dc14e84c5e2037fbd444450f3f668d86c29..c68a1c5147a9f96426e114d4da02f3e92f01e5dd 100644 --- a/djangoldp/migrations/0002_auto_20190905_1542.py +++ b/djangoldp/migrations/0002_auto_20190906_0642.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2019-09-05 15:42 +# Generated by Django 1.11 on 2019-09-06 06:42 from __future__ import unicode_literals -from django.db import migrations, models +from django.db import migrations +import djangoldp.fields class Migration(migrations.Migration): @@ -16,9 +17,9 @@ class Migration(migrations.Migration): model_name='ldpsource', name='container', ), - migrations.AlterField( + migrations.AddField( model_name='ldpsource', - name='id', - field=models.URLField(primary_key=True, serialize=False), + name='urlid', + field=djangoldp.fields.LDPUrlField(null=True, unique=True), ), ] diff --git a/djangoldp/migrations/0003_auto_20190911_0931.py b/djangoldp/migrations/0003_auto_20190911_0931.py new file mode 100644 index 0000000000000000000000000000000000000000..72aa15bfbea5ed5f146cec65899bd9632fc47dcb --- /dev/null +++ b/djangoldp/migrations/0003_auto_20190911_0931.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2019-09-11 09:31 +from __future__ import unicode_literals + +from django.db import migrations +import djangoldp.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('djangoldp', '0002_auto_20190906_0642'), + ] + + operations = [ + migrations.AlterField( + model_name='ldpsource', + name='urlid', + field=djangoldp.fields.LDPUrlField(blank=True, null=True, unique=True), + ), + ] diff --git a/djangoldp/models.py b/djangoldp/models.py index 73c1c17d9a7f9d23061cf51ae222e1d9006b3ab0..c9f545dca94268162909bd98c87a30370f805f95 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -1,10 +1,14 @@ +from django.conf import settings from django.contrib.auth.models import User from django.db import models from django.db.models.base import ModelBase +from django.db.models.signals import pre_save, post_save +from django.dispatch import receiver from django.urls import get_resolver -from django.utils.datastructures import MultiValueDict, MultiValueDictKeyError +from django.utils.datastructures import MultiValueDictKeyError from django.utils.decorators import classonlymethod +from djangoldp.fields import LDPUrlField from djangoldp.permissions import LDPPermissions User._meta.rdf_type = "foaf:user" @@ -12,6 +16,10 @@ User._meta.owner_field = "id" class Model(models.Model): + urlid = LDPUrlField(blank=True, null=True, unique=True) + + def __init__(self, *args, **kwargs): + super(Model, self).__init__(*args, **kwargs) @classmethod def get_view_set(cls): @@ -30,7 +38,10 @@ class Model(models.Model): return cls.__clean_path(path) def get_absolute_url(self): - return Model.resource_id(self) + if self.urlid is None or self.urlid != '': + return '{}{}'.format(settings.BASE_URL, Model.resource_id(self)) + else: + return self.urlid def get_container_id(self): return Model.container_id(self) @@ -54,7 +65,7 @@ class Model(models.Model): else: object_name = instance_or_model._meta.object_name.lower() view_name = '{}-detail'.format(object_name) - try : + try: slug_field = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][1][0]) except MultiValueDictKeyError: slug_field = Model.get_meta(instance_or_model, 'lookup_field', 'pk') @@ -133,20 +144,34 @@ class Model(models.Model): permissions = permission_class().filter_user_perms(user_or_group, obj_or_model, permissions) return [{'mode': {'@type': name.split('_')[0]}} for name in permissions] + @classmethod + def is_external(cls, value): + try: + return value.urlid is not None and not value.urlid.startswith(settings.SITE_URL) + except: + return False + class LDPSource(Model): - id = models.URLField(primary_key=True) federation = models.CharField(max_length=255) class Meta: rdf_type = 'ldp:Container' ordering = ('federation',) container_path = 'sources' - lookup_field = 'id' + lookup_field = 'federation' permissions = ( ('view_source', 'acl:Read'), ('control_source', 'acl:Control'), ) def __str__(self): - return "{}: {}".format(self.federation, self.id) + return "{}: {}".format(self.federation, self.urlid) + + +@receiver([post_save]) +def auto_urlid(sender, instance, **kwargs): + if isinstance(instance, Model) and (instance.urlid is None or instance.urlid == ''): + instance.urlid = instance.get_absolute_url() + instance.save() + diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 5560793a8bfd23b3d0a474ffc32a7811f9af1759..1af1ca6fc68eeb7d9b0dd1bc8cb5d8b587cfd291 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -3,6 +3,7 @@ from typing import Any from urllib import parse from django.conf import settings +from django.contrib.auth import get_user_model from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ValidationError as DjangoValidationError from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404 @@ -184,7 +185,10 @@ class JsonLdField(HyperlinkedRelatedField): class JsonLdRelatedField(JsonLdField): def to_representation(self, value): try: - return {'@id': super().to_representation(value)} + if Model.is_external(value): + return {'@id': value.urlid } + else: + return {'@id': super().to_representation(value)} except ImproperlyConfigured: return value.pk @@ -223,10 +227,20 @@ class JsonLdIdentityField(JsonLdField): def to_representation(self, value: Any) -> Any: try: - return Hyperlink(value.webid(), value) + if isinstance(value, str): + return Hyperlink(value, value) + else: + return Hyperlink(value.webid(), value) except AttributeError: return super().to_representation(value) + def get_attribute(self, instance): + if Model.is_external(instance): + return instance.urlid + else: + return super().get_attribute(instance) + + class LDPSerializer(HyperlinkedModelSerializer): url_field_name = "@id" @@ -254,10 +268,10 @@ class LDPSerializer(HyperlinkedModelSerializer): for field in data: if isinstance(data[field], dict) and '@id' in data[field]: data[field]['@id'] = data[field]['@id'].format(Model.container_id(obj), str(getattr(obj, slug_field))) - if not ('@id' in data or 'id' in data): + if 'urlid' in data and data['urlid'] is not None: + data['@id'] = data.pop('urlid')['@id'] + if not '@id' in data: data['@id'] = '{}{}'.format(settings.SITE_URL, Model.resource(obj)) - if 'id' in data: - data['@id'] = data.pop('id') rdf_type = Model.get_meta(obj, 'rdf_type', None) rdf_context = Model.get_meta(obj, 'rdf_context', None) if rdf_type is not None: @@ -326,6 +340,8 @@ class LDPSerializer(HyperlinkedModelSerializer): super().__init__(**kwargs) def get_value(self, dictionary): + if self.field_name == 'urlid': + self.field_name = '@id' try: object_list = dictionary["@graph"] if self.parent.instance is None: @@ -341,6 +357,9 @@ class LDPSerializer(HyperlinkedModelSerializer): except KeyError: value = super().get_value(dictionary) + if self.field_name == '@id' and value == './': + self.field_name = 'urlid' + return None return self.manage_empty(value) def manage_empty(self, value): @@ -371,6 +390,8 @@ class LDPSerializer(HyperlinkedModelSerializer): fields = '__all__' def to_internal_value(self, data): + if self.url_field_name in data and not 'urlid' in data and data[self.url_field_name].startswith('http'): + data['urlid'] = data[self.url_field_name] if data is '': return '' if self.url_field_name in data: @@ -384,6 +405,7 @@ class LDPSerializer(HyperlinkedModelSerializer): ret = OrderedDict() errors = OrderedDict() + fields = list(filter(lambda x: x.field_name in data, self._writable_fields)) for field in fields: @@ -419,7 +441,8 @@ class LDPSerializer(HyperlinkedModelSerializer): slug_field = Model.slug_field(self.__class__.Meta.model) ret[slug_field] = match.kwargs[slug_field] except Resolver404: - pass + if 'urlid' in data: + ret['urlid'] = data['urlid'] return ret else: @@ -438,6 +461,15 @@ class LDPSerializer(HyperlinkedModelSerializer): serializer.id = '{}{}/'.format(serializer.id, kwargs['context']['view'].nested_field) return serializer + def to_internal_value(self, data): + user_case = self.Meta.model is get_user_model() and '@id' in data and not data['@id'].startswith(settings.BASE_URL) + if user_case: + data['username'] = 'external' + ret = super().to_internal_value(data) + if user_case: + ret['username'] = data['@id'] + return ret + def get_value(self, dictionary): try: object_list = dictionary["@graph"] @@ -511,6 +543,8 @@ class LDPSerializer(HyperlinkedModelSerializer): field_name in validated_data) and not field_name is None: many_to_many.append((field_name, validated_data.pop(field_name))) validated_data = self.remove_empty_value(validated_data) + if model is get_user_model() and 'urlid' in validated_data and not 'username' in validated_data: + validated_data['username'] = validated_data.pop('urlid') instance = model.objects.create(**validated_data) for field_name, value in many_to_many: @@ -540,9 +574,9 @@ class LDPSerializer(HyperlinkedModelSerializer): else: setattr(instance, attr, value) - instance.save() self.save_or_update_nested_list(instance, nested_fields) + instance.save() return instance diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py index fe6de31f645f445108295ef3f325f6ea05ea8768..f70c1319ef00025805a32f627de288935be3896e 100644 --- a/djangoldp/tests/models.py +++ b/djangoldp/tests/models.py @@ -1,6 +1,8 @@ +import validators from django.conf import settings from django.contrib.auth import get_user_model from django.db import models +from django.urls import reverse_lazy from django.utils.datetime_safe import date from djangoldp.models import Model @@ -24,7 +26,7 @@ class Skill(Model): class JobOffer(Model): - title = models.CharField(max_length=255, blank=True, null=True) + title = models.CharField(max_length=255, null=True) skills = models.ManyToManyField(Skill, blank=True) slug = models.SlugField(blank=True, null=True, unique=True) date = models.DateTimeField(auto_now_add=True, blank=True) @@ -40,7 +42,7 @@ class JobOffer(Model): authenticated_perms = ['inherit', 'change', 'add'] owner_perms = ['inherit', 'delete', 'control'] nested_fields = ["skills"] - serializer_fields = ["@id", "title", "skills", "recent_skills", "resources", "slug", "some_skill"] + serializer_fields = ["@id", "title", "skills", "recent_skills", "resources", "slug", "some_skill", "urlid"] container_path = "job-offers/" lookup_field = 'slug' @@ -58,6 +60,7 @@ class Conversation(models.Model): class Resource(Model): joboffers = models.ManyToManyField(JobOffer, blank=True, related_name='resources') + description = models.CharField(max_length=255) class Meta: anonymous_perms = ['view', 'add', 'delete', 'add', 'change', 'control'] @@ -157,5 +160,28 @@ class Post(Model): owner_perms = ['inherit'] +class Circle(Model): + description = models.CharField(max_length=255) + team = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True) + + class Meta: + nested_fields = ["team"] + anonymous_perms = ['view', 'add', 'delete', 'add', 'change', 'control'] + authenticated_perms = ["inherit"] + rdf_type = 'hd:circle' + depth = 1 + + +def webid(self): + # hack : We user webid as username for external user (since it's an uniq identifier too) + if validators.url(self.username): + webid = self.username + else: + webid = '{0}{1}'.format(settings.BASE_URL, reverse_lazy('user-detail', kwargs={'pk': self.pk})) + return webid + + get_user_model()._meta.serializer_fields = ['@id', 'username', 'first_name', 'last_name', 'email', 'userprofile', - 'conversation_set', ] + 'conversation_set', 'circle_set'] +get_user_model().webid = webid +get_user_model()._meta.anonymous_perms = ['view', 'add'] diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py index 9b83a4fc2f7eaa059ada26a6689aceb6e95d284e..f0fb6b439daeedd5d4d86ef00f815fa6138324a8 100644 --- a/djangoldp/tests/runner.py +++ b/djangoldp/tests/runner.py @@ -49,7 +49,8 @@ settings.configure(DEBUG=False, 'djangoldp.tests', ), SITE_URL='http://happy-dev.fr', - REST_FRAMEWORK={ + BASE_URL='http://happy-dev.fr', + REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'djangoldp.pagination.LDPPagination', 'PAGE_SIZE': 5 }, @@ -71,7 +72,7 @@ failures = test_runner.run_tests([ 'djangoldp.tests.tests_delete', 'djangoldp.tests.tests_sources', 'djangoldp.tests.tests_pagination', - 'djangoldp.tests.tests_temp' + # 'djangoldp.tests.tests_temp' ]) if failures: diff --git a/djangoldp/tests/tests_get.py b/djangoldp/tests/tests_get.py index fb2c0b9925d0868fa4df0cd4b199be9a8ba58a38..e69f119f23cbef0094600ba50b1c330f6c58f360 100644 --- a/djangoldp/tests/tests_get.py +++ b/djangoldp/tests/tests_get.py @@ -42,14 +42,17 @@ class TestGET(APITestCase): def test_get_filtered_fields(self): skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1") skill2 = Skill.objects.create(title="Java", obligatoire="ok", slug="2") + skill3 = Skill.objects.create(urlid="http://external/skills/1") job = JobOffer.objects.create(title="job", slug="1") job.skills.add(skill) job.skills.add(skill2) + job.skills.add(skill3) job.save() response = self.client.get('/job-offers/{}/'.format(job.slug), content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertIn('recent_skills', response.data) self.assertEqual(response.data['recent_skills']['@id'], "http://happy-dev.fr/job-offers/1/recent_skills/") + self.assertEqual(response.data['skills']['ldp:contains'][2]['@id'], "http://external/skills/1") def test_get_reverse_filtered_fields(self): skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1") diff --git a/djangoldp/tests/tests_ldp_model.py b/djangoldp/tests/tests_ldp_model.py index 14217f94a0dc1985f0ee4ea8d71f64a43caa51e7..bc316ed50914dea6fb6fc99847392fa9d09e2562 100644 --- a/djangoldp/tests/tests_ldp_model.py +++ b/djangoldp/tests/tests_ldp_model.py @@ -16,7 +16,7 @@ class LDPModelTest(TestCase): def test_class_inheriting_ldp_model(self): dummy = LDPDummy.objects.create(some="text") self.assertEquals("/ldpdummys/", dummy.get_container_id()) - self.assertEquals("/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url()) + self.assertEquals("http://happy-dev.fr/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url()) self.assertEquals("/ldpdummys/", Model.container_id(dummy)) self.assertEquals("/ldpdummys/{}/".format(dummy.pk), Model.resource_id(dummy)) @@ -33,6 +33,6 @@ class LDPModelTest(TestCase): from django.urls import get_resolver dummy = LDPDummy.objects.create(some="text") view_name = '{}-list'.format(dummy._meta.object_name.lower()) - path = '/{}{}/'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk) + path = 'http://happy-dev.fr/{}{}/'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk) self.assertEquals(path, dummy.get_absolute_url()) diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py index 0674d12a6e7fe5bc85f5f332f7d7dddaffe8e30d..0c97d5c356520adfa266c1b75a6c5426b95ebbd1 100644 --- a/djangoldp/tests/tests_save.py +++ b/djangoldp/tests/tests_save.py @@ -1,13 +1,25 @@ +from django.contrib.auth import get_user_model from django.test import TestCase +from rest_framework.test import APIRequestFactory, APIClient from rest_framework.utils import json from djangoldp.models import Model from djangoldp.serializers import LDPSerializer -from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource +from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource, Post, Circle class Save(TestCase): + def setUp(self): + self.factory = APIRequestFactory() + self.client = APIClient() + self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com', + password='glass onion') + self.client.force_authenticate(self.user) + + def tearDown(self): + pass + def test_save_m2m_graph_with_many_nested(self): invoice = { "@graph": [ @@ -88,8 +100,8 @@ class Save(TestCase): self.assertIs(result.skills.count(), 0) def test_save_m2m_graph_with_nested(self): - skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire") - skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire") + skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a") + skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b") job = {"@graph": [ {"title": "job test", @@ -111,9 +123,9 @@ class Save(TestCase): self.assertEquals(result.skills.all()[0].title, "skill3 NEW") # creation on the fly def test_save_without_nested_fields(self): - skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire") - skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire") - job = {"title": "job test"} + skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a") + skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b") + job = {"title": "job test", "slug": "c"} meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills")} @@ -265,9 +277,24 @@ class Save(TestCase): data=json.dumps(body), content_type='application/ld+json') self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], "http://testserver/resources/{}/".format(resource.pk)) + self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], + "http://testserver/resources/{}/".format(resource.pk)) self.assertEqual(response.data['title'], "new job") + def test_nested_container_federated(self): + resource = Resource.objects.create() + body = { + 'http://happy-dev.fr/owl/#@id': "http://external.job/job/1", + } + + response = self.client.post('/resources/{}/joboffers/'.format(resource.pk), + data=json.dumps(body), + content_type='application/ld+json') + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], + "http://testserver/resources/{}/".format(resource.pk)) + self.assertEqual(response.data['@id'], "http://external.job/job/1") + def test_embedded_context_2(self): body = { '@id': "./", @@ -281,3 +308,33 @@ class Save(TestCase): response = self.client.post('/posts/', data=json.dumps(body), content_type='application/ld+json') self.assertEqual(response.status_code, 201) + + def test_auto_id(self): + body = { + '@id': "./", + 'content': "post update", + 'peer_user': "", + '@context': { + "@vocab": "http://happy-dev.fr/owl/#", + } + } + + response = self.client.post('/posts/', data=json.dumps(body), + content_type='application/ld+json') + self.assertEqual(response.status_code, 201) + saved_post = Post.objects.get(pk=1) + self.assertEqual(saved_post.urlid, "http://happy-dev.fr/posts/1/") + + def test_nested_container_user_federated(self): + circle = Circle.objects.create() + body = { + 'http://happy-dev.fr/owl/#@id': "http://external.user/user/1/", + } + + response = self.client.post('/circles/{}/team/'.format(circle.pk), + data=json.dumps(body), + content_type='application/ld+json') + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data['circle_set']['ldp:contains'][0]['@id'], + "http://testserver/circles/{}/".format(circle.pk)) + self.assertEqual(response.data['@id'], "http://external.user/user/1/") diff --git a/djangoldp/tests/tests_sources.py b/djangoldp/tests/tests_sources.py index a68327f56c190503210d48870be5a95f1bb3c786..e43f3e9457cf9d828da01b85b6531b8e199aeca9 100644 --- a/djangoldp/tests/tests_sources.py +++ b/djangoldp/tests/tests_sources.py @@ -13,6 +13,6 @@ class TestSource(APITestCase): pass def test_get_resource(self): - source = LDPSource.objects.create(federation="source_name", id="http://bar.foo/") + source = LDPSource.objects.create(federation="source_name", urlid="http://bar.foo/") response = self.client.get('/sources/{}/'.format(source.federation), content_type='application/ld+json') self.assertEqual(response.status_code, 200) diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py index 161703c81abc8562d445f07bd2e04fc503ee5de0..ef7792a2a2cb08b941457637778f05cb736becd5 100644 --- a/djangoldp/tests/tests_temp.py +++ b/djangoldp/tests/tests_temp.py @@ -1,19 +1,14 @@ -import json - from django.contrib.auth.models import User from django.test import TestCase from rest_framework.test import APIRequestFactory, APIClient -from djangoldp.tests.models import Resource, JobOffer, Invoice, Batch - - class TestTemp(TestCase): def setUp(self): self.factory = APIRequestFactory() self.client = APIClient() self.user = User.objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion') + self.client.force_authenticate(self.user) def tearDown(self): pass - diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py index 431db723282d3467746d77162ca92c2764ac843b..a9a7dca25d4582ecf4d16e21761c74c6f99c3755 100644 --- a/djangoldp/tests/tests_update.py +++ b/djangoldp/tests/tests_update.py @@ -4,7 +4,7 @@ from rest_framework.test import APIRequestFactory, APIClient from rest_framework.utils import json from djangoldp.serializers import LDPSerializer -from djangoldp.tests.models import Post, UserProfile, Resource +from djangoldp.tests.models import Post, UserProfile, Resource, Circle from djangoldp.tests.models import Skill, JobOffer, Conversation, Message @@ -264,7 +264,7 @@ class Update(TestCase): def test_put_resource(self): post = Post.objects.create(content="content") body = [{ - '@id': '/posts/{}/'.format(post.pk), + '@id': 'http://testserver.com/posts/{}/'.format(post.pk), 'http://happy-dev.fr/owl/#content': "post content"}] response = self.client.put('/posts/{}/'.format(post.pk), data=json.dumps(body), content_type='application/ld+json') @@ -401,7 +401,7 @@ class Update(TestCase): job = JobOffer.objects.create(title="first title", slug="job") body = { 'http://happy-dev.fr/owl/#joboffers': { - '@id': 'http://testserver/job-offers/{}/'.format(job.slug), + '@id': 'http://testserver.com/job-offers/{}/'.format(job.slug), } } @@ -410,7 +410,7 @@ class Update(TestCase): content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], - "http://testserver/job-offers/{}/".format(job.slug)) + "http://testserver.com/job-offers/{}/".format(job.slug)) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "first title") def test_m2m_new_link_bis(self): @@ -419,9 +419,9 @@ class Update(TestCase): body = { 'http://happy-dev.fr/owl/#joboffers': { - '@id': "http://testserver/resources/{}/joboffers/".format(resource.pk), + '@id': "http://testserver.com/resources/{}/joboffers/".format(resource.pk), 'ldp:contains': [ - {'@id': 'http://testserver/job-offers/{}/'.format(job.slug), + {'@id': 'http://testserver.com/job-offers/{}/'.format(job.slug), 'http://happy-dev.fr/owl/#title': "new job", }, ] @@ -433,7 +433,7 @@ class Update(TestCase): content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], - "http://testserver/job-offers/{}/".format(job.slug)) + "http://testserver.com/job-offers/{}/".format(job.slug)) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job") def test_m2m_new_link_embedded(self): @@ -450,7 +450,7 @@ class Update(TestCase): content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], - "http://testserver/job-offers/aaa/") + "http://happy-dev.fr/job-offers/aaa/") self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job") def test_m2m_existing_link(self): @@ -463,7 +463,7 @@ class Update(TestCase): # '@id': "http://testserver/resources/{}/joboffers/".format(resource.pk), 'ldp:contains': [ { - '@id': 'http://testserver/job-offers/{}/'.format(job.slug), + '@id': 'http://testserver.com/job-offers/{}/'.format(job.slug), 'http://happy-dev.fr/owl/#title': "new job", } ] @@ -475,5 +475,36 @@ class Update(TestCase): content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], - "http://testserver/job-offers/{}/".format(job.slug)) + "http://testserver.com/job-offers/{}/".format(job.slug)) self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job") + + def test_m2m_new_link_federated(self): + resource = Resource.objects.create() + body = { + 'http://happy-dev.fr/owl/#joboffers': { + 'http://happy-dev.fr/owl/#@id': 'http://external.job/job/1', + } + } + + response = self.client.put('/resources/{}/'.format(resource.pk), + data=json.dumps(body), + content_type='application/ld+json') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], + "http://external.job/job/1") + + def test_m2m_user_link_federated(self): + circle = Circle.objects.create(description="cicle name") + body = { + 'http://happy-dev.fr/owl/#description': 'circle name', + 'http://happy-dev.fr/owl/#team': { + 'http://happy-dev.fr/owl/#@id': 'http://external.user/user/1', + } + } + + response = self.client.put('/circles/{}/'.format(circle.pk), + data=json.dumps(body), + content_type='application/ld+json') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data['team']['ldp:contains'][0]['@id'], + "http://external.user/user/1") diff --git a/djangoldp/urls.py b/djangoldp/urls.py index f92a2ab43590fc8c27da7ae3b5e387e36cad273e..a83b98eb262e80c0dd805ce4bc186d02f4f74fbf 100644 --- a/djangoldp/urls.py +++ b/djangoldp/urls.py @@ -17,7 +17,7 @@ def __clean_path(path): urlpatterns = [ - url(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'id'], + url(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'urlid'], permission_classes=[LDPPermissions], )), url(r'^\.well-known/webfinger/?$', WebFingerView.as_view()), ] diff --git a/setup.cfg b/setup.cfg index ab33010f208f6f2b3903821d028fe6e87151557a..46e3b9622fcb881c7063e44d1d3dceca75f3a614 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ install_requires = [options.extras_require] dev = + validators factory_boy>=2.11.0 [semantic_release]