mirror of
https://github.com/flarum/framework.git
synced 2025-02-01 00:28:29 +08:00
fix: regressions
This commit is contained in:
parent
4e3b5f7c2c
commit
bc5bac0ac4
|
@ -91,11 +91,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"InvalidGroup"#g99',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
@ -166,11 +167,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Mods"#g4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -198,11 +200,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Admins"#g1 @"Mods"#g4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -232,11 +235,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Members"#g3 @"Guests"#g2',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -288,11 +292,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Mods"#g4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -319,11 +324,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 4,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Mods"#g4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -350,11 +356,12 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 4,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Ninjas"#g10',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -381,6 +388,7 @@ class GroupMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'New content with @"Mods"#g4 mention',
|
||||
],
|
||||
|
|
|
@ -82,11 +82,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@potato#4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -113,11 +114,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"POTATO$"#p4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -144,11 +146,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"potato"#p50',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -175,11 +178,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@“POTATO$”#p4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -206,11 +210,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"franzofflarum"#p215',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -237,11 +242,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"TOBY$"#p5 @"flarum"#2015 @"franzofflarum"#220 @"POTATO$"#3 @"POTATO$"#p4',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -384,11 +390,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad "#p6 User"#p9',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -436,11 +443,12 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#p9',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -467,6 +475,7 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#p9',
|
||||
],
|
||||
|
@ -495,6 +504,7 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#p9',
|
||||
],
|
||||
|
@ -523,6 +533,7 @@ class PostMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"acme"#p11',
|
||||
],
|
||||
|
|
|
@ -68,11 +68,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#flarum',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -96,11 +97,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#戦い',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -125,11 +127,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#franzofflarum',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -155,11 +158,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#test',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -183,11 +187,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#dev',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -211,11 +216,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#dev',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -239,11 +245,12 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#test #flarum #support #laravel #franzofflarum',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -365,6 +372,7 @@ class TagMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '#laravel',
|
||||
],
|
||||
|
|
|
@ -72,11 +72,12 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@potato',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -105,11 +106,12 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@potato',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => 2]],
|
||||
'discussion' => ['data' => ['type' => 'discussions', 'id' => 2]],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -136,6 +138,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"POTATO$"#3',
|
||||
],
|
||||
|
@ -167,6 +170,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@“POTATO$”#3',
|
||||
],
|
||||
|
@ -198,6 +202,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"franzofflarum"#82',
|
||||
],
|
||||
|
@ -229,6 +234,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"TOBY$"#4 @"POTATO$"#p4 @"franzofflarum"#82 @"POTATO$"#3',
|
||||
],
|
||||
|
@ -282,6 +288,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"potato_"#3',
|
||||
],
|
||||
|
@ -312,6 +319,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"potato_"#3',
|
||||
],
|
||||
|
@ -367,6 +375,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad "#p6 User"#5',
|
||||
],
|
||||
|
@ -419,6 +428,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#5',
|
||||
],
|
||||
|
@ -450,6 +460,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#5',
|
||||
],
|
||||
|
@ -478,6 +489,7 @@ class UserMentionsTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '@"Bad _ User"#5',
|
||||
],
|
||||
|
|
|
@ -45,6 +45,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'nickname' => 'new nickname',
|
||||
],
|
||||
|
@ -72,6 +73,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'nickname' => 'new nickname',
|
||||
],
|
||||
|
|
|
@ -119,6 +119,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'title' => 'ACME',
|
||||
],
|
||||
|
@ -133,6 +134,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'lastReadPostNumber' => 2,
|
||||
],
|
||||
|
@ -148,6 +150,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
|
@ -203,6 +206,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
|
@ -249,6 +253,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 4,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
|
@ -270,6 +275,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'isApproved' => 1,
|
||||
],
|
||||
|
@ -309,6 +315,7 @@ class ReplyNotificationTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'restricted-test-post',
|
||||
],
|
||||
|
|
|
@ -45,6 +45,7 @@ class UseForumTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'Test post',
|
||||
'content' => '<t><p>Hello, world!</p></t>'
|
||||
|
@ -65,6 +66,7 @@ class UseForumTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => '<t><p>Hello, world!</p></t>'
|
||||
],
|
||||
|
|
|
@ -91,6 +91,7 @@ class SuspendUserTest extends TestCase
|
|||
'authenticatedAs' => $authenticatedAs,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'suspendedUntil' => Carbon::now()->addDay(),
|
||||
'suspendReason' => 'Suspended for acme reasons.',
|
||||
|
|
|
@ -53,6 +53,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -75,6 +76,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -103,6 +105,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -125,6 +128,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -154,6 +158,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -189,6 +194,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -218,6 +224,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -248,6 +255,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -277,6 +285,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -307,6 +316,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
|
|
@ -225,6 +225,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
|
|
@ -75,6 +75,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'tags',
|
||||
'attributes' => [
|
||||
'name' => 'Dev Blog',
|
||||
'slug' => 'dev-blog',
|
||||
|
|
|
@ -1,106 +1,139 @@
|
|||
validation:
|
||||
accepted: "The :attribute must be accepted."
|
||||
active_url: "The :attribute is not a valid URL."
|
||||
after: "The :attribute must be a date after :date."
|
||||
after_or_equal: "The :attribute must be a date after or equal to :date."
|
||||
alpha: "The :attribute must only contain letters."
|
||||
alpha_dash: "The :attribute must only contain letters, numbers, dashes and underscores."
|
||||
alpha_num: "The :attribute must only contain letters and numbers."
|
||||
array: "The :attribute must be an array."
|
||||
before: "The :attribute must be a date before :date."
|
||||
before_or_equal: "The :attribute must be a date before or equal to :date."
|
||||
accepted: "The :attribute field must be accepted."
|
||||
accepted_if: "The :attribute field must be accepted when :other is :value."
|
||||
active_url: "The :attribute field must be a valid URL."
|
||||
after: "The :attribute field must be a date after :date."
|
||||
after_or_equal: "The :attribute field must be a date after or equal to :date."
|
||||
alpha: "The :attribute field must only contain letters."
|
||||
alpha_dash: "The :attribute field must only contain letters, numbers, dashes, and underscores."
|
||||
alpha_num: "The :attribute field must only contain letters and numbers."
|
||||
array: "The :attribute field must be an array."
|
||||
ascii: "The :attribute field must only contain single-byte alphanumeric characters and symbols."
|
||||
before: "The :attribute field must be a date before :date."
|
||||
before_or_equal: "The :attribute field must be a date before or equal to :date."
|
||||
between:
|
||||
numeric: "The :attribute must be between :min and :max."
|
||||
file: "The :attribute must be between :min and :max kilobytes."
|
||||
string: "The :attribute must be between :min and :max characters."
|
||||
array: "The :attribute must have between :min and :max items."
|
||||
array: "The :attribute field must have between :min and :max items."
|
||||
file: "The :attribute field must be between :min and :max kilobytes."
|
||||
numeric: "The :attribute field must be between :min and :max."
|
||||
string: "The :attribute field must be between :min and :max characters."
|
||||
boolean: "The :attribute field must be true or false."
|
||||
confirmed: "The :attribute confirmation does not match."
|
||||
date: "The :attribute is not a valid date."
|
||||
date_equals: "The :attribute must be a date equal to :date."
|
||||
date_format: "The :attribute does not match the format :format."
|
||||
different: "The :attribute and :other must be different."
|
||||
digits: "The :attribute must be :digits digits."
|
||||
digits_between: "The :attribute must be between :min and :max digits."
|
||||
dimensions: "The :attribute has invalid image dimensions."
|
||||
can: "The :attribute field contains an unauthorized value."
|
||||
confirmed: "The :attribute field confirmation does not match."
|
||||
current_password: "The password is incorrect."
|
||||
date: "The :attribute field must be a valid date."
|
||||
date_equals: "The :attribute field must be a date equal to :date."
|
||||
date_format: "The :attribute field must match the format :format."
|
||||
decimal: "The :attribute field must have :decimal decimal places."
|
||||
declined: "The :attribute field must be declined."
|
||||
declined_if: "The :attribute field must be declined when :other is :value."
|
||||
different: "The :attribute field and :other must be different."
|
||||
digits: "The :attribute field must be :digits digits."
|
||||
digits_between: "The :attribute field must be between :min and :max digits."
|
||||
dimensions: "The :attribute field has invalid image dimensions."
|
||||
distinct: "The :attribute field has a duplicate value."
|
||||
email: "The :attribute must be a valid email address."
|
||||
ends_with: "The :attribute must end with one of the following: :values."
|
||||
doesnt_end_with: "The :attribute field must not end with one of the following: :values."
|
||||
doesnt_start_with: "The :attribute field must not start with one of the following: :values."
|
||||
email: "The :attribute field must be a valid email address."
|
||||
ends_with: "The :attribute field must end with one of the following: :values."
|
||||
enum: "The selected :attribute is invalid."
|
||||
exists: "The selected :attribute is invalid."
|
||||
file: "The :attribute must be a file."
|
||||
file_too_large: "The :attribute is too large."
|
||||
file_upload_failed: "The :attribute failed to upload."
|
||||
extensions: "The :attribute field must have one of the following extensions: :values."
|
||||
file: "The :attribute field must be a file."
|
||||
filled: "The :attribute field must have a value."
|
||||
gt:
|
||||
numeric: "The :attribute must be greater than :value."
|
||||
file: "The :attribute must be greater than :value kilobytes."
|
||||
string: "The :attribute must be greater than :value characters."
|
||||
array: "The :attribute must have more than :value items."
|
||||
array: "The :attribute field must have more than :value items."
|
||||
file: "The :attribute field must be greater than :value kilobytes."
|
||||
numeric: "The :attribute field must be greater than :value."
|
||||
string: "The :attribute field must be greater than :value characters."
|
||||
gte:
|
||||
numeric: "The :attribute must be greater than or equal :value."
|
||||
file: "The :attribute must be greater than or equal :value kilobytes."
|
||||
string: "The :attribute must be greater than or equal :value characters."
|
||||
array: "The :attribute must have :value items or more."
|
||||
image: "The :attribute must be an image."
|
||||
array: "The :attribute field must have :value items or more."
|
||||
file: "The :attribute field must be greater than or equal to :value kilobytes."
|
||||
numeric: "The :attribute field must be greater than or equal to :value."
|
||||
string: "The :attribute field must be greater than or equal to :value characters."
|
||||
hex_color: "The :attribute field must be a valid hexadecimal color."
|
||||
image: "The :attribute field must be an image."
|
||||
in: "The selected :attribute is invalid."
|
||||
in_array: "The :attribute field does not exist in :other."
|
||||
integer: "The :attribute must be an integer."
|
||||
ip: "The :attribute must be a valid IP address."
|
||||
ipv4: "The :attribute must be a valid IPv4 address."
|
||||
ipv6: "The :attribute must be a valid IPv6 address."
|
||||
json: "The :attribute must be a valid JSON string."
|
||||
in_array: "The :attribute field must exist in :other."
|
||||
integer: "The :attribute field must be an integer."
|
||||
ip: "The :attribute field must be a valid IP address."
|
||||
ipv4: "The :attribute field must be a valid IPv4 address."
|
||||
ipv6: "The :attribute field must be a valid IPv6 address."
|
||||
json: "The :attribute field must be a valid JSON string."
|
||||
lowercase: "The :attribute field must be lowercase."
|
||||
lt:
|
||||
numeric: "The :attribute must be less than :value."
|
||||
file: "The :attribute must be less than :value kilobytes."
|
||||
string: "The :attribute must be less than :value characters."
|
||||
array: "The :attribute must have less than :value items."
|
||||
array: "The :attribute field must have less than :value items."
|
||||
file: "The :attribute field must be less than :value kilobytes."
|
||||
numeric: "The :attribute field must be less than :value."
|
||||
string: "The :attribute field must be less than :value characters."
|
||||
lte:
|
||||
numeric: "The :attribute must be less than or equal :value."
|
||||
file: "The :attribute must be less than or equal :value kilobytes."
|
||||
string: "The :attribute must be less than or equal :value characters."
|
||||
array: "The :attribute must not have more than :value items."
|
||||
array: "The :attribute field must not have more than :value items."
|
||||
file: "The :attribute field must be less than or equal to :value kilobytes."
|
||||
numeric: "The :attribute field must be less than or equal to :value."
|
||||
string: "The :attribute field must be less than or equal to :value characters."
|
||||
mac_address: "The :attribute field must be a valid MAC address."
|
||||
max:
|
||||
numeric: "The :attribute must not be greater than :max."
|
||||
file: "The :attribute must not be greater than :max kilobytes."
|
||||
string: "The :attribute must not be greater than :max characters."
|
||||
array: "The :attribute must not have more than :max items."
|
||||
mimes: "The :attribute must be a file of type: :values."
|
||||
mimetypes: "The :attribute must be a file of type: :values."
|
||||
array: "The :attribute field must not have more than :max items."
|
||||
file: "The :attribute field must not be greater than :max kilobytes."
|
||||
numeric: "The :attribute field must not be greater than :max."
|
||||
string: "The :attribute field must not be greater than :max characters."
|
||||
max_digits: "The :attribute field must not have more than :max digits."
|
||||
mimes: "The :attribute field must be a file of type: :values."
|
||||
mimetypes: "The :attribute field must be a file of type: :values."
|
||||
min:
|
||||
numeric: "The :attribute must be at least :min."
|
||||
file: "The :attribute must be at least :min kilobytes."
|
||||
string: "The :attribute must be at least :min characters."
|
||||
array: "The :attribute must have at least :min items."
|
||||
multiple_of: "The :attribute must be a multiple of :value."
|
||||
array: "The :attribute field must have at least :min items."
|
||||
file: "The :attribute field must be at least :min kilobytes."
|
||||
numeric: "The :attribute field must be at least :min."
|
||||
string: "The :attribute field must be at least :min characters."
|
||||
min_digits: "The :attribute field must have at least :min digits."
|
||||
missing: "The :attribute field must be missing."
|
||||
missing_if: "The :attribute field must be missing when :other is :value."
|
||||
missing_unless: "The :attribute field must be missing unless :other is :value."
|
||||
missing_with: "The :attribute field must be missing when :values is present."
|
||||
missing_with_all: "The :attribute field must be missing when :values are present."
|
||||
multiple_of: "The :attribute field must be a multiple of :value."
|
||||
not_in: "The selected :attribute is invalid."
|
||||
not_regex: "The :attribute format is invalid."
|
||||
numeric: "The :attribute must be a number."
|
||||
password: "The password is incorrect."
|
||||
not_regex: "The :attribute field format is invalid."
|
||||
numeric: "The :attribute field must be a number."
|
||||
password:
|
||||
letters: "The :attribute field must contain at least one letter."
|
||||
mixed: "The :attribute field must contain at least one uppercase and one lowercase letter."
|
||||
numbers: "The :attribute field must contain at least one number."
|
||||
symbols: "The :attribute field must contain at least one symbol."
|
||||
uncompromised: "The given :attribute has appeared in a data leak. Please choose a different :attribute."
|
||||
present: "The :attribute field must be present."
|
||||
regex: "The :attribute format is invalid."
|
||||
present_if: "The :attribute field must be present when :other is :value."
|
||||
present_unless: "The :attribute field must be present unless :other is :value."
|
||||
present_with: "The :attribute field must be present when :values is present."
|
||||
present_with_all: "The :attribute field must be present when :values are present."
|
||||
prohibited: "The :attribute field is prohibited."
|
||||
prohibited_if: "The :attribute field is prohibited when :other is :value."
|
||||
prohibited_unless: "The :attribute field is prohibited unless :other is in :values."
|
||||
prohibits: "The :attribute field prohibits :other from being present."
|
||||
regex: "The :attribute field format is invalid."
|
||||
required: "The :attribute field is required."
|
||||
required_array_keys: "The :attribute field must contain entries for: :values."
|
||||
required_if: "The :attribute field is required when :other is :value."
|
||||
required_if_accepted: "The :attribute field is required when :other is accepted."
|
||||
required_unless: "The :attribute field is required unless :other is in :values."
|
||||
required_with: "The :attribute field is required when :values is present."
|
||||
required_with_all: "The :attribute field is required when :values are present."
|
||||
required_without: "The :attribute field is required when :values is not present."
|
||||
required_without_all: "The :attribute field is required when none of :values are present."
|
||||
prohibited: "The :attribute field is prohibited."
|
||||
prohibited_if: "The :attribute field is prohibited when :other is :value."
|
||||
prohibited_unless: "The :attribute field is prohibited unless :other is in :values."
|
||||
same: "The :attribute and :other must match."
|
||||
same: "The :attribute field must match :other."
|
||||
size:
|
||||
numeric: "The :attribute must be :size."
|
||||
file: "The :attribute must be :size kilobytes."
|
||||
string: "The :attribute must be :size characters."
|
||||
array: "The :attribute must contain :size items."
|
||||
starts_with: "The :attribute must start with one of the following: :values."
|
||||
string: "The :attribute must be a string."
|
||||
timezone: "The :attribute must be a valid zone."
|
||||
array: "The :attribute field must contain :size items."
|
||||
file: "The :attribute field must be :size kilobytes."
|
||||
numeric: "The :attribute field must be :size."
|
||||
string: "The :attribute field must be :size characters."
|
||||
starts_with: "The :attribute field must start with one of the following: :values."
|
||||
string: "The :attribute field must be a string."
|
||||
timezone: "The :attribute field must be a valid timezone."
|
||||
unique: "The :attribute has already been taken."
|
||||
uploaded: "The :attribute failed to upload."
|
||||
url: "The :attribute format is invalid."
|
||||
uuid: "The :attribute must be a valid UUID."
|
||||
uppercase: "The :attribute field must be uppercase."
|
||||
url: "The :attribute field must be a valid URL."
|
||||
ulid: "The :attribute field must be a valid ULID."
|
||||
uuid: "The :attribute field must be a valid UUID."
|
||||
|
||||
attributes:
|
||||
username: username
|
||||
|
|
|
@ -123,12 +123,6 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||
return $pipe;
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.api.notification_serializers', function () {
|
||||
return [
|
||||
'discussionRenamed' => BasicDiscussionSerializer::class
|
||||
];
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.api_client.exclude_middleware', function () {
|
||||
return [
|
||||
HttpMiddleware\InjectActorReference::class,
|
||||
|
@ -159,22 +153,10 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||
|
||||
public function boot(Container $container): void
|
||||
{
|
||||
$this->setNotificationSerializers();
|
||||
|
||||
AbstractSerializeController::setContainer($container);
|
||||
|
||||
AbstractSerializer::setContainer($container);
|
||||
}
|
||||
|
||||
protected function setNotificationSerializers(): void
|
||||
{
|
||||
$serializers = $this->container->make('flarum.api.notification_serializers');
|
||||
|
||||
foreach ($serializers as $type => $serializer) {
|
||||
NotificationSerializer::setSubjectSerializer($type, $serializer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function populateRoutes(RouteCollection $routes): void
|
||||
{
|
||||
/** @var RouteHandlerFactory $factory */
|
||||
|
|
|
@ -12,6 +12,7 @@ class Context extends BaseContext
|
|||
protected ?SearchResults $search = null;
|
||||
protected int|string|null $modelId = null;
|
||||
protected array $internal = [];
|
||||
protected array $parameters = [];
|
||||
|
||||
public function withModelId(int|string|null $id): static
|
||||
{
|
||||
|
@ -53,4 +54,15 @@ class Context extends BaseContext
|
|||
{
|
||||
return RequestUtil::getActor($this->request);
|
||||
}
|
||||
|
||||
public function setParam(string $key, mixed $default = null): static
|
||||
{
|
||||
$this->parameters[$key] = $default;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParam(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $this->parameters[$key] ?? $default;
|
||||
}
|
||||
}
|
||||
|
|
32
framework/core/src/Api/Controller/ShowForumController.php
Normal file
32
framework/core/src/Api/Controller/ShowForumController.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Controller;
|
||||
|
||||
use Flarum\Api\Endpoint\Show;
|
||||
use Flarum\Api\JsonApi;
|
||||
use Flarum\Api\Resource\ForumResource;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class ShowForumController implements RequestHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected JsonApi $api
|
||||
) {}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->api
|
||||
->forResource(ForumResource::class)
|
||||
->forEndpoint(Show::class)
|
||||
->handle($request);
|
||||
}
|
||||
}
|
|
@ -78,8 +78,8 @@ trait ExtractsListingParams
|
|||
return [
|
||||
'filter' => RequestUtil::extractFilter($context->request),
|
||||
'sort' => RequestUtil::extractSort($context->request, $this->defaultSort, $this->getAvailableSorts($context)),
|
||||
'limit' => RequestUtil::extractLimit($context->request, $this->limit, $this->maxLimit) ?? -1,
|
||||
'offset' => RequestUtil::extractOffset($context->request),
|
||||
'limit' => $limit = (RequestUtil::extractLimit($context->request, $this->limit, $this->maxLimit) ?? -1),
|
||||
'offset' => RequestUtil::extractOffset($context->request, $limit),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Flarum\Api\Endpoint\Concerns;
|
||||
|
||||
use Flarum\Api\Context;
|
||||
use Flarum\Api\Schema\Attribute;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
trait ValidatesData
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function assertDataIsValid(Context $context, array $data, bool $validateAll): void
|
||||
{
|
||||
$rules = [
|
||||
'attributes' => [],
|
||||
'relationships' => [],
|
||||
];
|
||||
$messages = [];
|
||||
$attributes = [];
|
||||
|
||||
foreach ($context->fields($context->resource) as $field) {
|
||||
$writable = $field->isWritable($context->withField($field));
|
||||
|
||||
if (! $writable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $field instanceof Attribute ? 'attributes' : 'relationships';
|
||||
|
||||
$rules[$type] = array_merge($rules[$type], $field->getValidationRules($context));
|
||||
$messages = array_merge($messages, $field->getValidationMessages($context));
|
||||
$attributes = array_merge($attributes, $field->getValidationAttributes($context));
|
||||
}
|
||||
|
||||
// @todo: merge into a single validator.
|
||||
$attributeValidator = resolve(Factory::class)->make($data['attributes'], $rules['attributes'], $messages, $attributes);
|
||||
$relationshipValidator = resolve(Factory::class)->make($data['relationships'], $rules['relationships'], $messages, $attributes);
|
||||
|
||||
$attributeValidator->validate();
|
||||
$relationshipValidator->validate();
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ use Flarum\Api\Endpoint\Concerns\HasCustomRoute;
|
|||
use Flarum\Api\Endpoint\Concerns\HasEagerLoading;
|
||||
use Flarum\Api\Endpoint\Concerns\HasHooks;
|
||||
use Flarum\Api\Endpoint\Concerns\SavesData;
|
||||
use Flarum\Api\Endpoint\Concerns\ValidatesData;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RuntimeException;
|
||||
|
@ -29,7 +28,6 @@ class Create extends BaseCreate implements Endpoint
|
|||
use HasAuthorization;
|
||||
use HasEagerLoading;
|
||||
use HasCustomRoute;
|
||||
use ValidatesData;
|
||||
use HasHooks;
|
||||
|
||||
public function handle(Context $context): ?ResponseInterface
|
||||
|
@ -67,7 +65,7 @@ class Create extends BaseCreate implements Endpoint
|
|||
$this->fillDefaultValues($context, $data);
|
||||
$this->deserializeValues($context, $data);
|
||||
$this->mutateDataBeforeValidation($context, $data, true);
|
||||
$this->assertDataIsValid($context, $data, true);
|
||||
$this->assertDataValid($context, $data);
|
||||
|
||||
$this->setValues($context, $data);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ use Flarum\Api\Endpoint\Concerns\HasCustomRoute;
|
|||
use Flarum\Api\Endpoint\Concerns\HasEagerLoading;
|
||||
use Flarum\Api\Endpoint\Concerns\HasHooks;
|
||||
use Flarum\Api\Endpoint\Concerns\SavesData;
|
||||
use Flarum\Api\Endpoint\Concerns\ValidatesData;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RuntimeException;
|
||||
|
@ -27,7 +26,6 @@ class Update extends BaseUpdate implements Endpoint
|
|||
use HasAuthorization;
|
||||
use HasEagerLoading;
|
||||
use HasCustomRoute;
|
||||
use ValidatesData;
|
||||
use HasHooks;
|
||||
|
||||
public function handle(Context $context): ?ResponseInterface
|
||||
|
@ -70,7 +68,7 @@ class Update extends BaseUpdate implements Endpoint
|
|||
$this->assertFieldsValid($context, $data);
|
||||
$this->deserializeValues($context, $data);
|
||||
$this->mutateDataBeforeValidation($context, $data, false);
|
||||
$this->assertDataIsValid($context, $data, false);
|
||||
$this->assertDataValid($context, $data);
|
||||
$this->setValues($context, $data);
|
||||
|
||||
$context = $context->withModel($model = $resource->update($model, $context));
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Flarum\Api;
|
|||
use Flarum\Api\Endpoint\Endpoint;
|
||||
use Flarum\Api\Endpoint\EndpointRoute;
|
||||
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use Laminas\Diactoros\Uri;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
|
@ -18,6 +19,7 @@ class JsonApi extends BaseJsonApi
|
|||
{
|
||||
protected string $resourceClass;
|
||||
protected string $endpoint;
|
||||
protected ?Request $baseRequest = null;
|
||||
|
||||
public function forResource(string $resourceClass): self
|
||||
{
|
||||
|
@ -58,6 +60,13 @@ class JsonApi extends BaseJsonApi
|
|||
throw new BadRequestException('Invalid endpoint specified');
|
||||
}
|
||||
|
||||
public function withRequest(Request $request): self
|
||||
{
|
||||
$this->baseRequest = $request;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
$context = $this->makeContext($request);
|
||||
|
@ -65,27 +74,31 @@ class JsonApi extends BaseJsonApi
|
|||
return $context->endpoint->handle($context);
|
||||
}
|
||||
|
||||
public function execute(ServerRequestInterface|array $request, array $internal = []): mixed
|
||||
public function execute(array $body, array $internal = [], array $options = []): mixed
|
||||
{
|
||||
/** @var EndpointRoute $route */
|
||||
$route = (new $this->endpoint)->route();
|
||||
|
||||
if (is_array($request)) {
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody($request);
|
||||
$request = $this->baseRequest ?? ServerRequestFactory::fromGlobals();
|
||||
|
||||
if (! empty($options['actor'])) {
|
||||
$request = RequestUtil::withActor($request, $options['actor']);
|
||||
}
|
||||
|
||||
$request = $request
|
||||
->withMethod($route->method)
|
||||
->withUri(new Uri($route->path))
|
||||
->withParsedBody([
|
||||
...$body,
|
||||
'data' => [
|
||||
...($request->getParsedBody()['data'] ?? []),
|
||||
...($body['data'] ?? []),
|
||||
'type' => (new $this->resourceClass)->type(),
|
||||
],
|
||||
]);
|
||||
|
||||
$context = $this->makeContext($request)
|
||||
->withModelId($data['id'] ?? null);
|
||||
->withModelId($body['data']['id'] ?? null);
|
||||
|
||||
foreach ($internal as $key => $value) {
|
||||
$context = $context->withInternal($key, $value);
|
||||
|
|
|
@ -12,8 +12,11 @@ use Flarum\Api\Resource\Contracts\{
|
|||
Deletable
|
||||
};
|
||||
use Flarum\Api\Resource\Concerns\Bootable;
|
||||
use Flarum\Api\Resource\Concerns\ResolvesValidationFactory;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use RuntimeException;
|
||||
use Tobyz\JsonApiServer\Context;
|
||||
use Tobyz\JsonApiServer\Laravel\EloquentResource as BaseResource;
|
||||
|
||||
|
@ -28,6 +31,7 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
|||
{
|
||||
use Bootable;
|
||||
use DispatchEventsTrait;
|
||||
use ResolvesValidationFactory;
|
||||
|
||||
abstract public function model(): string;
|
||||
|
||||
|
@ -36,9 +40,20 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
|||
return new ($this->model());
|
||||
}
|
||||
|
||||
public function resource(object $model, Context $context): ?string
|
||||
{
|
||||
$baseModel = $this->model();
|
||||
|
||||
if ($model instanceof $baseModel) {
|
||||
return $this->type();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
throw new \RuntimeException('Not supported in Flarum, please use a model searcher instead https://docs.flarum.org/extend/search.');
|
||||
throw new RuntimeException('Not supported in Flarum, please use a model searcher instead https://docs.flarum.org/extend/search.');
|
||||
}
|
||||
|
||||
public function create(object $model, Context $context): object
|
||||
|
@ -110,27 +125,26 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
|||
//
|
||||
}
|
||||
|
||||
protected function bcSavingEvent(Context $context, array $data): ?object
|
||||
protected function newSavingEvent(Context $context, array $data): ?object
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function mutateDataBeforeValidation(Context $context, array $data, bool $validateAll): array
|
||||
{
|
||||
return $data;
|
||||
$dirty = $context->model->getDirty();
|
||||
|
||||
// @todo: decided to completely drop this.
|
||||
$savingEvent = $this->bcSavingEvent($context, $data);
|
||||
$savingEvent = $this->newSavingEvent($context, Arr::get($context->body(), 'data', []));
|
||||
|
||||
if ($savingEvent) {
|
||||
// BC Layer for Flarum 1.0
|
||||
// @todo: should we drop this or keep it for 2.0? another massive BC break.
|
||||
// @todo: replace with resource extenders
|
||||
$this->container->make(Dispatcher::class)->dispatch(
|
||||
$savingEvent
|
||||
);
|
||||
$this->container->make(Dispatcher::class)->dispatch($savingEvent);
|
||||
|
||||
return array_merge($data, $context->model->getDirty());
|
||||
$dirtyAfterEvent = $context->model->getDirty();
|
||||
|
||||
// Unlike 1.0, the saving events in 2.0 do not allow modifying the model.
|
||||
if ($dirtyAfterEvent !== $dirty) {
|
||||
throw new RuntimeException('You should modify the model through the saving event. Please use the resource extenders instead.');
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
namespace Flarum\Api\Resource;
|
||||
|
||||
use Flarum\Api\Resource\Concerns\Bootable;
|
||||
use Flarum\Api\Resource\Concerns\ResolvesValidationFactory;
|
||||
use Tobyz\JsonApiServer\Resource\AbstractResource as BaseResource;
|
||||
|
||||
abstract class AbstractResource extends BaseResource
|
||||
{
|
||||
use Bootable;
|
||||
use ResolvesValidationFactory;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Flarum\Api\Resource\Concerns;
|
||||
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
|
||||
trait ResolvesValidationFactory
|
||||
{
|
||||
/**
|
||||
* Called by the JSON:API server package to resolve the validation factory.
|
||||
*/
|
||||
public function validationFactory(): Factory
|
||||
{
|
||||
return resolve(Factory::class);
|
||||
}
|
||||
}
|
|
@ -289,7 +289,8 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||
// We will do this by running the PostReply command.
|
||||
$post = $api->forResource(PostResource::class)
|
||||
->forEndpoint(Create::class)
|
||||
->execute($context->request->withParsedBody([
|
||||
->withRequest($context->request)
|
||||
->execute([
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'content' => $context->request->getParsedBody()['data']['attributes']['content'],
|
||||
|
@ -303,7 +304,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||
],
|
||||
],
|
||||
],
|
||||
]), ['isFirstPost' => true]);
|
||||
], ['isFirstPost' => true]);
|
||||
|
||||
// Before we dispatch events, refresh our discussion instance's
|
||||
// attributes as posting the reply will have changed some of them (e.g.
|
||||
|
@ -327,7 +328,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||
);
|
||||
}
|
||||
|
||||
protected function bcSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
protected function newSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
{
|
||||
return new Saving($context->model, $context->getActor(), $data);
|
||||
}
|
||||
|
|
|
@ -71,8 +71,10 @@ class GroupResource extends AbstractDatabaseResource
|
|||
->writable()
|
||||
->required(),
|
||||
Schema\Str::make('color')
|
||||
->nullable()
|
||||
->writable(),
|
||||
Schema\Str::make('icon')
|
||||
->nullable()
|
||||
->writable(),
|
||||
Schema\Boolean::make('isHidden')
|
||||
->writable(),
|
||||
|
@ -99,7 +101,7 @@ class GroupResource extends AbstractDatabaseResource
|
|||
return $name;
|
||||
}
|
||||
|
||||
protected function bcSavingEvent(Context $context, array $data): ?object
|
||||
protected function newSavingEvent(Context $context, array $data): ?object
|
||||
{
|
||||
return new Saving($context->model, RequestUtil::getActor($context->request), $data);
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ class NotificationResource extends AbstractDatabaseResource
|
|||
->type('users')
|
||||
->includable(),
|
||||
Schema\Relationship\ToOne::make('subject')
|
||||
->type($subjectTypes)
|
||||
->collection($subjectTypes)
|
||||
->includable(),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ class PostResource extends AbstractDatabaseResource
|
|||
}
|
||||
}
|
||||
})
|
||||
->serialize(function (string|array $value, Context $context) {
|
||||
->serialize(function (null|string|array $value, Context $context) {
|
||||
// Prevent the string type from trying to convert array content (for event posts) to a string.
|
||||
$context->field->type = null;
|
||||
|
||||
|
@ -204,7 +204,10 @@ class PostResource extends AbstractDatabaseResource
|
|||
Schema\DateTime::make('editedAt'),
|
||||
Schema\Boolean::make('isHidden')
|
||||
->visible(fn (Post $post) => $post->hidden_at !== null)
|
||||
->writable(fn (Post $post, Context $context) => $context->getActor()->can('hide', $post))
|
||||
->writable(function (Post $post, Context $context) {
|
||||
return $context->endpoint instanceof Endpoint\Update
|
||||
&& $context->getActor()->can('hide', $post);
|
||||
})
|
||||
->set(function (Post $post, bool $value, Context $context) {
|
||||
if ($post instanceof CommentPost) {
|
||||
if ($value) {
|
||||
|
@ -271,7 +274,7 @@ class PostResource extends AbstractDatabaseResource
|
|||
);
|
||||
}
|
||||
|
||||
protected function bcSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
protected function newSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
{
|
||||
return new Saving($context->model, $context->getActor(), $data);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Flarum\Api\Resource;
|
|||
use Flarum\Api\Context;
|
||||
use Flarum\Api\Endpoint;
|
||||
use Flarum\Api\Schema;
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use Flarum\Http\SlugManager;
|
||||
use Flarum\Locale\TranslatorInterface;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
@ -12,6 +13,7 @@ use Flarum\User\Event\Deleting;
|
|||
use Flarum\User\Event\GroupsChanged;
|
||||
use Flarum\User\Event\RegisteringFromProvider;
|
||||
use Flarum\User\Event\Saving;
|
||||
use Flarum\User\Exception\NotAuthenticatedException;
|
||||
use Flarum\User\RegistrationToken;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
@ -65,7 +67,25 @@ class UserResource extends AbstractDatabaseResource
|
|||
return true;
|
||||
}),
|
||||
Endpoint\Update::make()
|
||||
->authenticated()
|
||||
->visible(function (User $user, Context $context) {
|
||||
$actor = $context->getActor();
|
||||
$body = $context->body();
|
||||
|
||||
// Require the user's current password if they are attempting to change
|
||||
// their own email address.
|
||||
|
||||
if (isset($body['data']['attributes']['email']) && $actor->id === $user->id) {
|
||||
$password = (string) Arr::get($body, 'meta.password');
|
||||
|
||||
if (! $actor->checkPassword($password)) {
|
||||
throw new NotAuthenticatedException;
|
||||
}
|
||||
}
|
||||
|
||||
$actor->assertRegistered();
|
||||
|
||||
return true;
|
||||
})
|
||||
->defaultInclude(['groups']),
|
||||
Endpoint\Delete::make()
|
||||
->authenticated()
|
||||
|
@ -81,13 +101,16 @@ class UserResource extends AbstractDatabaseResource
|
|||
|
||||
public function fields(): array
|
||||
{
|
||||
$translator = resolve(TranslatorInterface::class);
|
||||
|
||||
return [
|
||||
Schema\Str::make('username')
|
||||
->requiredOnCreate()
|
||||
->requiredOnCreateWithout(['token'])
|
||||
->unique('users', 'username', true)
|
||||
->regex('/^[a-z0-9_-]+$/i')
|
||||
->validationMessages([
|
||||
'username.regex' => resolve(TranslatorInterface::class)->trans('core.api.invalid_username_message')
|
||||
'username.regex' => $translator->trans('core.api.invalid_username_message'),
|
||||
'username.required_without' => $translator->trans('validation.required', ['attribute' => $translator->trans('validation.attributes.username')])
|
||||
])
|
||||
->minLength(3)
|
||||
->maxLength(30)
|
||||
|
@ -103,7 +126,10 @@ class UserResource extends AbstractDatabaseResource
|
|||
}
|
||||
}),
|
||||
Schema\Str::make('email')
|
||||
->requiredOnCreate()
|
||||
->requiredOnCreateWithout(['token'])
|
||||
->validationMessages([
|
||||
'email.required_without' => $translator->trans('validation.required', ['attribute' => $translator->trans('validation.attributes.email')])
|
||||
])
|
||||
->email(['filter'])
|
||||
->unique('users', 'email', true)
|
||||
->visible(function (User $user, Context $context) {
|
||||
|
@ -144,6 +170,9 @@ class UserResource extends AbstractDatabaseResource
|
|||
}),
|
||||
Schema\Str::make('password')
|
||||
->requiredOnCreateWithout(['token'])
|
||||
->validationMessages([
|
||||
'password.required_without' => $translator->trans('validation.required', ['attribute' => $translator->trans('validation.attributes.password')])
|
||||
])
|
||||
->minLength(8)
|
||||
->visible(false)
|
||||
->writable(function (User $user, Context $context) {
|
||||
|
@ -159,16 +188,17 @@ class UserResource extends AbstractDatabaseResource
|
|||
->writable(function (User $user, Context $context) {
|
||||
return $context->endpoint instanceof Endpoint\Create;
|
||||
})
|
||||
->set(function (User $user, ?string $value) {
|
||||
->set(function (User $user, ?string $value, Context $context) {
|
||||
if ($value) {
|
||||
$token = RegistrationToken::validOrFail($value);
|
||||
|
||||
$user->setAttribute('token', $token);
|
||||
$context->setParam('token', $token);
|
||||
$user->password ??= Str::random(20);
|
||||
|
||||
$this->applyToken($user, $token);
|
||||
}
|
||||
}),
|
||||
})
|
||||
->save(fn () => null),
|
||||
Schema\Str::make('displayName'),
|
||||
Schema\Str::make('avatarUrl'),
|
||||
Schema\Str::make('slug')
|
||||
|
@ -289,7 +319,7 @@ class UserResource extends AbstractDatabaseResource
|
|||
/** @param User $model */
|
||||
public function saved(object $model, \Tobyz\JsonApiServer\Context $context): ?object
|
||||
{
|
||||
if (($token = $model->getAttribute('token')) instanceof RegistrationToken) {
|
||||
if (($token = $context->getParam('token')) instanceof RegistrationToken) {
|
||||
$this->fulfillToken($model, $token);
|
||||
}
|
||||
|
||||
|
@ -303,7 +333,7 @@ class UserResource extends AbstractDatabaseResource
|
|||
);
|
||||
}
|
||||
|
||||
protected function bcSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
protected function newSavingEvent(\Tobyz\JsonApiServer\Context $context, array $data): ?object
|
||||
{
|
||||
return new Saving($context->model, $context->getActor(), $data);
|
||||
}
|
||||
|
@ -343,13 +373,17 @@ class UserResource extends AbstractDatabaseResource
|
|||
]);
|
||||
|
||||
if ($urlValidator->fails()) {
|
||||
throw new InvalidArgumentException('Provided avatar URL must be a valid URI.', 503);
|
||||
throw new ValidationException([
|
||||
'avatar_url' => 'Provided avatar URL must be a valid URI.',
|
||||
]);
|
||||
}
|
||||
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME);
|
||||
|
||||
if (! in_array($scheme, ['http', 'https'])) {
|
||||
throw new InvalidArgumentException("Provided avatar URL must have scheme http or https. Scheme provided was $scheme.", 503);
|
||||
throw new ValidationException([
|
||||
'avatar_url' => "Provided avatar URL must have scheme http or https. Scheme provided was $scheme.",
|
||||
]);
|
||||
}
|
||||
|
||||
$image = $this->imageManager->make($url);
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
namespace Flarum\Api\Schema;
|
||||
|
||||
use Flarum\Api\Schema\Concerns\EvaluatesCallbacks;
|
||||
use Flarum\Api\Schema\Concerns\HasValidationRules;
|
||||
use Tobyz\JsonApiServer\Schema\Field\Attribute as BaseAttribute;
|
||||
|
||||
class Attribute extends BaseAttribute
|
||||
{
|
||||
use HasValidationRules;
|
||||
use EvaluatesCallbacks;
|
||||
//
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Flarum\Api\Schema\Concerns;
|
||||
|
||||
use Tobyz\JsonApiServer\Context;
|
||||
|
||||
trait EvaluatesCallbacks
|
||||
{
|
||||
protected function evaluate(Context $context, mixed $callback): mixed
|
||||
{
|
||||
if (is_string($callback) || ! is_callable($callback)) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
return (isset($context->model))
|
||||
? $callback($context->model, $context)
|
||||
: $callback($context);
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Flarum\Api\Schema\Concerns;
|
||||
|
||||
use Flarum\Api\Endpoint\Create;
|
||||
use Flarum\Api\Endpoint\Update;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Tobyz\JsonApiServer\Context;
|
||||
|
||||
trait HasValidationRules
|
||||
{
|
||||
/**
|
||||
* @var array<array{rule: string|callable, condition: bool|callable}>
|
||||
*/
|
||||
protected array $rules = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $validationMessages = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $validationAttributes = [];
|
||||
|
||||
public function rules(array|string $rules, bool|callable $condition, bool $override = true): static
|
||||
{
|
||||
if (is_string($rules)) {
|
||||
$rules = explode('|', $rules);
|
||||
}
|
||||
|
||||
$rules = array_map(function ($rule) use ($condition) {
|
||||
return compact('rule', 'condition');
|
||||
}, $rules);
|
||||
|
||||
$this->rules = $override ? $rules : array_merge($this->rules, $rules);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function validationMessages(array $messages): static
|
||||
{
|
||||
$this->validationMessages = array_merge($this->validationMessages, $messages);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function validationAttributes(array $attributes): static
|
||||
{
|
||||
$this->validationAttributes = array_merge($this->validationAttributes, $attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function rule(string|callable $rule, bool|callable $condition = true): static
|
||||
{
|
||||
$this->rules[] = compact('rule', 'condition');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRules(): array
|
||||
{
|
||||
return $this->rules;
|
||||
}
|
||||
|
||||
public function getValidationRules(Context $context): array
|
||||
{
|
||||
$rules = array_map(
|
||||
fn ($rule) => $this->evaluate($context, $rule['rule']),
|
||||
array_filter(
|
||||
$this->rules,
|
||||
fn ($rule) => $this->evaluate($context, $rule['condition'])
|
||||
)
|
||||
);
|
||||
|
||||
return [
|
||||
$this->name => $rules
|
||||
];
|
||||
}
|
||||
|
||||
public function getValidationMessages(Context $context): array
|
||||
{
|
||||
return $this->validationMessages;
|
||||
}
|
||||
|
||||
public function getValidationAttributes(Context $context): array
|
||||
{
|
||||
return $this->validationAttributes;
|
||||
}
|
||||
|
||||
public function requiredOnCreate(): static
|
||||
{
|
||||
return $this->rule('required', fn ($model, Context $context) => $context->endpoint instanceof Create);
|
||||
}
|
||||
|
||||
public function requiredOnUpdate(): static
|
||||
{
|
||||
return $this->rule('required', fn ($model, Context $context) => !$context->endpoint instanceof Update);
|
||||
}
|
||||
|
||||
public function requiredWith(array $fields, bool|callable $condition): static
|
||||
{
|
||||
return $this->rule('required_with:' . implode(',', $fields), $condition);
|
||||
}
|
||||
|
||||
public function requiredWithout(array $fields, bool|callable $condition): static
|
||||
{
|
||||
return $this->rule('required_without:' . implode(',', $fields), $condition);
|
||||
}
|
||||
|
||||
public function requiredOnCreateWith(array $fields): static
|
||||
{
|
||||
return $this->requiredWith($fields, fn ($model, Context $context) => $context->endpoint instanceof Create);
|
||||
}
|
||||
|
||||
public function requiredOnUpdateWith(array $fields): static
|
||||
{
|
||||
return $this->requiredWith($fields, fn ($model, Context $context) => $context->endpoint instanceof Update);
|
||||
}
|
||||
|
||||
public function requiredOnCreateWithout(array $fields): static
|
||||
{
|
||||
return $this->requiredWithout($fields, fn ($model, Context $context) => $context->endpoint instanceof Create);
|
||||
}
|
||||
|
||||
public function requiredOnUpdateWithout(array $fields): static
|
||||
{
|
||||
return $this->requiredWithout($fields, fn ($model, Context $context) => $context->endpoint instanceof Update);
|
||||
}
|
||||
|
||||
public function nullable(bool $nullable = true): static
|
||||
{
|
||||
parent::nullable($nullable);
|
||||
|
||||
return $this->rule('nullable');
|
||||
}
|
||||
|
||||
public function unique(string $table, string $column, bool $ignorable = false, bool|callable $condition = true): static
|
||||
{
|
||||
return $this->rule(function ($model, Context $context) use ($table, $column, $ignorable) {
|
||||
$rule = Rule::unique($table, $column);
|
||||
|
||||
if ($ignorable && ($modelId = $context->model?->getKey())) {
|
||||
$rule = $rule->ignore($modelId, $context->model->getKeyName());
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}, $condition);
|
||||
}
|
||||
}
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
namespace Flarum\Api\Schema\Relationship;
|
||||
|
||||
use Flarum\Api\Schema\Concerns\EvaluatesCallbacks;
|
||||
use Flarum\Api\Schema\Concerns\HasValidationRules;
|
||||
use Tobyz\JsonApiServer\Schema\Field\ToMany as BaseToMany;
|
||||
|
||||
class ToMany extends BaseToMany
|
||||
{
|
||||
use HasValidationRules;
|
||||
use EvaluatesCallbacks;
|
||||
//
|
||||
}
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
namespace Flarum\Api\Schema\Relationship;
|
||||
|
||||
use Flarum\Api\Schema\Concerns\EvaluatesCallbacks;
|
||||
use Flarum\Api\Schema\Concerns\HasValidationRules;
|
||||
use Tobyz\JsonApiServer\Schema\Field\ToOne as BaseToOne;
|
||||
|
||||
class ToOne extends BaseToOne
|
||||
{
|
||||
use HasValidationRules;
|
||||
use EvaluatesCallbacks;
|
||||
//
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
namespace Flarum\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Locale\TranslatorInterface;
|
||||
|
@ -27,7 +26,6 @@ class Index
|
|||
protected SettingsRepositoryInterface $settings,
|
||||
protected UrlGenerator $url,
|
||||
protected TranslatorInterface $translator,
|
||||
protected ListDiscussionsController $controller
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -38,17 +36,14 @@ class Index
|
|||
$sort = Arr::pull($queryParams, 'sort');
|
||||
$q = Arr::pull($queryParams, 'q');
|
||||
$page = max(1, intval(Arr::pull($queryParams, 'page')));
|
||||
$filters = Arr::pull($queryParams, 'filter', []);
|
||||
|
||||
$sortMap = resolve('flarum.forum.discussions.sortmap');
|
||||
$limit = $this->controller->limit;
|
||||
|
||||
$params = [
|
||||
...$queryParams,
|
||||
'sort' => $sort && isset($sortMap[$sort]) ? $sortMap[$sort] : null,
|
||||
'filter' => $filters,
|
||||
'page' => [
|
||||
'offset' => ($page - 1) * $limit,
|
||||
'limit' => $limit
|
||||
'number' => $page
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class RegisterController implements RequestHandlerInterface
|
|||
|
||||
public function handle(Request $request): ResponseInterface
|
||||
{
|
||||
$params = ['data' => ['attributes' => $request->getParsedBody()]];
|
||||
$params = ['data' => ['type' => 'users', 'attributes' => $request->getParsedBody() ?? []]];
|
||||
|
||||
$response = $this->api->withParentRequest($request)->withBody($params)->post('/users');
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Foundation\ErrorHandling\ExceptionHandler;
|
||||
|
||||
use Flarum\Foundation\ErrorHandling\HandledError;
|
||||
use Throwable;
|
||||
use Tobyz\JsonApiServer\Exception\ErrorProvider;
|
||||
|
||||
class JsonApiExceptionHandler
|
||||
{
|
||||
public function handle(ErrorProvider&Throwable $e): HandledError
|
||||
{
|
||||
return (new HandledError(
|
||||
$e,
|
||||
'validation_error',
|
||||
$e->getJsonApiStatus()
|
||||
))->withDetails($e->getJsonApiErrors());
|
||||
}
|
||||
}
|
|
@ -73,12 +73,11 @@ class Registry
|
|||
|
||||
private function handleCustomTypes(Throwable $error): ?HandledError
|
||||
{
|
||||
$errorClass = $error::class;
|
||||
|
||||
if (isset($this->handlerMap[$errorClass])) {
|
||||
$handler = new $this->handlerMap[$errorClass];
|
||||
|
||||
return $handler->handle($error);
|
||||
foreach ($this->handlerMap as $class => $handler) {
|
||||
if ($error instanceof $class) {
|
||||
$handler = new $handler;
|
||||
return $handler->handle($error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -52,12 +52,6 @@ class ErrorServiceProvider extends AbstractServiceProvider
|
|||
return [
|
||||
InvalidParameterException::class => 'invalid_parameter',
|
||||
ModelNotFoundException::class => 'not_found',
|
||||
|
||||
TobyzJsonApiServerException\BadRequestException::class => 'invalid_parameter',
|
||||
TobyzJsonApiServerException\MethodNotAllowedException::class => 'method_not_allowed',
|
||||
TobyzJsonApiServerException\ForbiddenException::class => 'permission_denied',
|
||||
TobyzJsonApiServerException\ConflictException::class => 'io_error',
|
||||
// TobyzJsonApiServerException\UnprocessableEntityException::class => 'invalid_parameter', @todo
|
||||
];
|
||||
});
|
||||
|
||||
|
@ -68,6 +62,7 @@ class ErrorServiceProvider extends AbstractServiceProvider
|
|||
ExtensionException\CircularDependenciesException::class => ExtensionException\CircularDependenciesExceptionHandler::class,
|
||||
ExtensionException\DependentExtensionsException::class => ExtensionException\DependentExtensionsExceptionHandler::class,
|
||||
ExtensionException\MissingDependenciesException::class => ExtensionException\MissingDependenciesExceptionHandler::class,
|
||||
TobyzJsonApiServerException\ErrorProvider::class => Handling\ExceptionHandler\JsonApiExceptionHandler::class,
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@ class CheckCsrfToken implements Middleware
|
|||
|
||||
public function process(Request $request, Handler $handler): Response
|
||||
{
|
||||
// @todo: debugging
|
||||
if (true || in_array($request->getAttribute('routeName'), $this->exemptRoutes, true)) {
|
||||
if (in_array($request->getAttribute('routeName'), $this->exemptRoutes, true)) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
|
|
|
@ -99,10 +99,10 @@ class RequestUtil
|
|||
return ($page - 1) * $limit;
|
||||
}
|
||||
|
||||
public static function extractOffset(Request $request): int
|
||||
public static function extractOffset(Request $request, ?int $limit = 0): int
|
||||
{
|
||||
if ($request->getQueryParams()['page']['number'] ?? false) {
|
||||
return self::extractOffsetFromNumber($request, self::extractLimit($request));
|
||||
return self::extractOffsetFromNumber($request, $limit);
|
||||
}
|
||||
|
||||
$offset = (int) ($request->getQueryParams()['page']['offset'] ?? 0);
|
||||
|
|
|
@ -52,6 +52,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => $authenticatedAs,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'access-tokens',
|
||||
'attributes' => [
|
||||
'title' => 'Dev'
|
||||
]
|
||||
|
@ -74,6 +75,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => $authenticatedAs,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'access-tokens',
|
||||
'attributes' => [
|
||||
'title' => 'Dev'
|
||||
]
|
||||
|
|
|
@ -42,6 +42,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'Test post',
|
||||
'content' => '',
|
||||
|
@ -51,10 +52,11 @@ class CreateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
$body = (string) $response->getBody();
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode(), $body);
|
||||
|
||||
// The response body should contain details about the failed validation
|
||||
$body = (string) $response->getBody();
|
||||
$this->assertJson($body);
|
||||
$this->assertEquals([
|
||||
'errors' => [
|
||||
|
@ -78,6 +80,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => '',
|
||||
'content' => 'Test post',
|
||||
|
@ -114,6 +117,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -123,11 +127,13 @@ class CreateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode());
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode(), $body);
|
||||
|
||||
/** @var Discussion $discussion */
|
||||
$discussion = Discussion::firstOrFail();
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$data = json_decode($body, true);
|
||||
|
||||
$this->assertEquals('test - too-obscure', $discussion->title);
|
||||
$this->assertEquals('test - too-obscure', Arr::get($data, 'data.attributes.title'));
|
||||
|
@ -146,6 +152,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => '我是一个土豆',
|
||||
'content' => 'predetermined content for automated testing',
|
||||
|
@ -178,6 +185,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => '我是一个土豆',
|
||||
'content' => 'predetermined content for automated testing',
|
||||
|
@ -205,6 +213,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -219,6 +228,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'Second predetermined content for automated testing - too-obscure',
|
||||
|
@ -241,6 +251,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'predetermined content for automated testing - too-obscure',
|
||||
|
@ -255,6 +266,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'test - too-obscure',
|
||||
'content' => 'Second predetermined content for automated testing - too-obscure',
|
||||
|
|
|
@ -44,7 +44,8 @@ class ShowTest extends TestCase
|
|||
|
||||
$json = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('actor', Arr::get($json, 'data.relationships'));
|
||||
$this->assertArrayHasKey('actor', Arr::get($json, 'data.relationships'));
|
||||
$this->assertNull(Arr::get($json, 'data.relationships.actor.data'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,7 +44,7 @@ class CreateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
$this->assertEquals(400, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'groups',
|
||||
'attributes' => [
|
||||
'nameSingular' => 'flarumite',
|
||||
'namePlural' => 'flarumites',
|
||||
|
@ -68,10 +69,12 @@ class CreateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode());
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode(), $body);
|
||||
|
||||
// Verify API response body
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$data = json_decode($body, true);
|
||||
$this->assertEquals('flarumite', Arr::get($data, 'data.attributes.nameSingular'));
|
||||
$this->assertEquals('flarumites', Arr::get($data, 'data.attributes.namePlural'));
|
||||
$this->assertEquals('test', Arr::get($data, 'data.attributes.icon'));
|
||||
|
@ -95,6 +98,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'groups',
|
||||
'attributes' => [
|
||||
'nameSingular' => 'flarumite',
|
||||
'namePlural' => 'flarumites',
|
||||
|
|
|
@ -75,7 +75,7 @@ class ShowTest extends TestCase
|
|||
);
|
||||
|
||||
// Hidden group should not be returned for guest
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertEquals(404, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +109,7 @@ class ShowTest extends TestCase
|
|||
|
||||
// If group does not exist in database, controller
|
||||
// should reject the request with 404 Not found
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertEquals(404, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
protected function hiddenGroup(): array
|
||||
|
|
|
@ -53,6 +53,6 @@ class ListTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals(200, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,18 +68,21 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => $actorId,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => ['data' => ['id' => $discussionId]],
|
||||
'discussion' => [
|
||||
'data' => ['type' => 'discussions', 'id' => $discussionId]
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals($responseStatus, $response->getStatusCode());
|
||||
$this->assertEquals($responseStatus, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
public function discussionRepliesPrvider(): array
|
||||
|
@ -103,6 +106,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
|
@ -119,6 +123,7 @@ class CreateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'posts',
|
||||
'attributes' => [
|
||||
'content' => 'Second reply with predetermined content for automated testing - too-obscure',
|
||||
],
|
||||
|
@ -130,6 +135,6 @@ class CreateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertEquals(429, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,8 +71,10 @@ class ListTest extends TestCase
|
|||
$this->request('GET', '/api/posts', ['authenticatedAs' => 1])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), $body);
|
||||
$data = json_decode($body, true);
|
||||
|
||||
$this->assertEquals(5, count($data['data']));
|
||||
}
|
||||
|
|
|
@ -39,15 +39,19 @@ class CreateTest extends TestCase
|
|||
'POST',
|
||||
'/api/users',
|
||||
[
|
||||
'json' => ['data' => ['attributes' => []]],
|
||||
'json' => ['data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [],
|
||||
]],
|
||||
]
|
||||
)->withAttribute('bypassCsrfToken', true)
|
||||
);
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
$body = (string) $response->getBody();
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode(), $body);
|
||||
|
||||
// The response body should contain details about the failed validation
|
||||
$body = (string) $response->getBody();
|
||||
$this->assertJson($body);
|
||||
$this->assertEquals([
|
||||
'errors' => [
|
||||
|
@ -96,7 +100,7 @@ class CreateTest extends TestCase
|
|||
)->withAttribute('bypassCsrfToken', true)
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode());
|
||||
$this->assertEquals(201, $response->getStatusCode(), (string) $response->getBody());
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::where('username', 'test')->firstOrFail();
|
||||
|
@ -227,12 +231,12 @@ class CreateTest extends TestCase
|
|||
$this->assertJson($body);
|
||||
$decodedBody = json_decode($body, true);
|
||||
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
$this->assertEquals(422, $response->getStatusCode(), $body);
|
||||
|
||||
$firstError = $decodedBody['errors'][0];
|
||||
|
||||
// Check that the error is an invalid URI
|
||||
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must have scheme http or https. Scheme provided was '.$regToken['scheme'].'.', $firstError['detail']);
|
||||
$this->assertStringContainsString('Provided avatar URL must have scheme http or https. Scheme provided was '.$regToken['scheme'].'.', $firstError['detail']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,12 +305,12 @@ class CreateTest extends TestCase
|
|||
$this->assertJson($body);
|
||||
$decodedBody = json_decode($body, true);
|
||||
|
||||
$this->assertEquals(500, $response->getStatusCode());
|
||||
$this->assertEquals(422, $response->getStatusCode(), $body);
|
||||
|
||||
$firstError = $decodedBody['errors'][0];
|
||||
|
||||
// Check that the error is an invalid URI
|
||||
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must be a valid URI.', $firstError['detail']);
|
||||
$this->assertStringContainsString('Provided avatar URL must be a valid URI.', $firstError['detail']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +378,7 @@ class CreateTest extends TestCase
|
|||
)->withAttribute('bypassCsrfToken', true)
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode());
|
||||
$this->assertEquals(201, $response->getStatusCode(), (string) $response->getBody());
|
||||
|
||||
$user = User::where('username', $regToken->user_attributes['username'])->firstOrFail();
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
|
||||
$response = $this->createRequest(['admins'], 2);
|
||||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
@ -97,7 +97,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
|
||||
$response = $this->createRequest(['1'], 2);
|
||||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
@ -110,7 +110,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +129,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,7 +169,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
|
||||
$response = $this->createRequest(['admins'], 1);
|
||||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
@ -182,7 +182,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
|
||||
$response = $this->createRequest(['1'], 1);
|
||||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
@ -195,7 +195,7 @@ class GroupSearchTest extends TestCase
|
|||
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
|
||||
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
|
||||
$this->assertCount(0, $responseBodyContents['included'], json_encode($responseBodyContents));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -101,6 +101,7 @@ class PasswordEmailTokensTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'new-normal@machine.local'
|
||||
]
|
||||
|
@ -112,7 +113,7 @@ class PasswordEmailTokensTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals(200, $response->getStatusCode(), $response->getBody());
|
||||
$this->assertEquals(1, EmailToken::query()->where('user_id', 2)->count());
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class SendActivationEmailTest extends TestCase
|
|||
);
|
||||
|
||||
// We don't want to delay tests too long.
|
||||
EmailActivationThrottler::$timeout = 5;
|
||||
EmailActivationThrottler::$timeout = 1;
|
||||
sleep(EmailActivationThrottler::$timeout + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class SendPasswordResetEmailTest extends TestCase
|
|||
);
|
||||
|
||||
// We don't want to delay tests too long.
|
||||
PasswordResetThrottler::$timeout = 5;
|
||||
PasswordResetThrottler::$timeout = 1;
|
||||
sleep(PasswordResetThrottler::$timeout + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,13 +68,15 @@ class UpdateTest extends TestCase
|
|||
$response = $this->send(
|
||||
$this->request('PATCH', '/api/users/2', [
|
||||
'authenticatedAs' => 2,
|
||||
'json' => [],
|
||||
'json' => [
|
||||
'data' => []
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
// Test for successful response and that the email is included in the response
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertStringContainsString('normal@machine.local', (string) $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatusCode(), $body = (string) $response->getBody());
|
||||
$this->assertStringContainsString('normal@machine.local', $body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,13 +87,15 @@ class UpdateTest extends TestCase
|
|||
$response = $this->send(
|
||||
$this->request('PATCH', '/api/users/1', [
|
||||
'authenticatedAs' => 2,
|
||||
'json' => [],
|
||||
'json' => [
|
||||
'data' => []
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
// Make sure sensitive information is not made public
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertStringNotContainsString('admin@machine.local', (string) $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatusCode(), $body = (string) $response->getBody());
|
||||
$this->assertStringNotContainsString('admin@machine.local', $body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,6 +124,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'someOtherEmail@example.com',
|
||||
]
|
||||
|
@ -131,7 +136,7 @@ class UpdateTest extends TestCase
|
|||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(401, $response->getStatusCode());
|
||||
$this->assertEquals(401, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +149,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'someOtherEmail@example.com',
|
||||
]
|
||||
|
@ -180,7 +186,7 @@ class UpdateTest extends TestCase
|
|||
);
|
||||
|
||||
// We don't want to delay tests too long.
|
||||
EmailChangeThrottler::$timeout = 5;
|
||||
EmailChangeThrottler::$timeout = 1;
|
||||
sleep(EmailChangeThrottler::$timeout + 1);
|
||||
}
|
||||
|
||||
|
@ -223,6 +229,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'username' => 'iCantChangeThis',
|
||||
],
|
||||
|
@ -243,6 +250,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'preferences' => [
|
||||
'something' => 'else'
|
||||
|
@ -268,7 +276,7 @@ class UpdateTest extends TestCase
|
|||
'relationships' => [
|
||||
'groups' => [
|
||||
'data' => [
|
||||
['id' => 1, 'type' => 'group']
|
||||
['id' => 1, 'type' => 'groups']
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -289,6 +297,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'markedAllAsReadAt' => Carbon::now()
|
||||
],
|
||||
|
@ -309,6 +318,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'isEmailConfirmed' => true
|
||||
],
|
||||
|
@ -345,6 +355,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'someOtherEmail@example.com',
|
||||
]
|
||||
|
@ -368,6 +379,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'username' => 'iCantChangeThis',
|
||||
],
|
||||
|
@ -391,7 +403,7 @@ class UpdateTest extends TestCase
|
|||
'relationships' => [
|
||||
'groups' => [
|
||||
'data' => [
|
||||
['id' => 1, 'type' => 'group']
|
||||
['id' => 1, 'type' => 'groups']
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -412,6 +424,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'isEmailConfirmed' => true
|
||||
],
|
||||
|
@ -450,6 +463,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'someOtherEmail@example.com',
|
||||
]
|
||||
|
@ -471,6 +485,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'username' => 'iCanChangeThis',
|
||||
],
|
||||
|
@ -492,6 +507,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'email' => 'someOtherEmail@example.com',
|
||||
]
|
||||
|
@ -513,6 +529,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 3,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'username' => 'iCanChangeThis',
|
||||
],
|
||||
|
@ -537,7 +554,7 @@ class UpdateTest extends TestCase
|
|||
'relationships' => [
|
||||
'groups' => [
|
||||
'data' => [
|
||||
['id' => 4, 'type' => 'group']
|
||||
['id' => 4, 'type' => 'groups']
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -545,7 +562,7 @@ class UpdateTest extends TestCase
|
|||
],
|
||||
])
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals(200, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -585,7 +602,7 @@ class UpdateTest extends TestCase
|
|||
'relationships' => [
|
||||
'groups' => [
|
||||
'data' => [
|
||||
['id' => 1, 'type' => 'group']
|
||||
['id' => 1, 'type' => 'groups']
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -610,7 +627,7 @@ class UpdateTest extends TestCase
|
|||
'relationships' => [
|
||||
'groups' => [
|
||||
'data' => [
|
||||
['id' => 1, 'type' => 'group']
|
||||
['id' => 1, 'type' => 'groups']
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -632,6 +649,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 2,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'isEmailConfirmed' => true
|
||||
],
|
||||
|
@ -652,6 +670,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'preferences' => [
|
||||
'something' => 'else'
|
||||
|
@ -674,6 +693,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'markedAllAsReadAt' => Carbon::now()
|
||||
],
|
||||
|
@ -694,6 +714,7 @@ class UpdateTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'users',
|
||||
'attributes' => [
|
||||
'isEmailConfirmed' => true
|
||||
],
|
||||
|
@ -724,7 +745,7 @@ class UpdateTest extends TestCase
|
|||
],
|
||||
])
|
||||
);
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
$this->assertEquals(403, $response->getStatusCode(), (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,12 +33,14 @@ class EventTest extends TestCase
|
|||
return $api->forResource(GroupResource::class)
|
||||
->forEndpoint(Create::class)
|
||||
->execute([
|
||||
'attributes' => [
|
||||
'nameSingular' => 'test group',
|
||||
'namePlural' => 'test groups',
|
||||
'color' => '#000000',
|
||||
'icon' => 'fas fa-crown',
|
||||
]
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'nameSingular' => 'test group',
|
||||
'namePlural' => 'test groups',
|
||||
'color' => '#000000',
|
||||
'icon' => 'fas fa-crown',
|
||||
]
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ class SearchIndexTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => $type,
|
||||
'attributes' => [
|
||||
$attribute => 'test',
|
||||
],
|
||||
|
@ -93,6 +94,7 @@ class SearchIndexTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => $type,
|
||||
'attributes' => [
|
||||
$attribute => 'changed'
|
||||
]
|
||||
|
@ -137,6 +139,7 @@ class SearchIndexTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => $type,
|
||||
'attributes' => [
|
||||
'isHidden' => true
|
||||
]
|
||||
|
@ -162,6 +165,7 @@ class SearchIndexTest extends TestCase
|
|||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => $type,
|
||||
'attributes' => [
|
||||
'isHidden' => false
|
||||
]
|
||||
|
|
|
@ -34,10 +34,11 @@ class RegisterTest extends TestCase
|
|||
$this->request('POST', '/register')
|
||||
);
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
$body = (string) $response->getBody();
|
||||
|
||||
$this->assertEquals(422, $response->getStatusCode(), $body);
|
||||
|
||||
// The response body should contain details about the failed validation
|
||||
$body = (string) $response->getBody();
|
||||
$this->assertJson($body);
|
||||
$this->assertEquals([
|
||||
'errors' => [
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
namespace Flarum\Tests\integration\policy;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Api\Endpoint\Create;
|
||||
use Flarum\Api\JsonApi;
|
||||
use Flarum\Api\Resource\PostResource;
|
||||
use Flarum\Bus\Dispatcher;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
|
@ -93,9 +96,30 @@ class DiscussionPolicyTest extends TestCase
|
|||
$this->assertTrue($user->can('rename', $discussion));
|
||||
$this->assertFalse($user->can('rename', $discussionWithReply));
|
||||
|
||||
$this->app()->getContainer()->make(Dispatcher::class)->dispatch(
|
||||
new PostReply(1, User::findOrFail(1), ['attributes' => ['content' => 'test']], null)
|
||||
);
|
||||
/** @var JsonApi $api */
|
||||
$api = $this->app()->getContainer()->make(JsonApi::class);
|
||||
|
||||
$api
|
||||
->forResource(PostResource::class)
|
||||
->forEndpoint(Create::class)
|
||||
->execute(
|
||||
body: [
|
||||
'data' => [
|
||||
'attributes' => [
|
||||
'content' => 'test'
|
||||
],
|
||||
'relationships' => [
|
||||
'discussion' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'id' => '1'
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
options: ['actor' => User::findOrFail(1)]
|
||||
);
|
||||
|
||||
// Date further into the future
|
||||
Carbon::setTestNow('2025-01-01 13:00:00');
|
||||
|
|
Loading…
Reference in New Issue
Block a user