Add ability to disable operations per-table, etc. check for insert & update (single) errors better;

This commit is contained in:
2023-04-06 12:39:25 -05:00
parent 12a92c6330
commit b5c5fb7dd8
4 changed files with 380 additions and 51 deletions

View File

@ -39,6 +39,7 @@ import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
import com.kingsrook.qqq.api.model.actions.GetTableApiFieldsInput; import com.kingsrook.qqq.api.model.actions.GetTableApiFieldsInput;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData; import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData; import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
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;
@ -334,16 +335,26 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
boolean insertCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_INSERT); boolean insertCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_INSERT);
boolean countCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_COUNT); // todo - look at this - if table doesn't have count, don't include it in its input/output, etc boolean countCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_COUNT); // todo - look at this - if table doesn't have count, don't include it in its input/output, etc
if(!queryCapability && !getCapability && !updateCapability && !deleteCapability && !insertCapability) List<ApiOperation.EnabledOperationsProvider> operationProviders = List.of(apiInstanceMetaData, apiTableMetaData);
boolean getEnabled = ApiOperation.GET.isOperationEnabled(operationProviders) && getCapability;
boolean queryByQueryStringEnabled = ApiOperation.QUERY_BY_QUERY_STRING.isOperationEnabled(operationProviders) && queryCapability;
boolean insertEnabled = ApiOperation.UPDATE.isOperationEnabled(operationProviders) && insertCapability;
boolean insertBulkEnabled = ApiOperation.BULK_INSERT.isOperationEnabled(operationProviders) && insertCapability;
boolean updateEnabled = ApiOperation.INSERT.isOperationEnabled(operationProviders) && updateCapability;
boolean updateBulkEnabled = ApiOperation.BULK_UPDATE.isOperationEnabled(operationProviders) && updateCapability;
boolean deleteEnabled = ApiOperation.DELETE.isOperationEnabled(operationProviders) && deleteCapability;
boolean deleteBulkEnabled = ApiOperation.BULK_DELETE.isOperationEnabled(operationProviders) && deleteCapability;
if(!getEnabled && !queryByQueryStringEnabled && !insertEnabled && !insertBulkEnabled && !updateEnabled && !updateBulkEnabled && !deleteEnabled && !deleteBulkEnabled)
{ {
LOG.debug("Omitting table [" + tableName + "] because it does not have any supported capabilities"); LOG.debug("Omitting table [" + tableName + "] because it does not have any supported capabilities / enabled operations");
continue; continue;
} }
String tableApiName = StringUtils.hasContent(apiTableMetaData.getApiTableName()) ? apiTableMetaData.getApiTableName() : tableName; String tableApiName = StringUtils.hasContent(apiTableMetaData.getApiTableName()) ? apiTableMetaData.getApiTableName() : tableName;
String tableApiNameUcFirst = StringUtils.ucFirst(tableApiName); String tableApiNameUcFirst = StringUtils.ucFirst(tableApiName);
String tableLabel = table.getLabel(); String tableLabel = table.getLabel();
String primaryKeyName = table.getPrimaryKeyField();
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField()); QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
String primaryKeyLabel = primaryKeyField.getLabel(); String primaryKeyLabel = primaryKeyField.getLabel();
String primaryKeyApiName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, primaryKeyField); String primaryKeyApiName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, primaryKeyField);
@ -396,15 +407,18 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// 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) //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
componentSchemas.put(tableApiName + "SearchResult", new Schema() if(queryByQueryStringEnabled)
.withType("object") {
.withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/baseSearchResultFields"))) componentSchemas.put(tableApiName + "SearchResult", new Schema()
.withProperties(MapBuilder.of( .withType("object")
"records", new Schema() .withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/baseSearchResultFields")))
.withType("array") .withProperties(MapBuilder.of(
.withItems(new Schema() "records", new Schema()
.withAllOf(ListBuilder.of( .withType("array")
new Schema().withRef("#/components/schemas/" + tableApiName))))))); .withItems(new Schema()
.withAllOf(ListBuilder.of(
new Schema().withRef("#/components/schemas/" + tableApiName)))))));
}
// todo...? // todo...?
// includeAssociatedOrderLines=false&includeAssociatedExtrinsics=false&includeAssociatedOrderLinesExtrinsics // includeAssociatedOrderLines=false&includeAssociatedExtrinsics=false&includeAssociatedOrderLinesExtrinsics
@ -474,7 +488,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableReadPermissionName)); .withSecurity(getSecurity(tableReadPermissionName));
if(queryCapability) if(queryByQueryStringEnabled)
{ {
openAPI.getPaths().put(basePath + tableApiName + "/query", new Path() openAPI.getPaths().put(basePath + tableApiName + "/query", new Path()
// todo!! .withPost(queryPost) // todo!! .withPost(queryPost)
@ -541,12 +555,12 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully deleted the requested " + tableLabel)) .withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully deleted the requested " + tableLabel))
.withSecurity(getSecurity(tableDeletePermissionName)); .withSecurity(getSecurity(tableDeletePermissionName));
if(getCapability || updateCapability || deleteCapability) if(getEnabled || updateEnabled || deleteEnabled)
{ {
openAPI.getPaths().put(basePath + tableApiName + "/{" + primaryKeyApiName + "}", new Path() openAPI.getPaths().put(basePath + tableApiName + "/{" + primaryKeyApiName + "}", new Path()
.withGet(getCapability ? idGet : null) .withGet(getEnabled ? idGet : null)
.withPatch(updateCapability ? idPatch : null) .withPatch(updateEnabled ? idPatch : null)
.withDelete(deleteCapability ? idDelete : null) .withDelete(deleteEnabled ? idDelete : null)
); );
} }
@ -572,7 +586,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableInsertPermissionName)); .withSecurity(getSecurity(tableInsertPermissionName));
if(insertCapability) if(insertEnabled)
{ {
openAPI.getPaths().put(basePath + tableApiName + "/", new Path() openAPI.getPaths().put(basePath + tableApiName + "/", new Path()
.withPost(slashPost)); .withPost(slashPost));
@ -636,12 +650,12 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableDeletePermissionName)); .withSecurity(getSecurity(tableDeletePermissionName));
if(insertCapability || updateCapability || deleteCapability) if(insertBulkEnabled || updateBulkEnabled || deleteBulkEnabled)
{ {
openAPI.getPaths().put(basePath + tableApiName + "/bulk", new Path() openAPI.getPaths().put(basePath + tableApiName + "/bulk", new Path()
.withPost(insertCapability ? bulkPost : null) .withPost(insertBulkEnabled ? bulkPost : null)
.withPatch(updateCapability ? bulkPatch : null) .withPatch(updateBulkEnabled ? bulkPatch : null)
.withDelete(deleteCapability ? bulkDelete : null)); .withDelete(deleteBulkEnabled ? bulkDelete : null));
} }
} }

View File

@ -45,6 +45,7 @@ import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
import com.kingsrook.qqq.api.model.metadata.APILogMetaDataProvider; import com.kingsrook.qqq.api.model.metadata.APILogMetaDataProvider;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData; import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
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.actions.permissions.PermissionsHelper; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
@ -63,6 +64,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException; import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException; import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.LogPair;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
@ -350,14 +352,14 @@ public class QJavalinApiHandler
try try
{ {
setupSession(context, null, null); setupSession(context, null, null, null);
} }
catch(Exception e) catch(Exception e)
{ {
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// if we don't have a session, we won't be able to store the api log... // // if we don't have a session, we won't be able to store the api log... //
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
LOG.debug("No session in a 404; will not create api log", e); LOG.info("No session in a 404; will not create api log", e);
} }
handleException(context, new QNotFoundException("Could not find any resources at path " + context.path()), apiLog); handleException(context, new QNotFoundException("Could not find any resources at path " + context.path()), apiLog);
@ -620,10 +622,15 @@ public class QJavalinApiHandler
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public static void setupSession(Context context, AbstractActionInput input, String version) throws QModuleDispatchException, QAuthenticationException public static void setupSession(Context context, AbstractActionInput input, String version, ApiInstanceMetaData apiInstanceMetaData) throws QModuleDispatchException, QAuthenticationException
{ {
QSession session = QJavalinImplementation.setupSession(context, input); QSession session = QJavalinImplementation.setupSession(context, input);
session.setValue("apiVersion", version); session.setValue("apiVersion", version);
if(apiInstanceMetaData != null)
{
session.setValue("apiName", apiInstanceMetaData.getName());
session.setValue("apiLabel", apiInstanceMetaData.getLabel());
}
} }
@ -640,12 +647,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.GET);
String tableName = table.getName(); String tableName = table.getName();
GetInput getInput = new GetInput(); GetInput getInput = new GetInput();
setupSession(context, getInput, version); setupSession(context, getInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiGet", logPair("table", tableName), logPair("primaryKey", primaryKey)); QJavalinAccessLogger.logStart("apiGet", logPair("table", tableName), logPair("primaryKey", primaryKey));
getInput.setTableName(tableName); getInput.setTableName(tableName);
@ -801,7 +808,7 @@ public class QJavalinApiHandler
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// if it wasn't found from a Get, then try an Insert // // if it wasn't found from a Get, then try an Insert //
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
LOG.debug("Inserting " + tableName + " named " + userName); LOG.info("Inserting " + tableName + " named " + userName);
InsertInput insertInput = new InsertInput(); InsertInput insertInput = new InsertInput();
insertInput.setTableName(tableName); insertInput.setTableName(tableName);
QRecord record = new QRecord().withValue("name", userName); QRecord record = new QRecord().withValue("name", userName);
@ -836,7 +843,7 @@ public class QJavalinApiHandler
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// assume this may mean a dupe-key - so - try another fetch below // // assume this may mean a dupe-key - so - try another fetch below //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
LOG.debug("Caught error inserting " + tableName + " named " + userName + " - will try to re-fetch", e); LOG.info("Caught error inserting " + tableName + " named " + userName + " - will try to re-fetch", e);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -894,11 +901,11 @@ public class QJavalinApiHandler
{ {
List<String> badRequestMessages = new ArrayList<>(); List<String> badRequestMessages = new ArrayList<>();
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.QUERY_BY_QUERY_STRING);
String tableName = table.getName(); String tableName = table.getName();
QueryInput queryInput = new QueryInput(); QueryInput queryInput = new QueryInput();
setupSession(context, queryInput, version); setupSession(context, queryInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiQuery", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiQuery", logPair("table", tableName));
queryInput.setTableName(tableName); queryInput.setTableName(tableName);
@ -1123,36 +1130,54 @@ public class QJavalinApiHandler
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static QTableMetaData validateTableAndVersion(Context context, ApiInstanceMetaData apiInstanceMetaData, String version, String tableApiName) throws QNotFoundException private static QTableMetaData validateTableAndVersion(Context context, ApiInstanceMetaData apiInstanceMetaData, String version, String tableApiName, ApiOperation operation) throws QNotFoundException
{ {
QNotFoundException qNotFoundException = new QNotFoundException("Could not find any resources at path " + context.path()); QNotFoundException qNotFoundException = new QNotFoundException("Could not find any resources at path " + context.path());
QTableMetaData table = getTableByApiName(apiInstanceMetaData.getName(), version, tableApiName); QTableMetaData table = getTableByApiName(apiInstanceMetaData.getName(), version, tableApiName);
LogPair[] logPairs = new LogPair[] { logPair("apiName", apiInstanceMetaData.getName()), logPair("version", version), logPair("tableApiName", tableApiName), logPair("operation", operation) };
if(table == null) if(table == null)
{ {
LOG.info("404 because table is null", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
if(BooleanUtils.isTrue(table.getIsHidden())) if(BooleanUtils.isTrue(table.getIsHidden()))
{ {
LOG.info("404 because table isHidden", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table); ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
if(apiTableMetaDataContainer == null) if(apiTableMetaDataContainer == null)
{ {
LOG.info("404 because table apiMetaDataContainer is null", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(apiInstanceMetaData.getName()); ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(apiInstanceMetaData.getName());
if(apiTableMetaData == null) if(apiTableMetaData == null)
{ {
LOG.info("404 because table apiMetaData is null", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
if(BooleanUtils.isTrue(apiTableMetaData.getIsExcluded())) if(BooleanUtils.isTrue(apiTableMetaData.getIsExcluded()))
{ {
LOG.info("404 because table is excluded", logPairs);
throw (qNotFoundException);
}
if(!operation.isOperationEnabled(List.of(apiInstanceMetaData, apiTableMetaData)))
{
LOG.info("404 because api operation is not enabled", logPairs);
throw (qNotFoundException);
}
if(!table.isCapabilityEnabled(qInstance.getBackendForTable(table.getName()), operation.getCapability()))
{
LOG.info("404 because table capability is not enabled", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
@ -1160,11 +1185,13 @@ public class QJavalinApiHandler
List<APIVersion> supportedVersions = apiInstanceMetaData.getSupportedVersions(); List<APIVersion> supportedVersions = apiInstanceMetaData.getSupportedVersions();
if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion)) if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion))
{ {
LOG.info("404 because requested version is not supported", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
if(!apiTableMetaData.getApiVersionRange().includes(requestApiVersion)) if(!apiTableMetaData.getApiVersionRange().includes(requestApiVersion))
{ {
LOG.info("404 because table version range does not include requested version", logPairs);
throw (qNotFoundException); throw (qNotFoundException);
} }
@ -1351,12 +1378,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.INSERT);
String tableName = table.getName(); String tableName = table.getName();
InsertInput insertInput = new InsertInput(); InsertInput insertInput = new InsertInput();
setupSession(context, insertInput, version); setupSession(context, insertInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiInsert", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiInsert", logPair("table", tableName));
insertInput.setTableName(tableName); insertInput.setTableName(tableName);
@ -1392,6 +1419,22 @@ public class QJavalinApiHandler
InsertAction insertAction = new InsertAction(); InsertAction insertAction = new InsertAction();
InsertOutput insertOutput = insertAction.execute(insertInput); InsertOutput insertOutput = insertAction.execute(insertInput);
List<String> errors = insertOutput.getRecords().get(0).getErrors();
if(CollectionUtils.nullSafeHasContents(errors))
{
boolean isBadRequest = areAnyErrorsBadRequest(errors);
String message = "Error inserting " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors);
if(isBadRequest)
{
throw (new QBadRequestException(message));
}
else
{
throw (new QException(message));
}
}
LinkedHashMap<String, Serializable> outputRecord = new LinkedHashMap<>(); LinkedHashMap<String, Serializable> outputRecord = new LinkedHashMap<>();
outputRecord.put(table.getPrimaryKeyField(), insertOutput.getRecords().get(0).getValue(table.getPrimaryKeyField())); outputRecord.put(table.getPrimaryKeyField(), insertOutput.getRecords().get(0).getValue(table.getPrimaryKeyField()));
@ -1410,6 +1453,20 @@ public class QJavalinApiHandler
/*******************************************************************************
**
*******************************************************************************/
private static boolean areAnyErrorsBadRequest(List<String> errors)
{
boolean isBadRequest = errors.stream().anyMatch(e ->
e.contains("Missing value in required field")
|| e.contains("You do not have permission")
);
return isBadRequest;
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -1421,12 +1478,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.BULK_INSERT);
String tableName = table.getName(); String tableName = table.getName();
InsertInput insertInput = new InsertInput(); InsertInput insertInput = new InsertInput();
setupSession(context, insertInput, version); setupSession(context, insertInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiBulkInsert", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiBulkInsert", logPair("table", tableName));
insertInput.setTableName(tableName); insertInput.setTableName(tableName);
@ -1530,12 +1587,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.BULK_UPDATE);
String tableName = table.getName(); String tableName = table.getName();
UpdateInput updateInput = new UpdateInput(); UpdateInput updateInput = new UpdateInput();
setupSession(context, updateInput, version); setupSession(context, updateInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiBulkUpdate", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiBulkUpdate", logPair("table", tableName));
updateInput.setTableName(tableName); updateInput.setTableName(tableName);
@ -1672,12 +1729,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.BULK_DELETE);
String tableName = table.getName(); String tableName = table.getName();
DeleteInput deleteInput = new DeleteInput(); DeleteInput deleteInput = new DeleteInput();
setupSession(context, deleteInput, version); setupSession(context, deleteInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiBulkDelete", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiBulkDelete", logPair("table", tableName));
deleteInput.setTableName(tableName); deleteInput.setTableName(tableName);
@ -1804,12 +1861,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.UPDATE);
String tableName = table.getName(); String tableName = table.getName();
UpdateInput updateInput = new UpdateInput(); UpdateInput updateInput = new UpdateInput();
setupSession(context, updateInput, version); setupSession(context, updateInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiUpdate", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiUpdate", logPair("table", tableName));
updateInput.setTableName(tableName); updateInput.setTableName(tableName);
@ -1856,10 +1913,17 @@ public class QJavalinApiHandler
} }
else else
{ {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// boolean isBadRequest = areAnyErrorsBadRequest(errors);
// todo - could be smarter here, about some of these errors being 400, not 500... e.g., a missing required field //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String message = "Error updating " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors);
throw (new QException("Error updating " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors))); if(isBadRequest)
{
throw (new QBadRequestException(message));
}
else
{
throw (new QException(message));
}
} }
} }
@ -1888,12 +1952,12 @@ public class QJavalinApiHandler
try try
{ {
QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName); QTableMetaData table = validateTableAndVersion(context, apiInstanceMetaData, version, tableApiName, ApiOperation.DELETE);
String tableName = table.getName(); String tableName = table.getName();
DeleteInput deleteInput = new DeleteInput(); DeleteInput deleteInput = new DeleteInput();
setupSession(context, deleteInput, version); setupSession(context, deleteInput, version, apiInstanceMetaData);
QJavalinAccessLogger.logStart("apiDelete", logPair("table", tableName)); QJavalinAccessLogger.logStart("apiDelete", logPair("table", tableName));
deleteInput.setTableName(tableName); deleteInput.setTableName(tableName);

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.api.model.metadata; package com.kingsrook.qqq.api.model.metadata;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -41,7 +42,7 @@ import org.apache.commons.lang.BooleanUtils;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class ApiInstanceMetaData public class ApiInstanceMetaData implements ApiOperation.EnabledOperationsProvider
{ {
private String name; private String name;
private String label; private String label;
@ -56,6 +57,9 @@ public class ApiInstanceMetaData
private List<Server> servers; private List<Server> servers;
private Set<ApiOperation> enabledOperations = new HashSet<>();
private Set<ApiOperation> disabledOperations = new HashSet<>();
private boolean includeErrorTooManyRequests = true; private boolean includeErrorTooManyRequests = true;
@ -466,4 +470,124 @@ public class ApiInstanceMetaData
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public Set<ApiOperation> getEnabledOperations()
{
return (enabledOperations);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Set<ApiOperation> getDisabledOperations()
{
return (disabledOperations);
}
/*******************************************************************************
** Setter for enabledOperations
*******************************************************************************/
public void setEnabledOperations(Set<ApiOperation> enabledOperations)
{
this.enabledOperations = enabledOperations;
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiInstanceMetaData withEnabledOperations(Set<ApiOperation> enabledOperations)
{
this.enabledOperations = enabledOperations;
return (this);
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiInstanceMetaData withEnabledOperation(ApiOperation operation)
{
return withEnabledOperations(operation);
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiInstanceMetaData withEnabledOperations(ApiOperation... operations)
{
if(this.enabledOperations == null)
{
this.enabledOperations = new HashSet<>();
}
if(operations != null)
{
Collections.addAll(this.enabledOperations, operations);
}
return (this);
}
/*******************************************************************************
** Setter for disabledOperations
*******************************************************************************/
public void setDisabledOperations(Set<ApiOperation> disabledOperations)
{
this.disabledOperations = disabledOperations;
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiInstanceMetaData withDisabledOperations(Set<ApiOperation> disabledOperations)
{
this.disabledOperations = disabledOperations;
return (this);
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiInstanceMetaData withDisabledOperation(ApiOperation operation)
{
return withDisabledOperations(operation);
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiInstanceMetaData withDisabledOperations(ApiOperation... operations)
{
if(this.disabledOperations == null)
{
this.disabledOperations = new HashSet<>();
}
if(operations != null)
{
Collections.addAll(this.disabledOperations, operations);
}
return (this);
}
} }

View File

@ -23,9 +23,13 @@ package com.kingsrook.qqq.api.model.metadata.tables;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import com.kingsrook.qqq.api.ApiMiddlewareType; import com.kingsrook.qqq.api.ApiMiddlewareType;
import com.kingsrook.qqq.api.model.APIVersionRange; import com.kingsrook.qqq.api.model.APIVersionRange;
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData; import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -36,7 +40,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class ApiTableMetaData public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
{ {
private String initialVersion; private String initialVersion;
private String finalVersion; private String finalVersion;
@ -46,6 +50,9 @@ public class ApiTableMetaData
private List<QFieldMetaData> removedApiFields; private List<QFieldMetaData> removedApiFields;
private Set<ApiOperation> enabledOperations = new HashSet<>();
private Set<ApiOperation> disabledOperations = new HashSet<>();
/******************************************************************************* /*******************************************************************************
@ -283,4 +290,124 @@ public class ApiTableMetaData
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public Set<ApiOperation> getEnabledOperations()
{
return (enabledOperations);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Set<ApiOperation> getDisabledOperations()
{
return (disabledOperations);
}
/*******************************************************************************
** Setter for enabledOperations
*******************************************************************************/
public void setEnabledOperations(Set<ApiOperation> enabledOperations)
{
this.enabledOperations = enabledOperations;
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiTableMetaData withEnabledOperations(Set<ApiOperation> enabledOperations)
{
this.enabledOperations = enabledOperations;
return (this);
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiTableMetaData withEnabledOperation(ApiOperation operation)
{
return withEnabledOperations(operation);
}
/*******************************************************************************
** Fluent setter for enabledOperations
*******************************************************************************/
public ApiTableMetaData withEnabledOperations(ApiOperation... operations)
{
if(this.enabledOperations == null)
{
this.enabledOperations = new HashSet<>();
}
if(operations != null)
{
Collections.addAll(this.enabledOperations, operations);
}
return (this);
}
/*******************************************************************************
** Setter for disabledOperations
*******************************************************************************/
public void setDisabledOperations(Set<ApiOperation> disabledOperations)
{
this.disabledOperations = disabledOperations;
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiTableMetaData withDisabledOperations(Set<ApiOperation> disabledOperations)
{
this.disabledOperations = disabledOperations;
return (this);
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiTableMetaData withDisabledOperation(ApiOperation operation)
{
return withDisabledOperations(operation);
}
/*******************************************************************************
** Fluent setter for disabledOperations
*******************************************************************************/
public ApiTableMetaData withDisabledOperations(ApiOperation... operations)
{
if(this.disabledOperations == null)
{
this.disabledOperations = new HashSet<>();
}
if(operations != null)
{
Collections.addAll(this.disabledOperations, operations);
}
return (this);
}
} }