Hey @bubble - you broke the data model for Lists in SSAs! Please to fix!

Dear Bubble,

Whatever you’re doing on the backend, you broke the object model for Lists (and god knows what else) in SSAs.

According to the docs (and experience), a List object has the properties:

length and get

Some of us plugin devs (probably just ME, but does anybody else matter?) rely on these properties to understand if an object is a List.

However, we find suddenly that a List has the following properties instead:

_pointer and _call_metadata

If we do some_List.hasOwnProperty('get') we now get false.

And if we do Object.getOwnPropertyNames(some_List) we get:

_pointer,_call_metadata

This surely breaks not just my SSA plugins (like List Popper & Friends, used by about 1,300 apps), but probably your own as well. Please fix.

Example project here:

Runtime: https://list-shifter-dev-test.bubbleapps.io/version-test/potest?debug_mode=true

Editor: List-shifter-dev-test | Bubble Editor

It would be extremely shitty to change things in this way without giving anyone a heads-up, especially when the plugin API documentation is so incomplete.

I’m assuming this is a bug on your end for a bit. If it’s not, I’m not going to fix my free plugins to accommodate this random change. I’ll just charge users for the fixed version.

11 Likes

Hi @keith

Assuming you mean Server Side Actions, could it be related to this? Updating plugin editor to new Node version

I have a developer working on a plugin with a lot of server side actions hence I’ve been keeping an eye on the post I linked above.

So what I’m hearing @keith is that my karma is still net positive then.

I’ve been harping about this issue for the last 36 hours but I’m still surprised there hasn’t been more uproar

1 Like

I have no idea what this even means, but am adding my support.

If @keith says it is broke, then it is broke.

21 Likes

@bubble

Start talking to your community prior to you messing with things. It’s really not fair to break paying apps and without any remorse or apologies.

Your community is the only thing propping you up. If you fail us, we fail you.

3 Likes

I share that sentiment :sweat_smile:

5 Likes

Hey everyone!

The good news is that the data model for lists in SSA is not broken. What happened is simply a quirk of how .hasOwnProperty and Object.hasOwnPropertyNames interact with classes. During a recent refactor, the functions .get and .list were moved to truly be member methods on the List instance rather than being instantiated via this.get = function () {}. This means that those methods no longer show up as part of the “own properties” of the class instance, but it does not mean that those functions do not exist.

By the way, you can always check whether or not an object is a list by looking at the .list_api field on it, which will be true for lists and (currently) undefined for objects.
As an example of what I mean by differentiating member methods and things instantiated in the constructor:

class Example {
   constructor() {
      this.ownProp = function () {
           return 5
      }
    }
   
    notOwnProp() {
       return 6
    }
}

const inst = new Example()
console.log(inst.hasOwnProperty('ownProp')) // true
console.log(inst.hasOwnProperty('notOwnProp')) // false
console.log(Object.getOwnPrototypeNames(inst)) // ['ownProp']

console.log(inst.ownProp()) // 5
console.log(inst.notOwnProp()) // 6

Hope that helps!

7 Likes

As an additional note, in this case I am currently working on a quick fix to ensure that those two methods show up in the property list to avoid breaking plugins that may rely on this behavior. However, those methods being part of the property list is not part of our official API and things that are not part of our official API may change in the future.

EDIT: This fix is live, you should now be able to see get and length as part of the property list!

8 Likes

Thanks for the explainer there. Theres not a lot of “official” guidance on building shit in the first place when it comes to plugins. Maybe better documentation there could be a wise investment for you guys.

8 Likes

I completely understand the frustration. We have plans to make significant investments in the plugin building experience in the near future, so stay tuned for announcements and new features!

14 Likes

@alex.stanescu It would be quite awesome if you could post a list of these “investments/new features” so that the highly opinionated plugin developers here could have some input as to what we would consider valuable such that you could priortize your time and not work on features or functionality that might fall flat.

As an example, it would be highly useful to me if plugins could define and return their own custom types. This doesn’t seem like a far stretch since the API and Database plugins do this already. However, this might be the last item on your feature list. It would be nice to allow us (plugin developers) to give some input or feedback on what would be useful or maybe not so useful to us.

3 Likes

@bryan_333 indeed you now actually have net positive karma for so clearly demonstrating how things were failing for you and I’m glad I took a look at it. Based on other messages from Alex here I assume it should be working for you again. Please check in your app and confirm that with me when you’ve got a moment?

@alex.stanescu thank you very much for your attention to this issue. As @jared.gibb also notes, there is scant guidance for plugin developers. And, when we need to solve technical challenges with creating Bubble plugins, we often have to resort to a process of inspection and deduction to solve those.

What happened here is a really good example. The “official API” (the skimpy and not 100% correct “documentation” (which is really just help text) that appears in the plugin editor), tells us (of List objects) that “you can do .length() on it and you can do .get() on it”.

Similarly, it basically tells us of Things that “you can do .listProperties() on it and you can do .get() on it”.

The docs tell us nothing, BTW, about API response objects and anything we know about those we know via inspection and deduction.

So, since I have plugins that deal with Lists and are designed to work with any type of data we might receive from Bubble (and, similarly, publish such data back to Bubble from the plugin). I have had to work out entirely for myself ways of handling all that.

And one thing that has been true for years now is that the various types of Bubble objects that we must handle is that if we Object.getOwnPropertyNames() on them, we see those methods as properties and now we tell them apart.

So (for example), we have a need to know in some function if some object my_list (something we expect to be array-like) is a JavaScript array or if it’s a Bubble List object (which we can turn into an array). How can we tell them apart in a generic programmatic way? Well, they are both of type object. Now, we can identify my_list as an array if Array.isArray(my_list).

But now what if my_list fails that test? What the heck is it? We need to ensure it is what we expect it to be (a Bubble List object) before we attempt to .get() on it. How the hell else would one do this except something like:

my_list.hasOwnPropertyName('get') && my_list.hasOwnPropertyName('length') && my_list.get(0, my_list.length())

We MUST have a way to do this if we expect to write solid code, right, @alex.stanescu?

If get and length are NOT exposed as properties, how on earth are we to do this?

Further, I am still at a loss to understand how Bubble engineers would not understand this need and that removing those things as properties is a good idea.

The above is a serious question, by the way. If in the new world where a List exposes _pointer,_call_metadata what am I supposed to do to check for the existence of the get and length before trying .get??? I know of only way in JavaScript… .hasOwnProperty() but maybe I’m a dumb-dumb.

I was going to go on and explain how we differentiate between Things and API response objects and other generic objects, but you know where I’m going with that and I seriously need a response to the question above.

3 Likes

Further question @alex.stanescu: Are you folks contemplating a similar change on the client side? Because if you do that, that will break all of my plugins, all over the place, the “free” ones and the commercial ones.

Following this up.
As one might have noticed, I contribute a tiny bit, plugins wise, on Bubble…

2 Likes

It would be quite awesome if you could post a list of these “investments/new features” so that the highly opinionated plugin developers here could have some input as to what we would consider valuable such that you could priortize your time and not work on features or functionality that might fall flat.

Our product team’s process usually involves reaching out to users to get their opinions before making any decisions, so I’m sure they will reach out. In the meantime, I’ve passed along the idea of a “plugin ideaboard” to our product team for their consideration

3 Likes

And, @bryan_333, I’m still so cranky about all of this that I am having a senior moment and confused your messages with a private message from @thedew who sent me a really great loom showing how his setup for List Popper was completely correct, but that List Popper was sending back empty (for both popped item and for the remaining list).

Which, given we now know, OF COURSE this happened. Since my code says, “hey, that ain’t no List, and it sure as hell isn’t an array”, we have nothing to get and so it just returns nothing and does not attempt .get() on the List because, well, it does not pass the test of what a List is.

(@alex.stanescu, see?)

Anyway, @thedew, please check your great-looking app because you should see that List Popper is behaving normally now. (fingers crossed emoji)

Edit: I should have also said, @bryan_333, that while I jokingly chastised you about maligning List Popper, it was that mention that made me take a serious look when @thedew PM’ed me. I was like, “Huh, even though there is no possible way for List Popper to “break” since I’d not touched it lately, something could be broken in Bubble.”

If I’d have only seen 1 such message in isolation, I’d just be like, “Dude, you crazy.”

3 Likes

What the heck is it? We need to ensure it is what we expect it to be (a Bubble List object) before we attempt to .get() on it. How the hell else would one do this

The bubble List object exposes a property called list_api (and its converse exposes the property single_api). These are meant as the canonical way to check whether a class instance represents a list or a single object. Edit: this should have been in the documentation, but it isn’t. I’ll try to get that added so it’s more obvious

List exposes _pointer,_call_metadata

For what its worth, the prefix of _ is meant to indicate to you and everyone else that these are internal objects used for record keeping, and I’d strongly discourage using their contents in any plugin.

what am I supposed to do to check for the existence of the get and length before trying .get??? I know of only way in JavaScript…

Comparison to null (and/or checking if typeof foo.get === 'function') are the other two ways in general JS, and are more consistent to what you are actually asking, especially whenever class inheritence comes into play. In particular though, list_api is the best property to look at.

Are you folks contemplating a similar change on the client side? Because if you do that, that will break all of my plugins, all over the place, the “free” ones and the commercial ones.

In the immediate term, not as far as I’m aware. However in the longer term, we’re looking at completely revamping both our serverside and clientside plugin APIs (both internal and external), although that would come as a new plugin API version so you wouldn’t see any breakages

4 Likes

I honestly love this thread… It’s popcorn.

Sadly, the only reason it’s so hot is because Bubble never actually posts in here, it’s like finding a unicorn beside a pot of gold.

Anyhow, it’s very clear that you should employ at least 1 dedicated documentation expert full time, and we should see the fruits of their labors. And you should also have one dedicated (and daily posting) forum admin.

5 Likes

See what I’m talking about here? I am also aware of those properties, which of course I discovered for myself but I didn’t find them of much practical use. (Consider: Even in the case of the “anything” field type, one of them is scalar and the other is list, so there’s little generic need to understand if some random thing you’re getting from Bubble is scalar or a list.)

This would have practical application if one were writing a function that’s “get anything”, which in my early going with the Bubble Plugin API, I wrote. But then it turns out that in practice there is no need for that sort of a function.

NOW, if inspecting list_api is the canonical way to tell if a Bubble object is a list, well, that’s cool. But of course none of us had any freaking way of knowing that.

OK, so I’m still having a fit of rage, but apparently I do know of other ways to keep from hitting .get() before we’ve established it exists. But at least you know what I was getting at and thanks for answering.

For those playing along at home: I would not do this as typeof foo.get === 'function' && typeof foo.length === 'function' as that is (1) just way too much to type and (2) just painful to look at.

But the suggestion:

foo.get && foo.length && foo.get(0, foo.length())

is reasonable enough. Though I will say that if you’re dealing with a lot of code, I sure would prefer .hasOwnPropertyName() because then if I’m wondering, “hmm, where am I checking for the existence of properties?” I can just ctrl-f for hasOwn and find them all. Whereas, in dot notation, well, I might be doing anything there. But you say tomahto, I say tomato.

And that’s what I’d expect. Which is why in the situation we’re discussing here in this thread I was skeptical of reports of problems with SSAs… because code doesn’t just break itself.

Once again, I want to stress that, since there’s really zero documentation for the various plugin APIs, you can say stuff like, “Well, using .hasOwnProperty() on our objects isn’t strictly a part of the plugin API and so… ya know… we’re kinda not responsible for breaking that in your code”, but I can say, “Oh, c’mon, maaan.”

6 Likes