Like what you see? Have a play with our trial version.

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:

  1. A datasource needs to be created at the Client Organization. 
  2. 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).

Java
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>"";
}
C#
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>"";
    }
}
Go
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>
`
JavaScript
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
<?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);
?>
Python
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.

Java
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();
   	
   }
  
}
C#
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;
            }
        }
    }
}
Go
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
}
JavaScript
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
<?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();
?>
Python
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.

Java
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();

    }

}
C#
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;
            }
        }
    }
}
Go
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
}
JavaScript
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
<?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();
?>
Python
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).

Java
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();

    }

}
C#
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;
            }
        }
    }
}
Go
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
}
JavaScript
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
<?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();
?>
Python
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()


  • No labels