Compare commits

...

17 Commits
main ... master

40 changed files with 805 additions and 8 deletions

2
.gitignore vendored
View File

@ -35,3 +35,5 @@ out/
### VS Code ###
.vscode/
.DS_Store

View File

@ -1,9 +1,9 @@
# Sample Modules
- Including parent deps
- Spring Boot
- Spring Boot (2.4.1)
- Spring Dependency Management
- Gradle with Kotlin DSL (6.6.1)
- Kotlin Langauge
- Kotlin Langauge (1.4.21)
# Development
- Clone the modules
@ -25,4 +25,4 @@ include("gradle-sample-module-example")
### Implementation module in ```build.gradle.kts```
```gradle
implementation(project(":gradle-sample-module-example"))
```
```

View File

@ -1,16 +1,23 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
val springBootVersion = "2.4.2"
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id("org.springframework.boot") version "2.4.0" apply false
id("io.spring.dependency-management") version "1.0.10.RELEASE" apply false
kotlin("jvm") version "1.4.10" apply false
kotlin("plugin.spring") version "1.4.10" apply false
id("org.springframework.boot") version "2.4.2" apply false
id("io.spring.dependency-management") version "1.0.11.RELEASE" apply false
kotlin("jvm") version "1.4.21" apply false
kotlin("plugin.spring") version "1.4.21" apply false
kotlin("plugin.jpa") version "1.4.21" apply false
}
allprojects {
@ -38,4 +45,14 @@ subprojects {
apply {
plugin("io.spring.dependency-management")
}
the<io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension>().apply {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
}
}
tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
enabled = false
}

3
build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
bash gradlew clean && bash gradlew build -x test

3
clean.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
bash gradlew clean

37
customer-api/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
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/

View File

@ -0,0 +1,27 @@
plugins {
id("org.springframework.boot")
id("io.spring.dependency-management")
kotlin("jvm")
kotlin("plugin.spring")
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<Jar> {
enabled = true
}
tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
enabled = false
}

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,71 @@
package com.example.customerapi
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.io.Serializable
import kotlin.random.Random
@SpringBootApplication
class CustomerApiApplication @Autowired constructor(
private val customerRepository: CustomerRepository,
) : CommandLineRunner {
override fun run(vararg args: String?) {
val customer = Customer.create("Sambo - ${Random.nextInt(1000)}", "Chea - ${Random.nextInt(1000)}")
val saved = customerRepository.save(customer)
println(saved)
}
}
fun main(args: Array<String>) {
runApplication<CustomerApiApplication>(*args)
}
@Document
data class Customer(
@Id
val id: String? = null,
var firstName: String,
var lastName: String,
) : Serializable {
companion object {
fun create(
firstName: String,
lastName: String
): Customer {
return Customer(null, firstName, lastName)
}
}
}
@Repository
interface CustomerRepository : MongoRepository<Customer, String>
@RestController
@RequestMapping("/customers")
class CustomerController @Autowired constructor(
private val customerRepository: CustomerRepository
) {
@GetMapping
fun getAllCustomers(): Collection<Customer> {
return customerRepository.findAll()
}
@GetMapping("/create")
fun createCustomer(): Customer? {
val customer = Customer.create("Sambo - ${Random.nextInt(1000)}", "Chea - ${Random.nextInt(1000)}")
return customerRepository.save(customer)
}
}

View File

@ -0,0 +1 @@
spring.data.mongodb.uri=mongodb://192.168.0.202:27017/db-customer-api

View File

@ -0,0 +1,13 @@
package com.example.customerapi
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class CustomerApiApplicationTests {
@Test
fun contextLoads() {
}
}

37
demo/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
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/

31
demo/build.gradle.kts Normal file
View File

@ -0,0 +1,31 @@
plugins {
id("org.springframework.boot")
id("io.spring.dependency-management")
kotlin("jvm")
kotlin("plugin.spring")
}
dependencies {
api(project(":lib"))
api(project(":customer-api"))
api(project(":login-api"))
implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.3.4.RELEASE")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
enabled = true
}

BIN
demo/gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,41 @@
package com.example.demo
import com.example.lib.MyLib
import com.example.lib.MyUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@SpringBootApplication (scanBasePackages = ["com.example.demo", "com.example.loginapi","com.example.customerapi"])
class DemoApplication @Autowired constructor(
//customerRepository: CustomerRepository,
) : CommandLineRunner {
override fun run(vararg args: String?) {
MyLib.doOnMe()
println()
println("Hello JJKK: ${MyUtils.helloWorld()}")
}
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
@RestController
@RequestMapping("/oauth")
@PreAuthorize("isAuthenticated()")
class OAuthController {
@GetMapping
fun getMe(authentication: Authentication) : Any? {
return authentication
}
}

View File

@ -0,0 +1,27 @@
package com.example.demo
import com.example.loginapi.OauthResourceServerSecurity
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
/**
* @author sombochea <Sambo Chea>
* @email sombochea@cubetiqs.com
* @date 15/10/19
* @since 1.0
*/
@Configuration
@EnableResourceServer
class SecurityConfig : OauthResourceServerSecurity() {
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.exceptionHandling()
.and()
.authorizeRequests()
.antMatchers("/api/**", "/oauth", "/customers")
.access("#oauth2.hasAnyScope('read','write')")
.antMatchers("/actuator/**")
.hasAnyRole("SUPER_ADMIN", "SYS_ADMIN","ACTUATOR")
}
}

View File

@ -0,0 +1,2 @@
spring.data.mongodb.uri=mongodb://192.168.0.202:27017/db-customer-api
spring.main.allow-bean-definition-overriding=true

View File

@ -0,0 +1,13 @@
package com.example.demo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class DemoApplicationTests {
@Test
fun contextLoads() {
}
}

37
lib/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
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/

12
lib/build.gradle.kts Normal file
View File

@ -0,0 +1,12 @@
plugins {
id("java-library")
id("io.spring.dependency-management")
kotlin("jvm")
kotlin("plugin.spring")
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}

BIN
lib/gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,7 @@
package com.example.lib;
public final class MyUtils {
public static String helloWorld() {
return "Hello World";
}
}

View File

@ -0,0 +1,5 @@
package com.example.lib
object MyLib {
fun doOnMe() = print("hello")
}

37
login-api/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
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/

View File

@ -0,0 +1,35 @@
plugins {
id("org.springframework.boot")
id("io.spring.dependency-management")
kotlin("jvm")
kotlin("plugin.spring")
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.3.4.RELEASE")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<Jar> {
enabled = true
}
tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
enabled = false
}

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,20 @@
package com.example.loginapi
import org.springframework.security.oauth2.provider.OAuth2Authentication
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter
import org.springframework.stereotype.Component
/**
* @author sombochea <Sambo Chea>
* @email sombochea@cubetiqs.com
* @date 16/10/19
* @since 1.0
*/
@Component
class CubeJwtAccessTokenConverter : DefaultAccessTokenConverter() {
override fun extractAuthentication(map: Map<String?, *>?): OAuth2Authentication {
val authentication = super.extractAuthentication(map)
authentication.details = map
return authentication
}
}

View File

@ -0,0 +1,11 @@
package com.example.loginapi
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class LoginApiApplication
fun main(args: Array<String>) {
runApplication<LoginApiApplication>(*args)
}

View File

@ -0,0 +1,20 @@
package com.example.loginapi
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/login")
class LoginController {
@GetMapping
fun get(): Any? {
return RestClientUtils.getRestTemplate().getForObject("https://api-clinic.cubetiqs.com/info", Any::class.java)
}
@PostMapping
fun login(
@RequestParam(value = "username") username: String,
@RequestParam(value = "password") password: String,
): Any? {
return RestClientUtils.login(username, password)
}
}

View File

@ -0,0 +1,76 @@
package com.example.loginapi
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer
import org.springframework.security.oauth2.provider.token.DefaultTokenServices
import org.springframework.security.oauth2.provider.token.TokenStore
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore
/**
* @author sombochea
* @since 1.0
*/
@Configuration
@EnableResourceServer
open class OauthResourceServerSecurity :
ResourceServerConfigurerAdapter() {
private val jwtAccessTokenConverter: CubeJwtAccessTokenConverter = CubeJwtAccessTokenConverter()
@Value("\${spring.security.oauth2.resourceserver.jwt.public-key}")
var publicKey: String? = null
@Value("\${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
var jwkSetUri: String? = null
private var tokenStore: TokenStore? = null
override fun configure(resources: ResourceServerSecurityConfigurer) {
val resourceId = "cubetiq-clinic-dev"
println("Loaded system with resource id: $resourceId")
resources
.tokenStore(tokenStore())
.resourceId(resourceId)
.stateless(false)
}
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.exceptionHandling()
.and()
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasAnyScope('read','write')")
.antMatchers("/actuator/**")
.hasAnyRole("SUPER_ADMIN", "SYS_ADMIN","ACTUATOR")
}
@Bean
fun tokenServices(tokenStore: TokenStore?): DefaultTokenServices {
val tokenServices = DefaultTokenServices()
tokenServices.setTokenStore(tokenStore)
return tokenServices
}
@Bean
fun tokenStore(): TokenStore? {
if (tokenStore == null) {
tokenStore = JwkTokenStore(jwkSetUri, jwtAccessTokenConverter)
}
return tokenStore
}
@Bean
fun jwtAccessTokenConverter(): JwtAccessTokenConverter {
val converter = JwtAccessTokenConverter()
converter.accessTokenConverter = jwtAccessTokenConverter
converter.setVerifierKey(publicKey)
return converter
}
}

View File

@ -0,0 +1,115 @@
@file:Suppress("unused", "unused")
package com.example.loginapi
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import org.springframework.http.*
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.client.RestTemplate
import java.io.Serializable
import java.nio.charset.StandardCharsets
import java.util.*
/**
* @author sombochea
* @since 1.0
*/
object RestClientUtils {
private const val BEAN_NAME = "restTemplate"
private var restTemplate: RestTemplate? = null
@JvmStatic
fun setRestTemplate(restTemplate: RestTemplate?) {
RestClientUtils.restTemplate = restTemplate
}
@JvmStatic
fun getRestTemplate(): RestTemplate {
if (restTemplate == null) {
restTemplate = RestTemplate()
}
return restTemplate ?: throw Exception("rest client service load failed")
}
fun login(username: String, password: String): OAuthToken? {
val authEndpoint = "https://preprod-api-auth.staging.cubetiqs.com/api/oauth/token"
val httpHeaders = getHttpHeadersConfig()
val body: MultiValueMap<String, String> = LinkedMultiValueMap()
body.add("grant_type", "password")
body.add("username", username)
body.add("password", password)
val httpEntity = HttpEntity(body, httpHeaders)
println(httpEntity)
return getRestTemplate().postForEntity(authEndpoint, httpEntity, OAuthToken::class.java).body
}
private fun getHttpHeadersConfig(): HttpHeaders {
val httpHeaders = HttpHeaders()
val client = "cubetiq-clinic-dev"
val secret = "123456"
httpHeaders.contentType = MediaType.APPLICATION_FORM_URLENCODED
val clientDetail = "$client:$secret"
val oauthCodes = Base64.getEncoder().encode(clientDetail.toByteArray(StandardCharsets.US_ASCII))
httpHeaders["Authorization"] = "Basic " + String(oauthCodes)
httpHeaders["Tenant-ID"] = "TNA-00013067"
httpHeaders["User-Type"] = "INTERNAL"
return httpHeaders
}
@JsonIgnoreProperties(ignoreUnknown = true)
data class OAuthToken(
@JsonProperty(value = "access_token")
var accessToken: String? = null,
@JsonProperty(value = "token_type")
var tokenType: String? = null,
@JsonProperty(value = "refresh_token")
var refreshToken: String? = null,
@JsonProperty(value = "expires_in")
var expiresIn: Long? = null,
@JsonProperty(value = "scope")
var scope: String? = null,
@JsonProperty(value = "auditor")
var auditor: String? = null,
@JsonProperty(value = "tenant")
var tenant: String? = null,
@JsonProperty(value = "user_id")
var userId: String? = null,
@JsonProperty(value = "username")
var username: String? = null,
@JsonProperty(value = "jti")
var jti: String? = null,
var passcode: Boolean? = null,
var configs: Map<String, Any?>? = null,
@JsonProperty(value = "current_branch_id")
var currentBranchId: String? = null,
@JsonProperty(value = "current_branch")
var currentBranch: String? = null,
) : Serializable {
@JsonIgnore
fun addConfig(key: String, value: Any?) = apply {
if (this.configs == null) {
this.configs = mutableMapOf()
}
(this.configs as MutableMap)[key] = value
}
@JsonIgnore
fun addConfigs(configs: Map<String, Any?>?) = apply {
if (this.configs == null) {
this.configs = mutableMapOf()
}
if (configs != null) {
(this.configs as MutableMap).putAll(configs)
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.example.loginapi
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api/users")
class UserController {
@GetMapping("/me")
fun getMe(
authentication: Authentication,
): Any {
return authentication
}
}

View File

@ -0,0 +1,26 @@
package com.example.loginapi
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.http.SessionCreationPolicy
/**
* @author sombochea <Sambo Chea>
* @email sombochea@cubetiqs.com
* @date 15/10/19
* @since 1.0
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig : WebSecurityConfigurerAdapter() {
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}
}

View File

@ -0,0 +1,19 @@
server:
port: 8015
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: ${JWK_SET_URI:https://preprod-api-auth.staging.cubetiqs.com/.well-known/jwks.json}
public-key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhLjm/+1Maitij0pV4IVD
gpLZ7IAvlXxKyToTCRusFwsto3T5jZIr5pNFEPJN6XuO/2fHGlcIioRD6pC1xdHu
qoYwImNHjYrS2vRrVboBiMHgOqZ2/Qk2knyNC98vp6sBp8PDSAWSPkWgKPDR2RV0
sFoPVT+0TCtXPVrdOCPkHDvrg2M4H8NwRtec3bzv3KkIpf2TSuSSHwL9JENaXpJn
2POnZwjBADa2xIU4K3k9XdYrTDqqlnIfnj/irT8aUCQzyo5vfqy4n9eQjj/lSmhT
L76pnrIEvl0UjnfRfZ9prE6+bS2pF6d4cYXfATwC0lKkIgKjHPoyUnyleJ6qHDyN
CwIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,13 @@
package com.example.loginapi
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class LoginApiApplicationTests {
@Test
fun contextLoads() {
}
}

View File

@ -1 +1,3 @@
rootProject.name = "sample-modules"
rootProject.name = "sample-modules"
include("demo", "lib", "customer-api", "login-api")