tumblr / docs

Tumblr's public platform documentation.
Apache License 2.0
108 stars 26 forks source link

Posts lack field to differentiate between NPF/legacy posts #96

Closed marcustyphoon closed 1 year ago

marcustyphoon commented 1 year ago

As per conversation with Cyle in the New XKit discord:

https://github.com/tumblr/docs/blob/ec08f10278aaa46862990c7516e92f77e57c68d9/api.md?plain=1#L584-L586

Right now this doesn't appear to be true: is_blocks_post_format never appears, and even legacy posts have type: blocks.

There is therefore no current way to identify if a post is stored in NPF or legacy, which is important to be able to query so that one doesn't edit the post using the /posts/{post-id} NPF edit endpoint with fields like exclude_trail_items that break legacy posts.

(As an aside, the XKit Rewritten currently attempts to determine this by querying the private/undocumented shouldOpenInLegacy field on the root post, if available; I noticed a sort-of-bug where editing a legacy original post in the iOS app's editor will flip this to true even though the post is still legacy and it should probably still open in the legacy post editor on desktop.)

cyle commented 1 year ago

thanks for putting this up here, i'll see what i can do on the tumblr side to bring back the is_blocks_post_format ASAP 😄

cyle commented 1 year ago

added back! docs updated here: https://github.com/tumblr/docs/commit/e2ff89fd72171388be0635a0390609c498606c79#diff-923f2a1adb38cee10b42249687afeb99381f08c15498248b26a61bf94ee23bf6R1168

marcustyphoon commented 1 year ago

I'm seeing isBlocksPostFormat: true on an NPF reblog of a legacy root post, which is a type of post that I have observed being unsafe to edit with excludeTrailItems. Is this intentional?

cyle commented 1 year ago

no, that doesn't sound intentional, but as with most things at tumblr's scale, "it depends"... can you provide an example post here, or in a Support request if you're not comfortable providing examples here?

marcustyphoon commented 1 year ago

Sure! I'll make one.

https://www.tumblr.com/transienttest/703646659537698816/this-root-post-is-created-in-the-legacy-editor

await tumblr.apiFetch('/v2/blog/transienttest/posts?id=703646659537698816')

{
  "meta": {
    "status": 200,
    "msg": "OK"
  },
  "response": {
    "blog": {
      // etc
    },
    "posts": [
      {
        "objectType": "post",
        "type": "blocks",
        "originalType": "regular",
        "isBlocksPostFormat": true,
        "blogName": "transienttest",
        // etc
      }
    ],
    "totalPosts": 1
  }
}

(await tumblr.apiFetch('/v2/blog/transienttest/posts/703646659537698816') is functionally identical.)

cyle commented 1 year ago

ah, yes. this is where things get a little confusing, but actually i think the field is working as intended, i just need to explain what's going on.

post content, as actually stored in the database, can be in three possible states right now:

  1. fully legacy post content -- it's complicated, but in general this means HTML that may or may not be NPF-compatible, so we treat it like it's not. this is always the case when is_blocks_post_format: false; the legacy editor is the only "safe" way to edit these posts, and actually HTML mode in the legacy editor is the only truly safe way.
  2. fully Neue Post Format content -- literally stored as NPF JSON as per the spec (roughly, there are some optimizations invisible to you), no HTML in sight. the editors in the mobile apps and the new editor on web are the only way to edit these posts. we do not support going back to an HTML representation and allowing them to be persisted that way. in fact, the new HTML mode in the new editor on web is mostly for show in that regard -- we're not actually storing the HTML you provide there, we're converting it "best effort" to Neue Post Format.
  3. legacy post content that we know is forwards-compatible with the Neue Post Format -- this is what you're seeing here, and currently should only happen with reblogs. we can't vouch for the original content, but we know the reblog and the original content we copied is valid to be converted to a Neue Post Format representation, even though we're technically still storing it in the legacy post content format.

so... in that third case, where is_blocks_post_format: true for a reblog of a is_blocks_post_format: false original post, the entire reblog's post content should be safely fully editable in our new editors (but not the legacy editor). the important thing to remember here is that reblogs contain a copy of the original post, not a reference. if a user went through our new editors and reblogged a post, we're basically aware that the reblogger was OK with whatever they saw, hence us saying it's forwards-compatible. so the copy in the reblog trail of the new post is "NPF safe".

going back to your original purpose of using exclude_trail_items, i'm 99.99% confident in saying that you should be fine using that option for any post where is_blocks_post_format: true, even when the legacy post is is_blocks_post_format: false. if you can find cases where this is breaking something, please let me know.

does that make sense? 😅 it's a lot, i'm sorry.

marcustyphoon commented 1 year ago

That makes perfect sense to me! However, I do indeed observe the exclusion of trail items breaking (see the most recent post on that blog).

Out now but I'll investigate if it's an issue on our side when I get the chance.

cyle commented 1 year ago

ah, okay. yeah, i see what's going on there. in that case, the quickest fix on my side would be to return is_blocks_post_format: true only for posts that are fully stored in Neue Post Format, so excluding the third case above. would that suffice?

marcustyphoon commented 1 year ago

Yeah, that would be great!

cyle commented 1 year ago

cool. that's now the case!

marcustyphoon commented 1 year ago

Awesome; I'm observing that! This works great for my use case. Closing!


As an aside, for completeness' sake:

Doing a quick investigation of the difference between the first and third cases (for potential edits that don't make use of exclude_trail_items): I'm observing when comparing a beta reblog and a legacy reblog of the same legacy chat post that

a) 👍 : my NPF edit works on one and simply fails (http 400) on the other; if this holds, great, one can just attempt the edit and catch errors rather then preflighting their NPF edits with a check or whatever;

b) 👍 await tumblr.apiFetch('/v2/blog/transienttest/posts/703660093322985472?post_format=legacy') 400s on one and gives a legacy response on the other, if one wants to check which type a post is one-at-a-time;

c) 👎 I get an NPF response on await tumblr.apiFetch('/v2/blog/transienttest/posts?id=703660093322985472&npf=false') on both; this seems to contradict the documentation for that endpoint unless Tumblr's internal API helper works differently? If this endpoint is now NPF only I'm totally fine with that but the docs should be updated.

https://github.com/tumblr/docs/blob/e2ff89fd72171388be0635a0390609c498606c79/api.md?plain=1#L1114-L1128

https://github.com/tumblr/docs/blob/e2ff89fd72171388be0635a0390609c498606c79/api.md?plain=1#L1145

(not sure if the undocumented original_type field differing serves to provide a way to bulk-identify "type 3" posts, but of course not relying on undocumented fields that could vanish is the point!)

cyle commented 1 year ago

this seems to contradict the documentation for that endpoint unless Tumblr's internal API helper works differently? If this endpoint is now NPF only I'm totally fine with that but the docs should be updated.

yes, the internal API helper that Redpop uses works differently. it's NPF-only and ignores that npf query string parameter, which we put in place pretty much only for third-party API developers like you, but not those who are leveraging Redpop's API handler. little bit of undocumented nuance there. so the public API docs aren't wrong, because you're technically not using the public API. you're using something that's a little in-between. 😉

marcustyphoon commented 1 year ago

Indeed we aren't! Sounds good 👍 I don't think XKit Rewritten has a use case for "trying to edit NPF reblogs of legacy posts" that isn't covered by the mass post editor APIs, at the moment, so no need to worry about that side of things unless those endpoints vanish or something else comes up that we could do that I haven't thought of.

Edit: I wasn't totally right about this; I hadn't previously realized these hybrid posts can have interactability, tipping, and community labels toggled (it's just trail item exclusion that breaks. Also, they don't have to be reblogs; editing a legacy text post on mobile or via an NPF endpoint puts it in this state). XKit Rewritten doesn't have these functions now, but it could.