refac: article reads, story delete, no leaks, stored SEO status, rate limit, cleanup (%site%, article repo split, old lang repos, one default per site rule to domain)
This commit is contained in:
1 parent
9d284e65a9
commit
7c15206170
85 files changed
+1788
-593
No files matched your search
@@ -1,5 +1,5 @@
|
||||
Feature: Capabilities, translations, publish rules, delete and story move
|
||||
An article read tells the editor what the caller may do with the version and which readable sibling versions exist. Publishing respects disabled languages. Deleting the last version removes its story. A story moves to another site as a whole, atomically, never split.
|
||||
An article read tells the editor what the caller may do with the version and which readable sibling versions exist. Publishing respects disabled languages. Deleting the last version removes its story, and a whole article can be deleted at once, atomically. A story moves to another site as a whole, atomically, never split. Nothing reveals a version the caller cannot read.
|
||||
|
||||
Background:
|
||||
Given error responses are plain text, with extra data in headers
|
||||
@@ -9,6 +9,8 @@ Feature: Capabilities, translations, publish rules, delete and story move
|
||||
And "owner" holds every content permission at scope all
|
||||
And "writer1" (user 1) holds content.create, content.read, content.update, content.publish and content.delete at scope own, and content.read at scope all for online articles only
|
||||
And "reader" holds content.read at scope all only
|
||||
And "restricted" holds content.create at scope all and content.read at scope own
|
||||
And "deleter" holds content.delete at scope all and content.read at scope own
|
||||
|
||||
Scenario: Capabilities of the owner
|
||||
When the owner sends GET /v1/article/12
|
||||
@@ -30,8 +32,8 @@ Feature: Capabilities, translations, publish rules, delete and story move
|
||||
Scenario: Translations list readable versions only
|
||||
When writer1 sends GET /v1/article/10
|
||||
Then "translations" has entries for 10, 12 and 13 and none for 11
|
||||
And each entry has id, lang, title, is_online, language_enabled and capabilities
|
||||
And no count, placeholder or total names the hidden version
|
||||
And each entry has id, lang, title, is_online, language_enabled, excluded_from_public, source_article_id and capabilities
|
||||
And no entry, count, placeholder or total names the hidden version
|
||||
|
||||
Scenario: Translations are ordered default language first, then by tag
|
||||
When the owner sends GET /v1/article/12
|
||||
@@ -52,6 +54,7 @@ Feature: Capabilities, translations, publish rules, delete and story move
|
||||
When an anonymous client sends GET /v1/article/10
|
||||
Then "translations" is []
|
||||
And "capabilities" is ["read"]
|
||||
And the response has no "missing_langs", "excluded_from_public", "source_unavailable" or "partial_view"
|
||||
And the public shape is otherwise unchanged
|
||||
|
||||
Scenario: The source on the read
|
||||
@@ -62,6 +65,47 @@ Feature: Capabilities, translations, publish rules, delete and story move
|
||||
Then "source" is null
|
||||
And a version created empty has "source": null
|
||||
|
||||
Scenario Outline: Missing languages are the enabled languages without a readable version
|
||||
When <caller> sends GET /v1/article/10
|
||||
Then "missing_langs" is <missing>
|
||||
|
||||
Examples:
|
||||
| caller | missing |
|
||||
| owner | [] |
|
||||
| restricted | ["de","fr"] |
|
||||
| reader | [] |
|
||||
|
||||
Scenario: A version in a disabled language or without language is left out of public surfaces
|
||||
Given article 14 (es, offline, created by restricted) is in story 5 and es is disabled on journal
|
||||
And article 30 has lang "" and its own story
|
||||
When the owner sends GET /v1/article/14
|
||||
Then "excluded_from_public" is true
|
||||
And the translations entry of 14 has "excluded_from_public": true and the entry of 10 has false
|
||||
And GET /v1/article/30 has "excluded_from_public": true
|
||||
And GET /v1/article/10 has "excluded_from_public": false
|
||||
|
||||
Scenario: A deleted source and an unreadable source read the same
|
||||
Given article 14 tracks article 12 at content revision 2
|
||||
When the owner sends GET /v1/article/14
|
||||
Then "source" is {"article_id":12,"revision":2} and "source_unavailable" is false
|
||||
When the restricted user, who cannot read 12, sends GET /v1/article/14
|
||||
Then "source" is null and "source_unavailable" is true
|
||||
When article 12 is deleted and the owner sends GET /v1/article/14
|
||||
Then "source" is null and "source_unavailable" is true
|
||||
|
||||
Scenario: Translations name the source they track only when it is readable
|
||||
Given article 14 tracks article 12
|
||||
When the owner sends GET /v1/article/10
|
||||
Then the translations entry of 14 has "source_article_id": 12 and the entry of 10 has null
|
||||
When the restricted user sends GET /v1/article/14
|
||||
Then the translations entry of 14 has "source_article_id": null
|
||||
|
||||
Scenario: Partial view follows the read scope of the caller only
|
||||
When the owner sends GET /v1/article/10
|
||||
Then "partial_view" is false
|
||||
When the restricted user sends GET /v1/article/13
|
||||
Then "partial_view" is true, whether or not the story holds a version the restricted user cannot read
|
||||
|
||||
Scenario Outline: Publishing respects the language configuration
|
||||
Given a valid jwt cookie for a user holding content.publish
|
||||
When the client sends PATCH /v1/article/<id>/publish
|
||||
@@ -136,6 +180,40 @@ Feature: Capabilities, translations, publish rules, delete and story move
|
||||
Then the story remains
|
||||
And nothing in the response mentions the other version
|
||||
|
||||
Scenario: Deleting an article removes every version and the article
|
||||
When the owner sends DELETE /v1/story/5
|
||||
Then the response status is 204 with an empty body
|
||||
And articles 10, 11, 12 and 13 and story 5 are gone
|
||||
And one article.delete event per version is recorded with params.lang and params.story_id 5
|
||||
|
||||
Scenario: Versions the caller cannot read are deleted with the article
|
||||
When the deleter, who reads 10 and 13 only, sends DELETE /v1/story/5
|
||||
Then the response status is 204
|
||||
And articles 11 and 12 are deleted too
|
||||
|
||||
Scenario: Deleting an article needs delete permission on every version
|
||||
When writer1, whose content.delete is at scope own, sends DELETE /v1/story/5
|
||||
Then the response status is 403, the body is "missing_permission" and X-Missing-Permission is "content.delete"
|
||||
And every version remains
|
||||
And the answer is the same whether or not the story holds a version writer1 cannot read
|
||||
|
||||
Scenario: A failed delete removes nothing
|
||||
Given the database fails while the versions are deleted
|
||||
When the owner sends DELETE /v1/story/5
|
||||
Then the response status is 500 and the body is "Failed to delete article"
|
||||
And every version and story 5 remain
|
||||
|
||||
Scenario: An unknown or unreadable article answers the same not found
|
||||
When the owner sends DELETE /v1/story/9999
|
||||
Then the response status is 404 and the body is "Article not found"
|
||||
When the deleter sends DELETE /v1/story/77, whose only version is a draft by another user
|
||||
Then the response status is 404 and the body is "Article not found"
|
||||
And that version remains
|
||||
|
||||
Scenario: An invalid id is refused
|
||||
When the owner sends DELETE /v1/story/abc
|
||||
Then the response status is 400 with the uint32 id message
|
||||
|
||||
Scenario: Move a story
|
||||
When the owner sends POST /v1/story/5/move with {"site":"atelier"}
|
||||
Then the response status is 200
|
||||
|
||||
Reference in new issue
Block a user