Client Source Substitution is a mechanism for using a separate database for each Client Organization when viewing shared content from the Primary Organization. This allows a single copy of reports and dashboards to be consumed by each client with their own data, with minimal configuration.
A common scenario is that a new Tenant is onboarded into a Yellowfin instance, and reporting against their data needs to be enabled. The process involves several steps:
- A datasource needs to be created at the Client Organization.
- The new Client Datasource needs to be linked to the Primary Organization datasource on which all the shared content has been created.
There are two approaches for creating a new datasource at a Client Organization. A datasource can be created for the client by importing a copy of an existing datasource. There is also an end-point for creating a datasource directly.
Once the datasource is created, there is a single end-point to associate the Client Datasource with the Primary Organization’s datasource.
Create datasource via Import Clone
One approach to creating a new datasource for a new client organization is to clone an existing datasource, and change the connection details for a new connection. This can be done by exporting an existing datasource, altering the export file itself, and then reimporting it into Yellowfin.
When using Source Substitution, there should already be a placeholder datasource at the Primary Organization, to which all reporting content is attached. This datasource can be exported via the Export UI within Yellowfin. This will result in a YFX file being exported from Yellowfin.
YFX files are actually archive (zip) files that can be decompressed. Within the YFX there will be a ExportListHierarchy.json file and a YFExport.xml file. The YFExport.xml file contains a readable version of the exported datasource.
Within the YFExport.xml file the database connection settings can be changed. If the process of creating tenant data sources for source substitution is to be automated, then the contents of the YFExport.xml file could be used as a template, with tokens added where connection specific data needs to be injected.
The following extract from a YFExport.xml file shows in red where tokens could be replaced by an automated process for generating a datasource for use with client source substitution.
<source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> |
Once an export file has been automatically injected with tokens, it can be uploaded via the REST API using the POST /api/rpc/import-export/import-content end-point, Import Content.
This particular endpoint uses a form based submission paradigm. Two form bodies are required - one named “contentToProcess” with the YFX/XML file contents and another named “importOptions”, which contains the rules on how the import is processed. For this example, assuming the XML file contains a single datasource, this importOptions payload can be used:
[ { "itemIndex": 0, "optionKey": "SKIP", "optionValue": false }, { "itemIndex": 0, "optionKey": "OPTION", "optionValue": "ADD" } ]
The following code examples take a pre-exported database XML template and inject replacement values for the database host, database name, database user and password into the file. It is then imported into a Tenant (specified by the Client Reference Id).
package rest.code.examples; import java.io.IOException; import java.nio.charset.Charset; import java.util.Random; import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * Import a datasource for a client org using the Yellowfin REST API */ public class ImportDatasourceForClientOrg { public static void main(String[] args) throws Exception { System.out.print("Import Datasource for Client"); String host = "http://localhost:8080/Yellowfin"; String restUsername = "admin@yellowfin.com.au"; String restPassword = "test"; String tenantClientReferenceId = "NEWCLIENT"; String databaseUserName = "postgres"; String encryptedPassword = "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw"; String serverAddress = "localhost"; String databaseName = "testdata"; String token = generateTokenForTenant(host, restUsername, restPassword, tenantClientReferenceId); Integer tenantId = retrieveTenantIpIdForTenantName(host, token, tenantClientReferenceId); System.out.println("Tenant Id: " + tenantId); // Replace tokens with values String modifiedFileContents = importFileContents; modifiedFileContents = modifiedFileContents.replace("[USERNAME]", databaseUserName); modifiedFileContents = modifiedFileContents.replace("[TENANT_NAME]", tenantClientReferenceId); modifiedFileContents = modifiedFileContents.replace("[ENCRYPTED_PASSWORD]", encryptedPassword); modifiedFileContents = modifiedFileContents.replace("[SERVER_ADDRESS]", serverAddress); modifiedFileContents = modifiedFileContents.replace("[DATABASE_NAME]", databaseName); HttpEntity multipartEntity = MultipartEntityBuilder .create() .setMode(HttpMultipartMode.LEGACY) .setCharset(Charset.forName("UTF-8")) .addBinaryBody("contentToProcess", modifiedFileContents.getBytes("UTF-8"), ContentType.DEFAULT_BINARY, "YFExport.xml") .addTextBody("importOptions", "[{ \"itemIndex\": 0, \"optionKey\": \"SKIP\", \"optionValue\": false }, { \"itemIndex\": 0, \"optionKey\": \"OPTION\", \"optionValue\": \"ADD\" }]", ContentType.APPLICATION_JSON) .build(); System.out.println("Content-Type: " + multipartEntity.getContentType()); Content c = Request.post(host + "/api/rpc/import-export/import-content") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", multipartEntity.getContentType()) .addHeader("cache-control", "no-cache") .body(multipartEntity) .execute() .returnContent(); System.out.println(c.asString()); } /* * This function fetches a client organization's integer id for a given clientRefCode */ public static Integer retrieveTenantIpIdForTenantName(String host, String token, String tenantCode) throws IOException { Content c = Request.get(host + "/api/orgs") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement groupList = jsonObject.get("items"); JsonArray groups = groupList.getAsJsonArray(); for (int i=0; i < groups.size(); i++ ) { JsonObject group = groups.getAsJsonArray().get(i).getAsJsonObject(); if (!group.has("clientRefId")) continue; if (tenantCode.equals(group.get("clientRefId").getAsString())) return group.get("ipOrg").getAsInt(); } System.out.println("Tenant could not be found for code:" + tenantCode); System.exit(-1); return null; } /* * This function generates an access token for a user that will grant them access to * call REST API endpoints. */ public static String generateTokenForTenant(String host, String username, String password, String tenant) throws IOException { Content c = Request.post(host + "/api/refresh-tokens") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong()) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .bodyString("{ \"userName\": \""+ username + "\",\"password\": \""+ password + "\", \"clientOrgRef\": \"" + tenant + "\"}", null) .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement accessToken = jsonObject.getAsJsonObject("_embedded").getAsJsonObject("accessToken").get("securityToken"); if (accessToken!=null) { System.out.println("Access Token: " + accessToken); } else { System.out.println("Token not retrieved successfully"); System.exit(-1); } return accessToken.getAsString(); } private static String importFileContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --><data>\n" + " <info>\n" + " <exportversion>4</exportversion>\n" + " <exportsubversion>54</exportsubversion>\n" + " <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID>\n" + " <appversion>9.11</appversion>\n" + " <appsubversion>0.3</appsubversion>\n" + " <buildversion>20240607</buildversion>\n" + " <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate>\n" + " <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid>\n" + " </info>\n" + " <source>\n" + " <id>132641</id>\n" + " <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName>\n" + " <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription>\n" + " <databasePath>public</databasePath>\n" + " <userName>[USERNAME]</userName>\n" + " <password>[ENCRYPTED_PASSWORD]</password>\n" + " <connectionTypeCode>GENERICUSER</connectionTypeCode>\n" + " <connectionMethodCode>JDBC</connectionMethodCode>\n" + " <connectionDriver>org.postgresql.Driver</connectionDriver>\n" + " <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL>\n" + " <databaseTypeCode>POSTGRESQL</databaseTypeCode>\n" + " <minConnections>1</minConnections>\n" + " <maxConnections>5</maxConnections>\n" + " <connectionRefreshTime>180</connectionRefreshTime>\n" + " <connectionTimeOut>180</connectionTimeOut>\n" + " <accessCode>UNSECURE</accessCode>\n" + " <maxRowCount>10000</maxRowCount>\n" + " <maxAnalysisRowCount>0</maxAnalysisRowCount>\n" + " <broadcastPermitted>true</broadcastPermitted>\n" + " <subscribePermitted>true</subscribePermitted>\n" + " <dataProfileEnabled>true</dataProfileEnabled>\n" + " <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode>\n" + " <secondaryMinConnections>0</secondaryMinConnections>\n" + " <secondaryMaxConnections>0</secondaryMaxConnections>\n" + " <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime>\n" + " <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut>\n" + " <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime>\n" + " <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID>\n" + " <filterList/>\n" + " <sourceParameterList>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>DATABASE</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>[DATABASE_NAME]</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>HOSTNAME</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>[SERVER_ADDRESS]</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>ISOLATIONLEVEL</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue/>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>PORT</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>5432</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>SOURCECLASSNAME</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>false</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>USESCHEMA</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>true</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " <sourceParameter>\n" + " <id>0</id>\n" + " <parameterTypeCode>TEXT</parameterTypeCode>\n" + " <parameterKey>YF_DRIVER_SELECTION</parameterKey>\n" + " <parameterIndex>0</parameterIndex>\n" + " <parameterValue>org.postgresql.Driver</parameterValue>\n" + " <documentId>0</documentId>\n" + " <userVisible>true</userVisible>\n" + " </sourceParameter>\n" + " </sourceParameterList>\n" + " </source>\n" + " <translationDictionary/>\n" + " <refCodeDictionary/>\n" + "</data>"; }
using System.Net.Http.Headers; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace YellowfinAPIExamples { public class ImportDatasourceForClientOrg { static async Task Main(string[] args) { Console.WriteLine("Import Datasource for Client"); string host = "http://localhost:8080/Yellowfin"; string restUsername = "admin@yellowfin.com.au"; string restPassword = "test"; string tenantClientReferenceId = "NEWCLIENT"; string databaseUserName = "postgres"; string encryptedPassword = "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw"; string serverAddress = "localhost"; string databaseName = "testdata"; string token = await GenerateTokenForTenant(host, restUsername, restPassword, tenantClientReferenceId); int tenantId = await RetrieveTenantIpIdForTenantName(host, token, tenantClientReferenceId); Console.WriteLine("Tenant Id: " + tenantId); // Replace tokens with values string modifiedFileContents = ImportFileContents; modifiedFileContents = modifiedFileContents.Replace("[USERNAME]", databaseUserName) .Replace("[TENANT_NAME]", tenantClientReferenceId) .Replace("[ENCRYPTED_PASSWORD]", encryptedPassword) .Replace("[SERVER_ADDRESS]", serverAddress) .Replace("[DATABASE_NAME]", databaseName); using (var httpClient = new HttpClient()) { var content = new MultipartFormDataContent { { new ByteArrayContent(Encoding.UTF8.GetBytes(modifiedFileContents)), "contentToProcess", "YFExport.xml" }, { new StringContent( "[{ \"itemIndex\": 0, \"optionKey\": \"SKIP\", \"optionValue\": false }, { \"itemIndex\": 0, \"optionKey\": \"OPTION\", \"optionValue\": \"ADD\" }]", Encoding.UTF8, "application/json"), "importOptions" } }; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", "ts=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + " , nonce=" + new Random().NextInt64() + ", token=" + token); httpClient.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); httpClient.DefaultRequestHeaders.Add("cache-control", "no-cache"); HttpResponseMessage response = await httpClient.PostAsync(host + "/api/rpc/import-export/import-content", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } private static async Task<int> RetrieveTenantIpIdForTenantName(string host, string token, string tenantCode) { using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", "ts=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + " , nonce=" + new Random().NextInt64() + ", token=" + token); httpClient.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); HttpResponseMessage response = await httpClient.GetAsync(host + "/api/orgs"); string responseBody = await response.Content.ReadAsStringAsync(); JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseBody); JArray groups = (JArray)jsonObject["items"]; foreach (var group in groups) { if (group["clientRefId"] != null && tenantCode == group["clientRefId"].ToString()) { return (int)group["ipOrg"]; } } Console.WriteLine("Tenant could not be found for code:" + tenantCode); Environment.Exit(-1); return 0; } } private static async Task<string> GenerateTokenForTenant(string host, string username, string password, string tenant) { using (var client = new HttpClient()) { var request = new HttpRequestMessage(HttpMethod.Post, host + "/api/refresh-tokens"); request.Headers.Add("Authorization", "YELLOWFIN ts=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + ", nonce=" + new Random().NextInt64()); request.Headers.Add("Accept", "application/vnd.yellowfin.api-v1+json"); request.Content = new StringContent( JsonConvert.SerializeObject(new { userName = username, password = password, clientOrgRef = tenant }), Encoding.UTF8, "application/json" ); HttpResponseMessage response = await client.SendAsync(request); string responseContent = await response.Content.ReadAsStringAsync(); JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseContent); string accessToken = jsonObject["_embedded"]["accessToken"]["securityToken"].ToString(); if (!string.IsNullOrEmpty(accessToken)) { Console.WriteLine("Access Token: " + accessToken); } else { Console.WriteLine("Token not retrieved"); Environment.Exit(-1); } return accessToken; } } private static readonly string ImportFileContents = @"<?xml version=""1.0"" encoding=""UTF-8""?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --><data> <info> <exportversion>4</exportversion> <exportsubversion>54</exportsubversion> <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID> <appversion>9.11</appversion> <appsubversion>0.3</appsubversion> <buildversion>20240607</buildversion> <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate> <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid> </info> <source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>ISOLATIONLEVEL</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue/> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>PORT</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>5432</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>SOURCECLASSNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue> <documentId>0</documentId> <userVisible>false</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>USESCHEMA</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>true</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>YF_DRIVER_SELECTION</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>org.postgresql.Driver</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> </sourceParameterList> </source> <translationDictionary/> <refCodeDictionary/> </data>"; } }
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "math/rand" "mime/multipart" "net/http" "strings" "time" ) func main() { host := "http://localhost:8080/Yellowfin" restUsername := "admin@yellowfin.com.au" restPassword := "test" tenantClientReferenceId := "NEWCLIENT" databaseUserName := "postgres" encryptedPassword := "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw" serverAddress := "localhost" databaseName := "testdata" token, err := generateToken(host, restUsername, restPassword, tenantClientReferenceId) if err != nil { fmt.Println("Error generating token:", err) return } tenantId, err := retrieveTenantIpIdForTenantName(host, token, tenantClientReferenceId) if err != nil { fmt.Println("Error retrieving tenant ID:", err) return } fmt.Println("Tenant Id:", tenantId) // Replace tokens with values modifiedFileContents := strings.Replace(importFileContents, "[USERNAME]", databaseUserName, -1) modifiedFileContents = strings.Replace(modifiedFileContents, "[TENANT_NAME]", tenantClientReferenceId, -1) modifiedFileContents = strings.Replace(modifiedFileContents, "[ENCRYPTED_PASSWORD]", encryptedPassword, -1) modifiedFileContents = strings.Replace(modifiedFileContents, "[SERVER_ADDRESS]", serverAddress, -1) modifiedFileContents = strings.Replace(modifiedFileContents, "[DATABASE_NAME]", databaseName, -1) bodyBuffer := &bytes.Buffer{} writer := multipart.NewWriter(bodyBuffer) // Add file content part, err := writer.CreateFormFile("contentToProcess", "YFExport.xml") if err != nil { fmt.Println("Error creating form file:", err) return } part.Write([]byte(modifiedFileContents)) // Add text body err = writer.WriteField("importOptions", `[{"itemIndex": 0, "optionKey": "SKIP", "optionValue": false}, {"itemIndex": 0, "optionKey": "OPTION", "optionValue": "ADD"}]`) if err != nil { fmt.Println("Error writing field:", err) return } writer.Close() client := &http.Client{} req, err := http.NewRequest("POST", host+"/api/rpc/import-export/import-content", bodyBuffer) if err != nil { fmt.Println("Error creating request:", err) return } nonce := rand.Int63() req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("cache-control", "no-cache") resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return } fmt.Println(string(body)) } func generateToken(host, restUsername, restPassword, tenant string) (string, error) { nonce := rand.Int63() requestBody, err := json.Marshal(map[string]string{ "userName": restUsername, "password": restPassword, "clientOrgRef": tenant, }) if err != nil { fmt.Println("Error marshaling request body:", err) return "", err } client := &http.Client{} request, err := http.NewRequest("POST", host+"/api/refresh-tokens", bytes.NewBuffer(requestBody)) if err != nil { fmt.Println("Error creating request:", err) return "", err } request.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d", time.Now().UnixMilli(), nonce)) request.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") request.Header.Set("Content-Type", "application/json") response, err := client.Do(request) if err != nil { fmt.Println("Error sending request:", err) return "", err } defer response.Body.Close() responseBody, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Println("Error reading response body:", err) return "", err } var jsonResponse map[string]interface{} err = json.Unmarshal(responseBody, &jsonResponse) if err != nil { fmt.Println("Error parsing JSON response:", err) return "", err } accessToken, ok := jsonResponse["_embedded"].(map[string]interface{})["accessToken"].(map[string]interface{})["securityToken"].(string) if !ok { fmt.Println("Token not retrieved") return "", fmt.Errorf("Token not retrieved successfully") } return accessToken, nil } func retrieveTenantIpIdForTenantName(host, token, tenantCode string) (int, error) { client := &http.Client{} req, err := http.NewRequest("GET", host+"/api/orgs", nil) if err != nil { return 0, err } nonce := rand.Int63() req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return 0, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return 0, err } var jsonResponse map[string]interface{} err = json.Unmarshal(body, &jsonResponse) if err != nil { return 0, err } items, ok := jsonResponse["items"].([]interface{}) if !ok { return 0, fmt.Errorf("Invalid response format") } for _, item := range items { group, ok := item.(map[string]interface{}) if !ok { continue } if group["clientRefId"] == tenantCode { return int(group["ipOrg"].(float64)), nil } } return 0, fmt.Errorf("Tenant could not be found for code: %s", tenantCode) } const importFileContents = `<?xml version="1.0" encoding="UTF-8"?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --><data> <info> <exportversion>4</exportversion> <exportsubversion>54</exportsubversion> <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID> <appversion>9.11</appversion> <appsubversion>0.3</appsubversion> <buildversion>20240607</buildversion> <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate> <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid> </info> <source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>ISOLATIONLEVEL</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue/> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>PORT</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>5432</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>SOURCECLASSNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue> <documentId>0</documentId> <userVisible>false</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>USESCHEMA</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>true</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>YF_DRIVER_SELECTION</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>org.postgresql.Driver</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> </sourceParameterList> </source> <translationDictionary/> <refCodeDictionary/> </data> `
const fetch = require("node-fetch"); const FormData = require("form-data"); const { Buffer } = require('buffer'); async function main() { console.log("Import Datasource for Client"); const host = "http://localhost:8080/Yellowfin"; const restUsername = "admin@yellowfin.com.au"; const restPassword = "test"; const tenantClientReferenceId = "NEWCLIENT"; const databaseUserName = "postgres"; const encryptedPassword = "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw"; const serverAddress = "localhost"; const databaseName = "testdata"; const token = await generateTokenForTenant(host, restUsername, restPassword, tenantClientReferenceId); if (!token) { console.error("Failed to retrieve access token"); return; } const tenantId = await retrieveTenantIpIdForTenantName(host, token, tenantClientReferenceId); console.log("Tenant Id:", tenantId); // Replace tokens with values let modifiedFileContents = importFileContents; modifiedFileContents = modifiedFileContents.replace("[USERNAME]", databaseUserName); modifiedFileContents = modifiedFileContents.replace("[TENANT_NAME]", tenantClientReferenceId); modifiedFileContents = modifiedFileContents.replace("[ENCRYPTED_PASSWORD]", encryptedPassword); modifiedFileContents = modifiedFileContents.replace("[SERVER_ADDRESS]", serverAddress); modifiedFileContents = modifiedFileContents.replace("[DATABASE_NAME]", databaseName); const formData = new FormData(); formData.append('contentToProcess', Buffer.from(modifiedFileContents), { filename: 'YFExport.xml' }); formData.append('importOptions', JSON.stringify([ { itemIndex: 0, optionKey: "SKIP", optionValue: false }, { itemIndex: 0, optionKey: "OPTION", optionValue: "ADD" } ])); const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${token}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'cache-control': 'no-cache' }; try { const response = await fetch(`${host}/api/rpc/import-export/import-content`, { method: 'POST', headers: headers, body: formData }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const responseBody = await response.text(); console.log(responseBody); } catch (error) { console.error("Error:", error.message); } } async function retrieveTenantIpIdForTenantName(host, token, tenantCode) { const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${token}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; try { const response = await fetch(`${host}/api/orgs`, { method: 'GET', headers: headers }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const jsonResponse = await response.json(); const groups = jsonResponse.items; for (const group of groups) { if (group.clientRefId && tenantCode === group.clientRefId) { return group.ipOrg; } } console.log("Tenant could not be found for code:", tenantCode); process.exit(-1); } catch (error) { console.error("Error:", error.message); } return null; } async function generateTokenForTenant(host, username, password, tenant) { const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; const body = JSON.stringify({ userName: username, password: password, clientOrgRef: tenant }); try { const response = await fetch(`${host}/api/refresh-tokens`, { method: 'POST', headers: headers, body: body }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const jsonResponse = await response.json(); const accessToken = jsonResponse._embedded.accessToken.securityToken; if (accessToken) { console.log(`Access Token: ${accessToken}`); return accessToken; } else { console.log("Token not retrieved successfully"); process.exit(-1); } } catch (error) { console.error("Error:", error.message); } return null; } const importFileContents = `<?xml version="1.0" encoding="UTF-8"?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --> <data> <info> <exportversion>4</exportversion> <exportsubversion>54</exportsubversion> <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID> <appversion>9.11</appversion> <appsubversion>0.3</appsubversion> <buildversion>20240607</buildversion> <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate> <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid> </info> <source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>ISOLATIONLEVEL</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue/> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>PORT</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>5432</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>SOURCECLASSNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue> <documentId>0</documentId> <userVisible>false</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>USESCHEMA</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>true</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>YF_DRIVER_SELECTION</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>org.postgresql.Driver</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> </sourceParameterList> </source> <translationDictionary/> <refCodeDictionary/> </data> `; main().catch(console.error);
<?php function main($importFileContents) { $host = "http://localhost:8080/Yellowfin"; $restUsername = "admin@yellowfin.com.au"; $restPassword = "test"; try { $token = generateTokenForTenant($host, $restUsername, $restPassword, "NEWCLIENT"); $tenantClientReferenceId = "NEWCLIENT"; $databaseUserName = "postgres"; $encryptedPassword = "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw"; $serverAddress = "localhost"; $databaseName = "testdata"; // Replace tokens with values $modifiedFileContents = $importFileContents; $modifiedFileContents = str_replace("[USERNAME]", $databaseUserName, $modifiedFileContents); $modifiedFileContents = str_replace("[TENANT_NAME]", $tenantClientReferenceId, $modifiedFileContents); $modifiedFileContents = str_replace("[ENCRYPTED_PASSWORD]", $encryptedPassword, $modifiedFileContents); $modifiedFileContents = str_replace("[SERVER_ADDRESS]", $serverAddress, $modifiedFileContents); $modifiedFileContents = str_replace("[DATABASE_NAME]", $databaseName, $modifiedFileContents); // Build multipart entity $multipartEntity = buildMultipartEntity($modifiedFileContents); // Make HTTP request $response = sendMultipartRequest($host, $token, $multipartEntity); echo $response; } catch (Exception $e) { echo "Error: " . $e->getMessage(); } } function generateTokenForTenant($host, $username, $password, $tenant) { // Generate nonce $nonce = mt_rand(); // Create request body $requestBody = json_encode(array( "userName" => $username, "password" => $password, "clientOrgRef" => $tenant )); // Create request headers $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ); $response = httpRequest('POST', "$host/api/refresh-tokens", $headers, $requestBody); // Parse JSON response $jsonResponse = json_decode($response, true); // Get access token from response if (isset($jsonResponse["_embedded"]["accessToken"]["securityToken"])) { $accessToken = $jsonResponse["_embedded"]["accessToken"]["securityToken"]; echo "Access Token: " . $accessToken; return $accessToken; } else { throw new Exception("Token not retrieved successfully"); } } function buildMultipartEntity($fileContents) { // Build multipart entity $boundary = uniqid(); $multipartBody = "--$boundary\r\n"; $multipartBody .= 'Content-Disposition: form-data; name="contentToProcess"; filename="YFExport.xml"' . "\r\n"; $multipartBody .= "Content-Type: application/xml\r\n\r\n"; $multipartBody .= $fileContents . "\r\n"; $multipartBody .= "--$boundary\r\n"; $multipartBody .= 'Content-Disposition: form-data; name="importOptions"' . "\r\n"; $multipartBody .= "Content-Type: application/json\r\n\r\n"; $multipartBody .= '[{ "itemIndex": 0, "optionKey": "SKIP", "optionValue": false }, { "itemIndex": 0, "optionKey": "OPTION", "optionValue": "ADD" }]' . "\r\n"; $multipartBody .= "--$boundary--"; return $multipartBody; } function sendMultipartRequest($host, $token, $multipartBody) { $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . mt_rand() . ', token=' . $token, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: multipart/form-data; boundary=' . substr($multipartBody, 2, strpos($multipartBody, "\r\n") - 2), 'cache-control: no-cache' ); try { $response = httpRequest('POST', "$host/api/rpc/import-export/import-content", $headers, $multipartBody); return $response; } catch (Exception $e) { throw new Exception("Error sending request: " . $e->getMessage()); } } function httpRequest($method, $url, $headers, $data = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); if ($data !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($ch); if (curl_errno($ch)) { throw new Exception('Error: ' . curl_error($ch)); } curl_close($ch); return $response; } $importFileContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --> <data> <info> <exportversion>4</exportversion> <exportsubversion>54</exportsubversion> <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID> <appversion>9.11</appversion> <appsubversion>0.3</appsubversion> <buildversion>20240607</buildversion> <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate> <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid> </info> <source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>ISOLATIONLEVEL</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue/> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>PORT</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>5432</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>SOURCECLASSNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue> <documentId>0</documentId> <userVisible>false</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>USESCHEMA</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>true</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>YF_DRIVER_SELECTION</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>org.postgresql.Driver</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> </sourceParameterList> </source> <translationDictionary/> <refCodeDictionary/> </data> "; main($importFileContents); ?>
import json import random import time import requests from requests.exceptions import RequestException def main(): host = "http://localhost:8080/Yellowfin" rest_username = "admin@yellowfin.com.au" rest_password = "test" tenant_client_reference_id = "NEWCLIENT" database_user_name = "postgres" encrypted_password = "NDliYzg3NGItMDVjJVY8ywTSdIE3D1zdGplLLBUcEFqw" server_address = "localhost" database_name = "testdata" try: token = generate_token(host, rest_username, rest_password, tenant_client_reference_id) tenant_id = retrieve_tenant_id_for_tenant_name(host, token, tenant_client_reference_id) print("Tenant Id:", tenant_id) # Replace tokens with values modified_file_contents = import_file_contents.replace("[USERNAME]", database_user_name) \ .replace("[TENANT_NAME]", tenant_client_reference_id) \ .replace("[ENCRYPTED_PASSWORD]", encrypted_password) \ .replace("[SERVER_ADDRESS]", server_address) \ .replace("[DATABASE_NAME]", database_name) multipart_entity = MultipartEncoder( fields={ 'contentToProcess': ('YFExport.xml', modified_file_contents, 'application/xml'), 'importOptions': json.dumps([ {"itemIndex": 0, "optionKey": "SKIP", "optionValue": False}, {"itemIndex": 0, "optionKey": "OPTION", "optionValue": "ADD"} ]) } ) headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': multipart_entity.content_type, 'cache-control': 'no-cache' } response = requests.post(f'{host}/api/rpc/import-export/import-content', headers=headers, data=multipart_entity.to_string()) print(f"Content-Type: {multipart_entity.content_type}") print(response.text) except RequestException as e: print(f"Request Exception: {e}") def generate_token(host, username, password, tenant): request_body = json.dumps({ "userName": username, "password": password, "clientOrgRef": tenant }) headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } response = requests.post(f'{host}/api/refresh-tokens', headers=headers, data=request_body) if response.status_code == 200: json_response = response.json() access_token = json_response["_embedded"]["accessToken"]["securityToken"] print("Access Token:", access_token) return access_token else: raise Exception("Token not retrieved successfully") def retrieve_tenant_id_for_tenant_name(host, token, tenant_code): headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } response = requests.get(f'{host}/api/orgs', headers=headers) if response.status_code == 200: json_data = response.json() items = json_data.get('items', []) for item in items: if 'clientRefId' in item and item['clientRefId'] == tenant_code: return item['ipOrg'] print(f"Tenant could not be found for code: {tenant_code}") exit(-1) else: raise RequestException(f"Failed to retrieve tenant id. Status code: {response.status_code}") class MultipartEncoder: def __init__(self, fields): self.boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW' self.content_type = f'multipart/form-data; boundary={self.boundary}' self.data = self.encode(fields) def encode(self, fields): lines = [] for key, value in fields.items(): lines.append(f'--{self.boundary}') if isinstance(value, tuple): # file data filename, data, content_type = value lines.append(f'Content-Disposition: form-data; name="{key}"; filename="{filename}"') lines.append(f'Content-Type: {content_type}') lines.append('') lines.append(data) else: # regular form field lines.append(f'Content-Disposition: form-data; name="{key}"') lines.append('') lines.append(value) lines.append(f'--{self.boundary}--') lines.append('') return '\r\n'.join(lines) def to_string(self): return self.data import_file_contents = '''<?xml version="1.0" encoding="UTF-8"?><!-- Yellowfin export file --><!-- Generated at 2024-06-07 12:56 AEST (+1000) --> <data> <info> <exportversion>4</exportversion> <exportsubversion>54</exportsubversion> <transferHeaderUUID>c072afce-d02a-4b2c-ae06-c3b62c8d6ea8</transferHeaderUUID> <appversion>9.11</appversion> <appsubversion>0.3</appsubversion> <buildversion>20240607</buildversion> <exportdate>2024-06-07 12:56 AEST (+1000)</exportdate> <systemid>9e/Rsd4Jf6UwZGq/5Q8Ej1Giviw=</systemid> </info> <source> <id>132641</id> <sourceName>PostgreSQL Connection for [TENANT_NAME]</sourceName> <sourceDescription>PostgreSQL Connection for [TENANT_NAME]</sourceDescription> <databasePath>public</databasePath> <userName>[USERNAME]</userName> <password>[ENCRYPTED_PASSWORD]</password> <connectionTypeCode>GENERICUSER</connectionTypeCode> <connectionMethodCode>JDBC</connectionMethodCode> <connectionDriver>org.postgresql.Driver</connectionDriver> <databaseURL>jdbc:postgresql://[SERVER_ADDRESS]:5432/[DATABASE_NAME]></databaseURL> <databaseTypeCode>POSTGRESQL</databaseTypeCode> <minConnections>1</minConnections> <maxConnections>5</maxConnections> <connectionRefreshTime>180</connectionRefreshTime> <connectionTimeOut>180</connectionTimeOut> <accessCode>UNSECURE</accessCode> <maxRowCount>10000</maxRowCount> <maxAnalysisRowCount>0</maxAnalysisRowCount> <broadcastPermitted>true</broadcastPermitted> <subscribePermitted>true</subscribePermitted> <dataProfileEnabled>true</dataProfileEnabled> <localTimezoneCode>AUSTRALIA/LORD_HOWE</localTimezoneCode> <secondaryMinConnections>0</secondaryMinConnections> <secondaryMaxConnections>0</secondaryMaxConnections> <secondaryConnectionRefreshTime>0</secondaryConnectionRefreshTime> <secondaryConnectionTimeOut>0</secondaryConnectionTimeOut> <lastModifiedGMTDateTime>20240607025556.000000</lastModifiedGMTDateTime> <sourceUUID>7d7543c0-e9c7-4a80-ab14-73181e4d0694</sourceUUID> <filterList/> <sourceParameterList> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>DATABASE</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[DATABASE_NAME]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>HOSTNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>[SERVER_ADDRESS]</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>ISOLATIONLEVEL</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue/> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>PORT</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>5432</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>SOURCECLASSNAME</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>com.hof.sources.JDBCSourcePlatformImplementation</parameterValue> <documentId>0</documentId> <userVisible>false</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>USESCHEMA</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>true</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> <sourceParameter> <id>0</id> <parameterTypeCode>TEXT</parameterTypeCode> <parameterKey>YF_DRIVER_SELECTION</parameterKey> <parameterIndex>0</parameterIndex> <parameterValue>org.postgresql.Driver</parameterValue> <documentId>0</documentId> <userVisible>true</userVisible> </sourceParameter> </sourceParameterList> </source> <translationDictionary/> <refCodeDictionary/> </data> ''' if __name__ == "__main__": main()
Create a new datasource via the datasource service
A datasource can be created directly via the REST API using the POST /api/data-sources end-point, Create Data Source.
This takes a datasource model of this format:
{ "sourceName": "Client Database", "sourceDescription": ", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionString": "jdbc:postgresql://192.168.1.100:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/SYDNEY", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false, "sourceOptions": [ { "optionKey": "ISOLATIONLEVEL", "optionValue": "1.0", "valueDataType": "1" }, { "optionKey": "USESCHEMA", "optionValue": "true", "valueDataType": "6" }, { "optionKey": "HOSTNAME", "optionValue": "192.168.1.100", "valueDataType": "2" }, { "optionKey": "PORT", "optionValue": "5432", "valueDataType": "1" }, { "optionKey": "DATABASE", "optionValue": "testdata", "valueDataType": "2" }, { "optionKey": "YF_DRIVER_SELECTION", "optionValue": "org.postgresql.Driver", "valueDataType": "2" } ] }
The main body of the model is similar to the model returned by the GET /api/data-sources end-point. If creating a new source similar to the Primary Organization datasource, the data returned by GET /api/data-sources can be used as a template.
The sourceOptions in the creation model differ depending on the database type and are representative of the options that are exposed by the connection wizard when connecting to a new datasource.
It should be noted that although the connectionString is available in the model body, this is regenerated based on the source options when the datasource is created.
The following examples illustrate how to create a new datasource via the POST /api/data-sources end-point.
package rest.code.examples; import java.io.IOException; import java.util.Random; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * Create a datasource using the Yellowfin REST API */ public class CreateADataSource { public static void main(String[] args) throws Exception { String host = "http://localhost:8080/yellowfinHead"; String restUsername = "admin@yellowfin.com.au"; String restPassword = "test"; String createDataSourcePayload = "{\n" + " \"sourceName\": \"PostgreSQL Database Created Via Import\",\n" + " \"sourceDescription\": \"\",\n" + " \"sourceType\": \"POSTGRESQL\",\n" + " \"connectionType\": \"JDBC\",\n" + " \"connectionTypeCode\": \"GENERICUSER\",\n" + " \"connectionDriver\": \"org.postgresql.Driver\",\n" + " \"connectionString\": \"jdbc:postgresql://192.168.1.100:5432/testdata\",\n" + " \"connectionTimeout\": 180,\n" + " \"userName\": \"postgres\",\n" + " \"minimumConnections\": 1,\n" + " \"maximumConnections\": 5,\n" + " \"refreshTime\": 180,\n" + " \"timezone\": \"AUSTRALIA/SYDNEY\",\n" + " \"accessLevelCode\": \"UNSECURE\",\n" + " \"maxRows\": 10000,\n" + " \"maxAnalysisRows\": 0,\n" + " \"inheritChildSourceFilters\": false,\n" + " \"sourceLogIndicator\": false,\n" + " \"sourceOptions\": [\n" + "{\n" + "\"optionKey\": \"ISOLATIONLEVEL\",\n" + "\"optionValue\": \"1.0\",\n" + "\"valueDataType\": \"1\"\n" + "},\n" + "{\n" + "\"optionKey\": \"USESCHEMA\",\n" + "\"optionValue\": \"true\",\n" + "\"valueDataType\": \"6\"\n" + "},\n" + "{\n" + "\"optionKey\": \"HOSTNAME\",\n" + "\"optionValue\": \"192.168.1.100\",\n" + "\"valueDataType\": \"2\"\n" + "},\n" + "{\n" + "\"optionKey\": \"PORT\",\n" + "\"optionValue\": \"5432\",\n" + "\"valueDataType\": \"1\"\n" + "},\n" + "{\n" + "\"optionKey\": \"DATABASE\",\n" + "\"optionValue\": \"testdata\",\n" + "\"valueDataType\": \"2\"\n" + "},\n" + "{\n" + "\"optionKey\": \"YF_DRIVER_SELECTION\",\n" + "\"optionValue\": \"org.postgresql.Driver\",\n" + "\"valueDataType\": \"2\"\n" + "}\n" + " ]\n" + "}" ; String token = generateToken(host, restUsername, restPassword); System.out.println("Payload: " + createDataSourcePayload); Content c = Request.post(host + "/api/data-sources") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .bodyString(createDataSourcePayload, null) .execute().returnContent(); System.out.print(c.asString()); } public static String generateToken(String host, String username, String password) throws IOException { Content c = Request.post(host + "/api/refresh-tokens") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong()) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .bodyString("{ \"userName\": \""+ username + "\",\"password\": \""+ password + "\"}", null) .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement accessToken = jsonObject.getAsJsonObject("_embedded").getAsJsonObject("accessToken").get("securityToken"); if (accessToken!=null) { System.out.println("Access Token: " + accessToken); } else { System.out.println("Token not retrieved successfully"); System.exit(-1); } return accessToken.getAsString(); } }
using System.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace YellowfinAPIExamples { public class CreateADataSource { static async Task Main(string[] args) { string host = "http://localhost:8080/Yellowfin"; string restUsername = "admin@yellowfin.com.au"; string restPassword = "test"; string createDataSourcePayload = @" { ""sourceName"": ""PostgreSQL Database Created Via Import"", ""sourceDescription"": """", ""sourceType"": ""POSTGRESQL"", ""connectionType"": ""JDBC"", ""connectionTypeCode"": ""GENERICUSER"", ""connectionDriver"": ""org.postgresql.Driver"", ""connectionString"": ""jdbc:postgresql://192.168.1.100:5432/testdata"", ""connectionTimeout"": 180, ""userName"": ""postgres"", ""minimumConnections"": 1, ""maximumConnections"": 5, ""refreshTime"": 180, ""timezone"": ""AUSTRALIA/SYDNEY"", ""accessLevelCode"": ""UNSECURE"", ""maxRows"": 10000, ""maxAnalysisRows"": 0, ""inheritChildSourceFilters"": false, ""sourceLogIndicator"": false, ""sourceOptions"": [ { ""optionKey"": "ISOLATIONLEVEL"", ""optionValue"": ""1.0"", ""valueDataType"": ""1"" }, { ""optionKey"": ""USESCHEMA"", ""optionValue"": ""true"", ""valueDataType"": ""6"" }, { ""optionKey"": ""HOSTNAME"", ""optionValue"": ""192.168.1.100"", ""valueDataType"": ""2"" }, { ""optionKey"": ""PORT"", ""optionValue"": ""5432"", ""valueDataType"": ""1"" }, { ""optionKey"": ""DATABASE"", ""optionValue"": ""testdata"", ""valueDataType"": ""2"" }, { ""optionKey"": ""YF_DRIVER_SELECTION"", ""optionValue"": ""org.postgresql.Driver"", ""valueDataType"": ""2"" } ] }"; string token = await GenerateToken(host, restUsername, restPassword); Console.WriteLine("Payload: " + createDataSourcePayload); using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={new Random().NextInt64()}, token={token}"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); var content = new StringContent(createDataSourcePayload, System.Text.Encoding.UTF8, "application/json"); HttpResponseMessage response = await httpClient.PostAsync($"{host}/api/data-sources", content); if (response.IsSuccessStatusCode) { string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } else { Console.WriteLine("Failed to create data source. Status code: " + response.StatusCode); } } } static async Task<string> GenerateToken(string host, string restUsername, string restPassword) { using (var client = new HttpClient()) { // Generate nonce long nonce = new Random().NextInt64(); // Create HTTP request var request = new HttpRequestMessage(HttpMethod.Post, $"{host}/api/refresh-tokens"); request.Headers.Add("Authorization", $"YELLOWFIN ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}"); request.Headers.Add("Accept", "application/vnd.yellowfin.api-v1+json"); request.Content = new StringContent( JsonConvert.SerializeObject(new { userName = restUsername, password = restPassword }), System.Text.Encoding.UTF8, "application/json" ); // Send request and get response HttpResponseMessage response = await client.SendAsync(request); string responseContent = await response.Content.ReadAsStringAsync(); // Parse JSON response JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseContent); string accessToken = jsonObject["_embedded"]["accessToken"]["securityToken"].ToString(); if (!string.IsNullOrEmpty(accessToken)) { Console.WriteLine("Access Token: " + accessToken); } else { Console.WriteLine("Token not retrieved"); Environment.Exit(-1); } return accessToken; } } } }
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "time" ) func main() { host := "http://localhost:8080/Yellowfin" restUsername := "admin@yellowfin.com.au" restPassword := "test" createDataSourcePayload := `{ "sourceName": "PostgreSQL Database Created Via Import", "sourceDescription": "", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionString": "jdbc:postgresql://192.168.1.100:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/SYDNEY", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false, "sourceOptions": [ { "optionKey": "ISOLATIONLEVEL", "optionValue": "1.0", "valueDataType": "1" }, { "optionKey": "USESCHEMA", "optionValue": "true", "valueDataType": "6" }, { "optionKey": "HOSTNAME", "optionValue": "192.168.1.100", "valueDataType": "2" }, { "optionKey": "PORT", "optionValue": "5432", "valueDataType": "1" }, { "optionKey": "DATABASE", "optionValue": "testdata", "valueDataType": "2" }, { "optionKey": "YF_DRIVER_SELECTION", "optionValue": "org.postgresql.Driver", "valueDataType": "2" } ] }` token, err := generateToken(host, restUsername, restPassword) if err != nil { fmt.Println("Error generating token:", err) return } fmt.Println("Payload:", createDataSourcePayload) client := &http.Client{} req, err := http.NewRequest("POST", host+"/api/data-sources", bytes.NewBuffer([]byte(createDataSourcePayload))) if err != nil { fmt.Println("Error creating request:", err) return } nonce := rand.Int63() req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return } fmt.Println(string(body)) } func generateToken(host, restUsername, restPassword string) (string, error) { nonce := rand.Int63() requestBody, err := json.Marshal(map[string]string{ "userName": restUsername, "password": restPassword, }) if err != nil { fmt.Println("Error marshaling request body:", err) return "", err } client := &http.Client{} req, err := http.NewRequest("POST", host+"/api/refresh-tokens", bytes.NewBuffer(requestBody)) if err != nil { fmt.Println("Error creating request:", err) return "", err } req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d", time.Now().UnixMilli(), nonce)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return "", err } var jsonResponse map[string]interface{} err = json.Unmarshal(body, &jsonResponse) if err != nil { fmt.Println("Error parsing JSON response:", err) return "", err } accessToken, ok := jsonResponse["_embedded"].(map[string]interface{})["accessToken"].(map[string]interface{})["securityToken"].(string) if !ok { fmt.Println("Token not retrieved") return "", fmt.Errorf("Token not retrieved successfully") } return accessToken, nil }
const fetch = require("node-fetch"); async function main() { const host = "http://localhost:8080/Yellowfin"; const restUsername = "admin@yellowfin.com.au"; const restPassword = "test"; const createDataSourcePayload = `{ "sourceName": "PostgreSQL Database Created Via Import", "sourceDescription": "", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionString": "jdbc:postgresql://192.168.1.100:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/SYDNEY", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false, "sourceOptions": [ { "optionKey": "ISOLATIONLEVEL", "optionValue": "1.0", "valueDataType": "1" }, { "optionKey": "USESCHEMA", "optionValue": "true", "valueDataType": "6" }, { "optionKey": "HOSTNAME", "optionValue": "192.168.1.100", "valueDataType": "2" }, { "optionKey": "PORT", "optionValue": "5432", "valueDataType": "1" }, { "optionKey": "DATABASE", "optionValue": "testdata", "valueDataType": "2" }, { "optionKey": "YF_DRIVER_SELECTION", "optionValue": "org.postgresql.Driver", "valueDataType": "2" } ] }`; const token = await generateToken(host, restUsername, restPassword); if (!token) { console.error("Failed to retrieve access token"); return; } console.log("Payload:", createDataSourcePayload); const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${token}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; try { const response = await fetch(`${host}/api/data-sources`, { method: 'POST', headers: headers, body: createDataSourcePayload }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const responseBody = await response.text(); console.log(responseBody); } catch (error) { console.error("Error:", error.message); } } async function generateToken(host, restUsername, restPassword) { const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; const body = JSON.stringify({ userName: restUsername, password: restPassword }); try { const response = await fetch(`${host}/api/refresh-tokens`, { method: 'POST', headers: headers, body: body }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const jsonResponse = await response.json(); const accessToken = jsonResponse._embedded.accessToken.securityToken; if (accessToken) { console.log(`Access Token: ${accessToken}`); } else { console.log("Token not retrieved"); } return accessToken; } catch (error) { console.error("Error:", error.message); } return null; } main();
<?php function main() { $host = "http://localhost:8080/Yellowfin"; $restUsername = "admin@yellowfin.com.au"; $restPassword = "test"; $createDataSourcePayload = '{ "sourceName": "PostgreSQL Database Created Via Import", "sourceDescription": "", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionString": "jdbc:postgresql://192.168.1.100:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/SYDNEY", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false, "sourceOptions": [ { "optionKey": "ISOLATIONLEVEL", "optionValue": "1.0", "valueDataType": "1" }, { "optionKey": "USESCHEMA", "optionValue": "true", "valueDataType": "6" }, { "optionKey": "HOSTNAME", "optionValue": "192.168.1.100", "valueDataType": "2" }, { "optionKey": "PORT", "optionValue": "5432", "valueDataType": "1" }, { "optionKey": "DATABASE", "optionValue": "testdata", "valueDataType": "2" }, { "optionKey": "YF_DRIVER_SELECTION", "optionValue": "org.postgresql.Driver", "valueDataType": "2" } ] }'; try { $token = generateToken($host, $restUsername, $restPassword); } catch (Exception $e) { echo "Error generating token: " . $e->getMessage(); return; } echo "Payload: " . $createDataSourcePayload . "\n"; $nonce = mt_rand(); $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce . ', token=' . $token, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ); try { $response = httpRequest('POST', "$host/api/data-sources", $headers, $createDataSourcePayload); echo $response; } catch (Exception $e) { echo "Error sending request: " . $e->getMessage(); } } function generateToken($host, $restUsername, $restPassword) { // Generate nonce $nonce = mt_rand(); // Create request body $requestBody = json_encode(array( "userName" => $restUsername, "password" => $restPassword )); // Create request headers $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ); $response = httpRequest('POST', "$host/api/refresh-tokens", $headers, $requestBody); // Parse JSON response $jsonResponse = json_decode($response, true); // Get access token from response if (isset($jsonResponse["_embedded"]["accessToken"]["securityToken"])) { $accessToken = $jsonResponse["_embedded"]["accessToken"]["securityToken"]; echo "Access Token: " . $accessToken; return $accessToken; } else { throw new Exception("Token not retrieved successfully"); } } function httpRequest($method, $url, $headers, $data = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); if ($data !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($ch); if (curl_errno($ch)) { throw new Exception('Error: ' . curl_error($ch)); } curl_close($ch); return $response; } main(); ?>
import json import random import time import requests def main(): host = "http://localhost:8080/Yellowfin" rest_username = "admin@yellowfin.com.au" rest_password = "test" create_data_source_payload = ''' { "sourceName": "PostgreSQL Database Created Via Import", "sourceDescription": "", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionString": "jdbc:postgresql://192.168.1.100:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/SYDNEY", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false, "sourceOptions": [ { "optionKey": "ISOLATIONLEVEL", "optionValue": "1.0", "valueDataType": "1" }, { "optionKey": "USESCHEMA", "optionValue": "true", "valueDataType": "6" }, { "optionKey": "HOSTNAME", "optionValue": "192.168.1.100", "valueDataType": "2" }, { "optionKey": "PORT", "optionValue": "5432", "valueDataType": "1" }, { "optionKey": "DATABASE", "optionValue": "testdata", "valueDataType": "2" }, { "optionKey": "YF_DRIVER_SELECTION", "optionValue": "org.postgresql.Driver", "valueDataType": "2" } ] } ''' try: token = generate_token(host, rest_username, rest_password) except Exception as e: print(f"Error generating token: {e}") return print("Payload:", create_data_source_payload) nonce = random.randint(0, 2**63 - 1) headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={nonce}, token={token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } try: response = requests.post(f"{host}/api/data-sources", headers=headers, data=create_data_source_payload) response.raise_for_status() print(response.text) except requests.RequestException as e: print(f"Error sending request: {e}") def generate_token(host, rest_username, rest_password): nonce = random.randint(0, 2**63 - 1) # Create request body request_body = json.dumps({ "userName": rest_username, "password": rest_password }) # Create request headers headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={nonce}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } try: response = requests.post(f"{host}/api/refresh-tokens", headers=headers, data=request_body) response.raise_for_status() json_response = response.json() access_token = json_response["_embedded"]["accessToken"]["securityToken"] print("Access Token:", access_token) return access_token except requests.RequestException as e: raise Exception("Token not retrieved successfully") from e if __name__ == "__main__": main()
List Data Sources
A list of datasources can be retrieved with the GET /api/data-sources end-point, Get All Data Sources. This will return a datasource model of the following form:
{ "sourceId": 132780, "sourceName": "PostgreSQL Connection for NEWCLIENT", "sourceDescription": "PostgreSQL Connection for NEWCLIENT", "sourceType": "POSTGRESQL", "connectionType": "JDBC", "connectionTypeCode": "GENERICUSER", "connectionDriver": "org.postgresql.Driver", "connectionPath": "public", "connectionString": "jdbc:postgresql://localhost:5432/testdata", "connectionTimeout": 180, "userName": "postgres", "minimumConnections": 1, "maximumConnections": 5, "refreshTime": 180, "timezone": "AUSTRALIA/LORD_HOWE", "accessLevelCode": "UNSECURE", "maxRows": 10000, "maxAnalysisRows": 0, "inheritChildSourceFilters": false, "sourceLogIndicator": false }
This service can be used to find the internal integer identifier for the datasource (sourceId) for a given data source name.
The following code examples illustrate how to retrieve a list of data sources from the system.
package rest.code.examples; import java.io.IOException; import java.util.Random; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * List DataSources using the Yellowfin REST API */ public class ListDataSources { public static void main(String[] args) throws Exception { System.out.print("List Datasources"); String host = "http://localhost:8080/Yellowfin"; String restUsername = "admin@yellowfin.com.au"; String restPassword = "test"; String token = generateToken(host, restUsername, restPassword); Content c = Request.get(host + "/api/data-sources") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .execute().returnContent(); System.out.print(c.asString()); } /* * This function generates an access token for a user that will grant them access to * call REST API endpoints. */ public static String generateToken(String host, String username, String password) throws IOException { Content c = Request.post(host + "/api/refresh-tokens") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong()) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .bodyString("{ \"userName\": \""+ username + "\",\"password\": \""+ password + "\"}", null) .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement accessToken = jsonObject.getAsJsonObject("_embedded").getAsJsonObject("accessToken").get("securityToken"); if (accessToken!=null) { System.out.println("Access Token: " + accessToken); } else { System.out.println("Token not retrieved successfully"); System.exit(-1); } return accessToken.getAsString(); } }
using System.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace YellowfinAPIExamples { public class ListDataSources { static async Task Main(string[] args) { string host = "http://localhost:8080/Yellowfin"; string restUsername = "admin@yellowfin.com.au"; string restPassword = "test"; string token = await GenerateToken(host, restUsername, restPassword); Console.WriteLine("List Datasources"); using (var httpClient = new HttpClient()) { long nonce = new Random().NextInt64(); // Setup request headers httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}, token={token}"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); // Make GET request to retrieve data sources HttpResponseMessage response = await httpClient.GetAsync($"{host}/api/data-sources"); if (response.IsSuccessStatusCode) { string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } else { Console.WriteLine($"Failed to retrieve data sources. Status code: {response.StatusCode}"); } } } static async Task<string> GenerateToken(string host, string username, string password) { using (var client = new HttpClient()) { // Generate nonce long nonce = new Random().NextInt64(); // Create HTTP request var request = new HttpRequestMessage(HttpMethod.Post, $"{host}/api/refresh-tokens"); request.Headers.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); request.Content = new StringContent( JsonConvert.SerializeObject(new { userName = username, password = password }), System.Text.Encoding.UTF8, "application/json" ); // Send request and get response HttpResponseMessage response = await client.SendAsync(request); string responseContent = await response.Content.ReadAsStringAsync(); // Parse JSON response JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseContent); string accessToken = jsonObject["_embedded"]["accessToken"]["securityToken"].ToString(); if (!string.IsNullOrEmpty(accessToken)) { Console.WriteLine("Access Token: " + accessToken); } else { Console.WriteLine("Token not retrieved successfully"); Environment.Exit(-1); } return accessToken; } } } }
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "time" ) func main() { host := "http://localhost:8080/Yellowfin" restUsername := "admin@yellowfin.com.au" restPassword := "test" token, err := generateToken(host, restUsername, restPassword) if err != nil { fmt.Println("Error generating token:", err) return } fmt.Println("List Datasources") client := &http.Client{} req, err := http.NewRequest("GET", host+"/api/data-sources", nil) if err != nil { fmt.Println("Error creating request:", err) return } nonce := rand.Int63() req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return } fmt.Println(string(body)) } func generateToken(host, username, password string) (string, error) { // Generate nonce nonce := rand.Int63() // Create request body requestBody, err := json.Marshal(map[string]string{ "userName": username, "password": password, }) if err != nil { fmt.Println("Error marshaling request body:", err) return "", err } // Create HTTP client client := &http.Client{} // Create HTTP request request, err := http.NewRequest("POST", host+"/api/refresh-tokens", bytes.NewBuffer(requestBody)) if err != nil { fmt.Println("Error creating request:", err) return "", err } // Add request headers request.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d", time.Now().UnixMilli(), nonce)) request.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") request.Header.Set("Content-Type", "application/json") // Send HTTP request response, err := client.Do(request) if err != nil { fmt.Println("Error sending request:", err) return "", err } defer response.Body.Close() // Read response body responseBody, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Println("Error reading response body:", err) return "", err } // Parse JSON response var jsonResponse map[string]interface{} err = json.Unmarshal(responseBody, &jsonResponse) if err != nil { fmt.Println("Error parsing JSON response:", err) return "", err } // Get access token from response accessToken, ok := jsonResponse["_embedded"].(map[string]interface{})["accessToken"].(map[string]interface{})["securityToken"].(string) if !ok { fmt.Println("Token not retrieved") return "", fmt.Errorf("Token not retrieved successfully") } return accessToken, nil }
const fetch = require("node-fetch"); async function main() { const host = "http://localhost:8080/Yellowfin"; const restUsername = "admin@yellowfin.com.au"; const restPassword = "test"; const createUserPayload = JSON.stringify([{ userId: "user1", emailAddress: "user1@yellowfin.com.au", roleCode: "Consumer & Collaborator", password: "test", firstName: "User", lastName: "One", languageCode: "EN", timeZoneCode: "AUSTRALIA/SYDNEY" }]); const token = await generateToken(host, restUsername, restPassword); if (!token) { console.error("Failed to retrieve access token"); return; } console.log("Payload:", createUserPayload); const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${token}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; try { const response = await fetch(`${host}/api/data-sources`, { method: 'GET', headers: headers }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const responseBody = await response.text(); console.log(responseBody); } catch (error) { console.error("Error:", error.message); } } async function generateToken(host, restUsername, restPassword) { // Generate nonce const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); // Create request headers const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; // Create request body const body = JSON.stringify({ userName: restUsername, password: restPassword }); try { // Make POST request const response = await fetch(`${host}/api/refresh-tokens`, { method: 'POST', headers: headers, body: body }); // Check if request was successful if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } // Parse JSON response const jsonResponse = await response.json(); const accessToken = jsonResponse._embedded.accessToken.securityToken; if (accessToken) { console.log(`Access Token: ${accessToken}`); } else { console.log("Token not retrieved"); } return accessToken; } catch (error) { console.error("Error:", error.message); } return null; } main();
<?php function main() { $host = "http://localhost:8080/Yellowfin"; $restUsername = "admin@yellowfin.com.au"; $restPassword = "test"; $createUserPayload = json_encode(array( array( "userId" => "user1", "emailAddress" => "user1@yellowfin.com.au", "roleCode" => "Consumer & Collaborator", "password" => "test", "firstName" => "User", "lastName" => "One", "languageCode" => "EN", "timeZoneCode" => "AUSTRALIA/SYDNEY" ) )); try { $token = generateToken($host, $restUsername, $restPassword); } catch (Exception $e) { echo "Error generating token: " . $e->getMessage(); return; } echo "Payload: " . $createUserPayload . "\n"; $nonce = mt_rand(); $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce . ', token=' . $token, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ); try { $response = httpRequest('GET', "$host/api/data-sources", $headers); echo $response; } catch (Exception $e) { echo "Error listing data sources: " . $e->getMessage(); } } function generateToken($host, $restUsername, $restPassword) { // Generate nonce $nonce = mt_rand(); // Create request body $requestBody = json_encode(array( "userName" => $restUsername, "password" => $restPassword )); // Create request headers $headers = array( 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ); $response = httpRequest('POST', "$host/api/refresh-tokens", $headers, $requestBody); // Parse JSON response $jsonResponse = json_decode($response, true); // Get access token from response if (isset($jsonResponse["_embedded"]["accessToken"]["securityToken"])) { $accessToken = $jsonResponse["_embedded"]["accessToken"]["securityToken"]; echo "Access Token: " . $accessToken; return $accessToken; } else { throw new Exception("Token not retrieved successfully"); } } function httpRequest($method, $url, $headers, $data = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); if ($data !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($ch); if (curl_errno($ch)) { throw new Exception('Error: ' . curl_error($ch)); } curl_close($ch); return $response; } main(); ?>
import json import random import time import requests def main(): host = "http://localhost:8080/Yellowfin" rest_username = "admin@yellowfin.com.au" rest_password = "test" try: token = generate_token(host, rest_username, rest_password) list_data_sources(host, token) except Exception as e: print(f"Error: {e}") def list_data_sources(host, token): headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } try: response = requests.get(f'{host}/api/data-sources', headers=headers) response.raise_for_status() print(response.text) except requests.RequestException as e: print(f"Error listing data sources: {e}") def generate_token(host, username, password): nonce = random.randint(0, 2 ** 63 - 1) request_body = json.dumps({ "userName": username, "password": password }) headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={nonce}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } try: response = requests.post(f'{host}/api/refresh-tokens', headers=headers, data=request_body) response.raise_for_status() json_response = response.json() access_token = json_response["_embedded"]["accessToken"]["securityToken"] print("Access Token:", access_token) return access_token except requests.RequestException as e: print(f"Token retrieval error: {e}") raise Exception("Token not retrieved successfully") if __name__ == "__main__": main()
Attach a Client Datasource to a Primary Org Datasource for Source Substitution
A Client Datasource can be linked to a Primary Org Datasource (for use with Source Substitution) using the POST /api/data-sources/{primaryOrgSourceId}/client-data-sources/?clientSourceId={clientOrgSourceId} end-point, Add Client Data Source.
Where {primaryOrgSourceId} is the integer identifier for the primary organization datasource, and {clientOrgSourceId} is the integer identifier for the client org datasource to link.
The following examples illustrate how to link a Client Datasource (identified by its name) to a Primary Org Datasource (identified by its name).
package rest.code.examples; import java.io.IOException; import java.util.Random; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * Attach a Client Datasource to Primary Org Source for Source Substitution using the Yellowfin REST API */ public class AttachClientDatasourceToPrimarySource { public static void main(String[] args) throws Exception { System.out.print("Attach a Tenant Source to Primary Org Source for Source Substitution"); String host = "http://localhost:8080/Yellowfin"; String restUsername = "admin@yellowfin.com.au"; String restPassword = "test"; String primarySourceName = "PostgreSQL Connection at Primary Org"; String clientSourceName = "PostgreSQL Connection for NEWCLIENT"; String clientOrgReference = "NEWCLIENT"; String clientToken = generateToken(host, restUsername, restPassword, clientOrgReference); Integer clientOrgSourceId = findDataSourceForSourceNameAtClient(host, clientToken, clientSourceName); System.out.println("Client Org Source Id: " + clientOrgSourceId); String primaryToken = generateToken(host, restUsername, restPassword, null); Integer primaryOrgSourceId = findDataSourceForSourceNameAtClient(host, primaryToken, primarySourceName); System.out.println("Primary Org Source Id: " + clientOrgSourceId); Content c = Request.get(host + "/api/data-sources/" + primaryOrgSourceId + "/client-data-sources/?clientSourceId=" + clientOrgSourceId) .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + primaryToken) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .execute().returnContent(); System.out.print(c.asString()); } /* * This function finds a datasource for a given datasource name * call REST API endpoints. */ private static Integer findDataSourceForSourceNameAtClient(String host, String token, String sourceName) throws IOException { Content c = Request.get(host + "/api/data-sources") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement sourceList = jsonObject.get("items"); JsonArray sources = sourceList.getAsJsonArray(); for (int i=0; i < sources.size(); i++ ) { JsonObject source = sources.getAsJsonArray().get(i).getAsJsonObject(); if (sourceName.equals(source.get("sourceName").getAsString())) return source.get("sourceId").getAsInt(); } System.out.println("Data Source could not be found for name:" + sourceName); System.exit(-1); return null; } /* * This function generates an access token for a user that will grant them access to * call REST API endpoints. */ public static String generateToken(String host, String username, String password, String tenantClientOrgRefId) throws IOException { String tokenBody = "{ \"userName\": \""+ username + "\",\"password\": \""+ password + "\""; if (tenantClientOrgRefId!=null) { tokenBody = tokenBody + ",\"clientOrgRef\": \""+ tenantClientOrgRefId + "\""; } tokenBody = tokenBody + "}"; Content c = Request.post(host + "/api/refresh-tokens") .addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong()) .addHeader("Accept", "application/vnd.yellowfin.api-v1+json") .addHeader("Content-Type", "application/json") .bodyString(tokenBody, null) .execute().returnContent(); JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject(); JsonElement accessToken = jsonObject.getAsJsonObject("_embedded").getAsJsonObject("accessToken").get("securityToken"); if (accessToken!=null) { System.out.println("Access Token: " + accessToken); } else { System.out.println("Token not retrieved successfully"); System.exit(-1); } return accessToken.getAsString(); } }
using System.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace YellowfinAPIExamples { public class AttachClientDatasourceToPrimarySource { static async Task Main(string[] args) { Console.WriteLine("Attach a Tenant Source to Primary Org Source for Source Substitution"); string host = "http://localhost:8080/Yellowfin"; string restUsername = "admin@yellowfin.com.au"; string restPassword = "test"; string primarySourceName = "PostgreSQL Connection at Primary Org"; string clientSourceName = "PostgreSQL Connection for NEWCLIENT"; string clientOrgReference = "NEWCLIENT"; string clientToken = await GenerateToken(host, restUsername, restPassword, clientOrgReference); int clientOrgSourceId = await FindDataSourceForSourceNameAtClient(host, clientToken, clientSourceName); Console.WriteLine("Client Org Source Id: " + clientOrgSourceId); string primaryToken = await GenerateToken(host, restUsername, restPassword, null); int primaryOrgSourceId = await FindDataSourceForSourceNameAtClient(host, primaryToken, primarySourceName); Console.WriteLine("Primary Org Source Id: " + primaryOrgSourceId); using (var httpClient = new HttpClient()) { long nonce = new Random().NextInt64(); // Setup request headers httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}, token={primaryToken}"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); // Make GET request to attach client datasource to primary org source HttpResponseMessage response = await httpClient.GetAsync($"{host}/api/data-sources/{primaryOrgSourceId}/client-data-sources/?clientSourceId={clientOrgSourceId}"); if (response.IsSuccessStatusCode) { string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } else { Console.WriteLine($"Failed to attach client datasource. Status code: {response.StatusCode}"); } } } static async Task<int> FindDataSourceForSourceNameAtClient(string host, string token, string sourceName) { using (var httpClient = new HttpClient()) { // Setup request headers httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={new Random().NextInt64()}, token={token}"); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json")); // Make GET request to fetch data sources HttpResponseMessage response = await httpClient.GetAsync($"{host}/api/data-sources"); if (response.IsSuccessStatusCode) { string responseBody = await response.Content.ReadAsStringAsync(); // Parse JSON response JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseBody); JArray sources = jsonObject["items"].ToObject<JArray>(); // Iterate through sources to find the correct one foreach (JObject source in sources) { if (sourceName.Equals(source["sourceName"].ToString(), StringComparison.OrdinalIgnoreCase)) { return source["sourceId"].ToObject<int>(); } } } Console.WriteLine($"Data Source could not be found for name: {sourceName}"); Environment.Exit(-1); return -1; // Ideally unreachable, since Environment.Exit should terminate the process } } static async Task<string> GenerateToken(string host, string username, string password, string tenantClientOrgRefId) { using (var client = new HttpClient()) { // Generate nonce long nonce = new Random().NextInt64(); // Create HTTP request var request = new HttpRequestMessage(HttpMethod.Post, $"{host}/api/refresh-tokens"); request.Headers.Add("Authorization", $"YELLOWFIN ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}"); request.Headers.Add("Accept", "application/vnd.yellowfin.api-v1+json"); request.Content = new StringContent( JsonConvert.SerializeObject(new { userName = username, password = password, clientOrgRef = tenantClientOrgRefId }), System.Text.Encoding.UTF8, "application/json" ); // Send request and get response HttpResponseMessage response = await client.SendAsync(request); string responseContent = await response.Content.ReadAsStringAsync(); // Parse JSON response JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseContent); string accessToken = jsonObject["_embedded"]["accessToken"]["securityToken"].ToString(); if (!string.IsNullOrEmpty(accessToken)) { Console.WriteLine("Access Token: " + accessToken); } else { Console.WriteLine("Token not retrieved successfully"); Environment.Exit(-1); } return accessToken; } } } }
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "time" ) func main() { fmt.Println("Attach a Tenant Source to Primary Org Source for Source Substitution") host := "http://localhost:8080/Yellowfin" restUsername := "admin@yellowfin.com.au" restPassword := "test" primarySourceName := "PostgreSQL Connection at Primary Org" clientSourceName := "PostgreSQL Connection for NEWCLIENT" clientOrgReference := "NEWCLIENT" clientToken, err := generateToken(host, restUsername, restPassword, clientOrgReference) if err != nil { fmt.Println("Error generating client token:", err) return } clientOrgSourceId, err := findDataSourceForSourceNameAtClient(host, clientToken, clientSourceName) if err != nil { fmt.Println("Error finding client data source:", err) return } fmt.Println("Client Org Source Id:", clientOrgSourceId) primaryToken, err := generateToken(host, restUsername, restPassword, "") if err != nil { fmt.Println("Error generating primary token:", err) return } primaryOrgSourceId, err := findDataSourceForSourceNameAtClient(host, primaryToken, primarySourceName) if err != nil { fmt.Println("Error finding primary data source:", err) return } fmt.Println("Primary Org Source Id:", primaryOrgSourceId) client := &http.Client{} nonce := rand.Int63() req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/data-sources/%d/client-data-sources/?clientSourceId=%d", host, primaryOrgSourceId, clientOrgSourceId), nil) if err != nil { fmt.Println("Error creating request:", err) return } req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, primaryToken)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return } fmt.Println(string(body)) } func findDataSourceForSourceNameAtClient(host, token, sourceName string) (int, error) { client := &http.Client{} nonce := rand.Int63() req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/data-sources", host), nil) if err != nil { return 0, fmt.Errorf("Error creating request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return 0, fmt.Errorf("Error sending request: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return 0, fmt.Errorf("Error reading response body: %v", err) } var jsonResponse map[string]interface{} err = json.Unmarshal(body, &jsonResponse) if err != nil { return 0, fmt.Errorf("Error parsing JSON response: %v", err) } items := jsonResponse["items"].([]interface{}) for _, item := range items { source := item.(map[string]interface{}) if sourceName == source["sourceName"].(string) { sourceId := int(source["sourceId"].(float64)) return sourceId, nil } } return 0, fmt.Errorf("Data Source could not be found for name: %s", sourceName) } func generateToken(host, username, password, tenantClientOrgRefId string) (string, error) { nonce := rand.Int63() tokenBody := map[string]string{ "userName": username, "password": password, } if tenantClientOrgRefId != "" { tokenBody["clientOrgRef"] = tenantClientOrgRefId } requestBody, err := json.Marshal(tokenBody) if err != nil { return "", fmt.Errorf("Error marshaling request body: %v", err) } client := &http.Client{} req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/refresh-tokens", host), bytes.NewBuffer(requestBody)) if err != nil { return "", fmt.Errorf("Error creating request: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d", time.Now().UnixMilli(), nonce)) req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json") req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("Error sending request: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("Error reading response body: %v", err) } var jsonResponse map[string]interface{} err = json.Unmarshal(body, &jsonResponse) if err != nil { return "", fmt.Errorf("Error parsing JSON response: %v", err) } embedded := jsonResponse["_embedded"].(map[string]interface{}) accessToken := embedded["accessToken"].(map[string]interface{})["securityToken"].(string) if accessToken == "" { return "", fmt.Errorf("Token not retrieved successfully") } fmt.Println("Access Token:", accessToken) return accessToken, nil }
const fetch = require("node-fetch"); async function main() { console.log("Attach a Tenant Source to Primary Org Source for Source Substitution"); const host = "http://localhost:8080/Yellowfin"; const restUsername = "admin@yellowfin.com.au"; const restPassword = "test"; const primarySourceName = "PostgreSQL Connection at Primary Org"; const clientSourceName = "PostgreSQL Connection for NEWCLIENT"; const clientOrgReference = "NEWCLIENT"; const clientToken = await generateToken(host, restUsername, restPassword, clientOrgReference); const clientOrgSourceId = await findDataSourceForSourceNameAtClient(host, clientToken, clientSourceName); console.log("Client Org Source Id:", clientOrgSourceId); const primaryToken = await generateToken(host, restUsername, restPassword, null); const primaryOrgSourceId = await findDataSourceForSourceNameAtClient(host, primaryToken, primarySourceName); console.log("Primary Org Source Id:", primaryOrgSourceId); const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${primaryToken}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; try { const response = await fetch(`${host}/api/data-sources/${primaryOrgSourceId}/client-data-sources/?clientSourceId=${clientOrgSourceId}`, { method: 'GET', headers: headers }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const responseBody = await response.text(); console.log(responseBody); } catch (error) { console.error("Error:", error.message); } } async function findDataSourceForSourceNameAtClient(host, token, sourceName) { const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}, token=${token}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; try { const response = await fetch(`${host}/api/data-sources`, { method: 'GET', headers: headers }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const jsonResponse = await response.json(); const sources = jsonResponse.items; for (let i = 0; i < sources.length; i++) { const source = sources[i]; if (sourceName === source.sourceName) { return source.sourceId; } } console.log("Data Source could not be found for name:", sourceName); process.exit(-1); // Exit if data source not found } catch (error) { console.error("Error:", error.message); process.exit(-1); // Exit on error } } async function generateToken(host, username, password, tenantClientOrgRefId) { const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const headers = { 'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}`, 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' }; const requestBody = { userName: username, password: password }; if (tenantClientOrgRefId !== null) { requestBody.clientOrgRef = tenantClientOrgRefId; } try { const response = await fetch(`${host}/api/refresh-tokens`, { method: 'POST', headers: headers, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const jsonResponse = await response.json(); const accessToken = jsonResponse._embedded.accessToken.securityToken; if (accessToken) { console.log(`Access Token: ${accessToken}`); } else { console.log("Token not retrieved successfully"); process.exit(-1); // Exit if token not retrieved } return accessToken; } catch (error) { console.error("Error:", error.message); process.exit(-1); // Exit on error } } main();
<?php function main() { $host = "http://localhost:8080/Yellowfin"; $restUsername = "admin@yellowfin.com.au"; $restPassword = "test"; echo "Attach a Tenant Source to Primary Org Source for Source Substitution\n"; $primarySourceName = "PostgreSQL Connection at Primary Org"; $clientSourceName = "PostgreSQL Connection for NEWCLIENT"; $clientOrgReference = "NEWCLIENT"; try { $clientToken = generateToken($host, $restUsername, $restPassword, $clientOrgReference); $clientOrgSourceId = findDataSourceForSourceNameAtClient($host, $clientToken, $clientSourceName); echo "Client Org Source Id: $clientOrgSourceId\n"; $primaryToken = generateToken($host, $restUsername, $restPassword, null); $primaryOrgSourceId = findDataSourceForSourceNameAtClient($host, $primaryToken, $primarySourceName); echo "Primary Org Source Id: $primaryOrgSourceId\n"; $headers = [ 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . mt_rand() . ', token=' . $primaryToken, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ]; $url = "$host/api/data-sources/$primaryOrgSourceId/client-data-sources/?clientSourceId=$clientOrgSourceId"; $response = httpRequest('GET', $url, $headers); echo $response; } catch (Exception $e) { echo "Error: " . $e->getMessage(); } } function generateToken($host, $restUsername, $restPassword, $tenantClientOrgRefId = null) { $nonce = mt_rand(); $tokenBody = "{ \"userName\": \"$restUsername\", \"password\": \"$restPassword\""; if ($tenantClientOrgRefId !== null) { $tokenBody .= ", \"clientOrgRef\": \"$tenantClientOrgRefId\""; } $tokenBody .= "}"; $headers = [ 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ]; $response = httpRequest('POST', "$host/api/refresh-tokens", $headers, $tokenBody); $jsonResponse = json_decode($response, true); if (isset($jsonResponse["_embedded"]["accessToken"]["securityToken"])) { return $jsonResponse["_embedded"]["accessToken"]["securityToken"]; } else { throw new Exception("Token not retrieved successfully"); } } function findDataSourceForSourceNameAtClient($host, $token, $sourceName) { $headers = [ 'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . mt_rand() . ', token=' . $token, 'Accept: application/vnd.yellowfin.api-v1+json', 'Content-Type: application/json' ]; $response = httpRequest('GET', "$host/api/data-sources", $headers); $jsonObject = json_decode($response); if (isset($jsonObject->items)) { foreach ($jsonObject->items as $source) { if ($source->sourceName === $sourceName) { return $source->sourceId; } } } echo "Data Source could not be found for name: $sourceName\n"; exit(-1); } function httpRequest($method, $url, $headers, $data = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); if ($data !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($ch); if (curl_errno($ch)) { throw new Exception('Error: ' . curl_error($ch)); } curl_close($ch); return $response; } main(); ?>
import random import time import requests def main(): print("Attach a Tenant Source to Primary Org Source for Source Substitution") host = "http://localhost:8080/Yellowfin" rest_username = "admin@yellowfin.com.au" rest_password = "test" primary_source_name = "PostgreSQL Connection at Primary Org" client_source_name = "PostgreSQL Connection for NEWCLIENT" client_org_reference = "NEWCLIENT" try: client_token = generate_token(host, rest_username, rest_password, client_org_reference) client_org_source_id = find_data_source_for_source_name_at_client(host, client_token, client_source_name) print("Client Org Source Id:", client_org_source_id) primary_token = generate_token(host, rest_username, rest_password, None) primary_org_source_id = find_data_source_for_source_name_at_client(host, primary_token, primary_source_name) print("Primary Org Source Id:", primary_org_source_id) headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={primary_token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } url = f'{host}/api/data-sources/{primary_org_source_id}/client-data-sources/?clientSourceId={client_org_source_id}' response = requests.get(url, headers=headers) response.raise_for_status() print(response.text) except Exception as e: print(f"Error: {e}") def find_data_source_for_source_name_at_client(host, token, source_name): headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={token}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } url = f'{host}/api/data-sources' response = requests.get(url, headers=headers) response.raise_for_status() json_data = response.json() items = json_data.get('items', []) for source in items: if source['sourceName'] == source_name: return source['sourceId'] raise Exception(f"Data Source could not be found for name: {source_name}") def generate_token(host, username, password, tenant_client_org_ref_id): body = { "userName": username, "password": password } if tenant_client_org_ref_id: body['clientOrgRef'] = tenant_client_org_ref_id headers = { 'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}', 'Accept': 'application/vnd.yellowfin.api-v1+json', 'Content-Type': 'application/json' } response = requests.post(f'{host}/api/refresh-tokens', headers=headers, json=body) response.raise_for_status() json_response = response.json() access_token = json_response["_embedded"]["accessToken"]["securityToken"] print("Access Token:", access_token) return access_token if __name__ == "__main__": main()