In this guide, we will walk you through creating a very simple web app that shows a different embedded chart for each user selected from a drop-down. While this example is simple it highlights the possibilities of how you can use Pivot’s new embedding features.
In this example, we can imagine that the drop-down represents the currently logged-in user on your site. When the user logs in we fetch the signed link from the /embed endpoint and serve it in an iframe. For illustration purposes we will get a new link every time the user state is updated, however, it is only necessary to fetch a link when the user first logs in as it will be valid for the specific logged-in user until the configured time to live (TTL) or until the user logs out or clears their cookies.
Set up
Let’s start by getting some setup out of the way. First, let’s use create-react-app to create a basic React app using typescript with a dependency on BlueprintJS, which we will use as our component library
In App.tsx let’s add an iframe for the embedded visualization, our UsersMenu for switching users, and component state for storing the current user and the embed link URL.
We should now have a blank iframe and a menu of users to choose from.
Link set up
Now that we have a placeholder for our users let’s make a link and assign some filters to our users. Restricted embed links have two configurable fields:
linkAccessFilter: A SQL filter expression applied to the link that can be used on any data cube. The table name is always aliased as t.
cubeAccessFilter: The ID of a data cube access filter. The data cube must have access filters configured for this type of filter to work.
For the purposes of this example we will create a restricted link on a data cube with the following access filters.
Then we can use the filter IDs to create 3 users – each with a group and a linkAccessFilter – and add them to our user list in UsersMenu.tsx. Here each user has an accessFilter ID as well as a SQL filter that will be applied to the visualization.
JavaScript
exportconstUSERS= [// Adds the accessfilter t"recordedBy"='Mcgrogor, Sharon'{userName:"User 1",cubeAccessFilter:"85639959-dd28-467b-830c-04a2aa411dea",linkAccessFilter:undefined,},// Adds the accessfilter t"recordedBy"='Cleary, Grainne'// and a Sql filter on the year 2013{userName:"User 2",cubeAccessFilter:"05f828b4-87d3-43af-befd-297c10124d1c",linkAccessFilter:`t."year"='2013'`,},// Adds the accessfilter t"countryCode"='AU'// and a Sql filter on the province Victoria{userName:"User 3",cubeAccessFilter:"16b02481-3667-4be8-88e0-9de16dd1f971",linkAccessFilter:`t."stateProvince"='Victoria'`,},];
The back end
Now using the link we just generated let’s make an endpoint to sign it! First let’s start by creating a basic Express server in a source file named server.ts. Here is some boilerplate to get you started. You will need to install express and cors to allow the localhost sites to talk to each other. We will also install ts-node to avoid a TypeScript compilation step.
importexpressfrom"express";importcorsfrom"cors";constPORT:number=3001;constapp=express();// Will allow your react app running on localhost:3000 to access the apiapp.use(cors({origin: ["http://localhost:3000"],}),);app.listen(PORT,():void=>{console.log("Server is listening on port",PORT);});
Now run the server using ts-node:
npx ts-node-O'{"module":"commonjs"}'src/server.ts
Now for the good stuff. In a new source file called routes/embed.ts let’s create a new router that signs the link based on the user’s filters.
Creating the link requires 3 steps 1. Import the key: using the key provided when you created the embed link in Polaris, import the key using the crypto library from node. The key uses an ESCDA algorithm with a p-256 curve.
JavaScript
constKEY='your link secret key';constLINK='your link URL';constprivateKey=awaitcrypto.subtle.importKey("pkcs8",Buffer.from(KEY,"base64"),{name:"ECDSA",namedCurve:"P-256"},true, ["sign"],);
2. Sign the payload: Next we can use the key we just created and the user that will be supplied to generate a signature using the crypto library. Even if they are undefined, we need to sign a payload composed of the cubeAccessFilter, the linkAccessFilter and the current date. The order is also important: cubeAccessFilter, linkAccessFilter, then created.
3. Create the link: Lastly we need to compose the actual link. To do so we attach the signature as a base64 string and linkAccessFilter, cubeAccessFilter and date as URL parameters to the original link provided by Polaris. URLSearchParams may be helpful here.
Now for our final step let’s create a hook in order to fetch the link for the appropriate user. In a new file called getSignedLink.ts let’s create a function that makes a POST request to localhost:3000/embed with the user filters in the body. If the call is successful we can render the signed link in our iframe.
src/getSignedLink.ts
JavaScript
constEMBED_API_URL="http://localhost:3001/embed";exportasyncfunctiongetSignedLink(cubeAccessFilter?:string,linkAccessFilter?:string,): Promise<string>{constresponse=awaitfetch(EMBED_API_URL,{method:"POST",headers:{"Content-Type":"application/json",// Add any other headers if needed},body:JSON.stringify({cubeAccessFilter,linkAccessFilter}),});if (!response.ok) {thrownewError(`Response not ok: ${response.status}`);}constdata=awaitresponse.json();returndata.signedLink;}
We can call this function from a useEffect in App.tsx that will fetch a new URL whenever the user changes or “logs in”.
App.tsx
JavaScript
import"./App.css";import{useEffect,useState}from"react";import{getSignedLink}from"./getSignedLink";importUsersMenu,{User,USERS}from"./UsersMenu";functionApp(){const[link,setLink]=useState("");const[userId,setUserId]=useState(0);useEffect(()=>{asyncfunctiongenerateLink(){constuser: User =USERS[userId];try{setLink(awaitgetSignedLink(user.cubeAccessFilter,user.linkAccessFilter), );}catch (error) {console.error("Error generating signed link:",error);}}generateLink();}, [userId]);return (<divclassName="App"><iframewidth="100%"height={700}src={link}title="Embedded visualization"/><div><UsersMenuonUserIdChange={setUserId}userId={userId}/></div></div> );}exportdefaultApp;
Other blogs you might find interesting
No records found...
Nov 14, 2024
Recap: Druid Summit 2024 – A Vibrant Community Shaping the Future of Data Analytics
In today’s fast-paced world, organizations rely on real-time analytics to make critical decisions. With millions of events streaming in per second, having an intuitive, high-speed data exploration tool to...
Pivot by Imply: A High-Speed Data Exploration UI for Druid
In today’s fast-paced world, organizations rely on real-time analytics to make critical decisions. With millions of events streaming in per second, having an intuitive, high-speed data exploration tool to...