Compare commits
No commits in common. "main" and "kickstart" have entirely different histories.
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@ -1,11 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gradle" # See documentation for possible values
|
|
||||||
directory: "/" # Location of package manifests
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
../HELP.md
|
HELP.md
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
build/
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
@ -35,27 +35,3 @@ out/
|
|||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
8
.gitmodules
vendored
8
.gitmodules
vendored
@ -1,8 +0,0 @@
|
|||||||
[submodule "cubetiq-security-core"]
|
|
||||||
path = cubetiq-security-core
|
|
||||||
url = https://git.cubetiqs.com/cubetiq/cubetiq-security-core.git
|
|
||||||
branch = main
|
|
||||||
[submodule "cubetiq-security-web"]
|
|
||||||
path = cubetiq-security-web
|
|
||||||
url = https://git.cubetiqs.com/cubetiq/cubetiq-security-web.git
|
|
||||||
branch = main
|
|
@ -1,7 +1,7 @@
|
|||||||
# GraphQL & Spring Boot (Demo Project)
|
# GraphQL & Spring Boot (Demo Project)
|
||||||
- Spring Boot
|
- Spring Boot (2.6)
|
||||||
- DGS Framework
|
- GraphQL (11)
|
||||||
- Kotlin
|
- Kotlin (1.5.21)
|
||||||
|
|
||||||
# Contributors
|
# Contributors
|
||||||
- Sambo Chea <sombochea@cubetiqs.com>
|
- Sambo Chea <sombochea@cubetiqs.com>
|
@ -1,45 +1,53 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("org.springframework.boot") version "3.0.2" apply false
|
id("org.springframework.boot") version "2.6.0-SNAPSHOT"
|
||||||
id("io.spring.dependency-management") version "1.1.0" apply false
|
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||||
kotlin("jvm") version "1.8.0" apply false
|
kotlin("jvm") version "1.5.21"
|
||||||
kotlin("plugin.spring") version "1.8.0" apply false
|
kotlin("plugin.spring") version "1.5.21"
|
||||||
kotlin("plugin.jpa") version "1.8.0" apply false
|
kotlin("plugin.jpa") version "1.5.21"
|
||||||
id("com.netflix.dgs.codegen") version "5.6.6" apply false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
group = "com.cubetiqs"
|
||||||
repositories {
|
version = "0.0.1-SNAPSHOT"
|
||||||
mavenCentral()
|
java.sourceCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
|
||||||
|
|
||||||
group = "com.cubetiqs"
|
repositories {
|
||||||
version = "0.0.1-SNAPSHOT"
|
maven { url = uri("https://m.ctdn.net") }
|
||||||
|
}
|
||||||
|
|
||||||
val javaVersion = "17"
|
extra["graphqlVersion"] = "11.0.0"
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
dependencies {
|
||||||
sourceCompatibility = javaVersion
|
implementation("com.graphql-java:graphql-java-extended-scalars:16.0.0")
|
||||||
targetCompatibility = javaVersion
|
implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:${property("graphqlVersion")}")
|
||||||
}
|
implementation("com.graphql-java-kickstart:playground-spring-boot-starter:${property("graphqlVersion")}")
|
||||||
|
implementation("com.graphql-java-kickstart:voyager-spring-boot-starter:${property("graphqlVersion")}")
|
||||||
|
|
||||||
tasks.withType<KotlinCompile> {
|
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
||||||
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
|
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
||||||
|
|
||||||
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
runtimeOnly("org.postgresql:postgresql")
|
||||||
|
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
testImplementation("io.projectreactor:reactor-test")
|
||||||
|
testImplementation("com.graphql-java-kickstart:graphql-spring-boot-starter-test:${property("graphqlVersion")}")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile> {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
freeCompilerArgs = listOf("-Xjsr305=strict")
|
freeCompilerArgs = listOf("-Xjsr305=strict")
|
||||||
jvmTarget = javaVersion
|
jvmTarget = "11"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
tasks.withType<Test> {
|
||||||
apply {
|
useJUnitPlatform()
|
||||||
plugin("io.spring.dependency-management")
|
|
||||||
}
|
|
||||||
|
|
||||||
the<io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension>().apply {
|
|
||||||
imports {
|
|
||||||
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
23
client/.gitignore
vendored
23
client/.gitignore
vendored
@ -1,23 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
@ -1,3 +0,0 @@
|
|||||||
# React Client App
|
|
||||||
- Apollo Client
|
|
||||||
- GraphQL
|
|
@ -1,17 +0,0 @@
|
|||||||
const CracoLessPlugin = require('craco-less');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
plugin: CracoLessPlugin,
|
|
||||||
options: {
|
|
||||||
lessLoaderOptions: {
|
|
||||||
lessOptions: {
|
|
||||||
modifyVars: { '@primary-color': '#7c2cdb' },
|
|
||||||
javascriptEnabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "client",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@apollo/client": "^3.4.5",
|
|
||||||
"antd": "^5.0.0",
|
|
||||||
"graphql": "^16.0.0",
|
|
||||||
"react": "^18.0.0",
|
|
||||||
"react-dom": "^18.0.0",
|
|
||||||
"subscriptions-transport-ws": "^0.11.0",
|
|
||||||
"web-vitals": "^3.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "craco start",
|
|
||||||
"build": "craco build",
|
|
||||||
"test": "craco test",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@craco/craco": "^7.0.0",
|
|
||||||
"craco-less": "^2.0.0",
|
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
|
||||||
"@testing-library/react": "^13.0.0",
|
|
||||||
"@testing-library/user-event": "^12.1.10",
|
|
||||||
"@types/jest": "^29.0.0",
|
|
||||||
"@types/node": "^18.0.0",
|
|
||||||
"@types/react": "^18.0.0",
|
|
||||||
"@types/react-dom": "^18.0.0",
|
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"typescript": "^4.1.2"
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
@ -1,43 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>React App</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "React App",
|
|
||||||
"name": "Create React App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
@ -1 +0,0 @@
|
|||||||
@import '~antd/dist/antd.less';
|
|
@ -1,99 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import './App.less';
|
|
||||||
import {gql, useQuery, useSubscription} from "@apollo/client";
|
|
||||||
import { Table } from 'antd';
|
|
||||||
|
|
||||||
interface User {
|
|
||||||
id: number
|
|
||||||
code: string
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Account {
|
|
||||||
id: number
|
|
||||||
code: string
|
|
||||||
balance: number
|
|
||||||
user: User
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AccountResult {
|
|
||||||
fetchAccounts: Array<Account>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ACCOUNTS = gql`
|
|
||||||
{
|
|
||||||
fetchAccounts {
|
|
||||||
id
|
|
||||||
code
|
|
||||||
balance
|
|
||||||
user {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const SUB_ACCOUNTS = gql`
|
|
||||||
subscription {
|
|
||||||
fetchAccounts {
|
|
||||||
id
|
|
||||||
code
|
|
||||||
balance
|
|
||||||
user {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const HELLO = gql`
|
|
||||||
subscription {
|
|
||||||
hello
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const accountColumns = [
|
|
||||||
{
|
|
||||||
title: "Account ID",
|
|
||||||
dataIndex: "id",
|
|
||||||
key: "id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Account Code",
|
|
||||||
dataIndex: "code",
|
|
||||||
key: "code",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "User",
|
|
||||||
dataIndex: ["user", "name"],
|
|
||||||
key: "user.name",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
function App() {
|
|
||||||
// const {error, loading, data} = useQuery<AccountResult>(ACCOUNTS)
|
|
||||||
const {error, loading, data} = useSubscription<AccountResult>(SUB_ACCOUNTS)
|
|
||||||
// const {error, loading, data} = useSubscription(HELLO)
|
|
||||||
console.log(data)
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1>Accounts</h1>
|
|
||||||
{
|
|
||||||
loading || !data ? <p>Loading...</p> :
|
|
||||||
// data.fetchAccounts.map(account => (
|
|
||||||
// <>
|
|
||||||
// <div>Account ID: {account.id}</div>
|
|
||||||
// <div>Account Code: {account.code}</div>
|
|
||||||
// <div>Account User: {account.user.name}</div>
|
|
||||||
// </>
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
<Table dataSource={data.fetchAccounts} columns={accountColumns}/>
|
|
||||||
// <p>{`${data.hello}`}</p>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
@ -1,54 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import './index.less';
|
|
||||||
import App from './App';
|
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
|
|
||||||
import {split, HttpLink, ApolloClient, InMemoryCache} from '@apollo/client';
|
|
||||||
import {ApolloProvider} from '@apollo/client/react';
|
|
||||||
import {getMainDefinition} from '@apollo/client/utilities';
|
|
||||||
import {WebSocketLink} from '@apollo/client/link/ws';
|
|
||||||
|
|
||||||
const APP_HOST = process.env.APP_HOST || 'localhost:8081'
|
|
||||||
|
|
||||||
const httpLink = new HttpLink({
|
|
||||||
uri: `http://${APP_HOST}/graphql`
|
|
||||||
});
|
|
||||||
|
|
||||||
const wsLink = new WebSocketLink({
|
|
||||||
uri: `ws://${APP_HOST}/subscriptions`,
|
|
||||||
options: {
|
|
||||||
reconnect: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const splitLink = split(
|
|
||||||
({query}) => {
|
|
||||||
const definition = getMainDefinition(query);
|
|
||||||
return (
|
|
||||||
definition.kind === 'OperationDefinition' &&
|
|
||||||
definition.operation === 'subscription'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
wsLink,
|
|
||||||
httpLink,
|
|
||||||
);
|
|
||||||
|
|
||||||
const client = new ApolloClient({
|
|
||||||
link: splitLink,
|
|
||||||
cache: new InMemoryCache()
|
|
||||||
});
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<ApolloProvider client={client}>
|
|
||||||
<App/>
|
|
||||||
</ApolloProvider>
|
|
||||||
</React.StrictMode>,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
1
client/src/react-app-env.d.ts
vendored
1
client/src/react-app-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="react-scripts" />
|
|
@ -1,15 +0,0 @@
|
|||||||
import { ReportHandler } from 'web-vitals';
|
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
||||||
getCLS(onPerfEntry);
|
|
||||||
getFID(onPerfEntry);
|
|
||||||
getFCP(onPerfEntry);
|
|
||||||
getLCP(onPerfEntry);
|
|
||||||
getTTFB(onPerfEntry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reportWebVitals;
|
|
@ -1,5 +0,0 @@
|
|||||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
||||||
// allows you to do things like:
|
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
|
||||||
import '@testing-library/jest-dom';
|
|
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx"
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
|
11721
client/yarn.lock
11721
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
Subproject commit 612bafe9af476798a40a536c82112c63c8627f4f
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 62d0e718e59af79db15871bde67affd8c38b15e5
|
|
37
dgs-graphql/.gitignore
vendored
37
dgs-graphql/.gitignore
vendored
@ -1,37 +0,0 @@
|
|||||||
../HELP.md
|
|
||||||
.gradle
|
|
||||||
build/
|
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### STS ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
bin/
|
|
||||||
!**/src/main/**/bin/
|
|
||||||
!**/src/test/**/bin/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
.idea
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
out/
|
|
||||||
!**/src/main/**/out/
|
|
||||||
!**/src/test/**/out/
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
@ -1,43 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("org.springframework.boot")
|
|
||||||
id("io.spring.dependency-management")
|
|
||||||
kotlin("jvm")
|
|
||||||
kotlin("plugin.spring")
|
|
||||||
kotlin("plugin.jpa")
|
|
||||||
id("com.netflix.dgs.codegen")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api(project(":cubetiq-security-web"))
|
|
||||||
|
|
||||||
implementation(platform("com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:6.0.0"))
|
|
||||||
implementation("com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter")
|
|
||||||
runtimeOnly("com.netflix.graphql.dgs:graphql-dgs-subscriptions-websockets-autoconfigure")
|
|
||||||
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
|
||||||
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
|
||||||
runtimeOnly("org.postgresql:postgresql")
|
|
||||||
|
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
||||||
testImplementation("io.projectreactor:reactor-test")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<Test> {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask> {
|
|
||||||
packageName = "com.cubetiqs.graphql.demo.dgmodel"
|
|
||||||
schemaPaths = mutableListOf("${projectDir}/src/main/resources/schema")
|
|
||||||
generateClient = true
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo
|
|
||||||
|
|
||||||
import com.cubetiqs.sp.security.util.PasswordUtils
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
|
||||||
import org.springframework.boot.runApplication
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
class GraphqlDemoApplication @Autowired constructor(
|
|
||||||
passwordEncoder: PasswordEncoder,
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
PasswordUtils.setEncoder(passwordEncoder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
runApplication<GraphqlDemoApplication>(*args)
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.config
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
class GraphQLConfig
|
|
@ -1,17 +0,0 @@
|
|||||||
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) {
|
|
||||||
corsRegistry.addMapping("/**")
|
|
||||||
.allowedOrigins("*")
|
|
||||||
.allowedMethods("*")
|
|
||||||
.maxAge(3600)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.config
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.security.AuthService
|
|
||||||
import com.cubetiqs.sp.security.EnableCubetiqSecurityModule
|
|
||||||
import com.cubetiqs.sp.security.jwt.CubetiqJwtProperties
|
|
||||||
import com.cubetiqs.sp.security.jwt.JwtSecurityConfigurer
|
|
||||||
import com.cubetiqs.sp.security.support.AuthenticationExceptionEntryPoint
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
@EnableMethodSecurity(prePostEnabled = true)
|
|
||||||
@EnableCubetiqSecurityModule
|
|
||||||
class WebSecurityConfig @Autowired constructor(
|
|
||||||
private val authService: AuthService,
|
|
||||||
private val cubetiqJwtProperties: CubetiqJwtProperties,
|
|
||||||
) {
|
|
||||||
@Bean
|
|
||||||
fun passwordEncoder(): PasswordEncoder {
|
|
||||||
return BCryptPasswordEncoder(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
|
||||||
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
|
||||||
|
|
||||||
http.exceptionHandling().authenticationEntryPoint(AuthenticationExceptionEntryPoint())
|
|
||||||
|
|
||||||
http.apply(JwtSecurityConfigurer(authService, cubetiqJwtProperties))
|
|
||||||
|
|
||||||
http.authorizeHttpRequests().anyRequest().permitAll()
|
|
||||||
|
|
||||||
return http.build()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.context
|
|
||||||
|
|
||||||
import com.netflix.graphql.dgs.DgsComponent
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@DgsComponent
|
|
||||||
annotation class GMutation
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.context
|
|
||||||
|
|
||||||
import com.netflix.graphql.dgs.DgsComponent
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@DgsComponent
|
|
||||||
annotation class GQuery
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.context
|
|
||||||
|
|
||||||
import com.netflix.graphql.dgs.DgsComponent
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@DgsComponent
|
|
||||||
annotation class GSubscription
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.repository
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.domain.account.Account
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
import org.springframework.scheduling.annotation.Async
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
interface AccountRepository : JpaRepository<Account, Long> {
|
|
||||||
@Async
|
|
||||||
fun readAllBy(): CompletableFuture<Collection<Account>>
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.mutation
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GMutation
|
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.types.LoginResponse
|
|
||||||
import com.cubetiqs.graphql.demo.security.AuthService
|
|
||||||
import com.cubetiqs.sp.security.jwt.util.JwtTokenUtils
|
|
||||||
import com.netflix.graphql.dgs.DgsMutation
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
|
|
||||||
@GMutation
|
|
||||||
class LoginMutationResolver {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var authService: AuthService
|
|
||||||
|
|
||||||
@DgsMutation(field = DgsConstants.MUTATION.Login)
|
|
||||||
fun login(username: String, password: String): LoginResponse {
|
|
||||||
val auth = authService.login(username, password)
|
|
||||||
val token = JwtTokenUtils.createTokens(auth)
|
|
||||||
return LoginResponse(
|
|
||||||
accessToken = token.accessToken,
|
|
||||||
refreshToken = token.refreshToken,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.query
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GQuery
|
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.netflix.graphql.dgs.DgsQuery
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
@GQuery
|
|
||||||
class HelloQueryResolver {
|
|
||||||
@DgsQuery(field = "hello")
|
|
||||||
fun hello(): CompletableFuture<String> {
|
|
||||||
return Mono.just("Hello Query...!").toFuture()
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasAnyRole('ADMIN')")
|
|
||||||
@DgsQuery(field = DgsConstants.QUERY.HelloByName)
|
|
||||||
fun helloByName(name: String): CompletableFuture<String> {
|
|
||||||
return Mono.just("Hello $name...!").toFuture()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.subscription
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GSubscription
|
|
||||||
import com.cubetiqs.graphql.demo.domain.account.Account
|
|
||||||
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
|
||||||
import com.netflix.graphql.dgs.DgsSubscription
|
|
||||||
import graphql.schema.DataFetchingEnvironment
|
|
||||||
import kotlinx.coroutines.reactor.awaitSingle
|
|
||||||
import org.reactivestreams.Publisher
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import reactor.kotlin.core.publisher.toFlux
|
|
||||||
import reactor.kotlin.core.publisher.toMono
|
|
||||||
import java.time.Duration
|
|
||||||
|
|
||||||
@GSubscription
|
|
||||||
class HelloSubscriptionResolver {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var accountRepository: AccountRepository
|
|
||||||
|
|
||||||
@DgsSubscription(field = "hello")
|
|
||||||
fun hello(env: DataFetchingEnvironment): Publisher<Int> {
|
|
||||||
return Flux.range(1, 10).delayElements(Duration.ofSeconds(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
@DgsSubscription(field = "fetchAccounts")
|
|
||||||
fun fetchAccounts(env: DataFetchingEnvironment): Publisher<Collection<Account>> {
|
|
||||||
return Flux.generate<Collection<Account>?> { sink ->
|
|
||||||
sink.next(accountRepository.findAll())
|
|
||||||
}.delayElements(Duration.ofSeconds(1))
|
|
||||||
// return Mono.fromFuture(accounts).toFlux().delayElements(Duration.ofSeconds(1))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.security
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.domain.user.User
|
|
||||||
import com.cubetiqs.sp.security.jwt.util.JwtTokenUtils
|
|
||||||
import com.cubetiqs.sp.security.util.PasswordUtils
|
|
||||||
import org.springframework.security.core.GrantedAuthority
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
|
||||||
|
|
||||||
data class AuthDetails(
|
|
||||||
private var code: String? = null,
|
|
||||||
private var name: String? = null,
|
|
||||||
private var username: String? = null,
|
|
||||||
private var password: String? = null,
|
|
||||||
private var authorities: Collection<String>? = null,
|
|
||||||
private var enabled: Boolean? = null,
|
|
||||||
) : UserDetails {
|
|
||||||
override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
|
|
||||||
return authorities?.map { SimpleGrantedAuthority(it) }?.toMutableList() ?: mutableListOf(
|
|
||||||
SimpleGrantedAuthority(
|
|
||||||
"USER"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPassword(): String {
|
|
||||||
return password ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getUsername(): String {
|
|
||||||
return username ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isAccountNonExpired(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isAccountNonLocked(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isCredentialsNonExpired(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEnabled(): Boolean {
|
|
||||||
return enabled ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPasswordValid(password: String): Boolean {
|
|
||||||
return PasswordUtils.matches(password, this.getPassword())
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromUser(user: User): AuthDetails {
|
|
||||||
return AuthDetails(
|
|
||||||
code = user.code,
|
|
||||||
name = user.name,
|
|
||||||
username = user.username,
|
|
||||||
password = user.password,
|
|
||||||
enabled = user.enabled,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package com.cubetiqs.graphql.demo.security
|
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.security.access.AccessDeniedException
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class AuthService : UserDetailsService {
|
|
||||||
@Autowired
|
|
||||||
private lateinit var userRepository: UserRepository
|
|
||||||
|
|
||||||
private fun findAuthDetailsByUsername(username: String): AuthDetails {
|
|
||||||
val user = userRepository.queryByUsername(username).orElse(null) ?: throw Exception("User not found!")
|
|
||||||
return AuthDetails.fromUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadUserByUsername(username: String?): UserDetails {
|
|
||||||
return findAuthDetailsByUsername(username ?: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun login(username: String, password: String): AuthDetails {
|
|
||||||
val auth = findAuthDetailsByUsername(username)
|
|
||||||
if (auth.isPasswordValid(password)) {
|
|
||||||
return auth
|
|
||||||
} else {
|
|
||||||
throw AccessDeniedException("Username and password is incorrect!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
dgs-graphql/src/main/resources/.gitignore
vendored
1
dgs-graphql/src/main/resources/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
application-local.yml
|
|
@ -1,26 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
input ExpenseFilter {
|
|
||||||
isIncome: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type Expense {
|
|
||||||
id: ID
|
|
||||||
description: String
|
|
||||||
amount: Int
|
|
||||||
isIncome: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExpensePage {
|
|
||||||
list: [Expense]
|
|
||||||
totalPages: Int
|
|
||||||
currentPage: Int
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
type Query {
|
|
||||||
hello: String
|
|
||||||
|
|
||||||
helloByName(name: String!): String
|
|
||||||
|
|
||||||
fetchUsers: [User]!
|
|
||||||
|
|
||||||
fetchAccounts: [Account]!
|
|
||||||
|
|
||||||
fetchExpenses(filter: ExpenseFilter, pageNumber: Int, pageSize: Int) : ExpensePage
|
|
||||||
}
|
|
||||||
|
|
||||||
type Subscription {
|
|
||||||
hello: Int
|
|
||||||
|
|
||||||
fetchAccounts: [Account]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
login(username: String!, password: String!): LoginResponse
|
|
||||||
|
|
||||||
createUser(input: UserInput): User!
|
|
||||||
changeUserPassword(input: UserChangePasswordInput): User!
|
|
||||||
|
|
||||||
openAccount(input: AccountInput): Account!
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
type User {
|
|
||||||
id: ID
|
|
||||||
code: String
|
|
||||||
username: String
|
|
||||||
name: String
|
|
||||||
enabled: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
input UserInput {
|
|
||||||
username: String
|
|
||||||
password: String
|
|
||||||
name: String
|
|
||||||
enabled: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginResponse {
|
|
||||||
accessToken: String
|
|
||||||
refreshToken: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input UserChangePasswordInput {
|
|
||||||
username: String!
|
|
||||||
password: String!
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
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(
|
|
||||||
classes = [
|
|
||||||
DgsAutoConfiguration::class,
|
|
||||||
HelloSubscriptionResolver::class,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
class GraphqlDemoApplicationTests {
|
|
||||||
@Autowired
|
|
||||||
lateinit var dgsQueryExecutor: DgsQueryExecutor
|
|
||||||
|
|
||||||
@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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:base"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,3 +1,8 @@
|
|||||||
rootProject.name = "spring-graphql-demo"
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
include("cubetiq-security-core", "cubetiq-security-web", "dgs-graphql")
|
maven { url = uri("https://repo.spring.io/milestone") }
|
||||||
|
maven { url = uri("https://repo.spring.io/snapshot") }
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name = "graphql-demo"
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.cubetiqs.graphql.demo
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
class GraphqlDemoApplication
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
runApplication<GraphqlDemoApplication>(*args)
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,8 @@
|
|||||||
package com.cubetiqs.graphql.demo.config
|
package com.cubetiqs.graphql.demo.config
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.scheduling.annotation.EnableAsync
|
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement
|
import org.springframework.transaction.annotation.EnableTransactionManagement
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
@EnableAsync
|
|
||||||
class ManagementConfig
|
class ManagementConfig
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.cubetiqs.graphql.demo.context
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Component
|
||||||
|
annotation class GMutation(
|
||||||
|
@get:AliasFor(annotation = Component::class)
|
||||||
|
val value: String = "",
|
||||||
|
)
|
12
src/main/kotlin/com/cubetiqs/graphql/demo/context/GQuery.kt
Normal file
12
src/main/kotlin/com/cubetiqs/graphql/demo/context/GQuery.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.cubetiqs.graphql.demo.context
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Component
|
||||||
|
annotation class GQuery(
|
||||||
|
@get:AliasFor(annotation = Component::class)
|
||||||
|
val value: String = "",
|
||||||
|
)
|
@ -2,8 +2,8 @@ package com.cubetiqs.graphql.demo.domain
|
|||||||
|
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import jakarta.persistence.EntityListeners
|
import javax.persistence.EntityListeners
|
||||||
import jakarta.persistence.MappedSuperclass
|
import javax.persistence.MappedSuperclass
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@EntityListeners(AuditingEntityListener::class)
|
@EntityListeners(AuditingEntityListener::class)
|
@ -8,7 +8,7 @@ import org.springframework.data.annotation.CreatedDate
|
|||||||
import org.springframework.data.annotation.LastModifiedDate
|
import org.springframework.data.annotation.LastModifiedDate
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import jakarta.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "accounts", indexes = [
|
@Table(name = "accounts", indexes = [
|
@ -1,9 +1,9 @@
|
|||||||
package com.cubetiqs.graphql.demo.domain.account
|
package com.cubetiqs.graphql.demo.domain.account
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import jakarta.persistence.PostPersist
|
import javax.persistence.PostPersist
|
||||||
import jakarta.persistence.PrePersist
|
import javax.persistence.PrePersist
|
||||||
import jakarta.persistence.PreUpdate
|
import javax.persistence.PreUpdate
|
||||||
|
|
||||||
class AccountEntityListener {
|
class AccountEntityListener {
|
||||||
@PrePersist
|
@PrePersist
|
@ -7,7 +7,7 @@ import org.hibernate.Hibernate
|
|||||||
import org.springframework.data.annotation.CreatedDate
|
import org.springframework.data.annotation.CreatedDate
|
||||||
import org.springframework.data.annotation.LastModifiedDate
|
import org.springframework.data.annotation.LastModifiedDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import jakarta.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
@ -1,8 +1,8 @@
|
|||||||
package com.cubetiqs.graphql.demo.domain.user
|
package com.cubetiqs.graphql.demo.domain.user
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import jakarta.persistence.PrePersist
|
import javax.persistence.PrePersist
|
||||||
import jakarta.persistence.PreUpdate
|
import javax.persistence.PreUpdate
|
||||||
|
|
||||||
class UserEntityListener {
|
class UserEntityListener {
|
||||||
@PrePersist
|
@PrePersist
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.cubetiqs.graphql.demo.repository
|
||||||
|
|
||||||
|
import com.cubetiqs.graphql.demo.domain.account.Account
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface AccountRepository : JpaRepository<Account, Long>
|
@ -6,7 +6,6 @@ import org.springframework.data.domain.Pageable
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.data.jpa.repository.Query
|
import org.springframework.data.jpa.repository.Query
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface UserRepository : JpaRepository<User, Long> {
|
interface UserRepository : JpaRepository<User, Long> {
|
||||||
@ -15,7 +14,4 @@ interface UserRepository : JpaRepository<User, Long> {
|
|||||||
|
|
||||||
@Query("select (count(u) > 0) from User u where u.username = ?1")
|
@Query("select (count(u) > 0) from User u where u.username = ?1")
|
||||||
fun existsAllByUsername(username: String): Boolean
|
fun existsAllByUsername(username: String): Boolean
|
||||||
|
|
||||||
@Query("select u from User u where u.username = ?1")
|
|
||||||
fun queryByUsername(username: String): Optional<User>
|
|
||||||
}
|
}
|
@ -1,31 +1,25 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.mutation
|
package com.cubetiqs.graphql.demo.resolver.mutation
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GMutation
|
import com.cubetiqs.graphql.demo.context.GMutation
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.cubetiqs.graphql.demo.domain.account.Account
|
import com.cubetiqs.graphql.demo.domain.account.Account
|
||||||
import com.cubetiqs.graphql.demo.domain.account.AccountInput
|
import com.cubetiqs.graphql.demo.domain.account.AccountInput
|
||||||
import com.cubetiqs.graphql.demo.domain.account.AccountMapper
|
import com.cubetiqs.graphql.demo.domain.account.AccountMapper
|
||||||
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
||||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||||
import com.netflix.graphql.dgs.DgsMutation
|
import graphql.kickstart.tools.GraphQLMutationResolver
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.transaction.annotation.Propagation
|
import org.springframework.transaction.annotation.Propagation
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
@GMutation
|
@GMutation
|
||||||
class AccountMutationResolver {
|
class AccountMutationResolver @Autowired constructor(
|
||||||
@Autowired
|
private val accountRepository: AccountRepository,
|
||||||
private lateinit var accountRepository: AccountRepository
|
private val userRepository: UserRepository,
|
||||||
|
) : GraphQLMutationResolver {
|
||||||
@Autowired
|
|
||||||
private lateinit var userRepository: UserRepository
|
|
||||||
|
|
||||||
@DgsMutation(field = DgsConstants.MUTATION.OpenAccount)
|
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
fun openAccount(input: AccountInput): Account {
|
fun openAccount(input: AccountInput): Account {
|
||||||
val account = AccountMapper.fromInputToAccount(input)
|
val account = AccountMapper.fromInputToAccount(input)
|
||||||
val user = userRepository.findById(input.userId ?: 0).orElse(null)
|
val user = userRepository.findById(input.userId ?: 0).orElse(null) ?: throw Exception("User not found to open an account!")
|
||||||
?: throw Exception("User not found to open an account!")
|
|
||||||
account.user = user
|
account.user = user
|
||||||
return accountRepository.save(account)
|
return accountRepository.save(account)
|
||||||
}
|
}
|
@ -1,15 +1,11 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.mutation
|
package com.cubetiqs.graphql.demo.resolver.mutation
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GMutation
|
import com.cubetiqs.graphql.demo.context.GMutation
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.types.UserChangePasswordInput
|
|
||||||
import com.cubetiqs.graphql.demo.domain.user.User
|
import com.cubetiqs.graphql.demo.domain.user.User
|
||||||
import com.cubetiqs.graphql.demo.domain.user.UserInput
|
import com.cubetiqs.graphql.demo.domain.user.UserInput
|
||||||
import com.cubetiqs.graphql.demo.domain.user.UserMapper
|
import com.cubetiqs.graphql.demo.domain.user.UserMapper
|
||||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||||
import com.cubetiqs.sp.security.util.PasswordUtils
|
import graphql.kickstart.tools.GraphQLMutationResolver
|
||||||
import com.netflix.graphql.dgs.DgsMutation
|
|
||||||
import com.netflix.graphql.dgs.exceptions.DgsEntityNotFoundException
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.transaction.annotation.Propagation
|
import org.springframework.transaction.annotation.Propagation
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
@ -17,20 +13,12 @@ import org.springframework.transaction.annotation.Transactional
|
|||||||
@GMutation
|
@GMutation
|
||||||
class UserMutationResolver @Autowired constructor(
|
class UserMutationResolver @Autowired constructor(
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
) {
|
) : GraphQLMutationResolver {
|
||||||
@DgsMutation(field = DgsConstants.MUTATION.CreateUser)
|
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
fun createUser(input: UserInput): User {
|
fun createUser(input: UserInput): User {
|
||||||
if (userRepository.existsAllByUsername(input.username ?: "")) throw DgsEntityNotFoundException("Username has been already existed!")
|
if (userRepository.existsAllByUsername(input.username ?: "")) throw Exception("Username has been already existed!")
|
||||||
|
|
||||||
val user = UserMapper.fromInputToUser(input)
|
val user = UserMapper.fromInputToUser(input)
|
||||||
return userRepository.save(user)
|
return userRepository.save(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DgsMutation(field = DgsConstants.MUTATION.ChangeUserPassword)
|
|
||||||
fun changePassword(input: UserChangePasswordInput): User {
|
|
||||||
val user = userRepository.queryByUsername(input.username).orElse(null) ?: throw DgsEntityNotFoundException("User not found!")
|
|
||||||
user.password = PasswordUtils.encode(input.password)
|
|
||||||
return userRepository.save(user)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,18 +1,16 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.query
|
package com.cubetiqs.graphql.demo.resolver.query
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GQuery
|
import com.cubetiqs.graphql.demo.context.GQuery
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.cubetiqs.graphql.demo.domain.account.Account
|
import com.cubetiqs.graphql.demo.domain.account.Account
|
||||||
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
import com.cubetiqs.graphql.demo.repository.AccountRepository
|
||||||
import com.netflix.graphql.dgs.DgsQuery
|
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
|
|
||||||
@GQuery
|
@GQuery
|
||||||
class AccountQueryResolver @Autowired constructor(
|
class AccountQueryResolver @Autowired constructor(
|
||||||
private val accountRepository: AccountRepository,
|
private val accountRepository: AccountRepository,
|
||||||
) {
|
) : GraphQLQueryResolver {
|
||||||
@DgsQuery(field = DgsConstants.QUERY.FetchAccounts)
|
|
||||||
fun fetchAccounts(): Collection<Account> {
|
fun fetchAccounts(): Collection<Account> {
|
||||||
val accounts = accountRepository.findAll(Pageable.unpaged())
|
val accounts = accountRepository.findAll(Pageable.unpaged())
|
||||||
return accounts.content
|
return accounts.content
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.cubetiqs.graphql.demo.resolver.query
|
||||||
|
|
||||||
|
import com.cubetiqs.graphql.demo.context.GQuery
|
||||||
|
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
@GQuery
|
||||||
|
class HelloQueryResolver : GraphQLQueryResolver {
|
||||||
|
fun hello(): CompletableFuture<String> {
|
||||||
|
return Mono.just("Hello Query...!").toFuture()
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,16 @@
|
|||||||
package com.cubetiqs.graphql.demo.resolver.query
|
package com.cubetiqs.graphql.demo.resolver.query
|
||||||
|
|
||||||
import com.cubetiqs.graphql.demo.context.GQuery
|
import com.cubetiqs.graphql.demo.context.GQuery
|
||||||
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
|
|
||||||
import com.cubetiqs.graphql.demo.domain.user.User
|
import com.cubetiqs.graphql.demo.domain.user.User
|
||||||
import com.cubetiqs.graphql.demo.repository.UserRepository
|
import com.cubetiqs.graphql.demo.repository.UserRepository
|
||||||
import com.netflix.graphql.dgs.DgsQuery
|
import graphql.kickstart.tools.GraphQLQueryResolver
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
|
|
||||||
@GQuery
|
@GQuery
|
||||||
class UserQueryResolver @Autowired constructor(
|
class UserQueryResolver @Autowired constructor(
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
) {
|
) : GraphQLQueryResolver {
|
||||||
@DgsQuery(field = DgsConstants.QUERY.FetchUsers)
|
|
||||||
fun fetchUsers(): Collection<User> {
|
fun fetchUsers(): Collection<User> {
|
||||||
val users = userRepository.queryAllByEnabledIsTrue(Pageable.unpaged())
|
val users = userRepository.queryAllByEnabledIsTrue(Pageable.unpaged())
|
||||||
return users.content
|
return users.content
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.cubetiqs.graphql.demo.resolver.subscription
|
||||||
|
|
||||||
|
import graphql.kickstart.tools.GraphQLSubscriptionResolver
|
||||||
|
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 {
|
||||||
|
fun hello(env: DataFetchingEnvironment): Publisher<Int> {
|
||||||
|
return Flux.range(1, 10).delayElements(Duration.ofSeconds(1))
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,6 @@ server:
|
|||||||
|
|
||||||
# Spring Boot
|
# Spring Boot
|
||||||
spring:
|
spring:
|
||||||
profiles:
|
|
||||||
active: ${APP_PROFILE:}
|
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:graphql-demo}
|
url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:graphql-demo}
|
||||||
username: ${DB_USER:your-username}
|
username: ${DB_USER:your-username}
|
||||||
@ -19,8 +17,7 @@ spring:
|
|||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
enable_lazy_load_no_trans: ${HIBERNATE_LAZY_NO_TRANS:true}
|
enable_lazy_load_no_trans: ${HIBERNATE_LAZY_NO_TRANS:true}
|
||||||
main:
|
|
||||||
allow-bean-definition-overriding: true
|
|
||||||
# Spring Boot Actuator
|
# Spring Boot Actuator
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
@ -28,9 +25,15 @@ management:
|
|||||||
exposure:
|
exposure:
|
||||||
include: health,info,metrics
|
include: health,info,metrics
|
||||||
|
|
||||||
# DGS GraphQL
|
# GraphQL Voyager
|
||||||
dgs:
|
voyager:
|
||||||
graphql:
|
|
||||||
graphiql:
|
|
||||||
enabled: true
|
enabled: true
|
||||||
path: /graphiql
|
cdn:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# GraphQL Playground
|
||||||
|
graphql:
|
||||||
|
playground:
|
||||||
|
enabled: true
|
||||||
|
cdn:
|
||||||
|
enabled: false
|
59
src/main/resources/schema.graphqls
Normal file
59
src/main/resources/schema.graphqls
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
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!
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.cubetiqs.graphql.demo
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class GraphqlDemoApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user