How to pass an object set from Workshop to a custom React widget?

Following up on :

I want to pass numerous objects to my custom react widget, from Workshop. I don’t want to be limited by the 10k objects limit.

I got to read that “temporaryObjectSetRid” is the way to pass an object set and “hydrateObjectSetFromRid” should be used on the react widget side.

Is there an example of this ? How can I pass an object set from Workshop to my custom react widget and make use of it ?

There are a few hits about those APIs in those places:
https://github.com/palantir/workshop-iframe-custom-widget/blob/d8d5230cbc3f3ad35768dfa13ed0f5abcdb497f2/src/example/Example.tsx#L83

Here is an example of setup, to pass an Object set from Workshop to your custom react application:

Home.tsx

import React from "react";
import {
  MockPivotData,
} from "@hydrateobjectsetfromrid-example/sdk";
import Layout from "./Layout";
import {
  IAsyncValue,
  IWorkshopContext,
  useWorkshopContext,
  visitLoadingState,
  isAsyncValue_Loaded,
} from "@osdk/workshop-iframe-custom-widget";
import { EXAMPLE_CONFIG } from "./config";
import {
  // createAndFetchTempObjectSetRid,
  hydrateObjectSetFromRid,
} from "@osdk/client/internal";
import client from "./client";
// eslint-disable-next-line import/named
import { Osdk, Result } from "@osdk/client";



// ========= Wrapper widget =========

// Application wrapper for Workshop state loading
export const WidgetWrapper = () => {
  // useWorkshopContext() is imported from an npm library:
  // - it takes in the definition of input values required from Workshop, and the outputs values that are sent to Workshop, and events that should be configured in Workshop
  // - Returns a context object with an API that can be called to get values or set Workshop variables or execute Workshop events
  //
  // Example of getting an input value from Workshop:
  //      workshopContext["title"].getValue() -> returns string
  //
  // Example of setting an output value in Workshop:
  //      workshopContext["selectedTimelineObject"].set(value) -> void
  //
  // Example of executing an event in Workshop:
  //      workshopContext["eventOnTimelineClick"].executeEvent() -> void
  //

  const workshopContext = useWorkshopContext(EXAMPLE_CONFIG);
  console.log(workshopContext.status)

  // Note: we can have a proper management of the state on loading etc.
  return visitLoadingState(workshopContext, {
    loading: () => <>LOADING...</>,
    // If the Workshop context was loaded successfully, we pass it to our custom widget
    succeeded: (value) => {
      console.log("Workshop context loaded successfully:", value);
      return <MyCustomWidget loadedWorkshopContext={value} />
    },
    reloading: (previousValue) => {
      console.log("Workshop context is reloading:", previousValue);
      return <MyCustomWidget loadedWorkshopContext={previousValue} />
    },
    failed: (err) => {
      console.error("Failed to load workshop context:", err);
      return <div>SOMETHING WENT WRONG...</div>;
    },
  });
};

// ========= Utils functions =========

// Loads the value from the Workshop context. It will validate the value is present and has been loaded.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getValueFromAsyncValue = (asyncVal: IAsyncValue<any> | undefined) => {
  if (asyncVal != null && isAsyncValue_Loaded(asyncVal)) {
    const value = asyncVal.value;
    if (typeof value === "string") {
      return value; // return string as-is, no extra quotes
    }
    return JSON.stringify(value); // stringify non-string values
  }
  return undefined;
};


// ========= Custom widget =========

// Defines the interface of our "MyCustomWidget" widget
interface MyCustomWidgetProps {
  loadedWorkshopContext: IWorkshopContext<typeof EXAMPLE_CONFIG>;
}


// Defines the custom widget itself
const MyCustomWidget: React.FC<MyCustomWidgetProps> = props => {

  // We populat variables from the properties passed to the widget
  const { loadedWorkshopContext } = props; 

  const [aggregationCount, setAggregationCount] = React.useState<number | null>(null);

  // If Workshop context updates, then re-run the aggregation
  React.useEffect(() => {
    // Async function as we need it for the aggregation itself
    const resoveObjectSetAndRunAggregation = async () => {
      console.log("New Workshop state detected", loadedWorkshopContext)
      const temporaryObjectSetRidFieldValue: IAsyncValue<string | undefined> = loadedWorkshopContext.temporaryObjectSetRidField.fieldValue;
      const temporaryObjectSetString = getValueFromAsyncValue(temporaryObjectSetRidFieldValue);
      console.log("Extracted temp object set string", temporaryObjectSetString)

      // Check if client works
      console.log("trying client usage")
      const response: Result<Osdk.Instance<MockPivotData>> = await client(MockPivotData).fetchOneWithErrors("6d95f183-dce5-4d82-b20a-7ebf393293b8");
      console.log("trying to load one random object", response)

      // DEBUG
      // temporaryObjectSetString = "ri.object-set.main.temporary-object-set.d33663f3-e3f8-4abd-8a37-b3267da6a803"
      
      if (temporaryObjectSetString !== undefined) {
        console.log("Valid temporary object set rid", temporaryObjectSetString)
        // We have a valid object set rid, so we actually get the ObjectSet from it
        const potentialObjectSet = hydrateObjectSetFromRid(
          client,
          MockPivotData,
          temporaryObjectSetString
        );

        console.log("Object set retrieved. Running aggregation.", potentialObjectSet)

        // We run the aggregation on our object set
        const result2 = await potentialObjectSet.fetchOne("6d95f183-dce5-4d82-b20a-7ebf393293b8");
        console.log("Fetch one object result", result2)

        const result = await potentialObjectSet.aggregate({
          $select: { $count: "unordered" },
        });

        console.log("Aggregation result", result)
        // Do something with result here, e.g. set state
        setAggregationCount(result.$count);
      } else {
        console.log("Invalid temp object set string. Aborting.")
      }
    };

    resoveObjectSetAndRunAggregation();
  }, [loadedWorkshopContext]);

    return  <Layout>
      <h1>@hydrateobjectsetfromrid-example/sdk</h1>
      <p>
        Welcome to your Ontology SDK! Try using any of the following methods
        now.
      </p>
      <p>Number of object in object set: {aggregationCount}</p>
    </Layout>
};

config.tsx

import { IConfigDefinition } from "@osdk/workshop-iframe-custom-widget";

export const EXAMPLE_CONFIG = [
    {
    // Only compatible with https://www.npmjs.com/package/@osdk/client > 2.0
    fieldId: "temporaryObjectSetRidField", 
    field: {
      type: "single", 
      label: "Object set field (via temporary object set rid)", 
      fieldValue: {
        type: "inputOutput", 
        variableType: {
          type: "temporaryObjectSetRid", 
        }
      }
    }
  },
] as const satisfies IConfigDefinition;

router.tsx

import { createBrowserRouter } from "react-router-dom";
import AuthCallback from "./AuthCallback";
import {WidgetWrapper} from "./Home";

export const router = createBrowserRouter(
  [
    {
      path: "/",
      element: <WidgetWrapper />,
    },
    {
      // This is the route defined in your application's redirect URL
      path: "/auth/callback",
      element: <AuthCallback />,
    },
  ],
  { basename: import.meta.env.BASE_URL },
);

Don’t forget, in package.json:

...
  "dependencies": {
    "@hydrateobjectsetfromrid-example/sdk": "latest",
    "@osdk/client": "^2.2.0",
    "@osdk/foundry": "^2.6.0",
    "@osdk/oauth": "^1.1.0",
    "@osdk/react": "^0.3.0",
    "@osdk/workshop-iframe-custom-widget": "^1.1.0",
    "react": "^18",
    "react-dom": "^18",
    "react-router-dom": "^6.23.1"
  },
...

Then in Workshop, the object set will be passed to the widget, which will then have access to the object set “as an object set” which means, running aggregations, etc.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.