Recursively add all association component schemas; add recordCount to bulk api action logs

This commit is contained in:
2023-04-05 10:03:10 -05:00
parent 0268dad395
commit 12a92c6330
3 changed files with 128 additions and 23 deletions

View File

@ -27,9 +27,11 @@ import java.time.LocalDate;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.kingsrook.qqq.api.model.APIVersion; import com.kingsrook.qqq.api.model.APIVersion;
import com.kingsrook.qqq.api.model.APIVersionRange; import com.kingsrook.qqq.api.model.APIVersionRange;
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput; import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
@ -181,6 +183,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
* Each input primary key will also be included in the corresponding response object. * Each input primary key will also be included in the corresponding response object.
"""; """;
private Set<String> neededTableSchemas = new HashSet<>();
/******************************************************************************* /*******************************************************************************
@ -386,23 +390,9 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
////////////////////////////////////// //////////////////////////////////////
// build the schemas for this table // // build the schemas for this table //
////////////////////////////////////// //////////////////////////////////////
LinkedHashMap<String, Schema> tableFields = new LinkedHashMap<>(); Schema tableSchema = buildTableSchema(apiInstanceMetaData, table, tableApiFields);
Schema tableSchema = new Schema()
.withType("object")
.withProperties(tableFields);
componentSchemas.put(tableApiName, tableSchema); componentSchemas.put(tableApiName, tableSchema);
for(QFieldMetaData field : tableApiFields)
{
Schema fieldSchema = getFieldSchema(table, field);
tableFields.put(ApiFieldMetaData.getEffectiveApiFieldName(apiInstanceMetaData.getName(), field), fieldSchema);
}
//////////////////////////////////
// recursively add associations //
//////////////////////////////////
addAssociations(apiName, table, tableSchema);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// table as a search result (the base search result, plus the table itself) // // table as a search result (the base search result, plus the table itself) //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -665,6 +655,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
componentResponses.put("error" + HttpStatus.TOO_MANY_REQUESTS.getCode(), buildStandardErrorResponse("Too Many Requests. Your application has issued too many API requests in too short of a time frame.")); componentResponses.put("error" + HttpStatus.TOO_MANY_REQUESTS.getCode(), buildStandardErrorResponse("Too Many Requests. Your application has issued too many API requests in too short of a time frame."));
} }
ensureAllNeededTableSchemasExist(apiInstanceMetaData, version, componentSchemas);
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput(); GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput();
output.setOpenAPI(openAPI); output.setOpenAPI(openAPI);
output.setYaml(YamlUtils.toYaml(openAPI)); output.setYaml(YamlUtils.toYaml(openAPI));
@ -674,6 +666,76 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
/*******************************************************************************
** written for the use-case of, generating a single table's api, but it has
** associations that it references, so we need their schemas too - so, make
** sure they are all added to the componentSchemas map.
*******************************************************************************/
private void ensureAllNeededTableSchemasExist(ApiInstanceMetaData apiInstanceMetaData, String version, LinkedHashMap<String, Schema> componentSchemas) throws QException
{
String apiName = apiInstanceMetaData.getName();
boolean addedAny;
do
{
//////////////////////////////////////////////////////////////////////////
// mmm, kinda odd loops, to avoid concurrent modification, and so-forth //
//////////////////////////////////////////////////////////////////////////
addedAny = false;
for(String neededTableSchema : neededTableSchemas)
{
if(!componentSchemas.containsKey(neededTableSchema))
{
LOG.debug("Adding needed schema: " + neededTableSchema);
QTableMetaData table = QContext.getQInstance().getTable(neededTableSchema);
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(apiName);
String tableApiName = StringUtils.hasContent(apiTableMetaData.getApiTableName()) ? apiTableMetaData.getApiTableName() : table.getName();
List<QFieldMetaData> tableApiFields = new GetTableApiFieldsAction().execute(new GetTableApiFieldsInput()
.withTableName(table.getName())
.withVersion(version)
.withApiName(apiName)).getFields();
componentSchemas.put(tableApiName, buildTableSchema(apiInstanceMetaData, table, tableApiFields));
addedAny = true;
break;
}
}
}
while(addedAny);
}
/*******************************************************************************
**
*******************************************************************************/
private Schema buildTableSchema(ApiInstanceMetaData apiInstanceMetaData, QTableMetaData table, List<QFieldMetaData> tableApiFields)
{
LinkedHashMap<String, Schema> tableFields = new LinkedHashMap<>();
Schema tableSchema = new Schema()
.withType("object")
.withProperties(tableFields);
for(QFieldMetaData field : tableApiFields)
{
Schema fieldSchema = getFieldSchema(table, field);
tableFields.put(ApiFieldMetaData.getEffectiveApiFieldName(apiInstanceMetaData.getName(), field), fieldSchema);
}
//////////////////////////////////
// recursively add associations //
//////////////////////////////////
addAssociations(apiInstanceMetaData.getName(), table, tableSchema);
return (tableSchema);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -800,7 +862,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static void addAssociations(String apiName, QTableMetaData table, Schema tableSchema) private void addAssociations(String apiName, QTableMetaData table, Schema tableSchema)
{ {
for(Association association : CollectionUtils.nonNullList(table.getAssociations())) for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
{ {
@ -809,6 +871,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
ApiTableMetaData associatedApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(associatedTable).getApiTableMetaData(apiName), new ApiTableMetaData()); ApiTableMetaData associatedApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(associatedTable).getApiTableMetaData(apiName), new ApiTableMetaData());
String associatedTableApiName = StringUtils.hasContent(associatedApiTableMetaData.getApiTableName()) ? associatedApiTableMetaData.getApiTableName() : associatedTableName; String associatedTableApiName = StringUtils.hasContent(associatedApiTableMetaData.getApiTableName()) ? associatedApiTableMetaData.getApiTableName() : associatedTableName;
neededTableSchemas.add(associatedTable.getName());
tableSchema.getProperties().put(association.getName(), new Schema() tableSchema.getProperties().put(association.getName(), new Schema()
.withType("array") .withType("array")
.withItems(new Schema().withRef("#/components/schemas/" + associatedTableApiName))); .withItems(new Schema().withRef("#/components/schemas/" + associatedTableApiName)));
@ -823,8 +887,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
private Schema getFieldSchema(QTableMetaData table, QFieldMetaData field) private Schema getFieldSchema(QTableMetaData table, QFieldMetaData field)
{ {
Schema fieldSchema = new Schema() Schema fieldSchema = new Schema()
.withType(getFieldType(table.getField(field.getName()))) .withType(getFieldType(field))
.withFormat(getFieldFormat(table.getField(field.getName()))) .withFormat(getFieldFormat(field))
.withDescription(field.getLabel() + " for the " + table.getLabel() + "."); .withDescription(field.getLabel() + " for the " + table.getLabel() + ".");
if(!field.getIsEditable()) if(!field.getIsEditable())

View File

@ -1504,7 +1504,7 @@ public class QJavalinApiHandler
} }
} }
QJavalinAccessLogger.logEndSuccess(); QJavalinAccessLogger.logEndSuccess(logPair("recordCount", insertInput.getRecords().size()));
context.status(HttpStatus.Code.MULTI_STATUS.getCode()); context.status(HttpStatus.Code.MULTI_STATUS.getCode());
String resultString = JsonUtils.toJson(response); String resultString = JsonUtils.toJson(response);
context.result(resultString); context.result(resultString);
@ -1636,7 +1636,7 @@ public class QJavalinApiHandler
i++; i++;
} }
QJavalinAccessLogger.logEndSuccess(); QJavalinAccessLogger.logEndSuccess(logPair("recordCount", updateInput.getRecords().size()));
context.status(HttpStatus.Code.MULTI_STATUS.getCode()); context.status(HttpStatus.Code.MULTI_STATUS.getCode());
String resultString = JsonUtils.toJson(response); String resultString = JsonUtils.toJson(response);
context.result(resultString); context.result(resultString);
@ -1777,7 +1777,7 @@ public class QJavalinApiHandler
} }
} }
QJavalinAccessLogger.logEndSuccess(); QJavalinAccessLogger.logEndSuccess(logPair("recordCount", deleteInput.getPrimaryKeys().size()));
context.status(HttpStatus.Code.MULTI_STATUS.getCode()); context.status(HttpStatus.Code.MULTI_STATUS.getCode());
String resultString = JsonUtils.toJson(response); String resultString = JsonUtils.toJson(response);
context.result(resultString); context.result(resultString);

View File

@ -25,8 +25,11 @@ package com.kingsrook.qqq.api.actions;
import java.util.Set; import java.util.Set;
import com.kingsrook.qqq.api.BaseTest; import com.kingsrook.qqq.api.BaseTest;
import com.kingsrook.qqq.api.TestUtils; import com.kingsrook.qqq.api.TestUtils;
import com.kingsrook.qqq.api.model.APIVersion;
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput; import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput; import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData; import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
@ -53,8 +56,46 @@ class GenerateOpenApiSpecActionTest extends BaseTest
@Test @Test
void test() throws QException void test() throws QException
{ {
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION).withApiName(TestUtils.API_NAME)); for(ApiInstanceMetaData apiInstanceMetaData : ApiInstanceMetaDataContainer.of(QContext.getQInstance()).getApis().values())
System.out.println(output.getYaml()); {
for(APIVersion supportedVersion : apiInstanceMetaData.getSupportedVersions())
{
//////////////////////////////////////////////////////////////////////
// just making sure we don't throw on any apis in the test instance //
//////////////////////////////////////////////////////////////////////
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput()
.withVersion(supportedVersion.toString())
.withApiName(apiInstanceMetaData.getName()));
// System.out.println(output.getYaml());
}
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testSingleTable() throws QException
{
for(ApiInstanceMetaData apiInstanceMetaData : ApiInstanceMetaDataContainer.of(QContext.getQInstance()).getApis().values())
{
for(APIVersion supportedVersion : apiInstanceMetaData.getSupportedVersions())
{
for(QTableMetaData table : QContext.getQInstance().getTables().values())
{
//////////////////////////////////////////////////////////////////////
// just making sure we don't throw on any apis in the test instance //
//////////////////////////////////////////////////////////////////////
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput()
.withTableName(table.getName())
.withVersion(supportedVersion.toString())
.withApiName(apiInstanceMetaData.getName()));
// System.out.println(output.getYaml());
}
}
}
} }