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

If autolinking would make posts too long, don't #154

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

elyscape
Copy link

Summary

Substitutions that increase message length have the potential to grow a post past the ability of the database to store it. This manifests to users as their posts inexplicably failing. To avoid this fail state, we check to see if the post-substitution message is too long and, if so, don't apply the changes.

Unfortunately, the actual maximum post size is not currently accessible to plugins. In the meantime, we use the smallest value for which the server guarantees support, which is exposed through model.POST_MESSAGE_MAX_RUNES_V1 and is in practice 4000 runes.

Ticket Link

None.

@elyscape elyscape requested a review from levb as a code owner March 30, 2021 23:37
@mattermod
Copy link
Contributor

Hello @elyscape,

Thanks for your pull request! A Core Committer will review your pull request soon. For code contributions, you can learn more about the review process here.

@hanzei hanzei added the 2: Dev Review Requires review by a core committer label Mar 31, 2021
Copy link
Contributor

@hanzei hanzei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on this issue.:+1:

@@ -208,6 +209,11 @@ func (p *Plugin) ProcessPost(c *plugin.Context, post *model.Post) (*model.Post,
return nil, ""
}

if runes := utf8.RuneCountInString(postText); runes > model.POST_MESSAGE_MAX_RUNES_V1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For newer Mattermost instances or the one that updates, this will be longer:

This release includes support for post messages longer than the default of 4000 characters, but may require a manual database migration. This migration is entirely optional, and need only be done if you want to enable post messages up to 16383 characters. For many installations, no migration will be required, or the old limit remains sufficient.

https://docs.mattermost.com/administration/important-upgrade-notes.html

I'm hesitant to cap at the old limit as that might render the plugin useless for some instances.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fair, but we currently have no way to find the actual limit from a plug-in, and that excerpt notes that upgrading from 4000 characters may require a manual migration, which we can't expect every deployment to have performed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From looking at the server code base the UpdatePost RPC method does return an model.post.is_valid.msg.app_error error. (https://github.com/mattermost/mattermost-server/blob/ee3f986da0d9fbc69769dcec0c66d8493589757a/model/post.go#L299) Can we check for the specific error and return the original message in that case?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that this function is effectively the handler for the MessageWillBePosted() and MessageWillBeUpdated() hooks, meaning we can alter the Post object but need to allow persistence to be handled further down the chain.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ideal solution is to first provide a way for plugins to get the result of GetMaxPostSize(), and then have the plugin:

  1. Retrieve that value
  2. Update the Post object
  3. Call Post.IsValid() with its value
  4. Revert the object if the result is false

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a concern to me as it unexpectedly renders the plugin useless in some cases that previously worked. I'm 2/5 that it's not worth breaking that cases for the bug fix that the PR includes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanzei What would you suggest as a path forward?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From looking at the server code base the UpdatePost RPC method does return an model.post.is_valid.msg.app_error error. (mattermost/mattermost-server@ee3f986/model/post.go#L299) Can we check for the specific error and return the original message in that case?

@elyscape Do you think it's feasible to refactor the code and use that approach?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not. This function is effectively the handler for the MessageWillBePosted and MessageWillBeUpdated hooks, meaning we can alter the Post object but need to allow persistence to be handled further down the chain. From the documentation:

To reject a post, return an non-empty string describing why the post was rejected. To modify the post, return the replacement, non-nil *model.Post and an empty string. To allow the post without modification, return a nil *model.Post and an empty string. To dismiss the post, return a nil *model.Post and the const DismissPostError string.

We could call UpdatePost from MessageHasBeenPosted or MessageHasBeenUpdated, because those are events that fire after a message occurs, but not here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanzei As a side complication, even once we get access to the result of GetMaxPostSize(), we'll need a fallback path for versions of Mattermost that don't support it.

@@ -208,6 +209,11 @@ func (p *Plugin) ProcessPost(c *plugin.Context, post *model.Post) (*model.Post,
return nil, ""
}

if runes := utf8.RuneCountInString(postText); runes > model.POST_MESSAGE_MAX_RUNES_V1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simplicity of the current implementation. 0/5 we add it in "as is". We should probably open a ticket to make the MaxPostSize available to plugins, and to update Autolink once it's done; until the, we document the limitation? cc @aaronrothschild @justinegeffen

@elyscape
Copy link
Author

elyscape commented Apr 1, 2021

I updated the PR to fix the tests and also include the post ID logged when a message would be too long.

Edit: Well, that broke the build differently because we aren't populating Post.Id in the test. I'll fix that once I figure out if that field is always expected to have a value and, if not, what I should do.

Edit 2: Turns out that the field is never expected to have a value. Oh well.

Substitutions that increase message length have the potential to grow a
post past the ability of the database to store it. This manifests to
users as their posts inexplicably failing. To avoid this fail state, we
check to see if the post-substitution message is too long and, if so,
don't apply the changes.

Unfortunately, the actual maximum post size is not currently accessible
to plugins. In the meantime, we use the smallest value for which the
server guarantees support, which is exposed through
model.POST_MESSAGE_MAX_RUNES_V1 and is in practice 4000 runes.
@elyscape
Copy link
Author

elyscape commented Apr 1, 2021

Okay, tests fixed.

@elyscape elyscape requested a review from hanzei April 1, 2021 20:55
If the first return value of MessageWillBeUpdated is nil, the update is
rejected, with the second return value providing an explanation for the
rejection. To allow the update to occur without any changes, the
function should return the value of newPost that was passed in to it.

We don't reject any post updates, but if we did then even then we would
have to provide a rejection reason. As such, we don't need to alter the
logic of ProcessPost, and can instead simply put a check on its return
values.
@hanzei hanzei removed their request for review April 13, 2021 13:28
@levb
Copy link
Contributor

levb commented Apr 13, 2021

@elyscape @hanzei @iomodo we discussed it offline, and the proposal is to implement both failure modes ("unchanged" and "error") for this feature. It can be triggered by a new Link-level flag (UseOriginalOnError: true? ). The current master behavior should be the default.

@levb levb self-requested a review April 13, 2021 14:04
@elyscape
Copy link
Author

@levb Sounds good. I'll figure out how to implement that in the next week.

@elyscape
Copy link
Author

@levb @hanzei @iomodo I started looking into this and realized that I don't think there's actually a good way to implement it. As I understand it, the proposal is to implement this setting on a per-Autolink basis. The problem is that multiple Autolinks can apply to a single message, and processing applies all of them at once. If multiple Autolinks apply to a message but have differing values for UseOriginalOnError, it's unclear what should happen. I could have it try again with only the Autolinks that have UseOriginalOnError set to false, but that seems like it would provide a bad user experience. In my opinion, this should probably be a plugin-level setting. Thoughts?

@mattermod
Copy link
Contributor

This PR has been automatically labelled "stale" because it hasn't had recent activity.
A core team member will check in on the status of the PR to help with questions.
Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

@levb
Copy link
Contributor

levb commented Mar 30, 2022

0/5 change the semantics, and just let the processing stop at the point where it'd have exceeded the max post size. I understand that it is unpredictable in the sense of what links will be processed, but IMO it can be a simple, sufficent solution?

@hanzei hanzei self-assigned this Feb 21, 2023
@hanzei
Copy link
Contributor

hanzei commented Feb 23, 2023

@elyscape Coming back to this PR: The solution in #154 (comment) seems good to me. Do you still want to work on it?

@hanzei hanzei added Work In Progress Not yet ready for review and removed 2: Dev Review Requires review by a core committer labels Feb 23, 2023
@hanzei hanzei removed the request for review from levb February 23, 2023 20:40
@hanzei hanzei removed their assignment Feb 23, 2023
ayusht2810 pushed a commit to Brightscout/mattermost-plugin-autolink that referenced this pull request Feb 15, 2024
mattermost-community#192)

* Revert "Update main.go (mattermost-community#154)"

This reverts commit be4a281d0cc791d10e6e5ae917b325b2f054e475.

* Revert "[MM-33506] Use embed package to include plugin manifest (mattermost-community#145)"

This reverts commit ca9ee3c17c6920a636a33f378e17395afd6f329f.

* Revert "Don't generate manifest.ts (mattermost-community#127)"

This reverts commit 18d30b50bc7ba800c9f05bfd82970781db0aea3e.

* install-go-tools target, adopt gotestsum

* bring back make apply + automatic versioning

* Update build/manifest/main.go

Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>

* suppress git describe error when no tags match

* make version/release notes opt-in

* fix whitespace in Makefile

* document version management options

---------

Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>
mickmister added a commit that referenced this pull request Mar 14, 2024
* [MM-98] Update plugin with respect to phase 1 upgrades

* [MM-98] Fix lint

* [MM-98] Update Readme

* [MM-98] Review fixes regarding constant for golangci-lint

* Sync with playbooks: install-go-tools, gotestsum, and dynamic versions (#192)

* Revert "Update main.go (#154)"

This reverts commit be4a281d0cc791d10e6e5ae917b325b2f054e475.

* Revert "[MM-33506] Use embed package to include plugin manifest (#145)"

This reverts commit ca9ee3c17c6920a636a33f378e17395afd6f329f.

* Revert "Don't generate manifest.ts (#127)"

This reverts commit 18d30b50bc7ba800c9f05bfd82970781db0aea3e.

* install-go-tools target, adopt gotestsum

* bring back make apply + automatic versioning

* Update build/manifest/main.go

Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>

* suppress git describe error when no tags match

* make version/release notes opt-in

* fix whitespace in Makefile

* document version management options

---------

Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>

* Fetch plugin logs from server (#193)

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>

---------

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants