AttributeError exception when trying to get information on draft
newPost = project.post(headline="test draft", blocks=blocks, draft=True)
print('Check out your post at {}'.format(newPost.url))
"'NoneType' object has no attribute 'url'
The draft posts but the program stops here. Should this exception be handled?
So - this is on the top of my head, without having the code directly in front of me to validate, so like... mountains of salt ahead.
Last time I looked into this issue, it seems like a limitation of Cohost(* ish, more on this later). The short answer is there is Cohost itself doesn't have an API endpoint for retrieving drafts, and drafts are rendered on the server side (from what I can tell!).
Slightly longer:
Unfortunately, this is all kind of to do with how posts are made. Our current way of making a post involves firing some data off to Cohost, uploading the attachments, and then editing the post to include attachments, and then setting that post to public. If you have no attachments, we just fire it all up, no questions asked.
The problem with this is, the response we get back does include a post ID, but, doesn't include a URL, and, doesn't match up with the data get when we ask Cohost for posts (more on this later!). As such, we're left with two, both equally kinda sucky scenarios:
- Look up the posts on the current project, and just assume the most recent post is the one we just posted
- Try and ram all the data we assume we posted to Cohost into a post object, and hope it all works
We currently take approach 1, which, works fine enough. There are scenarios this isn't true, especially if you're doing a big attachment upload, and then make a tiny post containing just text, but, for the majority of users you won't even have to conceptualise this.
The problem comes in when you make a draft post however. Cohost has (last time I checked) no API to pull in drafts. Instead, the server actually renders the drafts page in one. There might well be some API I could use, but, last time I checked I couldn't find one. As such, there's no way to actually retrieve back the post we got from Cohost, so this just returns None.
You can actually a reference to this inside project.py, specifically:
if not draft:
return self.getPosts()[0] # this will be what we just posted
return None # TODO: Get drafts working!
To fix this, we could do some hackery by trying to do approach 2 (manually construct a fake post object), however, I don't really want to do this silently. My fear is we never want to assume everything we sent on the client will be identical to what Cohost actually did, as that will be frustrating to debug.
History over, onto solutions -
This isn't to say this isn't solvable, but, moreso that as of right now this is kind of intended behaviour. However, in restrospect, I don't think a None was the right type to return here, as you might want some idea of what was actually posted. I'll change this out to instead return the raw post ID, or perhaps a Draft object, which you can then make an informed decision how to handle this in your own code.
Thanks for reporting! 🥳
Update on the Draft Problem!
I haven't got round to work on this yet, but, draft pagination in the recent Cohost update should make this somewhat easier. The issue is the endpoint still returns rendered pages, just rendered in batches. However, I think because of this, I could do some hackery by only requesting the most recent draft, to reduce load on Cohost while still retrieving the actual post delivered to the site.
As it's still rendered, I've accepted I just need to build some parser to take in a post's HTML, and generate content blocks from it. This shouldn't be too difficult, but the next couple of months for me are a little hectic, so I may not find the time to do this. But! If I find a quiet evening, I might be able to get round to this ✨