mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Add table override name, isExcluded;
Remove more hidden, excluded, etc tables; Update to comply with rules of openapi spec;
This commit is contained in:
@ -26,10 +26,14 @@ import java.util.ArrayList;
|
|||||||
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 com.kingsrook.qqq.api.model.APIVersion;
|
||||||
|
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||||
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.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.fields.ApiFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
import com.kingsrook.qqq.api.model.openapi.Components;
|
import com.kingsrook.qqq.api.model.openapi.Components;
|
||||||
import com.kingsrook.qqq.api.model.openapi.Contact;
|
import com.kingsrook.qqq.api.model.openapi.Contact;
|
||||||
import com.kingsrook.qqq.api.model.openapi.Content;
|
import com.kingsrook.qqq.api.model.openapi.Content;
|
||||||
@ -54,9 +58,12 @@ import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
@ -64,6 +71,7 @@ import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
import io.javalin.http.HttpStatus;
|
import io.javalin.http.HttpStatus;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -71,6 +79,9 @@ import io.javalin.http.HttpStatus;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateOpenApiSpecInput, GenerateOpenApiSpecOutput>
|
public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateOpenApiSpecInput, GenerateOpenApiSpecOutput>
|
||||||
{
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(GenerateOpenApiSpecAction.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
@ -101,7 +112,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
openAPI.setTags(new ArrayList<>());
|
openAPI.setTags(new ArrayList<>());
|
||||||
openAPI.setPaths(new LinkedHashMap<>());
|
openAPI.setPaths(new LinkedHashMap<>());
|
||||||
|
|
||||||
LinkedHashMap<Integer, Response> componentResponses = new LinkedHashMap<>();
|
LinkedHashMap<String, Response> componentResponses = new LinkedHashMap<>();
|
||||||
LinkedHashMap<String, Schema> componentSchemas = new LinkedHashMap<>();
|
LinkedHashMap<String, Schema> componentSchemas = new LinkedHashMap<>();
|
||||||
LinkedHashMap<String, SecurityScheme> securitySchemes = new LinkedHashMap<>();
|
LinkedHashMap<String, SecurityScheme> securitySchemes = new LinkedHashMap<>();
|
||||||
openAPI.setComponents(new Components()
|
openAPI.setComponents(new Components()
|
||||||
@ -119,6 +130,16 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withScopes(scopes)
|
.withScopes(scopes)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
securitySchemes.put("bearerAuth", new SecurityScheme()
|
||||||
|
.withType("http")
|
||||||
|
.withScheme("bearer")
|
||||||
|
.withBearerFormat("JWT")
|
||||||
|
);
|
||||||
|
|
||||||
|
securitySchemes.put("basicAuth", new SecurityScheme()
|
||||||
|
.withType("http")
|
||||||
|
.withScheme("basic")
|
||||||
|
);
|
||||||
|
|
||||||
componentSchemas.put("baseSearchResultFields", new Schema()
|
componentSchemas.put("baseSearchResultFields", new Schema()
|
||||||
.withType("object")
|
.withType("object")
|
||||||
@ -140,20 +161,59 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
///////////////////
|
///////////////////
|
||||||
for(QTableMetaData table : qInstance.getTables().values())
|
for(QTableMetaData table : qInstance.getTables().values())
|
||||||
{
|
{
|
||||||
|
String tableName = table.getName();
|
||||||
|
|
||||||
if(table.getIsHidden())
|
if(table.getIsHidden())
|
||||||
{
|
{
|
||||||
|
LOG.debug("Omitting table [" + tableName + "] because it is marked as hidden");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String tableName = table.getName();
|
ApiTableMetaData apiTableMetaData = ApiTableMetaData.of(table);
|
||||||
String tableNameUcFirst = StringUtils.ucFirst(table.getName());
|
if(apiTableMetaData == null)
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + tableName + "] because it does not have any apiTableMetaData");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BooleanUtils.isTrue(apiTableMetaData.getIsExcluded()))
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + tableName + "] because its apiTableMetaData marks it as excluded");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
APIVersionRange apiVersionRange = apiTableMetaData.getApiVersionRange();
|
||||||
|
if(!apiVersionRange.includes(new APIVersion(version)))
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + tableName + "] because its api version range [" + apiVersionRange + "] does not include this version [" + version + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QBackendMetaData tableBackend = qInstance.getBackendForTable(tableName);
|
||||||
|
boolean queryCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_QUERY);
|
||||||
|
boolean getCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_GET);
|
||||||
|
boolean updateCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_UPDATE);
|
||||||
|
boolean deleteCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_DELETE);
|
||||||
|
boolean insertCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_INSERT);
|
||||||
|
boolean countCapability = table.isCapabilityEnabled(tableBackend, Capability.TABLE_COUNT);
|
||||||
|
|
||||||
|
if(!queryCapability && !getCapability && !updateCapability && !deleteCapability && !insertCapability)
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + tableName + "] because it does not have any supported capabilities");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tableApiName = StringUtils.hasContent(apiTableMetaData.getApiTableName()) ? apiTableMetaData.getApiTableName() : tableName;
|
||||||
|
String tableApiNameUcFirst = StringUtils.ucFirst(tableApiName);
|
||||||
String tableLabel = table.getLabel();
|
String tableLabel = table.getLabel();
|
||||||
String primaryKeyName = table.getPrimaryKeyField();
|
String primaryKeyName = table.getPrimaryKeyField();
|
||||||
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
||||||
String primaryKeyLabel = primaryKeyField.getLabel();
|
String primaryKeyLabel = primaryKeyField.getLabel();
|
||||||
|
|
||||||
List<QFieldMetaData> tableApiFields = new GetTableApiFieldsAction().execute(new GetTableApiFieldsInput().withTableName(tableName).withVersion(version)).getFields();
|
List<QFieldMetaData> tableApiFields = new GetTableApiFieldsAction().execute(new GetTableApiFieldsInput().withTableName(tableName).withVersion(version)).getFields();
|
||||||
|
|
||||||
|
ApiFieldMetaData apiFieldMetaData = ApiFieldMetaData.of(primaryKeyField);
|
||||||
|
String primaryKeyApiName = (apiFieldMetaData != null && StringUtils.hasContent(apiFieldMetaData.getApiFieldName())) ? apiFieldMetaData.getApiFieldName() : primaryKeyName;
|
||||||
|
|
||||||
String tableReadPermissionName = PermissionsHelper.getTablePermissionName(tableName, TablePermissionSubType.READ);
|
String tableReadPermissionName = PermissionsHelper.getTablePermissionName(tableName, TablePermissionSubType.READ);
|
||||||
if(StringUtils.hasContent(tableReadPermissionName))
|
if(StringUtils.hasContent(tableReadPermissionName))
|
||||||
{
|
{
|
||||||
@ -193,7 +253,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
// build the schemas for this table //
|
// build the schemas for this table //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
LinkedHashMap<String, Schema> tableFieldsWithoutPrimaryKey = new LinkedHashMap<>();
|
LinkedHashMap<String, Schema> tableFieldsWithoutPrimaryKey = new LinkedHashMap<>();
|
||||||
componentSchemas.put(tableName + "WithoutPrimaryKey", new Schema()
|
componentSchemas.put(tableApiName + "WithoutPrimaryKey", new Schema()
|
||||||
.withType("object")
|
.withType("object")
|
||||||
.withProperties(tableFieldsWithoutPrimaryKey));
|
.withProperties(tableFieldsWithoutPrimaryKey));
|
||||||
|
|
||||||
@ -211,18 +271,18 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentSchemas.put(tableName, new Schema()
|
componentSchemas.put(tableApiName, new Schema()
|
||||||
.withType("object")
|
.withType("object")
|
||||||
.withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/" + tableName + "WithoutPrimaryKey")))
|
.withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/" + tableApiName + "WithoutPrimaryKey")))
|
||||||
.withProperties(MapBuilder.of(
|
.withProperties(MapBuilder.of(
|
||||||
primaryKeyName, new Schema()
|
primaryKeyApiName, new Schema()
|
||||||
.withType(getFieldType(table.getField(primaryKeyName)))
|
.withType(getFieldType(table.getField(primaryKeyName)))
|
||||||
.withFormat(getFieldFormat(table.getField(primaryKeyName)))
|
.withFormat(getFieldFormat(table.getField(primaryKeyName)))
|
||||||
.withDescription(primaryKeyLabel + " for the " + tableLabel + ". Primary Key.")
|
.withDescription(primaryKeyLabel + " for the " + tableLabel + ". Primary Key.")
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
componentSchemas.put(tableName + "SearchResult", new Schema()
|
componentSchemas.put(tableApiName + "SearchResult", new Schema()
|
||||||
.withType("object")
|
.withType("object")
|
||||||
.withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/baseSearchResultFields")))
|
.withAllOf(ListBuilder.of(new Schema().withRef("#/components/schemas/baseSearchResultFields")))
|
||||||
.withProperties(MapBuilder.of(
|
.withProperties(MapBuilder.of(
|
||||||
@ -230,8 +290,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withType("array")
|
.withType("array")
|
||||||
.withItems(new Schema()
|
.withItems(new Schema()
|
||||||
.withAllOf(ListBuilder.of(
|
.withAllOf(ListBuilder.of(
|
||||||
new Schema().withRef("#/components/schemas/" + tableName),
|
new Schema().withRef("#/components/schemas/" + tableApiName),
|
||||||
new Schema().withRef("#/components/schemas/" + tableName + "WithoutPrimaryKey")
|
new Schema().withRef("#/components/schemas/" + tableApiName + "WithoutPrimaryKey")
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
@ -243,7 +303,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
Method queryGet = new Method()
|
Method queryGet = new Method()
|
||||||
.withSummary("Search the " + tableLabel + " table using multiple query string fields.")
|
.withSummary("Search the " + tableLabel + " table using multiple query string fields.")
|
||||||
.withDescription("TODO")
|
.withDescription("TODO")
|
||||||
.withOperationId("query" + tableNameUcFirst)
|
.withOperationId("query" + tableApiNameUcFirst)
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withParameters(ListBuilder.of(
|
.withParameters(ListBuilder.of(
|
||||||
new Parameter()
|
new Parameter()
|
||||||
@ -269,7 +329,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
""")
|
""")
|
||||||
.withIn("query")
|
.withIn("query")
|
||||||
.withSchema(new Schema().withType("string"))
|
.withSchema(new Schema().withType("string"))
|
||||||
.withExamples(buildOrderByExamples(primaryKeyName, tableApiFields)),
|
.withExamples(buildOrderByExamples(primaryKeyApiName, tableApiFields)),
|
||||||
new Parameter()
|
new Parameter()
|
||||||
.withName("booleanOperator")
|
.withName("booleanOperator")
|
||||||
.withDescription("Whether to combine query field as an AND or an OR. Default is AND.")
|
.withDescription("Whether to combine query field as an AND or an OR. Default is AND.")
|
||||||
@ -280,9 +340,9 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withResponse(HttpStatus.OK.getCode(), new Response()
|
.withResponse(HttpStatus.OK.getCode(), new Response()
|
||||||
.withDescription("Successfully searched the " + tableLabel + " table (though may have found 0 records).")
|
.withDescription("Successfully searched the " + tableLabel + " table (though may have found 0 records).")
|
||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema().withRef("#/components/schemas/" + tableName + "SearchResult"))
|
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName + "SearchResult"))
|
||||||
)))
|
)))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableReadPermissionName))));
|
.withSecurity(getSecurity(tableReadPermissionName));
|
||||||
|
|
||||||
for(QFieldMetaData tableApiField : tableApiFields)
|
for(QFieldMetaData tableApiField : tableApiFields)
|
||||||
{
|
{
|
||||||
@ -306,23 +366,27 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
Method queryPost = new Method()
|
Method queryPost = new Method()
|
||||||
.withSummary("Search the " + tableLabel + " table by posting a QueryFilter object.")
|
.withSummary("Search the " + tableLabel + " table by posting a QueryFilter object.")
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableReadPermissionName))));
|
.withSecurity(getSecurity(tableReadPermissionName));
|
||||||
|
|
||||||
openAPI.getPaths().put("/" + tableName + "/query", new Path()
|
if(queryCapability)
|
||||||
|
{
|
||||||
|
openAPI.getPaths().put("/" + tableApiName + "/query", new Path()
|
||||||
.withGet(queryGet)
|
.withGet(queryGet)
|
||||||
.withPost(queryPost)
|
// todo!! .withPost(queryPost)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Method idGet = new Method()
|
Method idGet = new Method()
|
||||||
.withSummary("Get one " + tableLabel + " by " + primaryKeyLabel)
|
.withSummary("Get one " + tableLabel + " by " + primaryKeyLabel)
|
||||||
.withDescription("TODO")
|
.withDescription("TODO")
|
||||||
.withOperationId("get" + tableNameUcFirst)
|
.withOperationId("get" + tableApiNameUcFirst)
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withParameters(ListBuilder.of(
|
.withParameters(ListBuilder.of(
|
||||||
new Parameter()
|
new Parameter()
|
||||||
.withName(primaryKeyName)
|
.withName(primaryKeyApiName)
|
||||||
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to get.")
|
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to get.")
|
||||||
.withIn("path")
|
.withIn("path")
|
||||||
|
.withRequired(true)
|
||||||
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
||||||
))
|
))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
@ -330,55 +394,60 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withResponse(HttpStatus.OK.getCode(), new Response()
|
.withResponse(HttpStatus.OK.getCode(), new Response()
|
||||||
.withDescription("Successfully got the requested " + tableLabel)
|
.withDescription("Successfully got the requested " + tableLabel)
|
||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema().withRef("#/components/schemas/" + tableName))
|
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName))
|
||||||
)))
|
)))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableReadPermissionName))));
|
.withSecurity(getSecurity(tableReadPermissionName));
|
||||||
|
|
||||||
Method idPatch = new Method()
|
Method idPatch = new Method()
|
||||||
.withSummary("Update one " + tableLabel + ".")
|
.withSummary("Update one " + tableLabel + ".")
|
||||||
.withDescription("TODO")
|
.withDescription("TODO")
|
||||||
.withOperationId("update" + tableNameUcFirst)
|
.withOperationId("update" + tableApiNameUcFirst)
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withParameters(ListBuilder.of(
|
.withParameters(ListBuilder.of(
|
||||||
new Parameter()
|
new Parameter()
|
||||||
.withName(primaryKeyName)
|
.withName(primaryKeyApiName)
|
||||||
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to update.")
|
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to update.")
|
||||||
.withIn("path")
|
.withIn("path")
|
||||||
|
.withRequired(true)
|
||||||
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
||||||
))
|
))
|
||||||
.withRequestBody(new RequestBody()
|
.withRequestBody(new RequestBody()
|
||||||
.withRequired(true)
|
.withRequired(true)
|
||||||
.withDescription("Field values to update in the " + tableLabel + " record.")
|
.withDescription("Field values to update in the " + tableLabel + " record.")
|
||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema().withRef("#/components/schemas/" + tableName))
|
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName))
|
||||||
)))
|
)))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
|
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
|
||||||
.withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully updated the requested " + tableLabel))
|
.withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully updated the requested " + tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableUpdatePermissionName))));
|
.withSecurity(getSecurity(tableUpdatePermissionName));
|
||||||
|
|
||||||
Method idDelete = new Method()
|
Method idDelete = new Method()
|
||||||
.withSummary("Delete one " + tableLabel + ".")
|
.withSummary("Delete one " + tableLabel + ".")
|
||||||
.withDescription("TODO")
|
.withDescription("TODO")
|
||||||
.withOperationId("delete" + tableNameUcFirst)
|
.withOperationId("delete" + tableApiNameUcFirst)
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withParameters(ListBuilder.of(
|
.withParameters(ListBuilder.of(
|
||||||
new Parameter()
|
new Parameter()
|
||||||
.withName(primaryKeyName)
|
.withName(primaryKeyApiName)
|
||||||
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to delete.")
|
.withDescription(primaryKeyLabel + " of the " + tableLabel + " to delete.")
|
||||||
.withIn("path")
|
.withIn("path")
|
||||||
|
.withRequired(true)
|
||||||
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
.withSchema(new Schema().withType(getFieldType(primaryKeyField)))
|
||||||
))
|
))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
|
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
|
||||||
.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(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableDeletePermissionName))));
|
.withSecurity(getSecurity(tableDeletePermissionName));
|
||||||
|
|
||||||
openAPI.getPaths().put("/" + tableName + "/{" + primaryKeyName + "}", new Path()
|
if(getCapability || updateCapability || deleteCapability)
|
||||||
.withGet(idGet)
|
{
|
||||||
.withPatch(idPatch)
|
openAPI.getPaths().put("/" + tableApiName + "/{" + primaryKeyApiName + "}", new Path()
|
||||||
.withDelete(idDelete)
|
.withGet(getCapability ? idGet : null)
|
||||||
|
.withPatch(updateCapability ? idPatch : null)
|
||||||
|
.withDelete(deleteCapability ? idDelete : null)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Method slashPost = new Method()
|
Method slashPost = new Method()
|
||||||
.withSummary("Create one " + tableLabel + " record.")
|
.withSummary("Create one " + tableLabel + " record.")
|
||||||
@ -386,7 +455,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withRequired(true)
|
.withRequired(true)
|
||||||
.withDescription("Values for the " + tableLabel + " record to create.")
|
.withDescription("Values for the " + tableLabel + " record to create.")
|
||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema().withRef("#/components/schemas/" + tableName + "WithoutPrimaryKey"))
|
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName + "WithoutPrimaryKey"))
|
||||||
)))
|
)))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.CREATED.getCode(), new Response()
|
.withResponse(HttpStatus.CREATED.getCode(), new Response()
|
||||||
@ -394,18 +463,21 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema()
|
.withSchema(new Schema()
|
||||||
.withType("object")
|
.withType("object")
|
||||||
.withProperties(MapBuilder.of(primaryKeyName, new Schema()
|
.withProperties(MapBuilder.of(primaryKeyApiName, new Schema()
|
||||||
.withType(getFieldType(primaryKeyField))
|
.withType(getFieldType(primaryKeyField))
|
||||||
.withExample("47")
|
.withExample("47")
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
)))
|
)))
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableInsertPermissionName))));
|
.withSecurity(getSecurity(tableInsertPermissionName));
|
||||||
|
|
||||||
openAPI.getPaths().put("/" + tableName + "/", new Path()
|
if(insertCapability)
|
||||||
|
{
|
||||||
|
openAPI.getPaths().put("/" + tableApiName + "/", new Path()
|
||||||
.withPost(slashPost)
|
.withPost(slashPost)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
// bulk paths //
|
// bulk paths //
|
||||||
@ -418,11 +490,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema()
|
.withSchema(new Schema()
|
||||||
.withType("array")
|
.withType("array")
|
||||||
.withItems(new Schema().withRef("#/components/schemas/" + tableName + "WithoutPrimaryKey"))))))
|
.withItems(new Schema().withRef("#/components/schemas/" + tableApiName + "WithoutPrimaryKey"))))))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyName, primaryKeyField, "post"))
|
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "post"))
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableInsertPermissionName))));
|
.withSecurity(getSecurity(tableInsertPermissionName));
|
||||||
|
|
||||||
Method bulkPatch = new Method()
|
Method bulkPatch = new Method()
|
||||||
.withSummary("Update multiple " + tableLabel + " records.")
|
.withSummary("Update multiple " + tableLabel + " records.")
|
||||||
@ -432,11 +504,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withContent(MapBuilder.of("application/json", new Content()
|
.withContent(MapBuilder.of("application/json", new Content()
|
||||||
.withSchema(new Schema()
|
.withSchema(new Schema()
|
||||||
.withType("array")
|
.withType("array")
|
||||||
.withItems(new Schema().withRef("#/components/schemas/" + tableName))))))
|
.withItems(new Schema().withRef("#/components/schemas/" + tableApiName))))))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyName, primaryKeyField, "patch"))
|
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "patch"))
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableUpdatePermissionName))));
|
.withSecurity(getSecurity(tableUpdatePermissionName));
|
||||||
|
|
||||||
Method bulkDelete = new Method()
|
Method bulkDelete = new Method()
|
||||||
.withSummary("Delete multiple " + tableLabel + " records.")
|
.withSummary("Delete multiple " + tableLabel + " records.")
|
||||||
@ -449,20 +521,23 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withItems(new Schema().withType(getFieldType(primaryKeyField)))
|
.withItems(new Schema().withType(getFieldType(primaryKeyField)))
|
||||||
.withExample(List.of(42, 47))))))
|
.withExample(List.of(42, 47))))))
|
||||||
.withResponses(buildStandardErrorResponses())
|
.withResponses(buildStandardErrorResponses())
|
||||||
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyName, primaryKeyField, "delete"))
|
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "delete"))
|
||||||
.withTags(ListBuilder.of(tableLabel))
|
.withTags(ListBuilder.of(tableLabel))
|
||||||
.withSecurity(ListBuilder.of(MapBuilder.of("OAuth2", List.of(tableDeletePermissionName))));
|
.withSecurity(getSecurity(tableDeletePermissionName));
|
||||||
|
|
||||||
openAPI.getPaths().put("/" + tableName + "/bulk", new Path()
|
if(insertCapability || updateCapability || deleteCapability)
|
||||||
.withPost(bulkPost)
|
{
|
||||||
.withPatch(bulkPatch)
|
openAPI.getPaths().put("/" + tableApiName + "/bulk", new Path()
|
||||||
.withDelete(bulkDelete));
|
.withPost(insertCapability ? bulkPost : null)
|
||||||
|
.withPatch(updateCapability ? bulkPatch : null)
|
||||||
|
.withDelete(deleteCapability ? bulkDelete : null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentResponses.put(HttpStatus.BAD_REQUEST.getCode(), buildStandardErrorResponse("Bad Request. Some portion of the request's content was not acceptable to the server. See error message in body for details.", "Parameter id should be given an integer value, but received string: \"Foo\""));
|
componentResponses.put("error" + HttpStatus.BAD_REQUEST.getCode(), buildStandardErrorResponse("Bad Request. Some portion of the request's content was not acceptable to the server. See error message in body for details.", "Parameter id should be given an integer value, but received string: \"Foo\""));
|
||||||
componentResponses.put(HttpStatus.UNAUTHORIZED.getCode(), buildStandardErrorResponse("Unauthorized. The required authentication credentials were missing or invalid.", "The required authentication credentials were missing or invalid."));
|
componentResponses.put("error" + HttpStatus.UNAUTHORIZED.getCode(), buildStandardErrorResponse("Unauthorized. The required authentication credentials were missing or invalid.", "The required authentication credentials were missing or invalid."));
|
||||||
componentResponses.put(HttpStatus.FORBIDDEN.getCode(), buildStandardErrorResponse("Forbidden. You do not have permission to access the requested resource.", "You do not have permission to access the requested resource."));
|
componentResponses.put("error" + HttpStatus.FORBIDDEN.getCode(), buildStandardErrorResponse("Forbidden. You do not have permission to access the requested resource.", "You do not have permission to access the requested resource."));
|
||||||
componentResponses.put(HttpStatus.INTERNAL_SERVER_ERROR.getCode(), buildStandardErrorResponse("Internal Server Error. An error occurred in the server processing the request.", "Database connection error. Try again later."));
|
componentResponses.put("error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode(), buildStandardErrorResponse("Internal Server Error. An error occurred in the server processing the request.", "Database connection error. Try again later."));
|
||||||
|
|
||||||
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput();
|
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput();
|
||||||
output.setOpenAPI(openAPI);
|
output.setOpenAPI(openAPI);
|
||||||
@ -473,11 +548,25 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static List<Map<String, List<String>>> getSecurity(String permissionName)
|
||||||
|
{
|
||||||
|
return ListBuilder.of(
|
||||||
|
MapBuilder.of("OAuth2", List.of(permissionName)),
|
||||||
|
MapBuilder.of("bearerAuth", List.of(permissionName)),
|
||||||
|
MapBuilder.of("basicAuth", List.of(permissionName))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("checkstyle:indentation")
|
@SuppressWarnings("checkstyle:indentation")
|
||||||
private Response buildMultiStatusResponse(String tableLabel, String primaryKeyName, QFieldMetaData primaryKeyField, String method)
|
private Response buildMultiStatusResponse(String tableLabel, String primaryKeyApiName, QFieldMetaData primaryKeyField, String method)
|
||||||
{
|
{
|
||||||
List<Object> example = switch(method.toLowerCase())
|
List<Object> example = switch(method.toLowerCase())
|
||||||
{
|
{
|
||||||
@ -485,7 +574,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
MapBuilder.of(LinkedHashMap::new)
|
MapBuilder.of(LinkedHashMap::new)
|
||||||
.with("statusCode", HttpStatus.CREATED.getCode())
|
.with("statusCode", HttpStatus.CREATED.getCode())
|
||||||
.with("statusText", HttpStatus.CREATED.getMessage())
|
.with("statusText", HttpStatus.CREATED.getMessage())
|
||||||
.with(primaryKeyName, "47").build(),
|
.with(primaryKeyApiName, "47").build(),
|
||||||
MapBuilder.of(LinkedHashMap::new)
|
MapBuilder.of(LinkedHashMap::new)
|
||||||
.with("statusCode", HttpStatus.BAD_REQUEST.getCode())
|
.with("statusCode", HttpStatus.BAD_REQUEST.getCode())
|
||||||
.with("statusText", HttpStatus.BAD_REQUEST.getMessage())
|
.with("statusText", HttpStatus.BAD_REQUEST.getMessage())
|
||||||
@ -517,7 +606,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
properties.put("error", new Schema().withType("string"));
|
properties.put("error", new Schema().withType("string"));
|
||||||
if(method.equalsIgnoreCase("post"))
|
if(method.equalsIgnoreCase("post"))
|
||||||
{
|
{
|
||||||
properties.put(primaryKeyName, new Schema().withType(getFieldType(primaryKeyField)));
|
properties.put(primaryKeyApiName, new Schema().withType(getFieldType(primaryKeyField)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response()
|
return new Response()
|
||||||
@ -538,7 +627,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Map<String, Example> buildOrderByExamples(String primaryKeyName, List<? extends QFieldMetaData> tableApiFields)
|
private Map<String, Example> buildOrderByExamples(String primaryKeyApiName, List<? extends QFieldMetaData> tableApiFields)
|
||||||
{
|
{
|
||||||
Map<String, Example> rs = new LinkedHashMap<>();
|
Map<String, Example> rs = new LinkedHashMap<>();
|
||||||
|
|
||||||
@ -547,7 +636,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
for(QFieldMetaData tableApiField : tableApiFields)
|
for(QFieldMetaData tableApiField : tableApiFields)
|
||||||
{
|
{
|
||||||
String name = tableApiField.getName();
|
String name = tableApiField.getName();
|
||||||
if(primaryKeyName.equals(name) || fieldsForExample4.contains(name) || fieldsForExample5.contains(name))
|
if(primaryKeyApiName.equals(name) || fieldsForExample4.contains(name) || fieldsForExample5.contains(name))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -567,16 +656,16 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.put(primaryKeyName, new ExampleWithSingleValue()
|
rs.put(primaryKeyApiName, new ExampleWithSingleValue()
|
||||||
.withSummary("order by " + primaryKeyName + " (by default is ascending)")
|
.withSummary("order by " + primaryKeyApiName + " (by default is ascending)")
|
||||||
.withValue("id"));
|
.withValue("id"));
|
||||||
|
|
||||||
rs.put(primaryKeyName + "Desc", new ExampleWithSingleValue()
|
rs.put(primaryKeyApiName + "Desc", new ExampleWithSingleValue()
|
||||||
.withSummary("order by " + primaryKeyName + " (descending)")
|
.withSummary("order by " + primaryKeyApiName + " (descending)")
|
||||||
.withValue("id desc"));
|
.withValue("id desc"));
|
||||||
|
|
||||||
rs.put(primaryKeyName + "Asc", new ExampleWithSingleValue()
|
rs.put(primaryKeyApiName + "Asc", new ExampleWithSingleValue()
|
||||||
.withSummary("order by " + primaryKeyName + " (explicitly ascending)")
|
.withSummary("order by " + primaryKeyApiName + " (explicitly ascending)")
|
||||||
.withValue("id asc"));
|
.withValue("id asc"));
|
||||||
|
|
||||||
if(fieldsForExample4.size() == 2)
|
if(fieldsForExample4.size() == 2)
|
||||||
@ -669,10 +758,10 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
private static Map<Integer, Response> buildStandardErrorResponses()
|
private static Map<Integer, Response> buildStandardErrorResponses()
|
||||||
{
|
{
|
||||||
return MapBuilder.of(
|
return MapBuilder.of(
|
||||||
HttpStatus.BAD_REQUEST.getCode(), new Response().withRef("#/components/responses/" + HttpStatus.BAD_REQUEST.getCode()),
|
HttpStatus.BAD_REQUEST.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.BAD_REQUEST.getCode()),
|
||||||
HttpStatus.UNAUTHORIZED.getCode(), new Response().withRef("#/components/responses/" + HttpStatus.UNAUTHORIZED.getCode()),
|
HttpStatus.UNAUTHORIZED.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.UNAUTHORIZED.getCode()),
|
||||||
HttpStatus.FORBIDDEN.getCode(), new Response().withRef("#/components/responses/" + HttpStatus.FORBIDDEN.getCode()),
|
HttpStatus.FORBIDDEN.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.FORBIDDEN.getCode()),
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR.getCode(), new Response().withRef("#/components/responses/" + HttpStatus.INTERNAL_SERVER_ERROR.getCode())
|
HttpStatus.INTERNAL_SERVER_ERROR.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ import java.util.Set;
|
|||||||
import com.kingsrook.qqq.api.actions.GenerateOpenApiSpecAction;
|
import com.kingsrook.qqq.api.actions.GenerateOpenApiSpecAction;
|
||||||
import com.kingsrook.qqq.api.actions.QRecordApiAdapter;
|
import com.kingsrook.qqq.api.actions.QRecordApiAdapter;
|
||||||
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.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.ApiInstanceMetaData;
|
||||||
@ -88,6 +87,7 @@ import io.javalin.apibuilder.ApiBuilder;
|
|||||||
import io.javalin.apibuilder.EndpointGroup;
|
import io.javalin.apibuilder.EndpointGroup;
|
||||||
import io.javalin.http.ContentType;
|
import io.javalin.http.ContentType;
|
||||||
import io.javalin.http.Context;
|
import io.javalin.http.Context;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@ -102,7 +102,9 @@ public class QJavalinApiHandler
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(QJavalinApiHandler.class);
|
private static final QLogger LOG = QLogger.getLogger(QJavalinApiHandler.class);
|
||||||
|
|
||||||
static QInstance qInstance;
|
private static QInstance qInstance;
|
||||||
|
|
||||||
|
private static Map<String, Map<String, QTableMetaData>> tableApiNameMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -198,35 +200,19 @@ public class QJavalinApiHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static APIVersionRange getApiVersionRange(QTableMetaData table)
|
|
||||||
{
|
|
||||||
ApiTableMetaData middlewareMetaData = ApiTableMetaData.of(table);
|
|
||||||
if(middlewareMetaData != null && middlewareMetaData.getInitialVersion() != null)
|
|
||||||
{
|
|
||||||
return (APIVersionRange.afterAndIncluding(middlewareMetaData.getInitialVersion()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (APIVersionRange.none());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static void doGet(Context context)
|
private static void doGet(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
String primaryKey = context.pathParam("primaryKey");
|
String primaryKey = context.pathParam("primaryKey");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
GetInput getInput = new GetInput();
|
GetInput getInput = new GetInput();
|
||||||
|
|
||||||
@ -277,15 +263,15 @@ public class QJavalinApiHandler
|
|||||||
private static void doQuery(Context context)
|
private static void doQuery(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
QQueryFilter filter = null;
|
QQueryFilter filter = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
List<String> badRequestMessages = new ArrayList<>();
|
List<String> badRequestMessages = new ArrayList<>();
|
||||||
|
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
QueryInput queryInput = new QueryInput();
|
QueryInput queryInput = new QueryInput();
|
||||||
setupSession(context, queryInput);
|
setupSession(context, queryInput);
|
||||||
@ -506,24 +492,74 @@ public class QJavalinApiHandler
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static void validateTableAndVersion(Context context, String version, QTableMetaData table) throws QNotFoundException
|
private static QTableMetaData validateTableAndVersion(Context context, String version, String tableApiName) throws QNotFoundException
|
||||||
{
|
{
|
||||||
|
QNotFoundException qNotFoundException = new QNotFoundException("Could not find any resources at path " + context.path());
|
||||||
|
|
||||||
|
QTableMetaData table = getTableByApiName(version, tableApiName);
|
||||||
|
|
||||||
if(table == null)
|
if(table == null)
|
||||||
{
|
{
|
||||||
throw (new QNotFoundException("Could not find any resources at path " + context.path()));
|
throw (qNotFoundException);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BooleanUtils.isTrue(table.getIsHidden()))
|
||||||
|
{
|
||||||
|
throw (qNotFoundException);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiTableMetaData apiTableMetaData = ApiTableMetaData.of(table);
|
||||||
|
if(apiTableMetaData == null)
|
||||||
|
{
|
||||||
|
throw (qNotFoundException);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BooleanUtils.isTrue(apiTableMetaData.getIsExcluded()))
|
||||||
|
{
|
||||||
|
throw (qNotFoundException);
|
||||||
}
|
}
|
||||||
|
|
||||||
APIVersion requestApiVersion = new APIVersion(version);
|
APIVersion requestApiVersion = new APIVersion(version);
|
||||||
List<APIVersion> supportedVersions = ApiInstanceMetaData.of(qInstance).getSupportedVersions();
|
List<APIVersion> supportedVersions = ApiInstanceMetaData.of(qInstance).getSupportedVersions();
|
||||||
if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion))
|
if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion))
|
||||||
{
|
{
|
||||||
throw (new QNotFoundException("This version of this API does not contain the resource path " + context.path()));
|
throw (qNotFoundException);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!getApiVersionRange(table).includes(requestApiVersion))
|
if(!apiTableMetaData.getApiVersionRange().includes(requestApiVersion))
|
||||||
{
|
{
|
||||||
throw (new QNotFoundException("This version of this API does not contain the resource path " + context.path()));
|
throw (qNotFoundException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QTableMetaData getTableByApiName(String version, String tableApiName)
|
||||||
|
{
|
||||||
|
if(tableApiNameMap.get(version) == null)
|
||||||
|
{
|
||||||
|
Map<String, QTableMetaData> map = new HashMap<>();
|
||||||
|
|
||||||
|
for(QTableMetaData table : qInstance.getTables().values())
|
||||||
|
{
|
||||||
|
ApiTableMetaData apiTableMetaData = ApiTableMetaData.of(table);
|
||||||
|
String name = table.getName();
|
||||||
|
if(apiTableMetaData != null && StringUtils.hasContent(apiTableMetaData.getApiTableName()))
|
||||||
|
{
|
||||||
|
name = apiTableMetaData.getApiTableName();
|
||||||
|
}
|
||||||
|
map.put(name, table);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableApiNameMap.put(version, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tableApiNameMap.get(version).get(tableApiName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -663,12 +699,12 @@ public class QJavalinApiHandler
|
|||||||
private static void doInsert(Context context)
|
private static void doInsert(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
|
|
||||||
@ -723,12 +759,12 @@ public class QJavalinApiHandler
|
|||||||
private static void bulkInsert(Context context)
|
private static void bulkInsert(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
|
|
||||||
@ -821,13 +857,13 @@ public class QJavalinApiHandler
|
|||||||
private static void doUpdate(Context context)
|
private static void doUpdate(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
String primaryKey = context.pathParam("primaryKey");
|
String primaryKey = context.pathParam("primaryKey");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
UpdateInput updateInput = new UpdateInput();
|
UpdateInput updateInput = new UpdateInput();
|
||||||
|
|
||||||
@ -900,13 +936,13 @@ public class QJavalinApiHandler
|
|||||||
private static void doDelete(Context context)
|
private static void doDelete(Context context)
|
||||||
{
|
{
|
||||||
String version = context.pathParam("version");
|
String version = context.pathParam("version");
|
||||||
String tableName = context.pathParam("tableName");
|
String tableApiName = context.pathParam("tableName");
|
||||||
String primaryKey = context.pathParam("primaryKey");
|
String primaryKey = context.pathParam("primaryKey");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
QTableMetaData table = validateTableAndVersion(context, version, tableApiName);
|
||||||
validateTableAndVersion(context, version, table);
|
String tableName = table.getName();
|
||||||
|
|
||||||
DeleteInput deleteInput = new DeleteInput();
|
DeleteInput deleteInput = new DeleteInput();
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.api.model.metadata.tables;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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.metadata.fields.ApiFieldMetaData;
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QMiddlewareTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QMiddlewareTableMetaData;
|
||||||
@ -40,6 +41,9 @@ public class ApiTableMetaData extends QMiddlewareTableMetaData
|
|||||||
private String initialVersion;
|
private String initialVersion;
|
||||||
private String finalVersion;
|
private String finalVersion;
|
||||||
|
|
||||||
|
private String apiTableName;
|
||||||
|
private Boolean isExcluded;
|
||||||
|
|
||||||
private List<QFieldMetaData> removedApiFields;
|
private List<QFieldMetaData> removedApiFields;
|
||||||
|
|
||||||
|
|
||||||
@ -54,6 +58,23 @@ public class ApiTableMetaData extends QMiddlewareTableMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public APIVersionRange getApiVersionRange()
|
||||||
|
{
|
||||||
|
if(getInitialVersion() == null)
|
||||||
|
{
|
||||||
|
return APIVersionRange.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (getFinalVersion() != null
|
||||||
|
? APIVersionRange.betweenAndIncluding(getInitialVersion(), getFinalVersion())
|
||||||
|
: APIVersionRange.afterAndIncluding(getInitialVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -218,4 +239,66 @@ public class ApiTableMetaData extends QMiddlewareTableMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for apiTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getApiTableName()
|
||||||
|
{
|
||||||
|
return (this.apiTableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for apiTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setApiTableName(String apiTableName)
|
||||||
|
{
|
||||||
|
this.apiTableName = apiTableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for apiTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiTableMetaData withApiTableName(String apiTableName)
|
||||||
|
{
|
||||||
|
this.apiTableName = apiTableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getIsExcluded()
|
||||||
|
{
|
||||||
|
return (this.isExcluded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIsExcluded(Boolean isExcluded)
|
||||||
|
{
|
||||||
|
this.isExcluded = isExcluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiTableMetaData withIsExcluded(Boolean isExcluded)
|
||||||
|
{
|
||||||
|
this.isExcluded = isExcluded;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public class Components
|
|||||||
{
|
{
|
||||||
private Map<String, Example> examples;
|
private Map<String, Example> examples;
|
||||||
private Map<String, Schema> schemas;
|
private Map<String, Schema> schemas;
|
||||||
private Map<Integer, Response> responses;
|
private Map<String, Response> responses;
|
||||||
private Map<String, SecurityScheme> securitySchemes;
|
private Map<String, SecurityScheme> securitySchemes;
|
||||||
|
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ public class Components
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for responses
|
** Getter for responses
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public Map<Integer, Response> getResponses()
|
public Map<String, Response> getResponses()
|
||||||
{
|
{
|
||||||
return (this.responses);
|
return (this.responses);
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ public class Components
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for responses
|
** Setter for responses
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setResponses(Map<Integer, Response> responses)
|
public void setResponses(Map<String, Response> responses)
|
||||||
{
|
{
|
||||||
this.responses = responses;
|
this.responses = responses;
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ public class Components
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for responses
|
** Fluent setter for responses
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public Components withResponses(Map<Integer, Response> responses)
|
public Components withResponses(Map<String, Response> responses)
|
||||||
{
|
{
|
||||||
this.responses = responses;
|
this.responses = responses;
|
||||||
return (this);
|
return (this);
|
||||||
|
@ -32,6 +32,7 @@ public class Parameter
|
|||||||
{
|
{
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
|
private Boolean required;
|
||||||
private String in;
|
private String in;
|
||||||
private Schema schema;
|
private Schema schema;
|
||||||
private Boolean explode;
|
private Boolean explode;
|
||||||
@ -223,4 +224,35 @@ public class Parameter
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for required
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getRequired()
|
||||||
|
{
|
||||||
|
return (this.required);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for required
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setRequired(Boolean required)
|
||||||
|
{
|
||||||
|
this.required = required;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for required
|
||||||
|
*******************************************************************************/
|
||||||
|
public Parameter withRequired(Boolean required)
|
||||||
|
{
|
||||||
|
this.required = required;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ package com.kingsrook.qqq.api.model.openapi;
|
|||||||
public class SecurityScheme
|
public class SecurityScheme
|
||||||
{
|
{
|
||||||
private String type;
|
private String type;
|
||||||
|
private String scheme;
|
||||||
|
private String bearerFormat;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -60,4 +62,66 @@ public class SecurityScheme
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for scheme
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getScheme()
|
||||||
|
{
|
||||||
|
return (this.scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for scheme
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setScheme(String scheme)
|
||||||
|
{
|
||||||
|
this.scheme = scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for scheme
|
||||||
|
*******************************************************************************/
|
||||||
|
public SecurityScheme withScheme(String scheme)
|
||||||
|
{
|
||||||
|
this.scheme = scheme;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for bearerFormat
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getBearerFormat()
|
||||||
|
{
|
||||||
|
return (this.bearerFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for bearerFormat
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setBearerFormat(String bearerFormat)
|
||||||
|
{
|
||||||
|
this.bearerFormat = bearerFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for bearerFormat
|
||||||
|
*******************************************************************************/
|
||||||
|
public SecurityScheme withBearerFormat(String bearerFormat)
|
||||||
|
{
|
||||||
|
this.bearerFormat = bearerFormat;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,19 +22,21 @@
|
|||||||
package com.kingsrook.qqq.api.actions;
|
package com.kingsrook.qqq.api.actions;
|
||||||
|
|
||||||
|
|
||||||
|
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.tables.ApiTableMetaData;
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -49,23 +51,111 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void test() throws QException
|
void test() throws QException
|
||||||
{
|
{
|
||||||
String version = TestUtils.V2023_Q1;
|
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION));
|
||||||
|
|
||||||
QInstance qInstance = QContext.getQInstance();
|
|
||||||
qInstance.withMiddlewareMetaData(new ApiInstanceMetaData()
|
|
||||||
.withCurrentVersion(new APIVersion(version))
|
|
||||||
);
|
|
||||||
|
|
||||||
for(QTableMetaData table : qInstance.getTables().values())
|
|
||||||
{
|
|
||||||
table.withMiddlewareMetaData(new ApiTableMetaData().withInitialVersion(version));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
new QInstanceEnricher(qInstance).enrich();
|
|
||||||
|
|
||||||
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(version));
|
|
||||||
System.out.println(output.getYaml());
|
System.out.println(output.getYaml());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testExcludedTables() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("supportedTable")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("hiddenTable")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withIsHidden(true)
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("excludedTable")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withIsExcluded(true)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("tableWithoutApiMetaData")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("tableWithFutureVersion")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2023_Q2)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("tableWithOnlyPastVersions")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)
|
||||||
|
.withFinalVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("tableWithNoSupportedCapabilities")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withoutCapabilities(Capability.TABLE_QUERY, Capability.TABLE_GET, Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE)
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
|
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION));
|
||||||
|
Set<String> apiPaths = output.getOpenAPI().getPaths().keySet();
|
||||||
|
assertTrue(apiPaths.stream().anyMatch(s -> s.contains("/supportedTable/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/hiddenTable/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/excludedTable/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/tableWithoutApiMetaData/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/tableWithoutApiMetaData/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/tableWithFutureVersion/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/tableWithNoSupportedCapabilities/")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testApiTableName() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("internalName")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withApiTableName("externalName")
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
|
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION));
|
||||||
|
Set<String> apiPaths = output.getOpenAPI().getPaths().keySet();
|
||||||
|
assertTrue(apiPaths.stream().anyMatch(s -> s.contains("/externalName/")));
|
||||||
|
assertTrue(apiPaths.stream().noneMatch(s -> s.contains("/internalName/")));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
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.metadata.tables.ApiTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
@ -35,6 +36,9 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||||
import kong.unirest.HttpResponse;
|
import kong.unirest.HttpResponse;
|
||||||
@ -72,6 +76,16 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
static void beforeAll() throws QInstanceValidationException
|
static void beforeAll() throws QInstanceValidationException
|
||||||
{
|
{
|
||||||
QInstance qInstance = TestUtils.defineInstance();
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("internalName")
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withMiddlewareMetaData(new ApiTableMetaData()
|
||||||
|
.withApiTableName("externalName")
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)));
|
||||||
|
|
||||||
qJavalinImplementation = new QJavalinImplementation(qInstance);
|
qJavalinImplementation = new QJavalinImplementation(qInstance);
|
||||||
qJavalinImplementation.startJavalinServer(PORT);
|
qJavalinImplementation.startJavalinServer(PORT);
|
||||||
qJavalinImplementation.getJavalinService().routes(new QJavalinApiHandler(qInstance).getRoutes());
|
qJavalinImplementation.getJavalinService().routes(new QJavalinApiHandler(qInstance).getRoutes());
|
||||||
@ -686,13 +700,33 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testDelete404()
|
void testDelete404()
|
||||||
{
|
{
|
||||||
HttpResponse<String> response = Unirest.delete(BASE_URL + "/api/" + VERSION + "/person/1")
|
HttpResponse<String> response = Unirest.delete(BASE_URL + "/api/" + VERSION + "/person/1").asString();
|
||||||
.asString();
|
|
||||||
assertErrorResponse(HttpStatus.NOT_FOUND_404, "Could not find Person with Id of 1", response);
|
assertErrorResponse(HttpStatus.NOT_FOUND_404, "Could not find Person with Id of 1", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testRenamedTable()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/internalName/query").asString();
|
||||||
|
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/externalName/query").asString();
|
||||||
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||||
|
assertEquals(0, jsonObject.getInt("count"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
Reference in New Issue
Block a user