Nancy OnError will not accept a Response object? - json

The Nancy documentation seems to say that Pipelines.OnError should return null - as opposed to BeforeResponse which allows both null and a Response object.
All the examples like this one and many code samples here on StackOverflow show a Response being returned in the OnError, just like in the BeforeRequest.
When I attempt to return an HTTPStatus string for the Pipelines.OnError, everything works OK!
But when I attempt to return a Response, I get a compiler error:
Operator '+=' cannot be applied to operands of type 'Nancy.ErrorPipeline' and 'lambda expression'
I'm emulating almost exactly the code in the Nancy example, except for the fact that mine is a TinyIocContainer while the example's is using a StructureMap container and a StructureMap derived bootstrapper
Here's my code:
const string errKey = "My proj error";
const string creationProblem = "Message creation (HTTP-POST)";
const string retrievalProblem = "Message retrieval (HTTP-GET)";
public void Initialize(IPipelines pipelines)
{
string jsonContentType = "application/json";
byte[] jsonFailedCreate = toJsonByteArray(creationProblem);
byte[] jsonFailedRetrieve = toJsonByteArray(retrievalProblem);
Response responseFailedCreate = new Response
{
StatusCode = HttpStatusCode.NotModified,
ContentType = jsonContentType,
Contents = (stream) =>
stream.Write(jsonFailedCreate, 0, jsonFailedCreate.Length)
};
Response responseFailedRetrieve = new Response
{
StatusCode = HttpStatusCode.NotFound,
ContentType = jsonContentType,
Contents = (stream) =>
stream.Write(jsonFailedRetrieve, 0, jsonFailedRetrieve.Length)
};
// POST - error in Create call
pipelines.OnError += (context, exception) =>
{
// POST - error during Create call
if (context.Request.Method == "POST")
return responsefailedCreate;
// GET - error during Retrieve call
else if (context.Request.Method == "GET")
return responseFailedRetrieve;
// All other cases - not supported
else
return HttpStatusCode.InternalServerError;
};
}
private byte[] toJsonByteArray(string plainString)
{
string jsonString = new JObject { { errKey, plainString } }.ToString();
byte[] result = Encoding.UTF8.GetBytes(jsonString);
return result;
}

I had the same problem and I found a nice approach to the problem: http://paulstovell.com/blog/consistent-error-handling-with-nancy.
you should override RequestStartup on the Bootstrapper, here my test code:
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.OnError.AddItemToEndOfPipeline((ctx, ex) =>
{
DefaultJsonSerializer serializer = new DefaultJsonSerializer();
Response error = new JsonResponse(ex.Message,serializer);
error.StatusCode = HttpStatusCode.InternalServerError;
return error;
});
base.RequestStartup(container, pipelines, context);
}

Related

Flutter compute method and filter results in json

I'm a really fresh new dev in flutter, I'm trying to filter results in a json response based on the title.
And i would like to use the compute method. This method only allow 2 arguments, so it's why i think i need to use a map to get the response.body and the query for filtering.
This is my HttpService :
class HttpService {
static List<Post> _parsePosts(Map map) {
print(map);
return map["body"].map((json) => Post.fromJson(json)).where((post) {
final titleLower = post.title.toLowerCase();
final queryLower = map["query"].toLowerCase();
return titleLower.contains(queryLower);
}).toList();
}
static Future<List<Post>> fetchPosts(String query) async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await retry(
() => http.get(uri).timeout(Duration(seconds: 10)),
retryIf: (e) => e is SocketException || e is TimeoutException,
);
Map posts = new Map();
posts["body"] = json.decode(response.body);
posts["query"] = query;
if (response.statusCode == 200) {
return compute(_parsePosts, posts);
} else {
throw Exception("Failed to load posts ${response.statusCode}");
}
}
}
The print(map); contains the map with values -> https://prnt.sc/131ldey
But the problem is reported on : return map["body"].map((json) => Post.fromJson(json)).where((post) {
with : _TypeError (type '(dynamic) => dynamic' is not a subtype of type '(dynamic) => bool' of 'test')
I really don't understand what is the reason of this error..
Your titleLower is not casted into a String type! So the contains method returns a dynamic value since it does not appear as the contains method of the String type for the VM !
final String myValue = ...;
return myValue.contains(‘a’);

TableContinuationToken not getting Deserialised from JSON correctly

I am having trouble trying to retrieve large datasets from Azure TableStorage. After several attempts at trying to get it in one go I have given up and am now using the TableContinuation Token, which is now not getting Deserialized correctly.The object is getting created but all the Next... values (i.e. NextRowKey, NextPartitionKey, etc are NULL, when the in stringresponse that gets created you can see the values it should be populating with...
The class I am passing contains a list of objects and the token
public class FlorDataset
{
public List<FlorData> Flors { get; set; }
public TableContinuationToken Token { get; set; }
}
The controller code is not exactly rocket science either....
[HttpGet, Route("api/list/{token}")]
public IHttpActionResult FindAll(string token)
{
try
{
TableContinuationToken actualToken = token == "None"
? null
: new TableContinuationToken()
{
NextPartitionKey = NextPartition,
NextRowKey = token,
NextTableName = NextTableName
};
var x = Run(actualToken);
Flors = x.Flors;
actualToken = x.Token;
NextTableName = actualToken.NextTableName;
NextPartition = actualToken.NextPartitionKey;
return Flors != null
? (IHttpActionResult)new IsoncOkResult<FlorDataset>(x, this)
: NotFound();
}
catch (Exception ex)
{
Trace.TraceError(ex.ToString());
return NotFound();
}
}
private FlorDataset Run(TableContinuationToken token)
{
return _repo.GetAllByYear("2016", token) as FlorDataset;
}
The calling code, which calls my fairly standard Web API 2 Controller is:
do
{
try
{
HttpResponseMessage response = null;
if (string.IsNullOrEmpty(token.NextRowKey))
{
response = await client.GetAsync("api/list/None");
}
else
{
response = await client.GetAsync($"api/list/{token.NextRowKey}");
}
if (response.IsSuccessStatusCode)
{
var stringresponse = await response.Content.ReadAsStringAsync();
var ds = JsonConvert.DeserializeObject<FlorDataset>(stringresponse);
token = ds.Token;
Flors.AddRange(ds.Flors);
}
else
{
token = null;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
token = null;
}
} while (token != null);
Okay, this is not the greatest solution, but it's the only thing that works so far in case anyone else is trying the same and stumbling across my question....
In the calling code bit you do a horrible bit of string replacement before you do the deserialisation.... I actually feel dirty just posting this, so if anyone comes up with a better answer, please feel free to share.....
if (response.IsSuccessStatusCode)
{
var stringresponse = await response.Content.ReadAsStringAsync();
stringresponse = stringresponse.Replace(">k__BackingField", "");
stringresponse = stringresponse.Replace("<", "");
var ds = JsonConvert.DeserializeObject<FlorDataset>(stringresponse);
token = ds.Token;
Flors.AddRange(ds.Flors);
}
Not nice, not pretty, but does work!!!! :-D Going to wash my fingers with bleach now!!!

How to return an Error from a generic method

The title doesn't describe the issue very well so let me explain my problem.
I have a generic function to consume API's via HTTP GET calls. Which looks like this:
public async static Task<T> GetAsync<T>(string Base_Url,string relative_URL, Utility.UriExtensions.NameValueCollection Params, Utility.UriExtensions.NameValueCollection headers = null)
{
Uri CompleteURL = new Uri(Base_Url + relative_URL, UriKind.Absolute);
if (Params != null)
CompleteURL = Utility.UriExtensions.CreateUriWithQuery(CompleteURL, Params);
if(headers!=null)
{
foreach(KeyValuePair<string,string> k in headers)
{
if (ApiHttpClient.DefaultRequestHeaders.ContainsKey(k.Key))
ApiHttpClient.DefaultRequestHeaders[k.Key] = k.Value;
else
ApiHttpClient.DefaultRequestHeaders.Add(k.Key, k.Value);
}
}
Debug.WriteLine("GET : " + CompleteURL);
using (var response = await ApiHttpClient.GetAsync(CompleteURL).AsTask(cancellationToken.Token).ConfigureAwait(false))
{
string responseData="";
if (response.IsSuccessStatusCode)
{
responseData = await response.Content.ReadAsStringAsync();
Debug.WriteLine(responseData);
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.DateParseHandling = DateParseHandling.DateTime;
settings.DefaultValueHandling = DefaultValueHandling.Populate;
settings.NullValueHandling = NullValueHandling.Include;
settings.TypeNameHandling = TypeNameHandling.None;
}
try
{
response.EnsureSuccessStatusCode();
return JsonConvert.DeserializeObject<T>(responseData);
}
catch
{
// Error
Debug.WriteLine(
"Error occurred, the status code is: {0} and Content : {1}",
response.StatusCode, response.Content);
}
return default(T);
}
}
In case of error, the error is handled inside catch body. I want to return these Error informations like StatusCode and Content of response when this happens. But I am unable to make any changes to this generic function. How should handle this.
For this purpose you can use out parameter, which you will pass in your method:
public async static Task<T> GetAsync<T>(string Base_Url,string relative_URL, Utility.UriExtensions.NameValueCollection Params, Utility.UriExtensions.NameValueCollection headers = null, out StatusCode code)
StatusCode statusCode;
GetAsync<Foo>(..., out statusCode);

best overloaded method match for `RestSharp.Deserialize<RootObject>(RestSharp.IRestResponse)' has some invalid arguments

So i am working this project on Xamarin forms, and get the error as in title on
var rootObject = deserial.Deserialize<RootObject>(gameJson);
I am supposed to return the list of games to my app.How can i remove the error?
public async Task<Game[]> GetGamesAsync(){
var client = new RestClient("http://mystore/");
var request = new RestRequest ("api/Games", Method.GET);
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
var apiKey = session ["ApiKey"];
var userId = session ["UserId"];
try
{
request.AddHeader ("authenticationkey",apiKey.ToString ());
request.AddHeader ("authenticationid",userId.ToString ());
}
catch{}
IRestResponse response = client.Execute (request);
statusCodeCheck (response);
var gameJson = response.Content;
if (response.StatusCode == HttpStatusCode.OK) {
RestSharp.Deserializers.JsonDeserializer deserial = new RestSharp.Deserializers.JsonDeserializer ();
var rootObject = deserial.Deserialize<RootObject>(gameJson);
return rootObject.games;
}
else if(response.StatusCode == HttpStatusCode.Forbidden){
return null;
}
}
Not sure you are looking for this but I also using Restsharp in portable library and I'm deserializing datacontracts with Json.NET's JsonConvert.DeserializeObject<T>
method. I have not encountered any problem with it yet.
Also another possible solution is that the returned data is wrapped and the main object is not the RootObject.

APEX, Unit Test, Callout No Response with Static Resource

Bit stuck on another one i'm afraid, i am trying to write a unit test for a bulk APEX class.
The class has a calllout to the google api, so i have created a static resource which i am feeding in via a mock, so i can complete testing of processing the JSON that is returned. However for some reason the response is always empty.
Now the very odd thing is that if i use exactly the same callout/JSON code, and the same mock code on a previous #future call, then it does return fine.
Here is the class:
global class mileage_bulk implements Database.Batchable<sObject>,
Database.AllowsCallouts
{
global Database.QueryLocator start(Database.BatchableContext BC)
{
String query = 'SELECT Id,Name,Amount,R2_Job_Ref__c,R2_Shipping_Post_Code__c,Shipping_Postcode_2__c FROM Opportunity WHERE R2_Shipping_Post_Code__c != null';
return Database.getQueryLocator(query);
//system.debug('Executing'+query);
}
global void execute(Database.BatchableContext BC, List<Opportunity> scope)
{
system.debug(scope);
for(Opportunity a : scope)
{
String startPostcode = null;
startPostcode = EncodingUtil.urlEncode('HP27DU', 'UTF-8');
String endPostcode = null;
String endPostcodeEncoded = null;
if (a.R2_Shipping_Post_Code__c != null){
endPostcode = a.R2_Shipping_Post_Code__c;
Pattern nonWordChar = Pattern.compile('[^\\w]');
endPostcode = nonWordChar.matcher(endPostcode).replaceAll('');
endPostcodeEncoded = EncodingUtil.urlEncode(endPostcode, 'UTF-8');
}
Double totalDistanceMeter = null;
Integer totalDistanceMile = null;
String responseBody = null;
Boolean firstRecord = false;
String ukPrefix = 'UKH';
if (a.R2_Job_Ref__c != null){
if ((a.R2_Job_Ref__c).toLowerCase().contains(ukPrefix.toLowerCase())){
system.debug('Is Hemel Job');
startPostcode = EncodingUtil.urlEncode('HP27DU', 'UTF-8');
} else {
system.debug('Is Bromsgrove Job');
startPostcode = EncodingUtil.urlEncode('B604AD', 'UTF-8');
}
}
// build callout
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('http://maps.googleapis.com/maps/api/directions/json?origin='+startPostcode+'&destination='+endPostcodeEncoded+'&units=imperial&sensor=false');
req.setMethod('GET');
req.setTimeout(60000);
system.debug('request follows');
system.debug(req);
try{
// callout
HttpResponse res = h.send(req);
// parse coordinates from response
JSONParser parser = JSON.createParser(res.getBody());
responseBody = res.getBody();
system.debug(responseBody);
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
(parser.getText() == 'distance')){
parser.nextToken(); // object start
while (parser.nextToken() != JSONToken.END_OBJECT){
String txt = parser.getText();
parser.nextToken();
//system.debug(parser.nextToken());
//system.debug(txt);
if (firstRecord == false){
//if (txt == 'text'){
//totalDistanceMile = parser.getText();
system.debug(parser.getText());
//}
if (txt == 'value'){
totalDistanceMeter = parser.getDoubleValue();
double inches = totalDistanceMeter*39.3701;
totalDistanceMile = (integer)inches/63360;
system.debug(parser.getText());
firstRecord = true;
}
}
}
}
}
} catch (Exception e) {
}
//system.debug(accountId);
system.debug(a);
system.debug(endPostcodeEncoded);
system.debug(totalDistanceMeter);
system.debug(totalDistanceMile);
// update coordinates if we get back
if (totalDistanceMile != null){
system.debug('Entering Function to Update Object');
a.DistanceM__c = totalDistanceMile;
a.Shipping_Postcode_2__c = a.R2_Shipping_Post_Code__c;
//update a;
}
}
update scope;
}
global void finish(Database.BatchableContext BC)
{
}
}
and here is the test class;
#isTest
private class mileage_bulk_tests{
static testMethod void myUnitTest() {
Opportunity opp1 = new Opportunity(name = 'Google Test Opportunity',R2_Job_Ref__c = 'UKH12345',R2_Shipping_Post_Code__c = 'AL35QW',StageName = 'qualified',CloseDate = Date.today());
insert opp1;
Opportunity opp2 = new Opportunity(name = 'Google Test Opportunity 2',StageName = 'qualified',CloseDate = Date.today());
insert opp2;
Opportunity opp3 = new Opportunity(name = 'Google Test Opportunity 3',R2_Job_Ref__c = 'UKB56789',R2_Shipping_Post_Code__c = 'AL35QW',StageName = 'qualified',CloseDate = Date.today());
insert opp3;
StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
mock.setStaticResource('googleMapsJSON');
mock.setStatusCode(200); // Or other appropriate HTTP status code
mock.setHeader('Content-Type', 'application/json'); // Or other appropriate MIME type like application/xml
//Set the mock callout mode
Test.setMock(HttpCalloutMock.class, mock);
system.debug(opp1);
system.debug(opp1.id);
//Call the method that performs the callout
Test.startTest();
mileage_bulk b = new mileage_bulk();
database.executeBatch((b), 10);
Test.stopTest();
}
}
Help greatly appreciated!
Thanks
Gareth
Not certain what 'googleMapsJSON' looks like, perhaps you could post for us.
Assuming your mock resource is well formatted, make sure the file extension is ".json" and was saved with UTF-8 encoding.
If #2 does not work, you should try saving your resource as .txt - I've run in to this before where it needed a plain text resource but expected application/json content type
Be certain that the resource name string you are providing has the same casing as the name of the resource. It is case sensitive.
Are you developing on a namespaced package environment? Try adding the namespace to the resource name if so.
Otherwise, your code looks pretty good at first glance.