#RepositoryRestResource's collectionResourceRel attribute not being obeyed - spring-data-mongodb

I have a MongoRepository of
#RepositoryRestResource(collectionResourceRel = "tools")
public interface ToolRepository extends MongoRepository<Tool, Long>
Tool can be one of 2 implementations:
public class Screwdriver extends Tool
public class Hammer extends Tool
Tool is mapped using #JsonTypeInfo
#JsonTypeInfo(use =
com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS, include =
As.PROPERTY, property = "_class")
public abstract class Tool
When I do toolRepository.findAll() this returns a JSON response of :
{
"_embedded" : {
"screwdrivers" : [ {
"name" : "Screwdriver",
...
} ],
"hammers" : [ {
"name" : "Hammer",
...
}
}
Expected response should be :
{
"_embedded" : {
"tools" : [ {
"name" : "Screwdriver",
...
},
{
"name" : "Hammer",
...
}
}
The collectionResourceRel is not being obeyed for classes with Json mapping data in.
Investigating further; PersistentEntitiesResourceMapping.getMetadataFor() (within Spring) is saying make sure that if there's no entry for these subclasses of tool inside the ResourceMetadata cache then use a TypeBasedCollectionResourceMapping which results in each class having its own entry in the json response.
Is there a way of telling Spring data rest that a specific subclass should be bound to a specific repository, in this case is there a way of telling Spring data rest that Screwdriver is part of the ToolRepository and therefore should use the collectionResourceRel of this repository?

Try to set these annotations:
#RestResource(rel = "tools", path = "tools")
public abstract class Tool {...}
#RepositoryRestResource(path = "tools", collectionResourceRel = "tools", itemResourceRel = "tool")
public interface ToolRepository extends MongoRepository<Tool, Long> {...}
See working example. It's not about Mongo but I'm sure it will be useful...

Related

Data Acces Objects Factory Design Pattern in React?

I'm currently going through some tutorial about React Design Patterns, subject: Custom Hooks.
While the concept feels awesome and seems familiar to me, the solution below provided by the tutor made me question how to deal with different data sources.
Is there something like the above mentioned DAO Factory Pattern, you can find in Frameworks like J2E?
Or how is the common approach to handle this challenges in React to make the code maintainable?
My first intension woulda be throwing the stuff into a Factory Component and having implementations for the specific providers, basically like it is shown in the two other Code snippets bewlow.
Is this the regular wayt to do it?
Any help, tips, additional sources to learn this would be highly appreciated.
Here's the solution, with the both possible implementations thrown into the React Form Component:
import axios from 'axios';
import { useDataSource } from './useDataSource';
import { useResource } from './useResource';
import { useUser } from './useUser';
const serverResource = resourceUrl => async () => {
const response = await axios.get(resourceUrl);
return response.data;
};
const localStorageResource = key => () => {
return localStorage.getItem(key);
}
export const UserInfo = ({ userId }) => {
// const user = useResource(`/users/${userId}`);
const user = useDataSource(serverResource(`/users/${userId}`));
const message = useDataSource(localStorageResource('message'));
const { name, age, hairColor, hobbies } = user || {};
return user ? (
<>
<h3>{name}</h3>
<p>Age: {age} years</p>
<p>Hair Color: {hairColor}</p>
<h3>Hobbies:</h3>
<ul>
{hobbies.map(hobby => <li key={hobby}>{hobby}</li>)}
</ul>
</>
) : <p>Loading...</p>;
}
And here's the DAO Factory Pattern example provided by Oracle:
src: https://www.oracle.com/java/technologies/dataaccessobject.html
// Abstract class DAO Factory
public abstract class DAOFactory {
// List of DAO types supported by the factory
public static final int CLOUDSCAPE = 1;
public static final int ORACLE = 2;
public static final int SYBASE = 3;
...
// There will be a method for each DAO that can be
// created. The concrete factories will have to
// implement these methods.
public abstract CustomerDAO getCustomerDAO();
public abstract AccountDAO getAccountDAO();
public abstract OrderDAO getOrderDAO();
...
public static DAOFactory getDAOFactory(
int whichFactory) {
switch (whichFactory) {
case CLOUDSCAPE:
return new CloudscapeDAOFactory();
case ORACLE :
return new OracleDAOFactory();
case SYBASE :
return new SybaseDAOFactory();
...
default :
return null;
}
}
}
...with a concrete implementation:
// Cloudscape concrete DAO Factory implementation
import java.sql.*;
public class CloudscapeDAOFactory extends DAOFactory {
public static final String DRIVER=
"COM.cloudscape.core.RmiJdbcDriver";
public static final String DBURL=
"jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";
// method to create Cloudscape connections
public static Connection createConnection() {
// Use DRIVER and DBURL to create a connection
// Recommend connection pool implementation/usage
}
public CustomerDAO getCustomerDAO() {
// CloudscapeCustomerDAO implements CustomerDAO
return new CloudscapeCustomerDAO();
}
public AccountDAO getAccountDAO() {
// CloudscapeAccountDAO implements AccountDAO
return new CloudscapeAccountDAO();
}
public OrderDAO getOrderDAO() {
// CloudscapeOrderDAO implements OrderDAO
return new CloudscapeOrderDAO();
}
...
}
how is the common approach to handle this challenges in React to make the code maintainable?
sure you can. As design patterns do not depend on programming language.
If you are using TypeScript, then you can use Abstract class
If you are using plain JavaScript, then you can use the following approach
An example of using Factory in React can be seen here.

Wizard using Orc.Controls.Wizard and Catel

I'm trying to create a wizard using Catel 5.4.0.0 and Orc.Wizard 2.0.0.0
I followed the example on https://opensource.wildgums.com/orc.wizard/ but when I call the wizard it returns the error "The view model of the view 'QuotationWizardPageView' could not be resolved.
Here is some code
using Orc.Wizard
using System
namespace ZMAdmin.ASWizard
{
public class QuotationWizardPage : WizardPageBase
{
public QuotationWizardPage()
{
Title = "Offerte";
Description = "Offerte";
}
}
}
using Orc.Wizard;
using System;
namespace ZMAdmin.ASWizard
{
public class QuotationWizardPageViewModel : WizardPageViewModelBase<QuotationWizardPage>
{
public QuotationWizardPageViewModel(QuotationWizardPage wizardPage)
: base(wizardPage)
{
}
}
}
using Catel.Ioc;
using Orc.Wizard;
namespace ZMAdmin.ASWizard : WizardBase
{
public ASWizard(ITypeFactory typeFactory)
: base (typeFactory)
{
Tittle = "Bedrijfsnaam";
this.AddPage<QuotationWizardPage>();
}
}
<catel.UserControl x:Class="ZMAdmin.Views.QuotationWizardPageView"
xmlns:catel="http://schemas.catelproject.com"
>
</catel.UserControl>
in the app.xaml.cs I've
var MyViewLocator = ServiceLocator.Default.ResolveType<IViewLocator>();
MyViewLocator.Register(typeof(QuotationWizardPageViewModel), typeof(QuotationWizardPageView));
And last I call the wizard using:
private async void OnAddQuotation()
{
await _wizardService.ShowWizardAsync<ASWizard.ASWizard>();
}
The wizard displays, but returns the error, not displaying the quotationView.
What am I overlooking?
If the code you posted is correct (but I see another issue where a namespace is inheriting from a base class), it's probably caused of namespace conflicts.
The view is located in ZMAdmin.Views
The view model is located in ZMAdmin.ASWizard
My recommendation is to use:
View models: ZMAdmin.ASWizard.ViewModels
Views: ZMAdmin.ASWizard.Views

in cakephp4 how to access a model within a model

How do i access another model within a model in cakephp4.2? The docs on this issue isnt clear to me and i can then run a query on this ? TableRegistry is deprecated now.
error Unknown method "getTableLocator" called on App\Model\Table\LessonsTable
//none of these no longer work
in model {
use Cake\ORM\Locator\LocatorAwareTrait;
class LessonsTable extends Table
{
..
private function getlessonRevenue(){
//$clients = $this->getTableLocator()->get('Clients');
// $cleints = TableRegistry::get('Clients');
// $this->Table = TableRegistry::get('Clients');
$clients = $this->getTableLocator()->get('Clients');
https://api.cakephp.org/4.0/class-Cake.ORM.TableRegistry.html
Try:
<?php
use Cake\ORM\Locator\LocatorAwareTrait; //<------------ add here
class ArchivesTable extends Table
{
use LocatorAwareTrait; // <--------------------------- and add here
public function myMethod()
{
$clients = $this->getTableLocator()->get('Clients');
}
and read https://book.cakephp.org/4/en/orm/table-objects.html#using-the-tablelocator
and learn how to use php trait https://www.phptutorial.net/php-tutorial/php-traits/

How to access the user Token in an injected service to reencode passwords?

I have the below code where I am trying to re-encode passwords as users log in (the database has bee migrated form a legacy website). However, I'm not sure what I'm doing wrong as I keep getting errors:
Attempted to call an undefined method named "forward" of class "AppBundle\Service\HubAuthenticator".
I have set things up as follows:
security.yml
security:
encoders:
AppBundle\Entity\Member:
id: club.hub_authenticator
services.yml
services:
//This should be central service than then calls the second
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
club.password_rehash:
class: AppBundle\Service\PasswordRehash
Hubauthenticator.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
function __construct($cost=13)
{
parent::__construct($cost);
}
function isPasswordValid($encoded, $raw, $salt)
{
// Test for legacy authentication (and conditionally rehash the password stored in the database if true)
if ($this->comparePasswords($encoded, sha1("saltA".$raw."saltB"))) {
$this->forward('club.password_rehash:rehash');
}
// Test for Symfony's Bcrypt authentication (any passwords just rehashed in previous step should work here)
if (parent::isPasswordValid($cost=13, $encoded,$raw,$salt)) return true ;
}
}
PasswordRehash.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class PasswordRehash extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder
{
// Customises BCryptPasswordEncoder class to use legacy SHA method
function rehash($member, $raw, $salt)
{
//Salt is null as Symfony documentation says it is better to generate a new one
parent::encodePassword($member->getPlainPassword, $salt=null ) ;
}
}
Some other previous attempts for completeness:
My guess is that the problem is that I am misunderstanding what objects are available to me. My understanding is that the user hasn't been authenticated at this point so have tried and removed the below attempts:
Trying to inject the $member into the HubAuthenticator service:
function __construct($cost=13)
{
parent::__construct($cost, \Member $member);
}
When trying to get the plainpassword to rehash:
$this->get('security.context')->getToken()->getUser()->getPlainPassword();
In your services, you can only access what dependencies you've injected.
So, to access the current user object, you need to pass it as argument:
service:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage" ]
Constructor:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
private $storage;
function __construct($cost = 13, TokenStorageInterface $storage)
{
parent::__construct($cost);
$this->storage = $storage;
// Now you can use:
// $user = $this->storage->getToken()->getUser();
}
}
Then, to access the second service, same way, inject it.
Add it to the service arguments:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage", "#club.password_rehash" ]
Add it to your constructor:
private $storage;
private $passwordRehash
function __construct($cost = 13, TokenStorageInterface $storage, PasswordRehash $passwordRehash)
{
parent::__construct($cost);
$this->storage = $storage;
$this->passwordRehash = $passwordRehash;
// Now you can use:
// $this->passwordRehash->rehash(...);
}
Hope this helps you.

How can model variables can be shared in both Service class and controller class (typescript/angular)

I have a Controller class, Service class, and Model class. I want to know how I can have access to the same model in both Controller and Service classes.
So for example :
Controller Class :
import { SearchModel } from "../../models/SearchModel";
import { SearchService } from "../../components/SearchService";
export class SearchController {
public searchModel: SearchModel;
static $inject = ['searchService'];
constructor(private searchService:SearchService) {
this.searchService = searchService;
}
public controllerMethod() {
console.log(this.searchModel.searchKeyword); //This works.
this.searchModel.searchKeyword = "CheckIfSharedObject";
this.searchService.serviceMethod();
}
}
Service Class :
import { SearchModel } from "../../models/SearchModel";
export class SearchService {
public searchModel: SearchModel;
constructor() { }
public serviceMethod() {
// This will not work. i.e this wont print 'CheckIfSharedObject'
console.log(this.searchModel.searchKeyword);
}
}
Model Class :
export class SearchModel {
constructor (
public searchKeyword: string
)
}
From the above example, I want controller and service to share the model variable searchKeyword in both the classes.
It works when we pass the model class object to the serviceMethod, but I dont want to do that. Is there a way we can make it work without explicitly passing the Model Class Object to Service class.
I want controller and service to share the model variable searchKeyword in both the classes
It is okay to have model modelled using an angularjs service. That will make the model a singleton that can be shared between the controller and the service.
I would even in fact say that your service should really be the model. That way you will only have "controller" + "service", but feel free to have three things if you want.

Resources