Goal: Build and publish a Foundry Function that powers a chart where viewers can choose the X‑axis (category), optional series (segment by), and metric (count or sum) at runtime.
What we will build
-
A TypeScript Function that:
-
Accepts an ObjectSet of a dummy ontology type (e.g., DemoOrder).
-
Lets users pick Category, Segment By, and Aggregate On at render time.
-
Returns a ThreeDimensionalAggregation suitable for bar/column charts (Category × Segment → Value).
-
-
A chart wired to this Function with dropdown controls for the three parameters.
Prerequisites
- An ontology object type with fields you want to bucket by (strings or dates) and fields you want to aggregate (numbers).
Replace imports that reference your org‑specific ontology path with the correct path in your deployment.
Implementation
1) Dynamic Code Example
import {
Function,
ThreeDimensionalAggregation,
Double,
IAggregatableProperty,
AggregatablePropertiesForResult,
IRange,
Timestamp,
} from "@foundry/functions-api";
import {
DemoOrder,
ObjectSet,
} from "@foundry/ontology-api";
import { BucketableProperties } from "@foundry/ontology-api/yourontology/ontology-api/DemoOrder";
import * as FunctionsApi from "@foundry/functions-api";
import {
IStringPropertyBucketing,
IDatePropertyBucketing,
} from "@foundry/functions-typescript-object-set-base";
export class Chart {
// toCamelCase to convert the for e.g. Order Category from dropdown to orderCategory (apiName in onotology manager properties)
private toCamelCase(input: string) {
return input
.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) {
if (+match === 0) return ""; // skip spaces
return index === 0 ? match.toLowerCase() : match.toUpperCase();
})
.replace(/\s+/g, "");
}
@Function()
public async variableChart(
objects: ObjectSet<DemoOrder>,
categoryValue: string,
aggregateOn: string,
segmentByValue: string
): Promise<ThreeDimensionalAggregation<string, string, Double>> {
let categoryProperty = this.toCamelCase(
categoryValue
) as keyof BucketableProperties;
let segmentByProperty = segmentByValue
? (this.toCamelCase(segmentByValue) as keyof BucketableProperties)
: undefined;
let aggregateOnProperty = this.toCamelCase(
aggregateOn
) as unknown as keyof AggregatablePropertiesForResult<
DemoOrder,
number
>;
// Start with the base query
const baseQuery = objects.groupBy((o: BucketableProperties) =>
(o[categoryProperty] as IStringPropertyBucketing).topValues()
);
// perform segment by operation
const aggregatedQuery = baseQuery.segmentBy((s) =>
(s[segmentByProperty!] as IStringPropertyBucketing).topValues()
);
// Perform the either count or sum aggregation based on parameter
const chartData =
aggregateOn === "Count"
? await aggregatedQuery.count()
: await aggregatedQuery.sum(
(s) => s[aggregateOnProperty!] as IAggregatableProperty<number>
);
return chartData;
}
}
2) Test the Function: Test the function in Code Repo and then commit and tag!
3) Add UI Controls: Add String Selector widget for each parameter in the workshop, then pass the selected variable from the string selector to the function-backed chart widget.
Result
Room for Enhancements
-
Whitelist properties: Instead of free‑form strings + toCamelCase, map user‑facing labels to known property keys.
-
Cardinality control, Null handling, and Performance
Troubleshooting
- Be mindful of type mismatches (Integer vs. Double).
- Verify that dropdowns correspond to the actual apiName of the property (see the details section in the properties tab of the object in the ontology manager).
- For instance, if the apiName of the property is countryName, use “Country Name” in the string selector, as we employ the toCamelCase function to convert “Country Name” back to countryName.
I hope I was clear with the steps ![]()
