Is it possible to inject a service in UrlMappings.groovy? Something like below...
MyService.groovy
class MyService {
String foo() {
return "test"
}
}
UrlMappings.groovy
class UrlMappings {
def myService
static mappings = {
"/${myService.foo()}"
}
}
I am using Grails 3.0.11
Thank you
Related
I want to inject the result of a Nest service method as dependency in a short way. Example is a logging facility, where a child logger is derived from the main logger with a new prefix.
It should be something like this (long version):
#Injectable()
class MyService {
private logger;
constructor(private loggerService: LoggerService) {
this.logger = loggerService.getChildLogger('prefix');
}
someMethod() {
this.logger.info('Hello');
}
}
But in a short version, something like this - maybe with a decorator:
#Injectable()
class MyService {
constructor(#logger('prefix') logger: LoggerService) {
}
someMethod() {
this.logger.info('Hello');
}
}
You could create providers for your loggers using factories injecting your LoggerService :
The LoggerService:
#Injectable()
export class LoggerService {
getChildLogger(scope: string) {
return new Logger(scope);
}
}
The MyService:
#Injectable()
export class MyService implements OnModuleInit {
constructor(#Inject('MY_SERVICE_LOGGER_TOKEN') public childLogger) {}
onModuleInit() {
this.childLogger.log('Hello World');
}
}
The module:
#Module({
providers: [
LoggerService,
MyService,
{
provide: 'MY_SERVICE_LOGGER_TOKEN',
useFactory: (loggerService) => loggerService.getChildLogger('scope'),
inject: [LoggerService]
},
]
})
export class MyModule {}
There is now a solution provided by Livio Brunner:
https://github.com/BrunnerLivio/nestjs-logger-decorator
Can any one tell me how to solve the issue of PostConstruct get called before mocking:
Service:
class MyService {
SecondService secondService // injected
#PostConstruct
void init() {
myFunction()
}
void myFunction() {
secondService.doSomething()
}
}
Test:
#TestFor(MyService)
class MyServiceSpec extends Specification {
void "testing my service"() {
given:
MyService service = GroovySpy(MyService) {
myFunction() >> null
}
then:
true
}
}
Gives following error:
Invocation of init method failed; nested exception is java.lang.NullPointerException: Cannot invoke method doSomething() on null object
If you have #TestFor(MyService) - MyService instance will be created automatically and you can use it as 'service'. And you don't need to create MyService manually.
So you can only delete #TestFor(MyService) or use it and remove MyService service.
But you also need to correctly mock 'secondService'
#FreshRuntime
#TestFor(MyService)
class MyServiceSpec extends Specification {
def secondService = GroovyMock(SecondService)
def doWithSpring = {
secondService(InstanceFactoryBean, secondService, SecondService)
}
void "testing my service"() {
when:
service.myFunction()
then:
1 * secondService.doSomething()
}
}
I have a class in src/groovy like this
public class MyClass {
#AutoWired
SomeOtherClass someOtherClass
String test() {
return someOtherClass.testMethod()
}
}
When I write a test for this method I am getting an error: Cannot invoke method testMethod() on null object.
This is my test :-
def "test test" () {
expect:
myClass.test() == "somevalue"
}
What am I doing wrong? Is there a way to mock the #Autowired class?
You need to mock your someOtherClass. Something like this
def "test test"(){
setup:
myClass.someOtherClass = Mock(SomeOtherClass)
myClass.someOtherClass.testMethod() >> "somevalue"
expect:
myClass.test() == "somevalue"
}
Though the previous answer should work, spock provide more elegant way of injecting beans as per need. You could use doWithSpring closure to declare beans just like spring dsl support provided in grails using resources.groovy.
class MyClass extends Specification{
def setup(){
static doWithSpring={
someOtherClass(SomeOtherClass)
//declare below if want to inject myClass somewhere else as a bean else not
/*myClass(MyClass){bean->
someOtherClass = someOtherClass
}*/
}
}
def "test test" () {
expect:
myClass.test() == "somevalue"
}
}
Every time I start my batch job it throws an IllegalStateException and says it detected a transaction in JobRepository. I did some research and removed all #Transactional annotations in my code.
I use the Grails Spring Batch Plugin you can find here, and I work with Grails 2.3.11 and Java 8. My code looks like this:
SimpleJobBatchConfig.groovy
beans {
xmlns batch:"http://www.springframework.org/schema/batch"
batch.job(id: 'simpleJob') {
batch.step(id: 'printStep') {
batch.tasklet(ref: 'printHelloWorld')
}
}
printHelloWorld(SimpleJobTasklet) { bean ->
bean.autowire = 'byName'
}
}
BatchTestController.groovy
class BatchelorController {
def batchTestService
def index() {
}
def launchSimpleJob() {
batchTestService.launchSimpleJob()
}
}
BatchTestService.groovy
class BatchTestService {
def springBatchService
def launchSimpleJob() {
springBatchService.launch("simpleJob")
}
}
SimpleJobTasklet.groovy
class SimpleJobTasklet implements Tasklet {
#Override
RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
println("Hello World!")
return RepeatStatus.FINISHED
}
}
Grails services are transactional by default. You can customize the settings for the whole class or per-method with #Transactional but if you have no annotations it's the same as having a class-scope Spring #Transactional annotation.
To make your service non-transactional, add static transactional = false, e.g.
class BatchTestService {
static transactional = false
def springBatchService
...
}
}
I've got grails domain class that looks like this
class Thing {
String name
static hasMany = [
variants: Variant
]
}
and another one like this
class Variant {
String name
static belongsTo = [
thing: Thing
]
}
I'm trying to get the hal renderer to do a deep rendering.
Is that possible? How should I achieve it?
Same problem here, it seems a known bug in grails (https://jira.grails.org/browse/GRAILS-10954)
There is a (not very nice) workaround,
#Transactional(readOnly = true)
class ProductController extends RestfulController {
def halPCollectionRenderer
def halPRenderer
static responseFormats = ['hal','json']
ProductController() {
super(Product)
}
#PostConstruct
void init() {
halPCollectionRenderer.mappingContext = mappingContext
halPRenderer.mappingContext = mappingContext
}
MappingContext getMappingContext() {
final context = new KeyValueMappingContext("")
context.addPersistentEntity(Product)
context.addPersistentEntity(Category)
return context
}
}
Hope it helps.