Getting 404 on existing Syndesis API endpoint for creating db snapshot - syndesis

I'm getting back a 404 for an endpoint in the Syndesis API, namely /api/v1/test-support/snapshot-db, which allows you to create a snapshot of the db.
On Restlet, I added the proper _oauth_proxy cookie, and issued a GET to <host>/api/v1/test-support/snapshot-db.
#Path("/test-support")
#org.springframework.stereotype.Component
#ConditionalOnProperty(value = "endpoints.test_support.enabled")
public class TestSupportHandler {
private static final Logger LOG = LoggerFactory.getLogger(TestSupportHandler.class);
private final DataManager dataMgr;
private final List<DataAccessObject<?>> daos;
private final OpenShiftService openShiftService;
#Context
private HttpServletRequest context;
private final DBI dbi;
private CacheManager cacheManager;
private Collection<BackendController> controllers;
public TestSupportHandler(DBI dbi, DataManager dataMgr, CacheManager cacheManager, List<DataAccessObject<?>> daos, OpenShiftService openShiftService, Collection<BackendController> controllers) {
this.dbi = dbi;
this.dataMgr = dataMgr;
this.cacheManager = cacheManager;
this.controllers = controllers;
this.daos = daos.stream().filter(x -> !x.isReadOnly()).collect(Collectors.toList());
this.openShiftService = openShiftService;
}
#GET
#Path("/snapshot-db")
#Produces(MediaType.APPLICATION_JSON)
public List<ModelData<?>> snapshotDB() {
LOG.info("user {} is making snapshot", context.getRemoteUser());
ArrayList<ModelData<?>> result = new ArrayList<>();
for (DataAccessObject<?> dao : daos) {
ListResult<? extends WithId<?>> l = dao.fetchAll();
for (WithId<?> entity : l.getItems()) {
#SuppressWarnings({"unchecked", "rawtypes"})
ModelData<?> modelData = new ModelData(entity.getKind(), entity);
result.add(modelData);
}
}
return result;
}
}
I get the following response:
{
"errorCode": 404,
"userMsg": "Given request is not acceptable",
"developerMsg": "RESTEASY003210: Could not find resource for full path: <host>/api/v1/test-support/snapshot-db"
}

Notice the #ConditionalOnProperty(value = "endpoints.test_support.enabled"). This means that this endpoint might be turn off depending on a parameters set in the environment. If you have access to the server pod check the setting of this param and set it to 'true'. Try using:
oc edit pod <server-podname>
and look for
spec:
containers:
- env:
- name: JAVA_APP_DIR
value: /deployments
- name: JAVA_OPTIONS
value: -Djava.net.preferIPv4Stack=true -Duser.home=/tmp
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: ENDPOINTS_TEST_SUPPORT_ENABLED
value: "false"
and set it to "true". If it doesn't stick try editing the dc
oc edit dc syndesis-server
Final note: if the Syndesis Operator is running in your install you may want to scale it down to zero or else it may undo your edits.
Good luck!
--Kurt

Related

Set deserializing in Spring Native image problem

I'm stuck with a problem on a built Spring Native image:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [collection type; class java.util.HashSet, contains [simple type, class java.lang.Object]]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.util.HashSet (no Creators, like default constructor, exist): no default no-arguments constructor found
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 5, column: 14] (through reference chain: com.example.app.payload.request.SignupRequest["roles"])] with root cause
From this description it seems that I need use custom deserializer, but the problem appears only if I run code from native image - same code works perfectly well if run from JAR.
POJOs are very simple:
public class SignupRequest {
#NotBlank
#Size(min = 3, max = 20)
private String username;
#NotBlank
#Size(max = 50)
#Email
private String email;
private Set<String> roles;
#NotBlank
#Size(min = 6, max = 40)
private String password;
// getters & setters no Lombok (but Lombok is in project)
}
Controller uses standard (nothing fancy) annotations:
public ResponseEntity<MessageResponse> registerUser(#Valid #RequestBody SignupRequest signUpRequest)
Has anyone encountered a similar problem?
Finally I found missing part - I have to add HashSet to SerializationHint:
#SpringBootApplication
#SerializationHint(types = {
java.util.HashSet.class
})
public class SpringNativeApplication {
public static void main(String[] args) {
// ...
}
}

spock testing of endpoint and repository

Working on my Spring 2.67 and Spock 2.1-groovy-3.0 testing. I have the basic testing working but now trying some integration testing without success. I have a controller with:
private ApiService apiService;
#Autowired
public ApiController(ApiService apiService) {
this.apiService = apiService;
}
#GetMapping("api/{scannedId}")
#ResponseBody
public ResponseEntity getScannedId(#PathVariable String scannedId) {
try {
logger.info("ApiKey Controller received GET /api/" + scannedId);
ApiKey found = apiService.retrieveValidApiKey(scannedId);
...
}
...
The apiService has :
private ApiRepository apiRepository;
#Autowired
public ApiService(ApiRepository apiRepository) {
this.apiRepository = apiRepository;
}
public ApiKey retrieveValidApiKey(String uuid) {
ApiKey anApi = apiRepository.getApiKeyByApiKey(uuid);
if (anApi == null) {
logger.info("ApiService.retrieveValidApiKey({}) failed to find a matching ApiKey", uuid);
return null;
}
I have a Spock test that seeds the database with two values and then successfully calls the /api endpoint. I have code in the test that confirms the two values were inserted, but when the actual ApiService class is called, they are not found:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestDatabase
class ApiControllerTest extends Specification {
#Shared Logger logger = LoggerFactory.getLogger(this.getClass())
#Autowired
ApiController apiController
#Autowired
ApiRepository apiRepository
#Transactional
def "GET by scannedId using apikey #apiKey should be #resultCode"() {
given:
def foundList = apiRepository.findAll()
logger.info("{} apiKeys in repository", foundList.size())
for (int i = 0; i < foundList.size(); i++) {
logger.info("Found ApiKey #{} apiKey: {} & uuid: {}", i, foundList.get(i).apiKey, foundList.get(i).uuid)
}
when:
def foundListCount = apiRepository.getApiKeyByApiKey(apiKey)
logger.info("FoundList: {}", foundListCount)
ResponseEntity<ApiKey> result = restTemplate.getForEntity( "/api/{scannedId}", ApiKey.class, apiKeyValue1)
logger.info("TestRestTemplate returned apikey: {}", result)
then:
assert result.getStatusCode() == resultCode
where:
apiKey || resultCode
"testApiKey3" || HttpStatus.NOT_FOUND
apiKeyValue1 || HttpStatus.OK
apiKeyValue2 || HttpStatus.OK
}
def setup() {
def apiKey1 = new ApiKey(apiKey: apiKeyValue1, uuid: uuid1, beginDate: beginDate1, endDate: endDate1)
def apiKey2 = new ApiKey(apiKey: apiKeyValue2, uuid: uuid2, beginDate: beginDate2, endDate: endDate2)
apiRepository.saveAndFlush(apiKey1)
apiRepository.saveAndFlush(apiKey2)
}
When I run the test, the logger in the test method spits out all the persisted values. But the test fails because the ApiService.getScannedId fails because it does not see the values persisted in test setup.
I cannot use the #DataJpaTest because the ApplicationContext isn't loaded then, so the endpoints fail.
I am not sure why Spock sees the values persisted via Repository, but the ApiService doesn't. Is it a context issue? I really would like to test without mocks here if at all possible.
The problem is that your test is annotated with #Transactional that means that only things that run in that method can see the data. The rest request you are sending out, will be handled by another thread that doesn't have access to the transaction and thus will not see the data.
You'll have to remove the annotation if you want it to work, but then you'll also have to clean the inserted data manually at the end of the test/cleanup(), since you can't rely on the transaction rollback.

REQUEST first used in a middleware but cannot be retrieved from a controller

REQUEST scope indicates a new instance be created for each request, according to Nest JS the request lifecycle is: middleware, pipes, guards, and interceptor. I am creating a service with scope REQUEST in a middle ware which inits a id of the service instance, and wiring this middleware to every endpoint, expecting fetching the exact same service instance in the controller and following service.
But found out that the service instance id is undefined. If I first use the service in a controller, it is fine: the same instance can be fetched from the following service.
Is this because of the context of the request lifecycle?
The service:
#Injectable({scope: Scope.REQUEST})
export class IdService {
private _id: string
get id(): string {
return this._id;
}
set id(value: string) {
this._id = value;
}
}
The middleware
import {v4 as uuidv4} from 'uuid';
#Injectable()
export class AddIdMiddleware implements NestMiddleware {
constructor(private idService: IdService) {}
use(req: any, res: any, next: () => void): any {
const pid = uuidv4()
this.idService.id = pid
console.log(`generated tranceId in idService is: ${this.idService.idd}`)
next();
}
}
controller
export class XXXController {
constructor( private idService: IdService) {}
#Get("/test")
public async getId() {
console.log(`the id generated in the PER-REQUEST scope is ${this.idService.id} !!!!!!!!`)
}
}

Dependency Injection of Logger in custom service

I can't understand how the Dependency Injection works for custom services in SF 4.3 and PHP 7.2
In a controller, this simple code dumps an object Logger correctly initialized :
use Psr\Log\LoggerInterface;
/**
* #Route("/mytest", name="default_mytest")
*/
public function MyTestLoggerAction(LoggerInterface $logger) {
dump($logger);
return $this->render('default/index.html.twig');
}
But in a custom service called Guards in Guards.php, $logger is a null value :
namespace App\Workflow\CompanyDeploying\Transitions\Guards;
use Psr\Log\LoggerInterface;
class Guards {
private $logger;
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
public function isValid() {
dump($this->logger);
}
}
i tried with :
using LoggerAwareTrait but nothing more happen, $logger always null.
adding #required on getLogger() and setting public to true in services.yml, $logger always null.
using public function isValid(LoggerInterface $logger) but all code who is asking for this isValid method returns "Too few arguments to function isValid()"
using a __contruct(LoggerInterface $logger) but anywhere i need this class, the code returns "Too few arguments to function __construct()"
First Edit
my services.yaml
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\EventListener\CompanyIndexer:
tags:
- { name: doctrine.event_listener, event: prePersist }
I tried to force autowire, autoconfig and visibility in services.yaml
App\Workflow\CompanyDeploying\Transitions\Guards\Guards:
autowire: true
autoconfigure: true
public: true
and add in my Guards.php
private $logger;
/**
* #required
*/
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
public function isValid() {
dump($this->logger);
}
But no success. I always dump a null value.
2nd Edit
I call the Guard service from an EventSubscriberInterface who are listening to Wokflow event :
public static function getSubscribedEvents()
{
return [
'workflow.company_deploying.enter.mystate' => 'onEnter',
'workflow.company_deploying.leave.mystate' => 'onLeave',
'workflow.company_deploying.guard.mystate' => 'guardMyTransition',
];
}
public function guardMyTransition(Event $event) {
$this->event = $event;
if (! $this->guardFactory(__FUNCTION__)->isValid()) {
$event->setBlocked(true);
}
}
protected function guardFactory($guardName) {
$guard = GuardsFactory::create($guardName);
$guard->setCompany($this->event->getSubject());
if (isset($this->entityManager)) $guard->setEntityManager($this->entityManager);
if (isset($this->previousState)) $guard->setPreviousState($this->previousState);
return $guard;
}
My GuardFactory initialize a sub-class of Guards.
In the var/cache, i have a getGuardService.php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'App\Workflow\CompanyDeploying\Transitions\Guards\Guards' shared autowired service.
include_once $this->targetDirs[3].'/src/Workflow/CompanyDeploying/Transitions/Guards/Guards.php';
$this->services['App\\Workflow\\CompanyDeploying\\Transitions\\Guards\\Guards'] = $instance = new \App\Workflow\CompanyDeploying\Transitions\Guards\Guards();
$instance->setLogger(($this->privates['monolog.logger'] ?? $this->getMonolog_LoggerService()));
return $instance;
I just need to easily use $logger (or any other service) in every class i need without writing ton of codes with a lot of setters.
Thanks for you help.
Solution
Dependency Injection doesn't work with this kind of factory call.

Grails injected services exposed as web methods in cxf

I have a service class that I expose as jaxws using the Grails Cxf plugin. In my service I have to inject another service class which I use in my web services. If I make the service field public I get unnecessary service methods generated like below:
retrieveLastRecordUpdateDate
setPricingContractService
retrieveRecordsUpdatedFromDate
retrieveAllRecordsByInsurance
getPricingContractService
If I make the field private I cannot inject the service class. How can I both inject the service and not expose it as a web service? Simplified code below:
class PricingContractWebService {
static expose = EndpointType.JAX_WS
def pricingContractService // private?
#WebMethod( operationName="retrieveAllRecordsByInsurance" )
#WebResult( name="pricingContractList" )
#XmlElement(name="healthCareCompany", required=true)
List<PricingContractDTO> retrieveAllRecordsByInsurance(#WebParam(partName = "HealthCareCompany", name = "healthCareCompany", ) final HealthCareCompany healthCareCompany) {
def pricingContractDTOList = []
pricingContractDTOList
}
#WebMethod( operationName="retrieveLastRecordUpdateDate" )
#WebResult( name="lastUpdateDate" )
Date retrieveLastRecordUpdateDate() {
}
#WebMethod( operationName="retrieveRecordsUpdatedFromDate" )
#WebResult( name="pricingContractList" )
#XmlElement(name="updateDate", required=true)
List<PricingContractDTO> retrieveRecordsUpdatedFromDate(#WebParam(name = "updateDate") final Date date) {
def pricingContractDTOList = []
pricingContractDTOList
}
}
You should make service endpoint private and add #Autowired before endpoint declaration:
#Autowired
private PricingContractService pricingContractService

Resources