Swagger (spray) path parameters inside resource url - scala

Say I have a parametrized resource url like
/customers/{CUSTOMER-ID}/ownedItems/{ITEM-ID}
How should I annotatate/split my spray routes (using the spray-swagger plugin) to generate a documentation that will recognize {CUSTOMER-ID} as a proper path parameter?
My problem is that the top level #Api annotation takes a path but no parameters, while the #ApiOperation can be annotated with path parameters but these get appended at the end. In other words, if I write:
#Api(value = "/customers/{CUSTOMER-ID}")
#ApiOperation(httpMethod = "GET")
#ApiImplicitParams(Array(
new ApiImplicitParam(name = "ITEM-ID", required = true, dataType = "string", paramType = "path"))
I get in the UI only ITEM-ID as testable parameter, while CUSTOMER-ID, while being reported as in { }, is just a string.
I'd like to have something where both are path parameters.
Any idea?

customers is your #Api entry point, not path parameters. Path parameters must be used only into #ApiOperation as follow (with more samples) :
#Api(value = "/customers")
#ApiOperation(value = "/{CUSTOMER-ID}/ownedItems/{ITEM-ID}", httpMethod = "GET")
#ApiImplicitParams(Array(
new ApiImplicitParam(name = "CUSTOMER-ID", required = true, dataType = "string", paramType = "path"),
new ApiImplicitParam(name = "ITEM-ID", required = true, dataType = "string", paramType = "path"))
#ApiOperation(value = "/{CUSTOMER-ID}", httpMethod = "GET")
#ApiImplicitParams(Array(
new ApiImplicitParam(name = "CUSTOMER-ID", required = true, dataType = "string", paramType = "path"))
#ApiOperation(value = "/", httpMethod = "POST")

Related

How can I get the EDM type from 'context' bound to an element in SAPUI5?

I have an SAPUI5 application.
I defined an element by the smart fields like the following:
<smartField:SmartField value="{GefahrInVerzug}" width="auto">
<smartField:configuration>
<smartField:Configuration preventInitialDataFetchInValueHelpDialog="false" displayBehaviour="descriptionOnly"/>
</smartField:configuration>
</smartField:SmartField>
The GefahrInVerzug field is defined as a boolean in my metadata:
<Property Name="GefahrInVerzug" Type="Edm.Boolean" sap:creatable="true"
sap:updatable="true" sap:deletable="true" sap:label="Gefahr in Verzug"/>
Assume I have the following handler for onInputChange event of the rendered control:
onInputChange: function (oEvent) {
var oField = oEvent.getSource(),
oContext = oField.getBindingContext();
//oContext.getEdmType();
}
How can I get the Edm Type by accessing the element (i.e. oField) or the context object (i.e. oContext).
In this case I am looking for a solution that return Edm.Boolean to me!
We can define the following functions in our controller to extract Edm Type from a field:
// Returns a list that contains a map between
// UI5 elements' types and the property that contains the value!
// Normally bound to the oData property
_getFieldTypeAttribute: function () {
var aFieldTypes = {
"sap.m.Input": "value",
"sap.m.Select": "selectedKey",
"sap.m.ComboBox": "selectedKey",
"sap.m.CheckBox": "selected",
"sap.m.DatePicker": "dateValue",
"sap.m.DateTimePicker": "value",
"sap.m.TextArea": "value",
"sap.m.Switch": "state",
"sap.ui.comp.smartfield.SmartField": "value"
};
return aFieldTypes;
},
// Extract the EDM type from Metadata
_getEdmType: function(oField, sPropertyName){
var regex = /\/([^(]+)/gm,
oContext = oField.getBindingContext(),
oModel = oContext.getModel(),
oMetaModel = oModel.getMetaModel(),
sBindingPath = oContext.getPath(),
sType = null;
//
var aMatches = regex.exec(sBindingPath);
if(aMatches.length > 0){
var sSetName = aMatches[1],
oEntitySet = oMetaModel.getODataEntitySet(sSetName),
sEntityType = oEntitySet.entityType,
oEntityType = oMetaModel.getODataEntityType(sEntityType),
oProperty = oMetaModel.getODataProperty(oEntityType, sPropertyName);
if (oProperty ) {
sType = oProperty.type;
}
}
//
return sType;
},
// Is fied when the input value is changed!
onInputChange: function (oEvent) {
var oField = oEvent.getSource(),
oContext = oField.getBindingContext(),
oModel = oContext.getModel(),
aFieldTypes = this._getFieldTypeAttribute(),
sFieldType = oField.getMetadata().getName(),
sFieldPath = oField.getBinding(aFieldTypes[sFieldType]).getPath(),
sPropertyName = sFieldPath && sFieldPath.startsWith("/") ? sFieldPath.substring(1) : sFieldPath,
sBindingPath = sPropertyName ? oContext.getPath() + "/" + sPropertyName : null;
console.log(this._getEdmType(oField, sPropertyName));
}
It prints Edm.Boolean for example when this function is fired for an element of boolean type!
Take a look here
onInputChange: function (oEvent) {
var oField = oEvent.getSource(),
oContext = oField.getBindingContext(),
sType = oContext.getProperty("/#EntityName/GefahrInVerzug/#type");
}
In metadata MaxLength , Type are in capital letters but in that name
you can’t get the metadata values.

Swagger / springfox generating response example automatically

Currently using springfox 2.9.2 to Swagger document an API created in Spring.
I want to add example response in the documentation, like in this image;
my understanding is that I can do something similar to this:
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success",
examples = #io.swagger.annotations.Example(
value = {
#ExampleProperty(value = "{'snapshot':{'type': 'AAA'}}", mediaType = "application/json")
}))
I'm placing this code snippet just above the GET method in this case.
unfortunately the 2 examples above always shows : identifier expected error
But I also see that I can do this too:
#ApiResponses(value = {
ApiResponse(code = 200, message = "Success", response = MyModel.class,
)
})
Also I see that I can add an example with #ApiOperation level:
#ApiOperation(value = "Create a Account", nickname = "createAccount", notes = "Create a account", response = AccountResponse.class, tags={ })
My questions are:
How can I add an example JSON response to my Swagger documentation?
It would be ideal to just point Swagger/Springfox to my model/bean and have it generate the example response automatically, and automatically update with each update for the bean/model. Is this what the second code snippet above is supposed to do?
Define example with annotation for dto:
#ApiModel("Crop")
public class CropDto {
#ApiModelProperty(name = "Unique guid", position = 1, example = "7aaee0e2-6884-4fd7-ba63-21d76723dce2")
public UUID id;
#ApiModelProperty(name = "Unique code", position = 2, example = "squ")
public String code;
#ApiModelProperty(name = "Unique name", position = 3, example = "Squash")
public String name;
#ApiModelProperty(position = 4, example = "Cucurbita pepo L.")
public String description;
}

How do I programmatically set configuration for the ImageResizer SQLReader plugin v4?

I was previously using v3 of ImageResizer but am now trying to use v4.
See: http://imageresizing.net/docs/v4/plugins/sqlreader
I need to programmatically set the several config options for the SQLReader plugin. I had this previous code, but it no longer works, stating that the type SqlReaderSettings could not be found:
// SqlReader Plugin
var fileSettings = new SqlReaderSettings
{
ConnectionString = ApplicationConfigurationContext.Current.DefaultSiteSqlConnectionString,
PathPrefix = "~/Images",
StripFileExtension = true,
ImageIdType = SqlDbType.UniqueIdentifier,
ImageBlobQuery = ???,
ModifiedDateQuery = ???,
ImageExistsQuery = ???,
CacheUnmodifiedFiles = true,
RequireImageExtension = true
};
I cannot use the web.config file to store some of these settings. Specifically the connection string may change at run-time, and cannot be stored un-encrypted in the web.config file due to company policy.
Thanks for the help.
Update: This is the code I now use. I did not add in this plugin from the Web.config as it would be redundant.
new SqlReaderPlugin
{
ConnectionString = ApplicationConfigurationContext.Current.DefaultSiteSqlConnectionString,
ImageIdType = SqlDbType.VarChar,
QueriesAreStoredProcedures = true,
ModifiedDateQuery = "procFileImageResizerSelectTimestamps",
ImageBlobQuery = "procFileImageResizerSelectData",
ExposeAsVpp = true,
VirtualFilesystemPrefix = filesUri,
RequireImageExtension = true,
StripFileExtension = true,
CacheUnmodifiedFiles = true
}.Install(Config.Current);
You can replace SqlReaderSettings with SqlReaderPlugin directly; it no longer uses a separate settings class. Nearly all the class members should be the same, so just change the name of the class you are initializing.

Map all sub-paths to a REST service

Given a minimal example of a Coldfusion rest service (named "FileStore"):
component
restpath = ""
rest = true
{
remote void function getFile(
required string path restargsource = "Path"
)
httpmethod = "GET"
restpath = "{path}"
{
var file = FileReadBinary( "/some/path/to/local/files/#path#" );
var mimetype = customFunctionToGetMimeType( getFileFromPath( path ) );
cfcontent( variable = file, type = mimetype );
}
}
This will match paths:
/rest/FileStore/file1.pdf
/rest/FileStore/file2.jpg
But if you try sub-directories - i.e.
/rest/FileStore/subdir1/file3.xml
/rest/FileStore/subdir2/subsubdir1/file4.raw
It returns HTTP status 404 Not Found (as, I'm assuming, it cannot find a matching REST service).
Is there a way to get the rest path to match all sub-paths?
Use URI Rewriting to perform a redirect remove the slashes from the path.
An example in Apache (taken from this answer) would be:
RewriteEngine on
RewriteRule ^(/rest/FileStore/[^/]*)/([^/]*/.*)$ $1__$2 [N]
RewriteRule ^(/rest/FileStore/[^/]*)/([^/]*)$ $1__$2 [R=302]
The first rule will replace the first slash (with __) if there are multiple slashes in the path (and repeat); the second rule will replace the final slash and perform a (temporary) redirect.
In the service you can then re-rewrite the path to include the slashes.
remote void function getFile(
required string path restargsource = "Path"
)
httpmethod = "GET"
restpath = "{path}"
{
var actual_path = Replace( path, "__", "/", "ALL" );
var file = FileReadBinary( "/some/path/to/local/files/#actual_path#" );
var filename = getFileFromPath( actual_path );
var mimetype = customFunctionToGetMimeType( filename );
cfheader( name = "content-disposition", value = "inline; filename=#filename#" );
cfcontent( variable = file, type = mimetype );
}
There are several issues with this:
If you are navigating through files which contain relative path links to other files in different sub-directories then the URI rewriting breaks these links.
If the original path contains the string used to replace slashes then this will break the file path.
Write a REST service for each level of the subdirectory and, internally, point it back to the original service.
remote void function getSubdir1File(
required string subdir1 restargsource = "Path",
required string file restargsource = "Path"
)
httpmethod = "GET"
restpath = "{subdir1}/{file}"
{
getFile( subdir1 & '/' & file );
}
remote void function getSubdir2File(
required string subdir1 restargsource = "Path",
required string subdir2 restargsource = "Path",
required string file restargsource = "Path"
)
httpmethod = "GET"
restpath = "{subdir1}/{subdir2}/{file}"
{
getFile( subdir1 & '/' & subdir2 & '/' & file );
}
Repeating ad-nauseum until you get to a sufficient depth that you've covered 99.9999%/sufficient of the use cases.
This addresses the issues with URI rewriting (allows relative path links within files and for file names to use all non-slash characters).
It is not DRY.
If you get a file in a directory beyond the implemented depth then it will not be found. (Although it could be coupled with URI rewriting to allow the file to be found even if it does then break internal links for these limited cases.)
Jersey (JAX-RS), which is what ColdFusion appears to use under the hood for its REST services, allows regular expressions in its #PATH notation.
Using a regular expression to match all characters (restpath = "{path:.*}") then you can simply match all sub-paths:
component
restpath = ""
rest = true
{
remote void function getFile(
required string path restargsource = "Path"
)
httpmethod = "GET"
restpath = "{path:.*}"
{
var file = FileReadBinary( "/some/path/to/local/files/#path#" );
var mimetype = customFunctionToGetMimeType( getFileFromPath( path ) );
cfcontent( variable = file, type = mimetype );
}
}
Thanks to this answer for the inspiration

Instance variable isn't accessible from method

I'm new to CoffeeScript but I have written a class with a constructor which assigns attributes to this. In my function these attributes aren't defined. Does anybody have an idea?
class ProcessVisualisation
constructer: (width, devMode = false) ->
#objProcess = null
#config =
devMode: false
loadProcess: (processPath) ->
console.log("loadProcess") if (#config.devMode) # <- config is not defined
that = #;
that.processPath = processPath
$.getJSON #processPath, {}, (response) ->
that.onProcessLoaded response
pv = new ProcessVisualisation(1023, true)
pv.loadProcess "data/process.json"
As stated in the comments, fix constructer and the function arrow.
class ProcessVisualisation
constructor: (width, devMode = false) ->
#objProcess = null
#config =
devMode: false
loadProcess: (processPath) =>
console.log("loadProcess") if (#config.devMode)
#processPath = processPath
$.getJSON #processPath, {}, (response) =>
#onProcessLoaded response
pv = new ProcessVisualisation(1023, true)
pv.loadProcess "data/process.json"