Can't serialize GeoJson coordinates in .Net Core controller - asp.net-core

I have a .net 6 API project that exports FeatureCollections. I'm using 'NetTopologySuite.IO.GeoJSON' version 2.0.4, and have a public API call like:
public async Task<IActionResult> ExportGeoJSON()
{
GeoJSON.Net.Feature.FeatureCollection ret = new GeoJSON.Net.Feature.FeatureCollection();
const double lat = -73.697913;
const double lon = 50.659193;
GeoJSON.Net.Geometry.Position coord = new GeoJSON.Net.Geometry.Position(lat, lon);
GeoJSON.Net.Geometry.Point pt = new GeoJSON.Net.Geometry.Point(coord);
GeoJSON.Net.Feature.Feature feat = new GeoJSON.Net.Feature.Feature(pt, null, Guid.NewGuid().ToString());
ret.Features.Add(feat);
return Ok(ret);
}
When i call this, i get back only:
{
"Type": "FeatureCollection",
"Features": [
{
"Type": "Feature",
"Id": "465f399d-b45c-47ed-b9e6-f395cd86b84b",
"Geometry": {
"Type": "Point"
}
},...
Ok, i look around and find out about GeoJSON4STJ https://github.com/NetTopologySuite/NetTopologySuite.IO.GeoJSON , so I put that into my Startup:
services.AddControllers()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
opts.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory());
});
and run it again, but no change at all. Exactly the same response. Am i missing something?

It seems that you are mixing libraries. NetTopologySuite.IO.GeoJSON (and NetTopologySuite.IO.GeoJSON4STJ) works with features from NetTopologySuite.Features package not from GeoJSON.Net.

Related

Filter products by category in a Shopware 6 Import/Export Profile

I created a Profile in shopware 6 with the api.
The body of the request is:
{
"id": "my99id2cb2784069938089a8a77ctest",
"label": "Label test",
"name": "name test",
"sourceEntity": "product",
"fileType": "text/csv",
"delimiter": ";",
"enclosure": "\"",
"createdAt": "2022-11-10T10:15:22Z",
"mapping": [{"key":"id","mappedKey":"id","position":0,"id":"someid4f7c6c478b8041cfd5fe0ae0c5"},{"key":"cover.media.url","mappedKey":"cover","position":1,"id":"someid0ffa7f48e6b135f6e754d6b93c"}],
"config": {"createEntities": false, "updateEntities": true}
}
Is there a way to specify that i want only product from certain categories? Not all of them
No, this currently isn't possible with just the profile and existing endpoints. You'll have to implement a custom api endpoint.
If you are able to do that follow the steps from this answer until you first retrieve a logId. Then instead of starting the process using the existing endpoint, request your new custom endpoint. The implementation could look similar to this:
/**
* #Route(defaults={"_routeScope"={"api"}})
*/
class CustomExportApiController extends AbstractController
{
private ImportExportFactory $importExportFactory;
public function __construct(ImportExportFactory $importExportFactory)
{
$this->importExportFactory = $importExportFactory;
}
/**
* #Route("/api/_action/custom/export/{logId}/{categoryId}", name="api.action.custom.export", methods={"POST"})
*/
public function customProductExport(string $logId, string $categoryId, Context $context): JsonResponse
{
$importExport = $this->importExportFactory->create($logId, 50, 50);
$logEntity = $importExport->getLogEntity();
if ($logEntity->getState() === Progress::STATE_ABORTED) {
return new JsonResponse(['success' => false]);
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('categoryTree', [$categoryId]));
$offset = 0;
do {
$progress = $importExport->export($context, $criteria, $offset);
$offset = $progress->getOffset();
} while (!$progress->isFinished());
return new JsonResponse(['success' => true]);
}
}
After calling your custom endpoint proceed with the steps as described in the answer linked above.

How to get the ressources address of a badge

I'm trying to build an Auction web application based on a scrypto smartcontract.
I have a register function that return a badge, from which I can build proof to call other methods that need authentication.
To build the proof, I need the address of the badge; using the pte-sdk, how would that be possible ?
I use :
const manifestRegistration = new ManifestBuilder()
//call the register function
.callMethod(auction.auctionId, "register", [])
//deposit the resource into my account
.callMethodWithAllResources(accountAddress, "deposit_batch")
.build()
.toString()
const receiptRegistration = await signTransaction(manifestRegistration);
console.log(receiptRegistrationt)
here is receipt :
{
"transactionHash": "b737899a3b78692d2ba49d83ccedeacd66f6168d107a2962828d621d6c73cb37",
"status": "Success",
"outputs": [
"{\"type\":\"Bucket\",\"value\":\"Bucket(1027u32)\"}",
"{\"type\":\"Unit\"}"
],
"logs": [],
"newPackages": [],
"newComponents": [],
"newResources": []
}
How do I know what is the resource in the returned bucket ?
Thank you
The first thing that pops into my head of solving this is returning the address as a string
pub fn register() -> (Bucket, String) -> {
...
(badge, badge.resource_address().to_string())
}
This should pop up in the outputs array

Is it possible to expose the same Swagger JSON in both Swagger 2.0 and Open API 3 formats with Swashbuckle in ASP .NET Core?

We have a legacy application that only works with Swagger 2.0 JSON format. For everything else we would like to use Open API format.
Is there any way with Swashbuckle .NET Core to expose JSON in different formats under separate URLs? It looks like the SerializeAsV2 property in the UseSwagger method options is global for all endpoints.
Basically I would like to have the following end points that contain the same API data in different formats.
/swagger/v1/openapi/swagger.json
/swagger/v1/swagger2/swagger.json
An alternative approach is to split the request pipeline:
// serve v3 from /swagger/v1/swagger.json
app.UseSwagger(o => o.RouteTemplate = "swagger/{documentName}/swagger.json");
// serve v2 from /swagger2/v1/swagger.json
app.Map("/swagger2", swaggerApp =>
swaggerApp.UseSwagger(options => {
// note the dropped prefix "swagger/"
options.RouteTemplate = "{documentName}/swagger.json";
options.SerializeAsV2 = true;
})
);
You can serialize the OpenAPI document as V2 and serve it yourself. Taking SwaggerMiddleware as reference:
First register SwaggerGenerator:
services.AddTransient<SwaggerGenerator>();
Then inject SwaggerGenerator and build the document. Serve it from an endpoint or a controller. You can take the version as a path parameter to decide which version to serve.
app.UseEndpoints(e =>
{
// ...
e.MapGet("/swagger/{documentName}/swagger.{version}.json", context =>
{
var documentName = context.Request.RouteValues.GetValueOrDefault("documentName", null) as string;
var version = context.Request.RouteValues.GetValueOrDefault("version", null) as string;
if (documentName is null || version is null)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return Task.CompletedTask;
}
// inject SwaggerGenerator
var swaggerGenerator = context.RequestServices.GetRequiredService<SwaggerGenerator>();
var doc = swaggerGenerator.GetSwagger(documentName);
// serialize the document as json
using var writer = new StringWriter(CultureInfo.InvariantCulture);
var serializer = new OpenApiJsonWriter(writer);
if (version == "v2")
{
doc.SerializeAsV2(serializer);
}
else
{
doc.SerializeAsV3(serializer);
}
var json = writer.ToString();
// serve it as json
context.Response.ContentType = MediaTypeNames.Application.Json;
return context.Response.WriteAsync(json, new UTF8Encoding(false));
});
});
When you visit /swagger/v1/openapi.v2.json, you'll get the OpenAPI doc serialized as v2.
{
"swagger": "2.0",
"info": {
"title": "ApiPlayground",
"version": "1.0"
},
"paths": { ... }
}
Whereas /swagger/v1/openapi.v3.json will give you the doc serialized as v3:
{
"openapi": "3.0.1",
"info": {
"title": "ApiPlayground",
"version": "1.0"
},
"paths": { ... },
"components": { ... }
}

Load dynamic SAML schemes for IdentityServer4 using ComponentSpace

We have centralized a IdentityServer4 that will act as service provider and there are multiple identity providers like Active Directory, Google, Facebook and also other SAML providers based on each tenant. i.e., one service provider and multiple identity providers.
To load the openId configs from database I am exactly following the https://stackoverflow.com/a/56941908/2922388 and it is working as expected for openid and now I need to integrate SAML providers in the same way.
I went through the "SAMLv20.Core-evaluation" from componentspace and was able to do the integration using appsettings.json successfully.
But I am not sure how to integrate it programatically as given in https://stackoverflow.com/a/56941908/2922388.
Here is what I have done so far
public class AccountController : ControllerBase
{
private readonly IOptionsMonitorCache<OpenIdConnectOptions> _openIdOptionsCache;
private readonly IOptionsMonitorCache<SamlAuthenticationOptions> _samlOptionsCache;
private readonly OpenIdConnectPostConfigureOptions _postConfigureOptions;
private readonly SamlPostConfigureAuthenticationOptions _samlPostConfigureOptions;
public AccountController(
IOptionsMonitorCache<OpenIdConnectOptions> openidOptionsCache,
IOptionsMonitorCache<SamlAuthenticationOptions> samlOptionsCache,
OpenIdConnectPostConfigureOptions postConfigureOptions,
SamlPostConfigureAuthenticationOptions samlPostConfigureOptions
)
{
_openIdOptionsCache = openidOptionsCache;
_samlOptionsCache = samlOptionsCache;
_postConfigureOptions = postConfigureOptions;
_samlPostConfigureOptions = samlPostConfigureOptions;
}
private async Task<IEnumerable<AuthenticationScheme>> LoadAuthenticationSchemesByTenant(IEnumerable<AuthenticationScheme> schemes, AuthProviderSetting tenantAuthProviderSetting)
{
dynamic configJson = JsonConvert.DeserializeObject(tenantAuthProviderSetting.tenantConfigJson);
switch (tenantAuthProviderSetting.AuthenticationType)
{
case AuthenticationTypes.OpenID:
var oidcOptions = new OpenIdConnectOptions
{
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SaveTokens = true,
Authority = configJson.Authority,
ClientId = configJson.ClientId,
ClientSecret = configJson.ClientSecret,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
ValidateIssuer = false
}
};
_schemeProvider.AddScheme(new AuthenticationScheme(tenantAuthProviderSetting.AuthenticationScheme, tenantAuthProviderSetting.DisplayName, typeof(OpenIdConnectHandler)));
_postConfigureOptions.PostConfigure(tenantAuthProviderSetting.AuthenticationScheme, oidcOptions);
_openIdOptionsCache.TryAdd(tenantAuthProviderSetting.AuthenticationScheme, oidcOptions);
schemes = await _schemeProvider.GetAllSchemesAsync();
break;
case AuthenticationTypes.SAML:
var samlOptions = new SamlAuthenticationOptions
{
PartnerName = delegate () { return "https://ExampleIdentityProvider"; },
SingleLogoutServicePath = "https://localhost:44313/SAML/SingleLogoutService",
// Not sure how to set other parameters here
};
_schemeProvider.AddScheme(new AuthenticationScheme(tenantAuthProviderSetting.AuthenticationScheme, tenantAuthProviderSetting.DisplayName, typeof(SamlAuthenticationHandler)));
_samlPostConfigureOptions.PostConfigure(tenantAuthProviderSetting.AuthenticationScheme, samlOptions);
_samlOptionsCache.TryAdd(tenantAuthProviderSetting.AuthenticationScheme, samlOptions);
schemes = await _schemeProvider.GetAllSchemesAsync();
break;
default:
schemes = await _schemeProvider.GetAllSchemesAsync();
break;
}
return schemes;
}
}
Here is the config through which I used to statically integrate with IdentityServer
"SAML": {
"$schema": "https://www.componentspace.com/schemas/saml-config-schema-v1.0.json",
"Configurations": [
{
"LocalServiceProviderConfiguration": {
"Name": "https://IdentityServer4",
"Description": "IdentityServer4",
"AssertionConsumerServiceUrl": "http://localhost:44380/SAML/AssertionConsumerService",
"SingleLogoutServiceUrl": "http://localhost:44380/SAML/SingleLogoutService",
"LocalCertificates": [
{
"FileName": "certificates/sp.pfx",
"Password": "password"
}
]
},
"PartnerIdentityProviderConfigurations": [
{
"Name": "https://ExampleIdentityProvider",
"Description": "Example Identity Provider",
"SignAuthnRequest": true,
"SingleSignOnServiceUrl": "https://localhost:44313/SAML/SingleSignOnService",
"SingleLogoutServiceUrl": "https://localhost:44313/SAML/SingleLogoutService",
"PartnerCertificates": [
{
"FileName": "certificates/idp.cer"
}
]
}
]
}
]
},
"PartnerName": "https://ExampleIdentityProvider"
Just to confirm, you want to add the SAML configuration dynamically?
The best way to do this is to implement the ISamlConfigurationResolver as described in the "Implementing ISamlConfigurationResolver" section of our Configuration Guide.
https://www.componentspace.com/Forums/8234/Configuration-Guide
Your implementation of ISamlConfigurationResolver is called whenever configuration is required. This means the SAML configuration is entirely dynamic.

Health Checks UI display results in categories

I have implemented Xabaril's Healthchecks in a simple .NET Core 3.0 API project. I am checking several URLs and SQL servers as a test and I successfully display the results in the HealthCheckUI. They all appear under the one 'category' that I defined in appSettings. Now, in the documentation you can see that you could have multiple of these categories, but it seems that this is meant to be used only when you display results from different sources.
What I want to do is have my API project check say 3 URIs and display them under a "Web" category, and then check 3 SQL servers and display them under a "SQL" category.
From this example here it seems that we can achieve the 2 different categories with this piece of code:
app
.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = _ => true
})
.UseHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
})
However in the Startup class, when we add checks we do not specify to which API endpoint they should be reported to so that we can categorize them:
services.AddHealthChecks().AddCheck...
Am I missing something or this UI client is not meant to be used like this?
I got this working in a Net Core 3.1 project using tags.
For example if you wanted a Database and API section in the Spa UI:
In ConfigureServices method of Startup.cs
services.AddHealthChecks()
.AddSqlServer("ConnectionString", name: "Application Db", tags: new[] { "database" })
.AddHangfire(options => { options.MinimumAvailableServers = 1; }, "Hangfire Db", tags: new[] { "database" })
.AddUrlGroup(new Uri("https://myapi.com/api/person"), HttpMethod.Get, "Person Endpoint", tags: new[] { "api" })
.AddUrlGroup(new Uri("https://myapi.com/api/organisation"), HttpMethod.Get, "Org Endpoint", tags: new[] { "api" })
.AddUrlGroup(new Uri("https://myapi.com/api/address"), HttpMethod.Get, "Address Endpoint", tags: new[] { "api" });
In Configure method of Startup.cs
config.MapHealthChecks("/health-check-database", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("database"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
config.MapHealthChecks("/health-check-api", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("api"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
Then appsettings.json something like:
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "Database Health Checks",
"Uri": "https://myapi.com/api/health-check-database"
},
{
"Name": "API Health Checks",
"Uri": "https://myapi.com/api/health-check-api"
}
],
"Webhooks": [],
"EvaluationTimeinSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}