I've been trying to document this API with Swashbuckle.Example. I have followed this tutorial. The SwaggerResponseExample attribute works but the SwaggerRequestExample is not rendered. I have also checked the json in /docs/v1 and again the Response Example was present but not the Request Example.
What's going on?
How do I debug this?
Controller.cs
[System.Web.Http.HttpPost]
[SwaggerOperation(OperationId = "SAAT_ciclotarifario_GetById")]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(GrupoFtFronteiraPostResponseExamples))]
[SwaggerRequestExample(typeof(GrupoFtFronteira), typeof(GrupoFtFronteiraPostRequestExamples))]
[ActionName("GrupoFtFronteira")]
public HttpResponseMessage PostGrupoFtFronteira([FromBody] GrupoFtFronteira requestBody)
{
try
{
var novoGrupoFtFronteira = _grupofronteiraNegocio.PostGrupoFtFronteira(requestBody);
return new HttpResponseMessage(HttpStatusCode.OK)
{
ReasonPhrase = "Success",
Content = new StringContent("idGrupoFtFronteira: " + novoGrupoFtFronteira.IdGrupoftfronteira, System.Text.Encoding.UTF8, "application/json")
};
}
catch (Exception e)
{
var msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
ReasonPhrase = "Internal Server Error",
Content = new StringContent("Erro ao tentar atualizar dados. " + e.Message, System.Text.Encoding.UTF8, "application/json")
};
throw new HttpResponseException(msg);
}
}
SwaggerConfig.cs
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.DocumentFilter<SwaggerFilterOutControllers>();
c.SingleApiVersion("v1", "ons.sact.servico.controller");
c.ApiKey("Authorization")
.Description("JWT token")
.Name("Bearer")
.In("header");
c.OperationFilter<ExamplesOperationFilter>();
})
.EnableSwaggerUi(c =>
{
c.EnableApiKeySupport("apiKey", "header");
});
}
}
I found it!
It seems that I was supposed to fill the Request Type inside this foreach ( KeyValuePair<string, Swashbuckle.Swagger.Schema> definition in schemaRegistry.Definitions)
There's probably a better way around this, since this is a hardcoded solution...
Anyway, thanks a lot for those who helped me out!
SwaggerFilterOutControllers.cs
void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
var models = new List<string>();
foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
{
Console.WriteLine(apiDescription.Route.RouteTemplate);
if (apiDescription.RelativePathSansQueryString().StartsWith("api/v1/")
&& !(apiDescription.RelativePathSansQueryString().StartsWith("api/v1/SAAT/ciclotarifario")
|| apiDescription.RelativePathSansQueryString().StartsWith("api/v1/SAAT/GrupoFtFronteira")))
{
string path = "/" + apiDescription.RelativePathSansQueryString();
swaggerDoc.paths.Remove(path);
}
}
foreach ( KeyValuePair<string, Swashbuckle.Swagger.Schema> definition in schemaRegistry.Definitions)
{
Console.WriteLine(definition.Key);
if (!(definition.Key.Equals("CiclotarifarioPostRequest") ||
definition.Key.Equals("GrupoFtFronteira")
))
{
models.Add(definition.Key.ToString());
}
}
foreach (string modelo in models)
{
swaggerDoc.definitions.Remove(modelo);
}
CreateHeadersDict();
var allOtherPaths = swaggerDoc.paths
.Select(entry => entry.Value)
.ToList();
foreach (var path in allOtherPaths)
{
AddHeadersToPath(path, HeaderType.TokenAuth);
}
}
Related
I was using the Microsoft Graph API 1.0 but have updated to the Beta in order to use CustomSecurityAttributeValue support.
I've managed to port most of the code but I can't see any way to process multiple results pages.
Previously you would just do something like
if (membersPage.NextPageRequest != null)
membersPage = await membersPage.NextPageRequest.GetAsync();
But NextPageRequest no longer exists, the only available information is OdataNextLink which is a string with no obvious way to request the next page or create a raw request using the url.
Code I have so far:
public async Task<IEnumerable<Microsoft.Graph.Beta.Models.User>> GetGraphUsersInGroups(IEnumerable<string> groupIds, string? searchText = null)
{
Dictionary<String, Microsoft.Graph.Beta.Models.User> users = new Dictionary<String, Microsoft.Graph.Beta.Models.User>();
foreach (var groupId in groupIds)
{
try
{
var membersPage = await GraphClient.Groups[groupId].Members
.GetAsync((memberRequest) => {
memberRequest.Headers.Add(new KeyValuePair<string, string>("$count", "true"));
memberRequest.Headers.Add(new KeyValuePair<string, string>("ConsistencyLevel", "eventual"));
memberRequest.QueryParameters.Count = true;
memberRequest.QueryParameters.Orderby = new[] { "displayName" };
if (searchText != null)
memberRequest.QueryParameters.Search = $"\"displayName:{searchText}\"";
});
while (membersPage != null)
{
foreach (var member in membersPage.Value.OfType<Microsoft.Graph.Beta.Models.User>())
{
users[member.Id] = member;
}
if (membersPage.OdataNextLink != null)
{
// How to use membersPage.OdataNextLink???
}
else
break;
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
return users.Values;
}
You should use the PageIterator, see an example below:
var users = new List<User>();
var userResponse = await serviceClient.Users.GetAsync((builder) => {
// builder.SomeStuff
});
// Added the namespace here, just for some clarity :-)
var pageIterator = Microsoft.Graph.PageIterator<User,UserCollectionResponse>
.CreatePageIterator(serviceClient, userResponse, (user) =>
{
users.Add(user.Id);
return true; });
I'm working on an application based on .NET Framework 4.8. I'm using Microsoft Batching API. The below are code snippets
public async Task<List<BatchResponse>> UpdateEventsInBatchAsync(string accessToken, Dictionary<int, Tuple<string, OfficeEvent>> absEvents)
{
var httpMethod = new HttpMethod("PATCH");
var batches = GetUpdateRequestBatches(absEvents, httpMethod);
var graphClient = GetGraphClient(accessToken);
var batchResponses = new List<BatchResponse>();
foreach (var batch in batches)
{
try
{
var batchResponseList = await ExecuteBatchRequestAsync(graphClient, batch).ConfigureAwait(false);
batchResponses.AddRange(batchResponseList);
}
catch (ClientException exc)
{
_logService.LogException("Error while processing update batch", exc);
batchResponses.Add(new BatchResponse
{ StatusCode = HttpStatusCode.InternalServerError, ReasonPhrase = exc.Message });
}
catch (Exception exc)
{
_logService.LogException("Error while processing update batch", exc);
batchResponses.Add(new BatchResponse { StatusCode = HttpStatusCode.InternalServerError, ReasonPhrase = exc.Message });
}
}
return batchResponses;
}
The respective methods used in the above code are mentioned below in respective order-
GetUpdateRequestBatches
private IEnumerable<BatchRequestContent> GetUpdateRequestBatches(Dictionary<int, Tuple<string, OfficeEvent>> absEvents, HttpMethod httpMethod)
{
var batches = new List<BatchRequestContent>();
var batchRequestContent = new BatchRequestContent();
const int maxNoBatchItems = 20;
var batchItemsCount = 0;
foreach (var kvp in absEvents)
{
System.Diagnostics.Debug.Write($"{kvp.Key} --- ");
System.Diagnostics.Debug.WriteLine(_serializer.SerializeObject(kvp.Value.Item2));
var requestUri = $"{_msOfficeBaseApiUrl}/me/events/{kvp.Value.Item1}";
var httpRequestMessage = new HttpRequestMessage(httpMethod, requestUri)
{
Content = _serializer.SerializeAsJsonContent(kvp.Value.Item2)
};
var requestStep = new BatchRequestStep(kvp.Key.ToString(), httpRequestMessage);
batchRequestContent.AddBatchRequestStep(requestStep);
batchItemsCount++;
// Max number of 20 request per batch. So we need to send out multiple batches.
if (batchItemsCount > 0 && batchItemsCount % maxNoBatchItems == 0)
{
batches.Add(batchRequestContent);
batchRequestContent = new BatchRequestContent();
batchItemsCount = 0;
}
}
if (batchRequestContent.BatchRequestSteps.Count < maxNoBatchItems)
{
batches.Add(batchRequestContent);
}
if (batches.Count == 0)
{
batches.Add(batchRequestContent);
}
return batches;
}
GetGraphClient
private static GraphServiceClient GetGraphClient(string accessToken)
{
var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
return graphClient;
}
ExecuteBatchRequestAsync
private async Task<List<BatchResponse>> ExecuteBatchRequestAsync(IBaseClient graphClient, BatchRequestContent batch)
{
BatchResponseContent response = await graphClient.Batch.Request().PostAsync(batch);
Dictionary<string, HttpResponseMessage> responses = await response.GetResponsesAsync();
var batchResponses = new List<BatchResponse>();
var failedReqKeys = new Dictionary<string, TimeSpan>();
foreach (var key in responses.Keys)
{
using (HttpResponseMessage httpResponseMsg = await response.GetResponseByIdAsync(key))
{
var responseContent = await httpResponseMsg.Content.ReadAsStringAsync();
string eventId = null;
var reasonPhrase = httpResponseMsg.ReasonPhrase;
if (!string.IsNullOrWhiteSpace(responseContent))
{
var eventResponse = JObject.Parse(responseContent);
eventId = (string)eventResponse["id"];
// If still null, then might error have occurred
if (eventId == null)
{
var errorResponse = _serializer.DeserializeObject<ErrorResponse>(responseContent);
var error = errorResponse?.Error;
if (error != null)
{
if (httpResponseMsg.StatusCode == (HttpStatusCode)429)
{
System.Diagnostics.Debug.WriteLine($"{httpResponseMsg.StatusCode} {httpResponseMsg.Content}");
var executionDelay = httpResponseMsg.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(5);
failedReqKeys.Add(key, executionDelay);
continue;
}
reasonPhrase = $"{error.Code} - {error.Message}";
}
}
}
var batchResponse = new BatchResponse
{
Key = key,
EventId = eventId,
StatusCode = httpResponseMsg.StatusCode,
ReasonPhrase = reasonPhrase
};
batchResponses.Add(batchResponse);
}
}
if (failedReqKeys.Count == 0) return batchResponses;
return await HandleFailedRequestsAsync(graphClient, failedReqKeys, batch, batchResponses).ConfigureAwait(false);
}
HandleFailedRequestsAsync
private async Task<List<BatchResponse>> HandleFailedRequestsAsync(IBaseClient graphClient, Dictionary<string, TimeSpan> failedReqKeys, BatchRequestContent batch, List<BatchResponse> batchResponses)
{
// Sleep for the duration as suggested in RetryAfter
var sleepDuration = failedReqKeys.Values.Max();
Thread.Sleep(sleepDuration);
var failedBatchRequests = batch.BatchRequestSteps.Where(b => failedReqKeys.Keys.Contains(b.Key)).ToList();
var failedBatch = new BatchRequestContent();
foreach (var kvp in failedBatchRequests)
{
failedBatch.AddBatchRequestStep(kvp.Value);
}
var failedBatchResponses = await ExecuteBatchRequestAsync(graphClient, failedBatch);
batchResponses.AddRange(failedBatchResponses);
return batchResponses;
}
I'm getting an error as on the first line in method ExecuteBatchRequestAsync as
Microsoft.Graph.ClientException: Code: invalidRequest
Message: Unable to deserialize content.
---> System.ObjectDisposedException: Cannot access a closed Stream.
Can anyone nudge me where I'm doing wrong?
I'm trying to update some fields by script. In Postman I'm sending the following request:
https://search.test.com/items_example/item/en_01_2/_update
the request's body:
{
"script": {
"inline": "ctx._source.Title = params.Title; ctx._source.Desc = params.Desc",
"params": {
"Title": "New Title",
"Desc": "New Desc"
}
}
}
But I have no idea how I can send this request using NEST. Can someone please help me?
Elasticsearch 5.4.1 version, NEST 5.6.1
Update it with your index settings & query
var elasticClient = new ElasticClient(settings);
var scriptParams = new Dictionary<string, object>
{
{"Title", "New Title"},
{"Desc", "New Desc"}
};
var response = elasticClient
.UpdateByQuery<dynamic>(q => q.Query(rq => rq.Term(....))
.Script(script =>
script.Inline(
$"ctx._source.Title = params.Title;" +
$"ctx._source.Desc = params.Desc ;"
)
.Params(scriptParams));
Edit: If you're looking for just Update, just change the syntax to
var response = elasticClient.Update<dynamic>(
"items_example/item/en_01_2" //This is your document path
, request => request.Script(
script =>
script.Inline(
$"ctx._source.Title = params.Title;" +
$"ctx._source.Desc = params.Desc ;"
)
.Params(scriptParams)));
If someone is here looking for the solution with NEST 6x version, please see below
public async Task UpdateId(MetaData request)
{
try
{
var scriptParams = new Dictionary<string, object>
{
{ "Id",request.Id}
};
var script = $"ctx._source.Id= params.Id;";
var indexResponse =
await EsClient.UpdateByQueryAsync<Doc>(
qd => qd.Index(request.IndexName)
.Conflicts(Conflicts.Proceed)
.Query(
rq => rq.Term("_id", request._id))
.Script(
s => s.Source(script)
.Params(scriptParams)
)
)
);
if (!indexResponse.IsValid)
{
}
}
catch (Exception ex)
{
}
}
[ElasticsearchType(Name = "_doc")]
public class Doc
{
}
I am trying to submit a BatchRequest and unsure of how to apply a filter or how to handle the callback.
class Program
{
static void Main(string[] args)
{
try
{
new Program().Run().Wait();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
Console.Read();
}
private async Task Run()
{
var privatekey = "private key";
var accountEmailAddress = "email address";
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(accountEmailAddress) {
Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
}.FromPrivateKey(privatekey));
var service = new AnalyticsService(new BaseClientService.Initializer() {
HttpClientInitializer = credentials,
ApplicationName = "Test"
});
var request = new BatchRequest(service);
request.Queue<DataResource.GaResource.GetRequest>(service.Data.Ga.Get("ga:XXXXXX", "30daysAgo", "yesterday", "ga:sessions"),
(content, error, i, message) =>
{
//callback code
});
await request.ExecuteAsync();
}
}
Two Questions:
How do I apply the following filter to the request?
ga:pagePath==/page1.html
How do I handle the callback and view the data returned?
Update
I believe I have solved the filter issue with the following code:
var req = service.Data.Ga.Get("ga:XXXXXX", "30daysAgo", "yesterday", "ga:sessions");
req.Filters = "ga:pagePath==/page1.html";
request.Queue<DataResource.GaResource.GetRequest>(req,
(content, error, i, message) =>
{
//callback code
});
I am still unsure of how to handle the callback. The "content" parameter is returned as the class: Google.Apis.Analytics.v3.DataResource.GaResource.GetRequest
I was finally able to get this working.
For future reference for anyone, here is a working .Net example of submitting a BatchRequest to the Analytics API.
class Program
{
static void Main(string[] args)
{
try
{
new Program().Run().Wait();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
Console.Read();
}
private async Task Run()
{
var privatekey = "private key";
var accountEmailAddress = "email address";
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(accountEmailAddress)
{
Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
}.FromPrivateKey(privatekey));
var service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "Test"
});
var request = new BatchRequest(service);
BatchRequest.OnResponse<GaData> callback = (content, error, i, message) =>
{
if (error != null)
{
Console.WriteLine("Error: {0}", error.Message);
}
else
{
if (content.Rows != null)
{
foreach (var item in content.Rows)
{
foreach (var item1 in item)
{
Console.WriteLine(item1);
}
}
}
else
{
Console.WriteLine("Not Found");
}
}
};
int counter = 0;
while (counter < 5)
{
var req = service.Data.Ga.Get("ga:XXXXX", "30daysAgo", "yesterday", "ga:sessions");
req.Filters = "ga:pagePath==/page" + counter + ".html";
request.Queue<GaData>(req, callback);
counter++;
}
await request.ExecuteAsync();
}
}
So I am not familiar with C# but I did look at the source code for the get method. And the GetRequest object has a filters property which you can set.
var request = service.Data.Ga.Get("ga:XXXXXX",
"30daysAgo", "yesterday", "ga:sessions");
request.dimensions("ga:pagePath");
request.filters("ga:pagePath==/page1.html");
I would encourage you to also look at the Core Reporting API reference for the full set of parameters.
I would imagine that the content object in the callback method would correspond to the GaData response object
I hope that helps some.
Yesterday I've found out how to create several async http requests without async/await. But today I need to do it in a loop: if some of responses don't satisfy some condition - I need to change a request for them and send these requests again. It may be repeated several times.
I've tried this code:
do
{
var loadingCoordinatesTasks = new List<Task<Terminal>>();
var totalCountOfTerminals = terminalPresetNode.ChildNodes.Count;
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
foreach (var terminal in terminals.Except(_terminalsWithCoordinates))
{
var address = terminal.GetNextAddress();
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(antecedent =>
{
// Parse the response
});
loadingCoordinatesTasks.Add(parsingTask);
}
Task.Factory.ContinueWhenAll(loadingCoordinatesTasks.ToArray(), antecedents =>
{
foreach (var antecedent in antecedents)
{
var terminalWithCoordinates = antecedent.Result;
if (antecedent.Status == TaskStatus.RanToCompletion &&
!terminalWithCoordinates.Coordinates.AreUnknown)
{
_terminalsWithCoordinates.Add(terminalWithCoordinates);
_countOfProcessedTerminals++;
}
}
});
} while (_countOfProcessedTerminals < totalCountOfTerminals);
but is it possible to check the condition in while just after every single set of requests executed?
You can perform the check after increasing the count:
_countOfProcessedTerminals++;
if (_countOfProcessedTerminals >= totalCountOfTerminals)
{
break;
}
Is _countOfProcessedTerminals thread-safe though?
I manage to do it using recursion:
public void RunAgainFailedTasks(IEnumerable<Task<Terminal>> tasks)
{
Task.Factory.ContinueWhenAll(tasks.ToArray(), antecedents =>
{
var failedTasks = new List<Task<Terminal>>();
foreach (var antecedent in antecedents)
{
var terminal = antecedent.Result;
// Previous request was failed
if (terminal.Coordinates.AreUnknown)
{
string address;
try
{
address = terminal.GetNextAddress();
}
catch (FormatException) // No versions more
{
continue;
}
var getCoordinatesTask = CreateGetCoordinatesTask(terminal, address);
failedTasks.Add(getCoordinatesTask);
}
else
{
_terminalsWithCoordinates.Add(terminal);
}
}
if (failedTasks.Any())
{
RunAgainFailedTasks(failedTasks);
}
else
{
// Display a map
}
}, CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
private Task<Terminal> CreateGetCoordinatesTask(Terminal terminal, string address)
{
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
webRequest.KeepAlive = false;
webRequest.ProtocolVersion = HttpVersion.Version10;
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(webReqTask =>
{
// Parse the response
});
return parsingTask;
}