Task: Add Netflix DGS framework and DGS codegen for GraphQL and still issued with subscriptions that not work on server and add some configurations and add webflux included web starter and removed all graphql kickstart and add frontend client with apollo client and react in typescript for graphql demo
This commit is contained in:
@@ -1,15 +1,6 @@
|
||||
package com.cubetiqs.graphql.demo.config
|
||||
|
||||
import graphql.scalars.ExtendedScalars
|
||||
import graphql.schema.idl.RuntimeWiring
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
class GraphQLConfig {
|
||||
@Bean
|
||||
fun extendsScalars(): RuntimeWiring.Builder {
|
||||
return RuntimeWiring.newRuntimeWiring()
|
||||
.scalar(ExtendedScalars.DateTime)
|
||||
}
|
||||
}
|
||||
class GraphQLConfig
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.cubetiqs.graphql.demo.config
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
class WebConfig : WebMvcConfigurer {
|
||||
override fun addCorsMappings(corsRegistry: CorsRegistry) {
|
||||
println("Hello World")
|
||||
corsRegistry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("*")
|
||||
.maxAge(3600)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
package com.cubetiqs.graphql.demo.context
|
||||
|
||||
import org.springframework.core.annotation.AliasFor
|
||||
import org.springframework.stereotype.Component
|
||||
import com.netflix.graphql.dgs.DgsComponent
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Component
|
||||
annotation class GMutation(
|
||||
@get:AliasFor(annotation = Component::class)
|
||||
val value: String = "",
|
||||
)
|
||||
@DgsComponent
|
||||
annotation class GMutation
|
||||
@@ -1,12 +1,8 @@
|
||||
package com.cubetiqs.graphql.demo.context
|
||||
|
||||
import org.springframework.core.annotation.AliasFor
|
||||
import org.springframework.stereotype.Component
|
||||
import com.netflix.graphql.dgs.DgsComponent
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Component
|
||||
annotation class GQuery(
|
||||
@get:AliasFor(annotation = Component::class)
|
||||
val value: String = "",
|
||||
)
|
||||
@DgsComponent
|
||||
annotation class GQuery
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.cubetiqs.graphql.demo.context
|
||||
|
||||
import com.netflix.graphql.dgs.DgsComponent
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@DgsComponent
|
||||
annotation class GSubscription
|
||||
@@ -6,20 +6,26 @@ import com.cubetiqs.graphql.demo.domain.account.AccountInput
|
||||
import com.cubetiqs.graphql.demo.domain.account.AccountMapper
|
||||
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||
import graphql.kickstart.tools.GraphQLMutationResolver
|
||||
import com.netflix.graphql.dgs.DgsData
|
||||
import com.netflix.graphql.dgs.DgsMutation
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.transaction.annotation.Propagation
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@GMutation
|
||||
class AccountMutationResolver @Autowired constructor(
|
||||
private val accountRepository: AccountRepository,
|
||||
private val userRepository: UserRepository,
|
||||
) : GraphQLMutationResolver {
|
||||
class AccountMutationResolver {
|
||||
@Autowired
|
||||
private lateinit var accountRepository: AccountRepository
|
||||
|
||||
@Autowired
|
||||
private lateinit var userRepository: UserRepository
|
||||
|
||||
@DgsData(parentType = "Mutation", field = "openAccount")
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
fun openAccount(input: AccountInput): Account {
|
||||
val account = AccountMapper.fromInputToAccount(input)
|
||||
val user = userRepository.findById(input.userId ?: 0).orElse(null) ?: throw Exception("User not found to open an account!")
|
||||
val user = userRepository.findById(input.userId ?: 0).orElse(null)
|
||||
?: throw Exception("User not found to open an account!")
|
||||
account.user = user
|
||||
return accountRepository.save(account)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.cubetiqs.graphql.demo.domain.user.User
|
||||
import com.cubetiqs.graphql.demo.domain.user.UserInput
|
||||
import com.cubetiqs.graphql.demo.domain.user.UserMapper
|
||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||
import graphql.kickstart.tools.GraphQLMutationResolver
|
||||
import com.netflix.graphql.dgs.DgsMutation
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.transaction.annotation.Propagation
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
@@ -13,7 +13,8 @@ import org.springframework.transaction.annotation.Transactional
|
||||
@GMutation
|
||||
class UserMutationResolver @Autowired constructor(
|
||||
private val userRepository: UserRepository,
|
||||
) : GraphQLMutationResolver {
|
||||
) {
|
||||
@DgsMutation(field = "createUser")
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
fun createUser(input: UserInput): User {
|
||||
if (userRepository.existsAllByUsername(input.username ?: "")) throw Exception("Username has been already existed!")
|
||||
|
||||
@@ -3,14 +3,15 @@ package com.cubetiqs.graphql.demo.resolver.query
|
||||
import com.cubetiqs.graphql.demo.context.GQuery
|
||||
import com.cubetiqs.graphql.demo.domain.account.Account
|
||||
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
||||
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||
import com.netflix.graphql.dgs.DgsQuery
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
||||
@GQuery
|
||||
class AccountQueryResolver @Autowired constructor(
|
||||
private val accountRepository: AccountRepository,
|
||||
) : GraphQLQueryResolver {
|
||||
) {
|
||||
@DgsQuery(field = "fetchAccounts")
|
||||
fun fetchAccounts(): Collection<Account> {
|
||||
val accounts = accountRepository.findAll(Pageable.unpaged())
|
||||
return accounts.content
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.cubetiqs.graphql.demo.resolver.query
|
||||
|
||||
import com.cubetiqs.graphql.demo.context.GQuery
|
||||
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||
import com.netflix.graphql.dgs.DgsQuery
|
||||
import reactor.core.publisher.Mono
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
@GQuery
|
||||
class HelloQueryResolver : GraphQLQueryResolver {
|
||||
class HelloQueryResolver {
|
||||
@DgsQuery(field = "hello")
|
||||
fun hello(): CompletableFuture<String> {
|
||||
return Mono.just("Hello Query...!").toFuture()
|
||||
}
|
||||
|
||||
@DgsQuery(field = "helloByName")
|
||||
fun helloByName(name: String): CompletableFuture<String> {
|
||||
return Mono.just("Hello $name...!").toFuture()
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,15 @@ package com.cubetiqs.graphql.demo.resolver.query
|
||||
import com.cubetiqs.graphql.demo.context.GQuery
|
||||
import com.cubetiqs.graphql.demo.domain.user.User
|
||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||
import com.netflix.graphql.dgs.DgsQuery
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
||||
@GQuery
|
||||
class UserQueryResolver @Autowired constructor(
|
||||
private val userRepository: UserRepository,
|
||||
) : GraphQLQueryResolver {
|
||||
) {
|
||||
@DgsQuery(field = "fetchUsers")
|
||||
fun fetchUsers(): Collection<User> {
|
||||
val users = userRepository.queryAllByEnabledIsTrue(Pageable.unpaged())
|
||||
return users.content
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.cubetiqs.graphql.demo.resolver.subscription
|
||||
|
||||
import graphql.kickstart.tools.GraphQLSubscriptionResolver
|
||||
import com.cubetiqs.graphql.demo.context.GSubscription
|
||||
import com.netflix.graphql.dgs.DgsSubscription
|
||||
import graphql.schema.DataFetchingEnvironment
|
||||
import org.reactivestreams.Publisher
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Flux
|
||||
import java.time.Duration
|
||||
|
||||
@Service
|
||||
class HelloSubscriptionResolver : GraphQLSubscriptionResolver {
|
||||
@GSubscription
|
||||
class HelloSubscriptionResolver {
|
||||
@DgsSubscription(field = "hello")
|
||||
fun hello(env: DataFetchingEnvironment): Publisher<Int> {
|
||||
return Flux.range(1, 10).delayElements(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ spring:
|
||||
properties:
|
||||
hibernate:
|
||||
enable_lazy_load_no_trans: ${HIBERNATE_LAZY_NO_TRANS:true}
|
||||
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
# Spring Boot Actuator
|
||||
management:
|
||||
endpoints:
|
||||
@@ -25,15 +26,8 @@ management:
|
||||
exposure:
|
||||
include: health,info,metrics
|
||||
|
||||
# GraphQL Voyager
|
||||
voyager:
|
||||
enabled: true
|
||||
cdn:
|
||||
enabled: false
|
||||
|
||||
# GraphQL Playground
|
||||
graphql:
|
||||
playground:
|
||||
enabled: true
|
||||
cdn:
|
||||
enabled: false
|
||||
# DGS GraphQL
|
||||
dgs:
|
||||
graphql:
|
||||
graphiql:
|
||||
enabled: true
|
||||
@@ -1,59 +0,0 @@
|
||||
enum AccountType {
|
||||
BASIC
|
||||
PREMIUM
|
||||
BUSINESS
|
||||
}
|
||||
|
||||
enum AccountCurrency {
|
||||
USD
|
||||
KHR
|
||||
}
|
||||
|
||||
type User {
|
||||
id: ID
|
||||
code: String
|
||||
username: String
|
||||
name: String
|
||||
enabled: Boolean
|
||||
}
|
||||
|
||||
type Account {
|
||||
id: ID
|
||||
code: String
|
||||
balance: Float
|
||||
currentBalance: Float
|
||||
type: AccountType
|
||||
currency: AccountCurrency
|
||||
user: User!
|
||||
}
|
||||
|
||||
input UserInput {
|
||||
username: String
|
||||
password: String
|
||||
name: String
|
||||
enabled: Boolean
|
||||
}
|
||||
|
||||
input AccountInput {
|
||||
userId: Int
|
||||
type: AccountType
|
||||
currency: AccountCurrency
|
||||
}
|
||||
|
||||
type Query {
|
||||
hello: String
|
||||
|
||||
fetchUsers: [User]!
|
||||
|
||||
fetchAccounts: [Account]!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
hello: Int
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(input: UserInput): User!
|
||||
|
||||
openAccount(input: AccountInput): Account!
|
||||
}
|
||||
26
src/main/resources/schema/account.graphql
Normal file
26
src/main/resources/schema/account.graphql
Normal file
@@ -0,0 +1,26 @@
|
||||
enum AccountType {
|
||||
BASIC
|
||||
PREMIUM
|
||||
BUSINESS
|
||||
}
|
||||
|
||||
enum AccountCurrency {
|
||||
USD
|
||||
KHR
|
||||
}
|
||||
|
||||
type Account {
|
||||
id: ID
|
||||
code: String
|
||||
balance: Float
|
||||
currentBalance: Float
|
||||
type: AccountType
|
||||
currency: AccountCurrency
|
||||
user: User!
|
||||
}
|
||||
|
||||
input AccountInput {
|
||||
userId: Int
|
||||
type: AccountType
|
||||
currency: AccountCurrency
|
||||
}
|
||||
16
src/main/resources/schema/expense.graphql
Normal file
16
src/main/resources/schema/expense.graphql
Normal file
@@ -0,0 +1,16 @@
|
||||
input ExpenseFilter {
|
||||
isIncome: Boolean
|
||||
}
|
||||
|
||||
type Expense {
|
||||
id: ID
|
||||
description: String
|
||||
amount: Int
|
||||
isIncome: Boolean
|
||||
}
|
||||
|
||||
type ExpensePage {
|
||||
list: [Expense]
|
||||
totalPages: Int
|
||||
currentPage: Int
|
||||
}
|
||||
21
src/main/resources/schema/schema.graphqls
Normal file
21
src/main/resources/schema/schema.graphqls
Normal file
@@ -0,0 +1,21 @@
|
||||
type Query {
|
||||
hello: String
|
||||
|
||||
helloByName(name: String!): String
|
||||
|
||||
fetchUsers: [User]!
|
||||
|
||||
fetchAccounts: [Account]!
|
||||
|
||||
fetchExpenses(filter: ExpenseFilter, pageNumber: Int, pageSize: Int) : ExpensePage
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
hello: Int
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(input: UserInput): User!
|
||||
|
||||
openAccount(input: AccountInput): Account!
|
||||
}
|
||||
14
src/main/resources/schema/user.graphql
Normal file
14
src/main/resources/schema/user.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
type User {
|
||||
id: ID
|
||||
code: String
|
||||
username: String
|
||||
name: String
|
||||
enabled: Boolean
|
||||
}
|
||||
|
||||
input UserInput {
|
||||
username: String
|
||||
password: String
|
||||
name: String
|
||||
enabled: Boolean
|
||||
}
|
||||
@@ -1,13 +1,55 @@
|
||||
package com.cubetiqs.graphql.demo
|
||||
|
||||
import com.cubetiqs.graphql.demo.resolver.subscription.HelloSubscriptionResolver
|
||||
import com.netflix.graphql.dgs.DgsQueryExecutor
|
||||
import com.netflix.graphql.dgs.autoconfig.DgsAutoConfiguration
|
||||
import graphql.ExecutionResult
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.reactivestreams.Publisher
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(
|
||||
classes = [
|
||||
DgsAutoConfiguration::class,
|
||||
HelloSubscriptionResolver::class,
|
||||
]
|
||||
)
|
||||
class GraphqlDemoApplicationTests {
|
||||
@Autowired
|
||||
lateinit var dgsQueryExecutor: DgsQueryExecutor
|
||||
|
||||
@Test
|
||||
fun contextLoads() {
|
||||
}
|
||||
@Test
|
||||
fun helloSubscription() {
|
||||
dgsQueryExecutor.execute(
|
||||
"""
|
||||
subscription {
|
||||
hello
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
.getData<Publisher<ExecutionResult>>()
|
||||
.subscribe(object : Subscriber<ExecutionResult> {
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
s.request(2)
|
||||
}
|
||||
|
||||
override fun onNext(t: ExecutionResult) {
|
||||
println(t.getData<Any?>())
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
println("Hello World")
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user