Remove Spring data rest self link templating in projections - spring-boot

{
"_embedded" : {
"patient" : {
"firstName" : "Kidus",
"_links" : {
"self" : {
"href" : "http://localhost:8090/api/patients/2{?projection}",
"templated" : true
},
}
}
As you can see i have an embedded entity(patient). It returns all the data including link to the entity but the link is templated. I'm not using a front-end HATEOAS client and i don't plan on changing course on that. So i would need a normal non-templated link. Is there any non-hacky way to achieve this?

You can force the template to be expanded in this way:
#GetMapping("/myresources/{id}")
public EntityModel<MyResource> myResource(String id) {
MyResource resource = ...;
return new EntityModel<>(
resource,
linkTo(methodOn(getClass()).myResource(id)).withSelfRel().expand(id));
}

You can modify the self link by using a custom RepresentationModelProcessor:
#Component
public class MyProjectionProcessor implements RepresentationModelProcessor<EntityModel<MyProjection>> {
private static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{\\?.*");
#Override
public EntityModel<MyProjection> process(final EntityModel<MyProjection> model) {
// copy the model without links
final EntityModel<MyProjection> newModel = EntityModel.of(model.getContent());
// loop over links
for (final Link link : model.getLinks()) {
Link newLink = link;
// replace self link if it is templated
if (link.hasRel(IanaLinkRelations.SELF) && link.isTemplated()) {
final String href = TEMPLATE_PATTERN.matcher(link.getHref()).replaceFirst("");
newLink = Link.of(href, IanaLinkRelations.SELF);
}
newModel.add(newLink);
}
return newModel;
}
}

Related

easyaDmin 3.3 create custom filter

I struggle to understand how i'm supposed to do a custom filter in easyadmin 3.3 . The documentation isnt helping. I alway got an error
Error: Class App\Entity\entity1 has no field or association named "field1"
So i tried to put the mapping as false to prevent this, following the doc and i'm getting this
Attempted to call an undefined method named "mapped" of class "App\Controller\Admin\Filter\customfilter".
here my code :
Crud controller :
public function configureFilters(Filters $filters): Filters
{
return $filters
->add(getAutoclaveFilter::new('fiedWithNoAssociation')->mapped(false))
;
}
custom filter :
class GetAutoclaveFilter implements FilterInterface
{
use FilterTrait;
public static function new(string $propertyName, $label = null): self
{
return (new self())
->setFilterFqcn(__CLASS__)
->setProperty($propertyName)
->setLabel($label);
}
public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
{
$queryBuilder->andWhere(sprintf('%s.%s = :value', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty()))
->setParameter('value',$filterDataDto );
}
And the entity :
public function fiedWithNoAssociation()
{
return $this->getEntityAssociated()->getEntity2();
}
What i'm doing wrong ? Is the mapped function not implemented yet ?
Use instead of mapped:
->setFormTypeOption('mapped', false)

When I create a template object to be used inside my Xamarin Forms code, is there a way I can use C# fluent with Build to create the template?

I have a template class that is in its own namespace and that I add to my code with `
new InfoLabel()`{ Text = "abc" };
Note that this is just a very simple example and I have other template objects that don't just depend on one thing, for example an object with 2-3 labels.
Is there a way that I can apply Xamarin C# fluent to create a templated object?
Here is the simple example object that I have:
namespace Test
{
public class InfoLabel : Label
{
public InfoLabel()
{
SetDynamicResource(FontFamilyProperty, Const.Fonts.DefaultRegular);
SetDynamicResource(FontSizeProperty, Const.Fonts.InfoTextFontSize);
SetDynamicResource(TextColorProperty, Const.Colors.InfoLabelColor);
LineBreakMode = LineBreakMode.WordWrap;
VerticalOptions = LayoutOptions.Start;
HorizontalTextAlignment = TextAlignment.Start;
}
}
}
What I would like to know is how I can set up the same thing using the latest C# fluent standards?
Here is the way I think it might be done. I used a Build() method but I would appreciate if someone more skilled than me could tell me if I am doing it correctly as this is a big change from what I am used to:
namespace Test
{
public class InfoLabel
{
public InfoLabel()
{
Build();
}
void Build() =>
new Label
{
LineBreakMode = LineBreakMode.WordWrap,
}
.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
Here is another idea that I have:
namespace Test
{
public class InfoLabel : Label
{
public InfoLabel()
{
LineBreakMode = LineBreakMode.WordWrap;
Build();
}
void Build() =>
this.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
Note that I am using an extension method for the resources.
You could create the instance of label like following
public class InfoLabel : Label
{
static InfoLabel CreateDefaultLabel()
{
return new InfoLabel
{
LineBreakMode = LineBreakMode.WordWrap,
}
.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
}
}
var label = InfoLabel.CreateDefaultLabel();
For more details of the usage of markup you could check this blog .

Holding classes in Dictionary

I have a question about holding a class in dictionary.So I am working on a project about a university.There are more than one faculty names.When user types a faculty name,I am directing user to appropriate faculty class with using context.call
So in here,if user enters show me computer engineering,user directed to the ShowComp class.
But using if-else makes code really unreadable.I thought that I can put these keywords to dictionary
But this time context.Call gives an error about the value type.What should I put dictionary value type.I couldn't figure it out.Can anyone help me please?
Since Dialogs inherit from IDialog<object>, you can put that in the dictionary:
private readonly Dictionary<string, IDialog<object>> options
= new Dictionary<string, IDialog<object>>
{ { "computer", new ShowComp() }, { "law", new ShowLaw() } };
public async Task GetFacilities(IDialogContext context, LuisResult result)
{
var entity = result.Entities.FirstOrDefault(e => options.ContainsKey(e.Entity));
if (entity != null)
{
IDialog<object> dialog = null;
if (options.TryGetValue(entity.Entity, out dialog))
{
context.Call(dialog, this.AfterResume);
}
}
}
Do it like this:
static Dictionary<String, Type> FACULTY_CLASS_MAP;
/**
* faculty class mapping.
*/
FACULTY_CLASS_MAP= new Dictionary<String, Type>
{
{ "Text", typeof(FacultyClass) }
}

Freemarker URL Template Loader

I would like to load Freemarker templates from one or more URLs so I subclassed the URLTemplate loader class and overrode the getURL(..) method to return the target URL (see below). I then added a couple of instances of this class to a multi template loader and added that to the Freemarker config. This works fine when the first URL returns a template but when it doesn't none of the other template loaders are called. What have I done wrong? I'm using v2.3 of Freemarker via the Restlet framework.
: : : : : : : : : :
TemplateLoader[] loaders = new TemplateLoader[] {
new MyTemplateLoader(new URL(request.getRootRef() + app.getRoot())),
new MyTemplateLoader(new URL(request.getRootRef() + "/"))
};
freemarkerConfig.setTemplateLoader(new MultiTemplateLoader(loaders));
: : : : : : : : : :
public class MyTemplateLoader extends URLTemplateLoader {
private URL root;
public MyTemplateLoader(URL root) {
super();
this.root = root;
}
#Override
protected URL getURL(String template) {
try {
URL tu = new URL(root, "./" + template);
return tu;
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
}
A template is considered to be missing if TemplateLoader.findTemplateSource returns null for it. If it returns a non-null object, then MultiTemplateLoader assumes that it has found the template. In the case of URLTemplateLoader, findTemplateSource just returns what getURL does. So you have to check if the target exists, and then return null as URL if it doesn't. This works well for ClassTemplateLoader because getResource returns null URL for missing resources. But in general (if you don't know what kind of URL do you have) you will have to open an URLConnection and then connect() to see if the target exists. Or at least I guess that most URLSrteamHandler-s will check if the target exists at that point.

Using file extension content negotiation with spring security plugin?

I'm trying to use the latest spring security plugin for grails, but I've hit a little bump.
I have a controller with this method:
#Secured(['ROLE_USER'])
def query = {
}
When I hit http://localhost:8080/myApp/myController/query, I get prompted for authorization as appropriate. However, I need to do content type negotiation via the filename extension. Using
grails.mime.file.extensions=true
I can use the same UrlMappings and get to my controller method via .../myApp/myController/query.js?params=blah. However, I am not prompted for authentication, and either the request goes through automatically or fails, depending on how I've set grails.plugins.springsecurity.rejectIfNoRule
How can I use file type negotiation with the spring security plugin?
Turn off grails.mime.file.extensions and add this filter:
class FileExtensionContentNegotiationFilters {
final static String DEFAULT_FORMAT = "js"
def filters = {
all(controller: '*', action: '*') {
before = {
addFormatToRequestByFileExtension(request)
}
after = {
}
afterView = {
}
}
}
protected addFormatToRequestByFileExtension(def request) {
String suffix = getSuffixFromPath(request.forwardURI)
String extension = FilenameUtils.getExtension(suffix)
if (extension.isEmpty()) {
request[GrailsApplicationAttributes.CONTENT_FORMAT] = DEFAULT_FORMAT
}
else {
request[GrailsApplicationAttributes.CONTENT_FORMAT] = extension
}
}
protected String getSuffixFromPath(String pathWithoutParams) {
int lastSlash = pathWithoutParams.lastIndexOf("/")
if (lastSlash < 0) {
return ""
}
return pathWithoutParams.substring(lastSlash + 1)
}
}
The solution above does not work for me as expected. A 404 response is generated when I request an URL with extension.
I come with another solution that does not need to turn off grails.mime.file.extensions and does not need an extra Filter. Instead, it's a modification of the plugin's class org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition. What you need to do is to edit the method determineUrl as shown below (look at the comments to identify the changes):
#Override
protected String determineUrl(final FilterInvocation filterInvocation) {
HttpServletRequest request = filterInvocation.getHttpRequest();
HttpServletResponse response = filterInvocation.getHttpResponse();
GrailsWebRequest existingRequest = WebUtils.retrieveGrailsWebRequest();
String requestUrl = request.getRequestURI().substring(request.getContextPath().length());
/** The following 2 lines were added */
int indexOfPeriod = requestUrl.indexOf('.');
String requestUrlForMatching = (indexOfPeriod != -1) ? requestUrl.substring(0, indexOfPeriod) : requestUrl;
String url = null;
try {
GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response,
ServletContextHolder.getServletContext());
WebUtils.storeGrailsWebRequest(grailsRequest);
Map<String, Object> savedParams = copyParams(grailsRequest);
/* Use requestUrlForMatching instead of requestUrl */
for (UrlMappingInfo mapping : _urlMappingsHolder.matchAll(requestUrlForMatching)) {
configureMapping(mapping, grailsRequest, savedParams);
url = findGrailsUrl(mapping);
if (url != null) {
break;
}
}
}
finally {
if (existingRequest == null) {
WebUtils.clearGrailsWebRequest();
}
else {
WebUtils.storeGrailsWebRequest(existingRequest);
}
}
if (!StringUtils.hasLength(url)) {
// probably css/js/image
url = requestUrl;
}
return lowercaseAndStripQuerystring(url);
}
The problem is that URLs with extensions do not match any URL in UrlMappings using UrlMappingsHolder.matchAll method. So, the solution is to ommit the extension prior to look for matches. With this changes everything works as expected.
I also create a pull request with the fix available at https://github.com/grails-plugins/grails-spring-security-core/pull/24
You can see the changes at https://github.com/arcesino/grails-spring-security-core/commit/19f87168ec4422b4fe06cc6914adeb1bae4b8752
Tested with version 1.2.7.3

Resources