I have created a Tableau connector 3 with boilerplate earthquake-data and i try to parse from an api a json file instead of the boilerplate's geojson. The api returns json.
The code below is from the Parser file that i changed.
When i import it to Tableau, it gives me an error that means nonthing.
Unable to complete action
Internal Error - An unexpected error occurred and the operation could not be completed.
Error Code: 0213F756
Is there a way to write a json connector?
What could be wrong? Is there a way to test my code? Can you see a reason why this isn't working?
Any help is appreciated.
import { Parser } from '@tableau/taco-toolkit/handlers'
export default class MyParser extends Parser {
parse(fetcherResult, { dataContainer }) {
// Create a containerBuilder
const containerBuilder = Parser.createContainerBuilder(dataContainer);
// Flatten the JSON query results
let tables = Object.keys(fetcherResult.data);
// Loop through each "key" in the data results
tables.forEach((tableName) => {
// Fetch an array of data for the key
let originalData = fetcherResult.data[tableName];
// log(`Parsing data for table: ${tableName}`);
// Flatten the data into key-value pairs
let flattenedData = Object.fromEntries(
Object.entries(originalData).map(([key, value]) => {
const generatedKeyValuePairs = [];
generateNestedKeyNameAndValue(value, "", generatedKeyValuePairs);
return [key, Object.fromEntries(generatedKeyValuePairs)];
})
);
// Loop through each "row" of data and expand any rows with nested arrays
let tableDataArrays = []
for (let rowNumber in Object.keys(flattenedData)){
tableDataArrays.push(rowSplitter(flattenedData[rowNumber]));
}
// That left us with an array of arrays (some w/ 1 row, others with N rows). Need to flatten once more
let tableData = tableDataArrays.flat();
// Get a reference to this table
let { isNew, tableBuilder } = containerBuilder.getTable(tableName);
// Loop over each row in tableData
let columnsDictionary = {};
tableData.forEach( row => {
// Loop over every field in each row (not every row will have the same fields)
Object.entries(row).map(([fieldname, value]) => {
// Have we already determined this fields datatype? and does it have a defined dataType? if the datatype is numeric, what if later values are string?
if (!columnsDictionary[fieldname] || !columnsDictionary[fieldname].dataType || columnsDictionary[fieldname].dataType === DataType.Int || columnsDictionary[fieldname].dataType === DataType.Float){
// This is a new field, record it's data type
columnsDictionary[fieldname] = getTableauDatatype(fieldname, value);
} })})
// Loop through all the columns
let columns = [];
Object.entries(columnsDictionary).map(([columnname, column]) => {
// Since it's possible that some fields we all null, we need to double check and assign them a default value (string)
column.dataType = column.dataType ? column.dataType : DataType.String;
// Push to the columns array
columns.push(column);
})
// If this is a new table, add the columns to the tableBuilder
if (isNew) {
tableBuilder.addColumnHeaders(columns);
}
tableData.map( row => {
// Figure out what keys are already existing in the row
let rowFields = Object.keys(row);
// Are any columns missing?
if (rowFields.length !== columns.length){
columns.map( column => {
if (!(column.id in row)){
if (column.dataType === DataType.Datetime){
log('missing datetime value')
//row[column.id] = convertDatetimeString(null);
} else {
row[column.id] = null;
}
}
})
}
})
tableBuilder.addRows(tableData);
})
return containerBuilder.getDataContainer()
}
}