mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Add ability to disable operations per-table, etc. check for insert & update (single) errors better;
This commit is contained in:
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user