Swagger / springfox generating response example automatically - rest

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;
}

Related

Using [google-text-to-speech] v1Beta1 version in c#

Does any one know how to use the V1Beta1 version in c#. It will be a great help if some one can point me to some example. Google has not created a Client for this version.
https://cloud.google.com/text-to-speech/docs/reference/rest/v1beta1/text/synthesize#TimepointType
I had the same problem (for Google.Apis.Texttospeech.v1) and could not find an example anywhere. I pieced it together from some Ruby Api docs, so, this works for me.
How to use Google.Apis.Texttospeech.v1 in C#:
var request = new Google.Apis.Texttospeech.v1.Data.SynthesizeSpeechRequest();
request.AudioConfig = new Google.Apis.Texttospeech.v1.Data.AudioConfig()
{
AudioEncoding = "Linear16" // = WAV, or "MP3", "OGG_OPUS"
};
request.Input = new Google.Apis.Texttospeech.v1.Data.SynthesisInput
{
Text = "Sample Speaker Text" // Or use Ssml = "<speak> Your SSML Text.
</speak>"
};
request.Voice = new Google.Apis.Texttospeech.v1.Data.VoiceSelectionParams
{
LanguageCode = "en-EN",
Name = "en-US-Wavenet-C"
};
var service = new Google.Apis.Texttospeech.v1.TexttospeechService(new
Google.Apis.Services.BaseClientService.Initializer
{
ApplicationName = "My Sample App",
ApiKey = "[Your API Key]"
}
);
var response = service.Text.Synthesize(request).Execute();
using (var output = System.IO.File.Create("audioclip.wav"))
{
var bytes = System.Convert.FromBase64String(response.AudioContent);
output.Write(bytes, 0, bytes.Length);
}

MapStruct protobuf List to Pojo Mapping

As part of GRPC api I am trying to map proto autogenerated classes to pojo. This is the .proto file
message AccountModelProto
{
repeated VerificationModelProto verification = 1;
}
message VerificationModelProto
{
string status = 1;
string comment = 2;
string verificationType = 3;
repeated VerificationAttributeModelProto verificationAttributes = 4;
}
message VerificationAttributeModelProto
{
string type = 1;
string label = 2;
bool attributeStatus = 3;
}
The mapper for the above code is. I referred to the examples provided by mapstruct according to it I don't need to provide explicit mapping of VerificationModelProto to List but I am getting compiler error
error: Can't map property "Collection verification" to "VerificationModelProto verificationList". Consider to declare/implement a mapping method: "VerificationModelProto map(Collection value)".
#Mapping(source = "verification", target = "verificationList", qualifiedByName = "verificationModelToVerificationProtoMapping")
AccountModelProto map(AccountModel accountModel);
#Named("verificationModelToVerificationProtoMapping")
default VerificationModelProto map (VerificationModel verificationModel)
{
VerificationModelProto.Builder builder = VerificationModelProto.newBuilder()
.setComment(verificationModel.getComment())
.setStatus(verificationModel.getStatus().toString())
.setVerificationType(verificationModel.getVerificationType());
for(VerificationAttributeModel verificationAttributeModel: verificationModel.getVerificationAttributes())
{
builder.addVerificationAttributes(getVerificationAttributeBuilder(verificationAttributeModel));
}
return builder.build();
}
default VerificationAttributeModelProto getVerificationAttributeBuilder(VerificationAttributeModel verificationAttributeModel)
{
VerificationAttributeModelProto.Builder builder = VerificationAttributeModelProto.newBuilder()
.setAttributeStatus(verificationAttributeModel.getAttributeStatus())
.setType(verificationAttributeModel.getType())
.setLabel(verificationAttributeModel.getLabel());
return builder.build();
}
How to get through this. I added CollectionMappingStrategy as CollectionMappingStrategy.ADDER_PREFERRED.

How to support tokenized and untokenized search at the same time

I try to make hibernate search to support both tokenized and untokenized search(pardon me if I use the wrong term here). An example is as following.
I have a list of entities of the following type.
#Entity
#Indexed
#NormalizerDef(name = "lowercase",
filters = {
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class)
}
)
public class Deal {
//other fields omitted for brevity purposes
#Field(store = Store.YES)
#Field(name = "name_Sort", store = Store.YES, normalizer= #Normalizer(definition="lowercase"))
#SortableField(forField = "name_Sort")
#Column(name = "NAME")
private String name = "New Deal";
//Getters/Setters omitted here
}
I also used the keyword method to build the query builder shown as follows. The getSearchableFields method returns a list of searchable fields. In the this example, "name" will be in this returned list as the field name in Deal is searchable.
protected Query inputFilterBuilder() {
return queryBuilder.keyword()
.wildcard().onFields(getSearchableFields())
.matching("*" + searchRequest.getQuery().toLowerCase() + "*").createQuery();
}
This setup works fine when I only use an entire words to search. For example, if I have two Deal entity, one's name is "Practical Concrete Hat" and the other one's name is "Practical Cotton Cheese". When searching by "Practical", I get these two entities back. But when searching by "Practical Co", I get 0 entity back. The reason is because the field name is tokenized and "Practical Co" is not a key word.
My question is how to support both search at the same time so these 2 entities are returned if searching by "Practical" or "Practical Co".
I read through the official hibernate search documentation and my hunch is that I should add one more field that is for untokenized search. Perhaps the way I construct the query builder needs to be updated as well?
Update
Not working solution using SimpleQueryString.
Based on the provided answer, I've written the following query builder logic. However, it doesn't work.
protected Query inputFilterBuilder() {
String[] searchableFields = getSearchableFields();
if(searchableFields.length == 0) {
return queryBuilder.simpleQueryString().onField("").matching("").createQuery();
}
SimpleQueryStringMatchingContext simpleQueryStringMatchingContext = queryBuilder.simpleQueryString().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
simpleQueryStringMatchingContext = simpleQueryStringMatchingContext.andField(searchableFields[i]);
}
return simpleQueryStringMatchingContext
.matching("\"" + searchRequest.getQuery() + "\"").createQuery();
}
Working solution using separate analyzer for query and phrase queries.
I found from the official documentation that we can use phrase queries to search for more than one word. So I wrote the following query builder method.
protected Query inputFilterBuilder() {
String[] searchableFields = getSearchableFields();
if(searchableFields.length == 0) {
return queryBuilder.phrase().onField("").sentence("").createQuery();
}
PhraseMatchingContext phraseMatchingContext = queryBuilder.phrase().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
phraseMatchingContext = phraseMatchingContext.andField(searchableFields[i]);
}
return phraseMatchingContext.sentence(searchRequest.getQuery()).createQuery();
}
This does not work for search using more than one word with a space in between. Then I added separate analyzers for indexing and querying as suggested, all of a sudden, it works.
Analyzers definitons:
#AnalyzerDef(name = "edgeNgram", tokenizer = #TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = EdgeNGramFilterFactory.class,
params = {
#Parameter(name = "minGramSize", value = "1"),
#Parameter(name = "maxGramSize", value = "10")
})
})
#AnalyzerDef(name = "edgeNGram_query", tokenizer = #TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class)
})
Annotation for Deal name field:
#Field(store = Store.YES, analyzer = #Analyzer(definition = "edgeNgram"))
#Field(name = "edgeNGram_query", store = Store.YES, analyzer = #Analyzer(definition = "edgeNGram_query"))
#Field(name = "name_Sort", store = Store.YES, normalizer= #Normalizer(definition="lowercase"))
#SortableField(forField = "name_Sort")
#Column(name = "NAME")
private String name = "New Deal";
Code that override name field's analyzer to use the query analyzer
String[] searchableFields = getSearchableFields();
if(searchableFields.length > 0) {
EntityContext entityContext = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity(this.getClass().getAnnotation(SearchType.class).clazz()).overridesForField(searchableFields[0], "edgeNGram_query");
for(int i = 1; i < searchableFields.length; i++) {
entityContext.overridesForField(searchableFields[i], "edgeNGram_query");
}
queryBuilder = entityContext.get();
}
Follow up question
Why does the above tweak actually works?
Your problem here is the wildcard query. Wildcard queries do not support tokenization: they only work on single tokens. In fact, they don't even support normalization, which is why you had to lowercase the user input yourself...
The solution would not be to mix tokenized and untokenized search (that's possible, but wouldn't really solve your problem). The solution would be to forget about wildcard queries altogether and use an edgengram filter in your analyzer.
See this answer for an extended explanation.
If you use the ELasticsearch integration, you will have to rely on a hack to make the "query-only" analyzer work properly. See here.

How to set TotalAmount for SalesReciept

I'm using QBO Rest API V3 SDK and trying to create a deposit onto an account. It seems there isn't a deposit transaction anymore, so am trying to use a SalesReciept to do so.
The call is succeeding and the transaction is created however the SalesReciept is returned with a TotalAmount of zero. When I look at the QBO application it shows a 0 Deposit amount as well.
I noticed there was a UnitPrice on the API, but was missing from the SDK, so I hand crafted a web request and it still came back with a 0.
If there is another approach I should take let me know.
var deposit = new SalesReceipt()
{
DepositToAccountRef = new ReferenceType()
{
Value = "1",
name = "MyAccount"
},
TxnDate = transaction.TransactionDate,
TxnDateSpecified = true,
TotalAmt = transaction.Amount,
TotalAmtSpecified = true,
Line = new[]
{
new Line()
{
Amount = transaction.Amount,
AmountSpecified = true,
Description = transaction.DisplayBody,
DetailType = LineDetailTypeEnum.SalesItemLineDetail,
DetailTypeSpecified = true,
AnyIntuitObject = new SalesItemLineDetail()
{
ItemRef = new ReferenceType(){
Value = qboIntegration.IncomeAccountId,
name = GetIncomeAccountName(),
},
Qty = 1,
QtySpecified = true,
TaxInclusiveAmt = transaction.Amount,
TaxInclusiveAmtSpecified = true,
ServiceDate = transaction.TransactionDate,
ServiceDateSpecified = true,
},
}
},
};
I've not tried this using .net sdk. You can try the following ( Ref - SO Thread).
AnyIntuitObject = new SalesItemLineDetail()
{
ItemElementName = ItemChoiceType.UnitPrice,
AnyIntuitObject = amount,
...
},
DetailType = LineDetailTypeEnum.SalesItemLineDetail
To get the correct object structure you can create a salesReceipt from QBO UI(with desired attribute value) and retrieve the same using getById endpoint.
To do the above, you can use the ApiExplorer tool as well.
https://developer.intuit.com/apiexplorer?apiname=V3QBO
Thanks
After contacting quickbooks. It is not possible with the current iteration (V3) of the api. They are considering adding this in the next version.

IPP v3 Invoice QueryService not working with Skip/Take when using non-default fields

I'm trying to query invoices using the .NET IPP DevKit v3.
Following all the directions found on the documentation site, I can query invoices and add skip/take/order by/where/etc to the query when using ONLY default fields. But, as soon as I add non-default fields, skip/take/order by/where/etc does NOT seem to work.
Here's the error:
System.ArgumentException was unhandled
HResult=-2147024809
Message=Expression of type 'System.Collections.Generic.IEnumerable`1[<>f__AnonymousType0`3[Intuit.Ipp.Data.Invoice,Intuit.Ipp.Data.Line[],Intuit.Ipp.Data.LinkedTxn[]]]' cannot be used for parameter of type 'System.Linq.IQueryable`1[<>f__AnonymousType0`3[Intuit.Ipp.Data.Invoice,Intuit.Ipp.Data.Line[],Intuit.Ipp.Data.LinkedTxn[]]]' of method 'System.Linq.IQueryable`1[<>f__AnonymousType0`3[Intuit.Ipp.Data.Invoice,Intuit.Ipp.Data.Line[],Intuit.Ipp.Data.LinkedTxn[]]] Skip[<>f__AnonymousType0`3](System.Linq.IQueryable`1[<>f__AnonymousType0`3[Intuit.Ipp.Data.Invoice,Intuit.Ipp.Data.Line[],Intuit.Ipp.Data.LinkedTxn[]]], Int32)'
Source=System.Core
What am I missing here?
Code:
string AppToken = "your AppToken goes here";
string AppConsumerKey = "your AppConsumerKey goes here";
string AppConsumerKeySecret = "your AppConsumerKeySecret goes here";
string AccessToken = "your AccessToken goes here";
string AccessTokenSecret = "your AccessTokenSecret goes here";
string RealmCompanyId = "your RealmId goes here";
OAuthRequestValidator oauthValidator = new OAuthRequestValidator(AccessToken, AccessTokenSecret, AppConsumerKey, AppConsumerKeySecret);
ServiceContext context = new ServiceContext(AppToken, RealmCompanyId, IntuitServicesType.QBD, oauthValidator);
QueryService<Intuit.Ipp.Data.Invoice> qs = new QueryService<Intuit.Ipp.Data.Invoice>(context);
// This works...
var defaultQuery = qs.Select(c => c).Skip(0).Take(10).OrderBy(c => c.Id);
var defaultList = defaultQuery.ToList();
// This works...
var nonDefaultQuery = qs.Select(c => new { c, c.Line, c.LinkedTxn });
var nonDefaultList = nonDefaultQuery.ToList();
// This does NOT work!!
var nonDefaultQueryWithSkip = qs.Select(c => new { c, c.Line, c.LinkedTxn }).Skip(0).Take(10);
var nonDefaultListWithSkip = nonDefaultQueryWithSkip.ToList();
I tried on the API explorer-
Select *,Line.*, LinkedTxn.* FROM Invoice startPosition 1 maxResults 10 (which is your last query) and it works fine but not from .net sdk. I will double check this on the .net SDK and get back to you. Can you verify that you get the correct results on API explorer from this query?
This now works in the latest version (IppDotNetSdkForQuickBooksApiV3.2.0.0)
Here's an example:
QueryService<Intuit.Ipp.Data.Invoice> qs = new QueryService<Intuit.Ipp.Data.Invoice>(context);
string query = string.Format("SELECT *, Line.* FROM Invoice ORDERBY Id STARTPOSITION {0} MAXRESULTS {1}", startPos, pageSize);
var recs = qs.ExecuteIdsQuery(query);
foreach (Intuit.Ipp.Data.Invoice rec in recs)
{
// do stuff...
}
.