Solved

Using ElementProxy to get data fields for selected Jira card

  • 23 February 2024
  • 12 replies
  • 88 views

Badge +3

Hello Lucid Devs and community.  I'm trying to get access to the data fields attached to a LucidSpark Card that was created using your Jira data connector/integration. I spoke with you all recently and you had suggested I leverage ElementProxy, but I’m having trouble getting it to work. Would greatly appreciate any help about where I’m going wrong. If you have any example code for this scenario that would be helpful as well. 

My 'extension.ts' file (stripped down to focus on just getting data fields):

import {
EditorClient,
Menu,
MenuType,
Viewport,
CardBlockProxy,
DataProxy,
ElementProxy,
CollectionProxy,
} from "lucid-extension-sdk";

const client = new EditorClient();
const menu = new Menu(client);
const viewport = new Viewport(client);
const data = new DataProxy(client);

async function listDataFieldsForSelectedElement(elementId: string) {
const elementProxy = new ElementProxy(elementId, client);
let fieldsSet = new Set();

for (const referenceKey of elementProxy.referenceKeys.values()) {
const dataItemProxy = referenceKey.getItem();
console.log("DataItemProxy:", dataItemProxy);

if (!dataItemProxy) {
console.error("DataItemProxy not found for referenceKey:", referenceKey);
continue;
}

const collectionProxy = dataItemProxy.collection;
console.log("CollectionProxy:", collectionProxy);

// Check if the CollectionProxy object has the fields you expect
if (collectionProxy && "getFields" in collectionProxy) {
const fields = collectionProxy.getFields();
console.log("Fields in Collection:", fields);
fields.forEach((field) => fieldsSet.add(field));
} else {
console.error(
"CollectionProxy is undefined or does not have the getFields method for dataItemProxy:",
dataItemProxy
);
}
}

return Array.from(fieldsSet);
}

client.registerAction("readCardProperties", () => {
const selectedItems = viewport.getSelectedItems(true);

if (!selectedItems || selectedItems.length === 0) {
client.alert("No items are selected.");
return;
}

for (const item of selectedItems) {
if (item instanceof CardBlockProxy) {
try {
listDataFieldsForSelectedElement(item.id)
.then((refFieldsAsString) => {
const content = `Ref Fields: ${refFieldsAsString.join(", ")}`;
console.log("Content:", content);
})
.catch((error) => {
// Handle any errors that occur during the fetch
console.error("Error reading card properties:", error);
});
} catch (error) {
console.error("Error reading card properties:", error.message);
}
} else {
console.error("Selected item is not a CardBlockProxy instance.");
}
}
});

client.registerAction("itemsSelected", () => {
const items = viewport.getSelectedItems(true);
return items && items.length > 0;
});

menu.addMenuItem({
label: "Read Card Properties",
action: "readCardProperties",
menuType: MenuType.Context,
visibleAction: "itemsSelected",
});

Screen capture of browser console:

Copy of console log output when action called in case you want to see it:

EditorClient, fields: MapProxy}client: EditorClient {nextId: 0, callbacks: Map(3)}collection: CollectionProxyclient: EditorClient {nextId: 0, callbacks: Map(3)}id: "r5+QdQnqOxwpdmF4iPKubi5yo54="items: MapProxygetItem: (primaryKey) => {…}length: 1name: ""arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

    at Function.invokeGetter (<anonymous>:3:28)]caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

    at Function.invokeGetter (<anonymous>:3:28)][[FunctionLocation]]: collectionproxy.js:29[[Prototype]]: ƒ ()[[Scopes]]: Scopes[7]getKeys: () => {…}size: (...)[[Prototype]]: Objectproperties: WriteableMapProxy {getKeys: ƒ, getItem: ƒ, setter: ƒ}[[Prototype]]: PropertyStoreProxyfields: MapProxy {getKeys: ƒ, getItem: ƒ}primaryKey: "\"AGTA-846\""[[Prototype]]: Object

extension.ts:39 CollectionProxy: CollectionProxy {id: 'r5+QdQnqOxwpdmF4iPKubi5yo54=', client: EditorClient, properties: WriteableMapProxy, items: MapProxy}client: EditorClient {nextId: 0, callbacks: Map(3)}id: "r5+QdQnqOxwpdmF4iPKubi5yo54="items: MapProxy {getKeys: ƒ, getItem: ƒ}properties: WriteableMapProxy {getKeys: ƒ, getItem: ƒ, setter: ƒ}[[Prototype]]: PropertyStoreProxyconstructor: class CollectionProxygetBranchedFrom: ƒ getBranchedFrom()getFields: ƒ getFields()length: 0name: "getFields"arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

    at Function.invokeGetter (<anonymous>:3:28)]caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

    at Function.invokeGetter (<anonymous>:3:28)][[FunctionLocation]]: collectionproxy.js:86[[Prototype]]: ƒ ()[[Scopes]]: Scopes[7]getLocalChanges: ƒ getLocalChanges()getName: ƒ getName()getSchema: ƒ getSchema()getSyncCollectionId: ƒ getSyncCollectionId()patchItems: ƒ patchItems(patch)[[Prototype]]: Object

2c2fc614ad2208c1a1f0bcb45e551fca796817167ded9dc88b07efd38f95c5.js.br:9551 Action readCardProperties took 2.80ms

extension.ts:78  Error reading card properties: Error: Collection not found: r5+QdQnqOxwpdmF4iPKubi5yo54=

icon

Best answer by Connor B101 11 March 2024, 17:27

View original

Comments

Userlevel 2
Badge +3

Hey Matt!

We looked into this, and it turns out there is a bug on our end. The developer who found the issue out sick today, so I don’t have an ETA on the fix (the rest of us don’t know the details 😅).

Hopefully, I will get back to you soon once our developer is feeling better. Apologies for the delay and thanks for your partnership!

Badge +3

Thanks for following up Ian! Well I at least hope it’s not too difficult of an issue for you guys to resolve. Appreciate very much you all looking into it - please keep me posted.

Matt

Userlevel 2
Badge +3

Okay! So what we found is our code is not returning all the collections it should be. One of our developers is working on a fix. We expect to have it released middle of next week (with the caveat that this is an “expectation” and timelines may change).

Thanks for your partnership Matt!

Badge +3

That’s great news Ian - thanks for keeping me informed and for your team’s responsiveness. Looking forward to the fix!

 

Matt

Userlevel 2
Badge +3

Hey Matt! We believe we fixed the bug. Could you try it again?

Badge +3

Hi Ian - I will look closely at it today and let you know. Fingers crossed!

Badge +3

OK good news but [maybe] another problem. Or perhaps I’m doing something wrong? I now am able to retrieve the collection from a DataItemProxy, and I can pull out a list of the Field names using ‘getFields’ on the collection. However, when I try to iterate through all the fields to grab their values I’m getting an odd error saying that the “Collection not found.” Maybe this is a misleading error, and I’m not using the MapProxy calls correctly? I know the Collection is accessible.

Thanks as always for all your help! Here are the details:

 

Function ‘listDataFieldsAndValues’


I started a new module file called ‘utilityfunctions.ts’ to hold the functions outside of ‘extension.ts’ in order to keep things clean and readable. For POC purposes I created a function called ‘listDataFieldsAndValues’ that takes a client and an elementId, and hopefully will eventually return a Map with field names and value. I just want to be able to get at all the data field names and values and put them in the console at this point. I’ve added a handful of troubleshooting console entries as you can see.This function is imported and called from extension.ts based on selected items.

// This module contains a set of functions that can be used to interact with the Lucidc editor.
// Functions here are intended to be used as utility functions for the NextGen Roadmaps extension.
// Written by Matt Wyman in March 2024.

import { ElementProxy, BlockProxy, MapProxy } from "lucid-extension-sdk";

export async function listDataFieldsAndValues(client: any, elementId: string) {
const elementProxy = new ElementProxy(elementId, client);

let fieldsSet = new Set();
let keysSet = new Set();

// Map to store field values for each field
let fieldValuesMap = new Map();

for (const referenceKey of elementProxy.referenceKeys.values()) {
const dataItemProxy = referenceKey.getItem();
console.log("DataItemProxy:", dataItemProxy);
if (!dataItemProxy) {
console.error("DataItemProxy not found for referenceKey:", referenceKey);
continue;
}

const primaryKey = dataItemProxy.primaryKey;
keysSet.add(primaryKey);
console.log("PrimaryKey:", primaryKey);

const collectionProxy = dataItemProxy.collection;
console.log("CollectionProxy:", collectionProxy);

if (collectionProxy && typeof collectionProxy.getFields === "function") {
try {
const fields = collectionProxy.getFields();

console.log("Fields in Collection:", fields);

if (Array.isArray(fields)) {
for (const field of fields) {
try {
console.log(`Attempting to fetch value for field: ${field}`);
const fieldValue = dataItemProxy.fields.get(field);
if (fieldValue !== undefined) {
// Proceed with processing if the field value is not undefined
fieldsSet.add(field);
console.log(`Value for ${field}:`, fieldValue);
if (!fieldValuesMap.has(field)) {
fieldValuesMap.set(field, []);
}
fieldValuesMap.get(field).push(fieldValue);
} else {
console.error(`Field ${field} is undefined or has no value`);
}
} catch (fieldError) {
console.error(
`Error fetching value for field ${field}:`,
fieldError
);
}
}
} else {
console.error("fields is not an array", fields);
}
} catch (getFieldsError) {
console.error(
"Error fetching fields from collectionProxy",
getFieldsError
);
}
} else {
console.error(
"CollectionProxy is undefined or does not have the getFields method",
collectionProxy
);
}
}
console.log("Keys Set:", keysSet);
console.log("Fields Set:", fieldsSet);
console.log("Field Values Map:", fieldValuesMap);

return Array.from(fieldsSet);
}

export function connectBlocks(block1: BlockProxy, block2: BlockProxy) {
block1.getPage().addLine({
endpoint1: {
connection: block1,
linkX: 0.5,
linkY: 1,
},
endpoint2: {
connection: block2,
linkX: 0.5,
linkY: 0,
},
});
}

export function toggleHighlight(block: BlockProxy) {
block.properties.set("Fillcolor", "#ff0000ff");
}

export function getBlockClassName(block: BlockProxy) {
return block.getClassName;
}

Screen shot of Console

Notice that I get the DataItemProxy and a value Collection with list of fields, but when I go to fetch the value of each field I get an error each time (it repeats the error for all of the fields).

 

Badge

Hey Matt!

 

It looks like we missed a few other places in our code where we needed to address the bug that prevented you from seeing the jira card fields in the first place. We have a fix up and will let you know when it hits production. Thanks for being patient as we work through this problem on our end!

Badge

The fix should be released now! This should address the ‘no collection’ errors you have been seeing. Let us know if anything else comes up!

Badge +3

Hi Connor - thanks for the quick turnaround on this bug. I did a quick test and so far so good! I’m now able to get at the field values it appears. Will let you know after some further testing today if I encounter any other issues but looks to be fixed. 😁

 

Matt

Badge +3

Hello again Connor and the rest of the Lucid team. I was able to test further and I think maybe I’ve found a related bug, although I’ll be happy if I’m mistaken. I am now able to read the field values thanks to your fix and can confirm it works well so far. However, when I try to update a field value using ‘patchItems’ on the CollectionProxy I am getting the same type of error message as before saying it “could not find collection...” 

I set up a simple demonstration in the code below for your review, with a hard-coded update value in string format. I picked a field called “summary” which is just a string to hopefully rule out type compatibility issues. Thanks again for all of your help!

Here’s my function getFieldValueFromElement, and you can see that all of the read the fields from the collection (and display the collection itself) up until I execute the patch command.

export async function getFieldValueFromElement(
client: EditorClient,
elementId: string,
fieldName: string
) {
// Obtain an ElementProxy for the given elementId
const elementProxy = new ElementProxy(elementId, client);
// Check if elementProxy is null or undefined
if (!elementProxy) {
throw new Error(`Element with ID ${elementId} not found.`);
}

// Iterate over reference keys to find the associated data item
for (const referenceKey of elementProxy.referenceKeys.values()) {
const dataItemProxy = referenceKey.getItem();
if (!dataItemProxy) {
continue; // Skip if no data item proxy is associated with this reference key
}

// Obtain the CollectionProxy from the DataItemProxy
const collectionProxy = dataItemProxy.collection;
if (collectionProxy) {
// Get all field names for the collection
const fields = collectionProxy.getFields();
console.log("Fields in Collection:", fields);
if (fields.includes(fieldName)) {
// Get the value for the specified field
const fieldValue = dataItemProxy.fields.get(fieldName);

if (fieldValue !== undefined) {
/************************************************************** */
// Test code for patch ability
// Assuming passed in field name contains a string value
/************************************************************** */

const testNewValue = "Test updated value";

console.log(`Attempting to change value of ${fieldName}`);
console.log(`Current Value of ${fieldName}:`, fieldValue);

const primaryKey = dataItemProxy.primaryKey;

console.log(`Primary Key:`, primaryKey);

const changedItems = new Map();
changedItems.set(primaryKey, { [fieldName]: testNewValue });

console.log("Changed Items Map:", changedItems);
console.log("CollectionProxy:", collectionProxy);

collectionProxy.patchItems({ changed: changedItems });

console.log(
`New Value of ${fieldName}:`,
dataItemProxy.fields.get(fieldName)
);
/************************************************************** */

return fieldValue; // Return the found value
}
}
}
}

// If the loop completes without returning, the field was not found
throw new Error(
`Field '${fieldName}' not found in any referenced data items.`
);
}

Here’s a capture of the console log:

 

Userlevel 3
Badge +7

Hi @mwyman thank you for your question. Just closing out this thread - I see that you also started a new thread and I understand you’ve received direct support from our Development team on this! Thanks for using our APIs.

Reply