Cannot access .get(0,len), .listProperties(), .getAll() method on objects retrieved from a nested structure

Dear All

I am currently building a ZIP archive creator that should work with Folder & File structure that the user of the plugin defines within his Bubble app and then enters into the plugin action interface as ‘App Type’ and Field of ‘App Type’ for the nested data that needs to be accessed. If I get the program to work, it will be available under MIT license for you guys.

I’m currently running into trouble accessing objects that are nested within another object.

For the life of me the .get(fieldName) on BubbleThings, and the .get(start, length) are not returning what they should.

I want to do the following (simplified for privacy_folder_1 which I’m currently struggling with – Error handling & edge case handling ommited as it is not relevant to the question):

async function(properties, contex) {
    const INIT_FOLDER = await properties.foldertoconvert; 
    const currentUserID = await context.currentUser.get(ID);

    // this should return the list of users in the "privacy_folder_1" field of the INIT_Folder
    const privacyRecords = await INIT_FOLDER.get(await properties.privacy_folder_1); 

    console.log(`privacyRecords  name:` + privacyRecords + ` this is of type : ${typeof privacyRecords}`);
    // -> privacyRecords name: [object Object] this is of type: object

    console.log('privacyRecords are a Bubble List: ' + await context.isBubbleList(privacyRecords));
    // -> privacyRecords are a BubbleList: true

    const userIdList = await privacyRecords.get(0, 1);

    console.log(JSON.stringify(privacyRecords);

Then this get’s logged (or in my real code I return it under a variable which I store in my App’s DB):

 [{"_pointer":["1111111x111111xi.E.returnsCorrectUserID"],"_call_metadata":  {"server_base_url":"https://some-appdomain.com/","access_token":"sometoken","appname":"some-appname","app_version":"test","plugin_api_version":"4","app_properties":["custom.fields":["name_text","type_text","Created By","Slug","Created Date","Modified Date","_id"],"custom.type": [...ETC]

And when I

console.log(userIdList);

I get

[{}] 

I also tried .get(‘_id’) which doesn’t work as the method is not available (if I remember correctly or returns a null) and I tried to JSON.parse(JSON.stringify(privacyRecords))[0]._pointer to recreate a JS object from the bubble _pointer response but this didn’t work as well (as I think I ran into the hard limits as the _pointer lists everything.

Now what I don’t understand is why the .get(0, 1) doesn’t retrieve the string that is clearly there in the privacyRecords object, considering the privacyRecords is a BubbleList which has the method .get(0, length).

I would greatly appreciate help from someone who understands the methods and their perks a little better than I do.

Feel free to ask any questions
.
.
.
.
.
.
Here some more context:

The folderstructure that I have in my folder structure is as follows (and is infinitely nested):

{
  "folder": {
    "name": "exampleName",
    "privacy_folder_1": [
      "In here is a list of User Objects that have access to the folder"
    ],
    "privacy_folder_2": [
      "In here is another list of User Objects that have access to the folder"
    ],
    "files": [
      {
        "name": "this is a file nested in a folder",
        "privacy_file_1": [
          "In here is a list of users with access to this file"
        ],
        "privacy_file_2": [
          "In here is a another list of users with access to this file"
        ],
        "url": "https://S3link.com"
      }
    ],
    "subfolders": [
      {
        "name": "subFolderOf_exampleName",
        "privacy_folder_1": [],
        "privacy_folder_2": [],
        "files": [],
        "subfolders": []
      }
    ]
  }
}

For reference, this is my current ‘properties’ object:

properties = {
     file: String (type ID)
     filedata: String
     filelist: List of Objects
     folder: String (type ID)
     foldertoconvert: Object
     create_from: String (one of the options)
     subfolders: String - doing obj.get(value) will return a Not set
     filesinfolder: String - doing obj.get(value) will return a Not set
     privacy_folder_1: String - doing obj.get(value) will return a Not set
     privacy_folder_2: String - doing obj.get(value) will return a Not set
     privacy_file_1: String - doing obj.get(value) will return a Not set
     privacy_file_2: String - doing obj.get(value) will return a Not set
     filename: String - doing obj.get(value) will return a Not set
     foldername: String - doing obj.get(value) will return a Not set
     dummyuser: Bubble User Object
}

In which code block are you trying to run this code? initialize or update?
Maybe share your code?

It’s for a server-side action with the async function(properties, context)

Here’s the full code salad I currently have, thought it’s easier to simplify as above but feel free to take a closer look in that one:

async function(properties, context) {
    const MAX_DOWNLOADSIZE = 1024 * 1024 * 1024; // One GB in bytes
    const axios = require('axios');
    const {Blob} = require('buffer');


    // standard Bubble fields
    const ID = '_id';
    const TYPE = '_type';
    const CREATEDBY = 'Created By'; // user _id
    const SLUG = 'Slug';
    const DATECREATED = 'Created Date'; // notation: 2024-11-05T07:05:42.312Z
    const DATEMODIFIED = 'Modified Date'; // notation: 2024-11-05T07:05:42.312Z

    // Application specific fields
    const PRIVACY_FOLDER_1 = await properties.privacy_folder_1;
    const PRIVACY_FOLDER_2 = await properties.privacy_folder_2;
    const PRIVACY_FILE_1 = await properties.privacy_file_1;
    const PRIVACY_FILE_2 = await properties.privacy_file_2;
    const INIT_FOLDER = await properties.foldertoconvert;

    // context Constants
    const currentUserID = await context.currentUser.get(ID);
    // console.log('user ID current user: ' + currentUserID); // this logs the correct user ID
    const apiKey = await context.keys.api_key; // API Key for authenticated requests

    // // probably not needed
    // let findPointer = async (obj, getVal) => {
    //     if (Array.isArray(obj)) {
    //         return findPointer(obj[0], getVal);
    //     } else if (obj && typeof obj == "object" && obj._pointer) {
    //         return await obj._pointer[getVal];
    //     } else {
    //         return null;
    //     }
    // }

    // should in the end only return true or false -> currently returns other values as well for debugging
    let getAccessCheck = async (_instantiatedObject, _privacyFields) => {
        if (_privacyFields.length === 0) {
            return true;
        } else {
            let privacyObjArray = [];
            let userIDs = [];
            let fieldIndex = 1;
            for (let Field of _privacyFields) {
                let privacyRecords = await _instantiatedObject.get(Field);
                console.log(`privacy records of Field ${fieldIndex} :` + privacyRecords + ` this is of type : ${typeof privacyRecords}`); // when a Field doesn't contain anything, this returns an Object with null
                fieldIndex++;
                let singleAPI;
                let listLength;

                // check if privacyRecords are a list or single Object for logic of further processing
                if (privacyRecords) {
                    singleAPI = await _instantiatedObject.single_api;
                    console.log('single API from await: ' + singleAPI);
                } else {
                    singleAPI = false;
                }

                // get the propper Objects of privacyRecords into the privacyObjArray for further processing
                if (privacyRecords && !singleAPI) {
                    console.log('created Objects because privacy Records contained & singleAPI was false');
                    listLength = await privacyRecords.length();
                    for (let Object in await privacyRecords.get(0, listLength)){
                        privacyObjArray.push(Object);
                        let userId;
                        if (Array.isArray(Object) && Object[0] && Object[0]._pointer) {
                            userId = Object[0]._pointer[ID];
                        } else if (Object._pointer) {
                            userId = Object._pointer[ID];
                        } else {
                            userId = await Object.get(ID);
                        }
                        userIDs.push(userId);
                    }
                } else if (privacyRecords && singleAPI) {
                    console.log('created Objects because privacy Records contained & singleAPI was true');
                    console.log('privacyRecords are a Bubble Thing: ' + await context.isBubbleThing(privacyRecords));
                    console.log('privacyRecords are a Bubble List: ' + await context.isBubbleList(privacyRecords));
                    privacyObjArray.push(privacyRecords);
                    let userId = privacyRecords.get(0, 1);
                    userIDs.push(userId);
                }
            }
            // check if User is allowed to access the Object
            if (userIDs.length == 0 || userIDs.includes(currentUserID)) {
                return {
                    accessible: true,
                    statusCode: 200,
                    listFiles: JSON.stringify(privacyObjArray),
                    folderStructure: JSON.stringify(userIDs),
                }
            } else {
                return {
                    accessible: false,
                    statusCode: 400,
                    listFiles: JSON.stringify(privacyObjArray),
                    folderStructure: JSON.stringify(userIDs),
                }
            }
        }
        }

    let accessCheck = await getAccessCheck(INIT_FOLDER, [PRIVACY_FOLDER_1, PRIVACY_FOLDER_2]);

    console.log('user has access: ' + accessCheck.accessible);
    
    return {
        success: accessCheck.accessible,
        statusCode: accessCheck.statusCode,
        listFiles: accessCheck.listFiles,
        folderStructure: accessCheck.folderStructure,
        fileListSelected: '',
    };
}

Hi Rico
I think I solved it. As most times in programming, it was my mistake, and not ‘the system’

I will clarify later what I did and how I was able to solve it.

Best
Florian

1 Like