Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
jovuit
funkwhale
Commits
87bc011e
Commit
87bc011e
authored
Jan 23, 2020
by
Eliot Berriot
Browse files
Actor description/summary
parent
6785deb9
Changes
6
Hide whitespace changes
Inline
Side-by-side
api/funkwhale_api/federation/migrations/0023_actor_summary_obj.py
0 → 100644
View file @
87bc011e
# Generated by Django 2.2.9 on 2020-01-22 11:01
from
django.db
import
migrations
,
models
import
django.db.models.deletion
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'common'
,
'0007_auto_20200116_1610'
),
(
'federation'
,
'0022_auto_20191204_1539'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'actor'
,
name
=
'summary_obj'
,
field
=
models
.
ForeignKey
(
blank
=
True
,
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
to
=
'common.Content'
),
),
]
api/funkwhale_api/federation/models.py
View file @
87bc011e
...
...
@@ -189,6 +189,9 @@ class Actor(models.Model):
name
=
models
.
CharField
(
max_length
=
200
,
null
=
True
,
blank
=
True
)
domain
=
models
.
ForeignKey
(
Domain
,
on_delete
=
models
.
CASCADE
,
related_name
=
"actors"
)
summary
=
models
.
CharField
(
max_length
=
500
,
null
=
True
,
blank
=
True
)
summary_obj
=
models
.
ForeignKey
(
"common.Content"
,
null
=
True
,
blank
=
True
,
on_delete
=
models
.
SET_NULL
)
preferred_username
=
models
.
CharField
(
max_length
=
200
,
null
=
True
,
blank
=
True
)
public_key
=
models
.
TextField
(
max_length
=
5000
,
null
=
True
,
blank
=
True
)
private_key
=
models
.
TextField
(
max_length
=
5000
,
null
=
True
,
blank
=
True
)
...
...
api/funkwhale_api/federation/serializers.py
View file @
87bc011e
...
...
@@ -21,6 +21,18 @@ from . import activity, actors, contexts, jsonld, models, tasks, utils
logger
=
logging
.
getLogger
(
__name__
)
class
TruncatedCharField
(
serializers
.
CharField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
truncate_length
=
kwargs
.
pop
(
"truncate_length"
)
super
().
__init__
(
*
args
,
**
kwargs
)
def
to_internal_value
(
self
,
v
):
v
=
super
().
to_internal_value
(
v
)
if
v
:
v
=
v
[:
self
.
truncate_length
]
return
v
class
LinkSerializer
(
jsonld
.
JsonLdSerializer
):
type
=
serializers
.
ChoiceField
(
choices
=
[
contexts
.
AS
.
Link
,
contexts
.
AS
.
Image
])
href
=
serializers
.
URLField
(
max_length
=
500
)
...
...
@@ -76,7 +88,11 @@ class ActorSerializer(jsonld.JsonLdSerializer):
preferredUsername
=
serializers
.
CharField
()
manuallyApprovesFollowers
=
serializers
.
NullBooleanField
(
required
=
False
)
name
=
serializers
.
CharField
(
required
=
False
,
max_length
=
200
)
summary
=
serializers
.
CharField
(
max_length
=
None
,
required
=
False
)
summary
=
TruncatedCharField
(
truncate_length
=
common_models
.
CONTENT_TEXT_MAX_LENGTH
,
required
=
False
,
allow_null
=
True
,
)
followers
=
serializers
.
URLField
(
max_length
=
500
,
required
=
False
)
following
=
serializers
.
URLField
(
max_length
=
500
,
required
=
False
,
allow_null
=
True
)
publicKey
=
PublicKeySerializer
(
required
=
False
)
...
...
@@ -113,11 +129,12 @@ class ActorSerializer(jsonld.JsonLdSerializer):
ret
[
"followers"
]
=
instance
.
followers_url
if
instance
.
following_url
:
ret
[
"following"
]
=
instance
.
following_url
if
instance
.
summary
:
ret
[
"summary"
]
=
instance
.
summary
if
instance
.
manually_approves_followers
is
not
None
:
ret
[
"manuallyApprovesFollowers"
]
=
instance
.
manually_approves_followers
if
instance
.
summary_obj_id
:
ret
[
"summary"
]
=
instance
.
summary_obj
.
rendered
ret
[
"@context"
]
=
jsonld
.
get_default_context
()
if
instance
.
public_key
:
ret
[
"publicKey"
]
=
{
...
...
@@ -146,7 +163,6 @@ class ActorSerializer(jsonld.JsonLdSerializer):
"inbox_url"
:
self
.
validated_data
.
get
(
"inbox"
),
"following_url"
:
self
.
validated_data
.
get
(
"following"
),
"followers_url"
:
self
.
validated_data
.
get
(
"followers"
),
"summary"
:
self
.
validated_data
.
get
(
"summary"
),
"type"
:
self
.
validated_data
[
"type"
],
"name"
:
self
.
validated_data
.
get
(
"name"
),
"preferred_username"
:
self
.
validated_data
[
"preferredUsername"
],
...
...
@@ -181,11 +197,22 @@ class ActorSerializer(jsonld.JsonLdSerializer):
def
save
(
self
,
**
kwargs
):
d
=
self
.
prepare_missing_fields
()
d
.
update
(
kwargs
)
return
models
.
Actor
.
objects
.
update_or_create
(
fid
=
d
[
"fid"
],
defaults
=
d
)[
0
]
actor
=
models
.
Actor
.
objects
.
update_or_create
(
fid
=
d
[
"fid"
],
defaults
=
d
)[
0
]
common_utils
.
attach_content
(
actor
,
"summary_obj"
,
self
.
validated_data
[
"summary"
]
)
return
actor
def
validate_summary
(
self
,
value
):
if
value
:
return
value
[:
500
]
def
validate
(
self
,
data
):
validated_data
=
super
().
validate
(
data
)
if
"summary"
in
data
:
validated_data
[
"summary"
]
=
{
"content_type"
:
"text/html"
,
"text"
:
data
[
"summary"
],
}
else
:
validated_data
[
"summary"
]
=
None
return
validated_data
class
APIActorSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -828,18 +855,6 @@ def include_image(repr, attachment):
repr
[
"image"
]
=
None
class
TruncatedCharField
(
serializers
.
CharField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
truncate_length
=
kwargs
.
pop
(
"truncate_length"
)
super
().
__init__
(
*
args
,
**
kwargs
)
def
to_internal_value
(
self
,
v
):
v
=
super
().
to_internal_value
(
v
)
if
v
:
v
=
v
[:
self
.
truncate_length
]
return
v
class
MusicEntitySerializer
(
jsonld
.
JsonLdSerializer
):
id
=
serializers
.
URLField
(
max_length
=
500
)
published
=
serializers
.
DateTimeField
()
...
...
api/funkwhale_api/users/serializers.py
View file @
87bc011e
...
...
@@ -11,6 +11,7 @@ from versatileimagefield.serializers import VersatileImageFieldSerializer
from
funkwhale_api.activity
import
serializers
as
activity_serializers
from
funkwhale_api.common
import
serializers
as
common_serializers
from
funkwhale_api.common
import
utils
as
common_utils
from
funkwhale_api.federation
import
models
as
federation_models
from
.
import
adapters
from
.
import
models
...
...
@@ -27,6 +28,7 @@ class ASCIIUsernameValidator(validators.RegexValidator):
username_validators
=
[
ASCIIUsernameValidator
()]
NOOP
=
object
()
class
RegisterSerializer
(
RS
):
...
...
@@ -106,6 +108,7 @@ class UserBasicSerializer(serializers.ModelSerializer):
class
UserWriteSerializer
(
serializers
.
ModelSerializer
):
avatar
=
avatar_field
summary
=
common_serializers
.
ContentSerializer
(
required
=
False
,
allow_null
=
True
)
class
Meta
:
model
=
models
.
User
...
...
@@ -115,8 +118,20 @@ class UserWriteSerializer(serializers.ModelSerializer):
"avatar"
,
"instance_support_message_display_date"
,
"funkwhale_support_message_display_date"
,
"summary"
,
]
def
update
(
self
,
obj
,
validated_data
):
if
not
obj
.
actor
:
obj
.
create_actor
()
summary
=
validated_data
.
pop
(
"summary"
,
NOOP
)
obj
=
super
().
update
(
obj
,
validated_data
)
if
summary
!=
NOOP
:
common_utils
.
attach_content
(
obj
.
actor
,
"summary_obj"
,
summary
)
return
obj
class
UserReadSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -150,17 +165,24 @@ class UserReadSerializer(serializers.ModelSerializer):
class
MeSerializer
(
UserReadSerializer
):
quota_status
=
serializers
.
SerializerMethodField
()
summary
=
serializers
.
SerializerMethodField
()
class
Meta
(
UserReadSerializer
.
Meta
):
fields
=
UserReadSerializer
.
Meta
.
fields
+
[
"quota_status"
,
"instance_support_message_display_date"
,
"funkwhale_support_message_display_date"
,
"summary"
,
]
def
get_quota_status
(
self
,
o
):
return
o
.
get_quota_status
()
if
o
.
actor
else
0
def
get_summary
(
self
,
o
):
if
not
o
.
actor
or
not
o
.
actor
.
summary_obj
:
return
return
common_serializers
.
ContentSerializer
(
o
.
actor
.
summary_obj
).
data
class
PasswordResetSerializer
(
PRS
):
def
get_email_options
(
self
):
...
...
api/tests/federation/test_serializers.py
View file @
87bc011e
...
...
@@ -53,7 +53,8 @@ def test_actor_serializer_from_ap(db):
assert
actor
.
type
==
"Person"
assert
actor
.
preferred_username
==
payload
[
"preferredUsername"
]
assert
actor
.
name
==
payload
[
"name"
]
assert
actor
.
summary
==
payload
[
"summary"
]
assert
actor
.
summary_obj
.
text
==
payload
[
"summary"
]
assert
actor
.
summary_obj
.
content_type
==
"text/html"
assert
actor
.
fid
==
actor_url
assert
actor
.
manually_approves_followers
is
True
assert
actor
.
private_key
is
None
...
...
@@ -89,7 +90,7 @@ def test_actor_serializer_only_mandatory_field_from_ap(db):
assert
actor
.
manually_approves_followers
is
None
def
test_actor_serializer_to_ap
():
def
test_actor_serializer_to_ap
(
db
):
expected
=
{
"@context"
:
jsonld
.
get_default_context
(),
"id"
:
"https://test.federation/user"
,
...
...
@@ -100,7 +101,6 @@ def test_actor_serializer_to_ap():
"outbox"
:
"https://test.federation/user/outbox"
,
"preferredUsername"
:
"user"
,
"name"
:
"Real User"
,
"summary"
:
"Hello world"
,
"manuallyApprovesFollowers"
:
False
,
"publicKey"
:
{
"id"
:
"https://test.federation/user#main-key"
,
...
...
@@ -109,7 +109,7 @@ def test_actor_serializer_to_ap():
},
"endpoints"
:
{
"sharedInbox"
:
"https://test.federation/inbox"
},
}
ac
=
models
.
Actor
(
ac
=
models
.
Actor
.
objects
.
create
(
fid
=
expected
[
"id"
],
inbox_url
=
expected
[
"inbox"
],
outbox_url
=
expected
[
"outbox"
],
...
...
@@ -119,11 +119,15 @@ def test_actor_serializer_to_ap():
public_key
=
expected
[
"publicKey"
][
"publicKeyPem"
],
preferred_username
=
expected
[
"preferredUsername"
],
name
=
expected
[
"name"
],
domain
=
models
.
Domain
(
pk
=
"test.federation"
),
summary
=
expected
[
"summary"
],
domain
=
models
.
Domain
.
objects
.
create
(
pk
=
"test.federation"
),
type
=
"Person"
,
manually_approves_followers
=
False
,
)
content
=
common_utils
.
attach_content
(
ac
,
"summary_obj"
,
{
"text"
:
"hello world"
,
"content_type"
:
"text/markdown"
}
)
expected
[
"summary"
]
=
content
.
rendered
serializer
=
serializers
.
ActorSerializer
(
ac
)
assert
serializer
.
data
==
expected
...
...
@@ -1127,14 +1131,17 @@ def test_local_actor_serializer_to_ap(factories):
preferred_username
=
expected
[
"preferredUsername"
],
name
=
expected
[
"name"
],
domain
=
models
.
Domain
.
objects
.
create
(
pk
=
"test.federation"
),
summary
=
expected
[
"summary"
],
type
=
"Person"
,
manually_approves_followers
=
False
,
)
content
=
common_utils
.
attach_content
(
ac
,
"summary_obj"
,
{
"text"
:
"hello world"
,
"content_type"
:
"text/markdown"
}
)
user
=
factories
[
"users.User"
]()
user
.
actor
=
ac
user
.
save
()
ac
.
refresh_from_db
()
expected
[
"summary"
]
=
content
.
rendered
expected
[
"icon"
]
=
{
"type"
:
"Image"
,
"mediaType"
:
"image/jpeg"
,
...
...
api/tests/users/test_views.py
View file @
87bc011e
import
pytest
from
django.urls
import
reverse
from
funkwhale_api.common
import
serializers
as
common_serializers
from
funkwhale_api.common
import
utils
as
common_utils
from
funkwhale_api.users.models
import
User
...
...
@@ -105,7 +107,10 @@ def test_can_fetch_data_from_api(api_client, factories):
# login required
assert
response
.
status_code
==
401
user
=
factories
[
"users.User"
](
permission_library
=
True
)
user
=
factories
[
"users.User"
](
permission_library
=
True
,
with_actor
=
True
)
summary
=
{
"content_type"
:
"text/plain"
,
"text"
:
"Hello"
}
summary_obj
=
common_utils
.
attach_content
(
user
.
actor
,
"summary_obj"
,
summary
)
api_client
.
login
(
username
=
user
.
username
,
password
=
"test"
)
response
=
api_client
.
get
(
url
)
assert
response
.
status_code
==
200
...
...
@@ -115,6 +120,10 @@ def test_can_fetch_data_from_api(api_client, factories):
assert
response
.
data
[
"email"
]
==
user
.
email
assert
response
.
data
[
"name"
]
==
user
.
name
assert
response
.
data
[
"permissions"
]
==
user
.
get_permissions
()
assert
(
response
.
data
[
"summary"
]
==
common_serializers
.
ContentSerializer
(
summary_obj
).
data
)
def
test_can_get_token_via_api
(
api_client
,
factories
):
...
...
@@ -202,6 +211,20 @@ def test_user_can_patch_his_own_settings(logged_in_api_client):
assert
user
.
privacy_level
==
"me"
def
test_user_can_patch_description
(
logged_in_api_client
):
user
=
logged_in_api_client
.
user
payload
=
{
"summary"
:
{
"content_type"
:
"text/markdown"
,
"text"
:
"hello"
}}
url
=
reverse
(
"api:v1:users:users-detail"
,
kwargs
=
{
"username"
:
user
.
username
})
response
=
logged_in_api_client
.
patch
(
url
,
payload
,
format
=
"json"
)
assert
response
.
status_code
==
200
user
.
refresh_from_db
()
assert
user
.
actor
.
summary_obj
.
content_type
==
payload
[
"summary"
][
"content_type"
]
assert
user
.
actor
.
summary_obj
.
text
==
payload
[
"summary"
][
"text"
]
def
test_user_can_request_new_subsonic_token
(
logged_in_api_client
):
user
=
logged_in_api_client
.
user
user
.
subsonic_api_token
=
"test"
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment