Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent SlackObjectFormationError raising when empty text is provided. #1267

Open
1 task done
kezabelle opened this issue Sep 20, 2022 · 5 comments
Open
1 task done
Assignees
Labels
enhancement M-T: A feature request for new functionality
Milestone

Comments

@kezabelle
Copy link

Given the following:

>>> SectionBlock(text="").to_dict()
SlackObjectFormationError: text or fields attribute must be specified

This is all correct enough and good enough. What's happening behind the scenes appears to be that TextObject.parse is used and does a truthy test on the incoming text, and returns None as opposed to an empty string.

Where that begins to go wrong, is here:

>>> SectionBlock(text=MarkdownTextObject(text="")).to_dict()
{'text': {'type': 'mrkdwn'}, 'type': 'section'}

Slack will reject that as erroneous. If it's sent back as part of the acknowledgement response as a response_action we won't necessarily see (receive) an error related to failing to parse the JSON schema. Slack's block kit builder says of it: Missing required field: text

Category

  • slack_sdk.models (UI component builders)

Desired outcome

Ideally, I'd like to be able to trust (more) that the validation present in slack_sdk is going to raise on invalid configurations early, and consistently -- that is, where possible I'd like to assume that putting together something "empty" even if I've opted to use the classes instead of an equivalent base type (str, dict) should raise.

@hello-ashleyintech hello-ashleyintech self-assigned this Sep 20, 2022
@hello-ashleyintech hello-ashleyintech added enhancement M-T: A feature request for new functionality and removed untriaged labels Sep 20, 2022
@hello-ashleyintech
Copy link
Contributor

hello-ashleyintech commented Sep 20, 2022

Hi, @kezabelle! Thanks so much for writing in!

I took a look at the issue you described and agree that the difference in behavior can feel confusing. I'll be reaching out to a member of my team to get additional context on whether this behavior is expected or not and to look into what can be done here. I'll add any updates or additional findings here within the next day or so!

@eddyg
Copy link
Contributor

eddyg commented Sep 20, 2022

For reference, when I updated the text presence check here, it was implemented like this:

 if text and len(text.strip()) > 0:

It may be appropriate to do something like that here as well?

@hello-ashleyintech
Copy link
Contributor

hello-ashleyintech commented Sep 21, 2022

Hi, @kezabelle!

I chatted with a member of my team about this and we identified that adding in error handling for the described scenario could potentially cause breaking changes. Because of this, and also since there's still some SDK side validation for the scenario, we are hesitant to add in additional error handling here and alter the behavior of SectionBlock for these use cases. However, if the error message is confusing, we can definitely look into updating it!

@seratch seratch modified the milestones: 1.x, 3.x Sep 29, 2022
@kezabelle
Copy link
Author

kezabelle commented Sep 29, 2022

Hey, @hello-ashleyintech, thanks for taking the time to look into it. I appreciate it. Apologies for the delay in responding, I've been away.

I'll preface everything hereafter with a degree of uncertainty, as I've primarily been branching out to using slack_sdk only for the type classes in models (ignoring all the other submodules) to replace fragile/ad-hoc dictionary construction for blocks. So please, take the following with a grain of salt and ignorance rather than being in any way combative.

[...] we identified that adding in error handling for the described scenario could potentially cause breaking changes.

Would you be able to describe in what scenarios it could produce valid output which Slack would actually consume?
From my (comparatively limited) experience, failure to encode and raise an exception for the invalid schema only pushes the problem to the communication point with Slack's API, and whether you can even surface that error depends on whether that block is being pushed via an API call to views.update (et al) or as a response_action in the acknowledgement HTTP body.
This hit me in production in both scenarios due to what looked like an innocuous change in text output, and it's only because submitting data to the API returned a JSON schema error caused an error, that I noticed it.

On that set of assumptions (which may be incorrect!), what breaking changes would this cause that aren't already broken? Any tightening of validation is always a backwards incompatible, breaking change in potentia, but this would seem to me (naively) to simply be bringing the API in-line with the schema.

[...] also since there's still some SDK side validation for the scenario [...]

Again, unfamiliar with the rest of the modules, but where might I expect to find this? I'm presuming SDK here means somewhere within slack_sdk rather than at the API boundary for communicating with Slack via HTTP? In broad strokes, in an ideal world and in isolation, if that validation exists elsewhere in the project, I would argue that it's in the wrong place given there is some expectation of validation on the classes themselves -- but, perfect is the enemy of good, and I fully understand that real world behaviours may have called for it to be elsewhere.

However, if the error message is confusing, we can definitely look into updating it!

There error message itself is fine, honestly. It's the complete absence of an error in another scenario which amounts to the same error, that I'm finding fault with. Were there no validation, I'd just chalk it up to something to deal with myself, but as there is some I now need to mentally keep track of which validations the slack_sdk.models package "bothers" with and which I need to manually check forever.


In case it's easier to read the (pseudo-)code I'd roughly be (naively) proposing, off the to of my head the validation would be something like the below, give or take personal proclivities for type-checking or shape-checking and stylistic choices for brevity etc:

if self.text is not None:
    # handle TextObjects (or anything with `.text` attributes)
    if hasattr(self.text, "text"):
        if not self.text.text.strip():
             raise ...
    # handle dictionaries (or anything with __getitem__(text))
    if isinstance(self.text, Mapping):
          if 'text' not in self.text:
             raise ...
          if not self.text['text'].strip():
             raise ...
    # handle strings (or really anything with strip as a method which returns truthy/falsy)
    if not self.text.strip():
             raise ...

@hello-ashleyintech
Copy link
Contributor

@kezabelle Thanks for the reply! 🙌

Would you be able to describe in what scenarios it could produce valid output which Slack would actually consume?

If any valid string that is not empty is passed in to the MarkdownTextObject or PlainTextObject for the text property, this would be a valid output.

On that set of assumptions (which may be incorrect!), what breaking changes would this cause that aren't already broken?

This is a great question! On receiving this issue, I had actually tried to implement error handling for this locally in a similar place where the text or fields attribute must be specified was implemented. Sadly, the returning of an error for empty strings being passed into MarkdownTextObject or PlainTextObjects ended up breaking quite a few tests. This is what prompted me to consult the team internally, to see if all the errors being thrown from the tests were a valid tradeoff to add additional validation.

Basically, with my tested implementation (similar to what @/eddyg had suggested), it seemed like throwing an error in that particular location would end up altering the behavior of SectionBlock, which could cause a breaking change, which is why the unit tests were returning errors. Because of this cascading effect and potentially wider impact, we are hesitant to add additional error handling at this time.

Again, unfamiliar with the rest of the modules, but where might I expect to find this?

Within the Block Kit, there is some validation. Here is an example of a PlainTextObject being passed into a Block Kit - as you can see, it throws an error, must be more than 0 characters. The same would get thrown if a MarkdownTextObject was passed in (this can be emulated by changing the type to mrkdwn).

Let me know if you have any additional questions here! This is helpful feedback and definitely something we can consider a better pathway for in the future, especially if more folks run into similar issues with this. 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement M-T: A feature request for new functionality
Projects
None yet
Development

No branches or pull requests

4 participants