Skip to content

Commit

Permalink
avniproject#762 | Working code with MetabaseQueryBuilder and Question…
Browse files Browse the repository at this point in the history
…CreationService
  • Loading branch information
ombhardwajj committed Sep 7, 2024
1 parent 910ffb5 commit 8ab39d2
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 125 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.avni.server.dao.metabase;

import org.avni.server.domain.metabase.Collection;
import org.avni.server.domain.metabase.CreateCollectionRequest;
import org.avni.server.domain.metabase.CollectionResponse;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Repository;
Expand All @@ -11,8 +11,8 @@ public CollectionRepository(RestTemplateBuilder restTemplateBuilder) {
super(restTemplateBuilder);
}

public CollectionResponse save(Collection collection) {
public CollectionResponse save(CreateCollectionRequest createCollectionRequest) {
String url = metabaseApiUrl + "/collection";
return postForObject(url, collection, CollectionResponse.class);
return postForObject(url, createCollectionRequest, CollectionResponse.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,59 +26,101 @@ public Database save(Database database) {
return database;
}

public Database getDatabaseByName(String databaseName) {
String url = metabaseApiUrl + "/database";
public Database getDatabaseById(int id) {
String url = metabaseApiUrl + "/database/" + id; // This is the correct endpoint for fetching by ID
try {
// Fetch database details by ID
String jsonResponse = getForObject(url, String.class);
// Parse the JSON response to a Database object
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(jsonResponse, Database.class);
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve database with ID " + id, e);
}
}


public Database getDatabaseByName(String name) {
String url = metabaseApiUrl + "/database"; // Assuming this is the correct endpoint

// Get the response from the API
String jsonResponse = getForObject(url, String.class);

try {
List<Database> databases = objectMapper.readValue(jsonResponse, new TypeReference<List<Database>>() {});
return databases.stream()
.filter(db -> db.getName().equals(databaseName))
.findFirst()
.orElseThrow(() -> new RuntimeException("Database with name " + databaseName + " not found."));
// Parse the response as a JSON object and then retrieve the "data" array
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(jsonResponse);

// Extract the "data" array from the response
JsonNode dataArray = rootNode.path("data");

// Iterate through the databases in the "data" array to find the one with the matching name
for (JsonNode dbNode : dataArray) {
Database db = objectMapper.treeToValue(dbNode, Database.class);
if (db.getName().equals(name)) {
return db;
}
}

// Throw an exception if no matching database is found
throw new RuntimeException("Database with name " + name + " not found.");
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve database", e);
}
}

public MetabaseCollectionInfo getCollectionByName(String collectionName) {
String url = metabaseApiUrl + "/collection";
String jsonResponse = getForObject(url, String.class);

public CollectionInfoResponse getCollectionByName(String collectionName) {
String url = metabaseApiUrl + "/collection";
try {
List<MetabaseCollectionInfo> collections = objectMapper.readValue(jsonResponse, new TypeReference<List<MetabaseCollectionInfo>>() {});
String jsonResponse = getForObject(url, String.class);

ObjectMapper objectMapper = new ObjectMapper();
List<CollectionInfoResponse> collections = objectMapper.readValue(
jsonResponse, new TypeReference<List<CollectionInfoResponse>>() {}
);

return collections.stream()
.filter(coll -> coll.getName().equals(collectionName))
.filter(collection -> collection.getName().equals(collectionName))
.findFirst()
.orElseThrow(() -> new RuntimeException("Collection with name " + collectionName + " not found."));
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve collection", e);
}
}


public void createQuestionForTable(Database database, TableDetails tableDetails, String addressTableName, String addressField, String tableField) throws Exception {
TableDetails addressTableDetails = getTableDetailsByDisplayName(database, addressTableName);
TableDetails addressTableDetails = getTableDetailsByName(database, addressTableName);
FieldDetails joinField1 = getFieldDetailsByName(database, addressTableName, addressField);
FieldDetails joinField2 = getFieldDetailsByName(database, tableDetails.getName(), tableField);

ArrayNode joinsArray = objectMapper.createArrayNode();
MetabaseQuery query = new MetabaseQueryBuilder(database, joinsArray)
MetabaseQuery query = new MetabaseQueryBuilder(database, joinsArray, objectMapper)
.forTable(tableDetails)
.joinWith(addressTableDetails, joinField1, joinField2)
.build();

System.out.println("Request body:");
System.out.println("Database ID: " + database.getId());
System.out.println("Table Name: " + tableDetails.getName());

MetabaseRequestBody requestBody = new MetabaseRequestBody(
"Address + " + tableDetails.getDisplayName(),
query,
VisualizationType.TABLE,
null,
objectMapper.createObjectNode(),
getCollectionByName(database.getName()).getId(),
getCollectionByName(database.getName()).getIdAsInt(),
CardType.QUESTION
);

postForObject(metabaseApiUrl + "/card", requestBody.toJson(objectMapper), JsonNode.class);
}




public FieldDetails getFieldDetailsByName(Database database, String tableName, String fieldName) {
List<FieldDetails> fieldsList = getFields(database);
String snakeCaseTableName = S.toSnakeCase(tableName);
Expand Down Expand Up @@ -111,12 +153,20 @@ public List<FieldDetails> getFields(Database database) {
}
}

public TableDetails getTableDetailsByName(Database database, String tableName) {
MetabaseDatabaseInfo databaseInfo = getDatabaseDetails(database);
return databaseInfo.getTables().stream()
.filter(tableDetail -> tableDetail.getName().equalsIgnoreCase(tableName)) // Matching by 'name'
.findFirst()
.orElseThrow(() -> new RuntimeException("Table with name " + tableName + " not found."));
}

public TableDetails getTableDetailsByDisplayName(Database database, String tableName) {
MetabaseDatabaseInfo databaseInfo = getDatabaseDetails(database);
return databaseInfo.getTables().stream()
.filter(tableDetail -> tableDetail.nameMatches(tableName))
.findFirst()
.orElseThrow(() -> new RuntimeException("Table with name " + tableName + " not found."));
.orElseThrow(() -> new RuntimeException("Table with display name " + tableName + " not found."));
}

public DatabaseSyncStatus getInitialSyncStatus(int databaseId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.avni.server.domain.metabase;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CollectionInfoResponse {

private String name;
private String id;
private boolean isPersonal;

public CollectionInfoResponse() {
}

public CollectionInfoResponse(@JsonProperty("name") String name,
@JsonProperty("id") Object id,
@JsonProperty("is_personal") boolean isPersonal) {
this.name = name;
this.isPersonal = isPersonal;

if (id instanceof Integer) {
this.id = String.valueOf(id);
} else {
this.id = id.toString();
}
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getId() {
return id;
}

public int getIdAsInt() {
try {
return Integer.parseInt(id);
} catch (NumberFormatException e) {
throw new RuntimeException("Failed to convert id to integer: " + id, e);
}
}

public boolean isPersonal() {
return isPersonal;
}

public void setPersonal(boolean personal) {
isPersonal = personal;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.avni.server.domain.metabase;

public class Collection {
public class CreateCollectionRequest {
private String name;
private String description;

public Collection(String name, String description) {
public CreateCollectionRequest(String name, String description) {
this.name = name;
this.description = description;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.avni.server.domain.metabase;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Database {
private Integer id;
private String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.avni.server.domain.metabase;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class DatabaseDetails {
private String host;
private String port;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package org.avni.server.domain.metabase;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class MetabaseQuery {
private final Database database;
private final ArrayNode joins;
private final int databaseId;
private final ObjectNode queryNode;

public MetabaseQuery(Database database, ArrayNode joins) {
this.database = database;
this.joins = joins;
public MetabaseQuery(int databaseId, ObjectNode queryNode) {
this.databaseId = databaseId;
this.queryNode = queryNode;
}

public int getDatabaseId() {
return database.getId();
return databaseId;
}


public ObjectNode toJson(ObjectMapper objectMapper) {
ObjectNode queryNode = objectMapper.createObjectNode();
queryNode.put("database", database.getId());
queryNode.set("joins", joins);
queryNode.put("type", "query");
return queryNode;
public ObjectNode toJson() {
return queryNode; // Return the query node directly
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,57 @@
// to be completed
package org.avni.server.domain.metabase;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class MetabaseQueryBuilder {
private final Database database;
private ArrayNode joins;
private final ArrayNode joinsArray;
private final ObjectMapper objectMapper;
private ObjectNode queryNode;

public MetabaseQueryBuilder(Database database, ArrayNode joins) {
public MetabaseQueryBuilder(Database database, ArrayNode joinsArray, ObjectMapper objectMapper) {
this.database = database;
this.joins = joins;
this.joinsArray = joinsArray;
this.objectMapper = objectMapper;
this.queryNode = objectMapper.createObjectNode();
}

public MetabaseQueryBuilder forTable(TableDetails tableDetails) {
// code to be added
queryNode.put("source-table", tableDetails.getId());
queryNode.put("database", database.getId());
return this;
}


public MetabaseQueryBuilder joinWith(TableDetails joinTable, FieldDetails originField, FieldDetails destinationField) {
// Build the join condition and add to the joins array
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode joinCondition = objectMapper.createArrayNode();

joinCondition.add(ConditionType.EQUAL.getOperator());
joinCondition.add(objectMapper.createArrayNode().add("field").add(originField.getId()));
joinCondition.add(objectMapper.createArrayNode().add("field").add(destinationField.getId()));

joins.add(joinCondition);
public MetabaseQueryBuilder joinWith(TableDetails addressTable, FieldDetails joinField1, FieldDetails joinField2) {
ObjectNode joinNode = objectMapper.createObjectNode();
joinNode.put("fields", "all");
joinNode.put("alias", addressTable.getName());
joinNode.put("source-table", addressTable.getId());

ArrayNode conditionArray = objectMapper.createArrayNode();
conditionArray.add("=");

ArrayNode leftField = objectMapper.createArrayNode();
leftField.add("field");
leftField.add(joinField1.getId());
leftField.add(objectMapper.createObjectNode().put("base-type", "type/Integer"));
conditionArray.add(leftField);

ArrayNode rightField = objectMapper.createArrayNode();
rightField.add("field");
rightField.add(joinField2.getId());
rightField.add(objectMapper.createObjectNode().put("base-type", "type/Integer").put("join-alias", addressTable.getName()));
conditionArray.add(rightField);

joinNode.set("condition", conditionArray);
joinsArray.add(joinNode);
queryNode.set("joins", joinsArray);
return this;
}


public MetabaseQuery build() {
return new MetabaseQuery(database, joins);
queryNode.put("type", "query");
return new MetabaseQuery(database.getId(), queryNode);
}
}
Loading

0 comments on commit 8ab39d2

Please sign in to comment.