Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement user service #248

Closed
Closed
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
"orta.vscode-jest",
"humao.rest-client",
"vscjava.vscode-java-pack",
"Pivotal.vscode-boot-dev-pack"
"Pivotal.vscode-boot-dev-pack",
"mtxr.sqltools",
"mtxr.sqltools-driver-mysql"
],
"forwardPorts": [
80,
Expand Down
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"orta.vscode-jest",
"humao.rest-client",
"vscjava.vscode-java-pack",
"Pivotal.vscode-boot-dev-pack"
"Pivotal.vscode-boot-dev-pack",
"mtxr.sqltools",
"mtxr.sqltools-driver-mysql"
]
}
18 changes: 17 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,21 @@
"watch": false,
"onSave": "test-file",
"onStartup": []
}
},
"java.configuration.updateBuildConfiguration": "disabled",
"sqltools.connections": [
{
"mysqlOptions": {
"authProtocol": "default"
},
"previewLimit": 50,
"server": "localhost",
"port": 3306,
"driver": "MariaDB",
"name": "challenge-mariadb",
"database": "challenge",
"username": "root",
"password": "changeme"
}
]
}
5 changes: 5 additions & 0 deletions apps/challenge-mariadb/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MARIADB_USER=admin
MARIADB_PASSWORD=changeme
MARIADB_ROOT_PASSWORD=changeme

MYSQL_DATABASE=challenge
1 change: 1 addition & 0 deletions apps/challenge-mariadb/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM mariadb:10.8.3
17 changes: 17 additions & 0 deletions apps/challenge-mariadb/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: "3.8"

services:
challenge-mariadb:
image: sagebionetworks/challenge-mariadb:latest
container_name: challenge-mariadb
restart: always
env_file:
- .env
volumes:
- challenge-mariadb:/data/db
ports:
- "3306:3306"

volumes:
challenge-mariadb:
name: challenge-mariadb
32 changes: 32 additions & 0 deletions apps/challenge-mariadb/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"root": "apps/challenge-mariadb",
"sourceRoot": "apps/challenge-mariadb/src",
"projectType": "application",
"targets": {
"prepare": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "shx cp .env.example .env",
"cwd": "apps/challenge-mariadb"
}
},
"serve": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "docker compose up",
"cwd": "apps/challenge-mariadb"
}
},
"build-image": {
"executor": "@nx-tools/nx-docker:build",
"options": {
"context": "apps/challenge-mariadb",
"push": false,
"tags": [
"sagebionetworks/challenge-mariadb:latest"
]
}
}
},
"tags": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM user;
60 changes: 52 additions & 8 deletions apps/challenge-user-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
Expand All @@ -29,13 +33,45 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>18.0.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.212</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
Expand All @@ -49,13 +85,21 @@
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>
6 changes: 4 additions & 2 deletions apps/challenge-user-service/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"build": {
"executor": "@nxrocks/nx-spring-boot:build",
"options": {
"root": "apps/challenge-user-service"
"root": "apps/challenge-user-service",
"args": ["-e"]
}
},
"test": {
Expand Down Expand Up @@ -42,7 +43,8 @@
"serve": {
"executor": "@nxrocks/nx-spring-boot:serve",
"options": {
"root": "apps/challenge-user-service"
"root": "apps/challenge-user-service",
"args": ["-e"]
}
},
"build-image": {
Expand Down
59 changes: 59 additions & 0 deletions apps/challenge-user-service/requests.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@serviceRegistryHost = http://localhost:8081
@apiGatewayHost = http://localhost:8082
@userServiceHost = http://localhost:8083
@keycloakHost = http://localhost:8080

### Check user service info

GET {{userServiceHost}}/actuator/info

### Check user service info via the API gateway

GET {{apiGatewayHost}}/user/actuator/info


### Get access token from Keycloak

# @clientId = challenge-core-client
# @clientSecret = O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX
@clientId = challenge-api-client
@clientSecret = mg2DrRcxHx19PIITibdOnbNEbJUKjGKb
@username = luke
@password = changeme

# @name login

POST {{keycloakHost}}/realms/test/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=password
&scope=email
&client_id={{clientId}}
&client_secret={{clientSecret}}
&username={{username}}
&password={{password}}

### Check user service info using access token

GET {{apiGatewayHost}}/user/actuator/info
Authorization: Bearer {{login.response.body.$.access_token}}

### Create user

# POST {{apiGatewayHost}}/user/api/v1/users/register
POST {{userServiceHost}}/api/v1/users/register
Content-Type: application/json

{
"email": "test@gmail.com",
"password": "changeme",
"identification": "12345"
}

### List all the users

# from the API gateway => requires authentication / unauthorized
# from the user service => works

# GET {{apiGatewayHost}}/user/api/v1/users
GET {{userServiceHost}}/api/v1/users
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sagebionetworks.challenge.configuration;

import lombok.RequiredArgsConstructor;
import org.keycloak.admin.client.resource.RealmResource;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class KeycloakManager {

private final KeycloakProperties keycloakProperties;

public RealmResource getKeyCloakInstanceWithRealm() {
return keycloakProperties.getInstance().realm(keycloakProperties.getRealm());
}

public KeycloakProperties getProperties() {
return this.keycloakProperties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.sagebionetworks.challenge.configuration;

import lombok.extern.slf4j.Slf4j;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class KeycloakProperties {
@Value("${app.config.keycloak.server-url}")
private String serverUrl;

@Value("${app.config.keycloak.realm}")
private String realm;

@Value("${app.config.keycloak.clientId}")
private String clientId;

@Value("${app.config.keycloak.client-secret}")
private String clientSecret;

private static Keycloak keycloakInstance = null;

public Keycloak getInstance() {

log.info("realm: " + realm);

if (keycloakInstance == null) {
keycloakInstance = KeycloakBuilder
.builder()
.serverUrl(serverUrl)
.realm(realm)
.grantType("client_credentials")
.clientId(clientId)
.clientSecret(clientSecret)
.build();
}
return keycloakInstance;
}

public String getRealm() {
return realm;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.sagebionetworks.challenge.controller;

import com.fasterxml.jackson.annotation.JsonView;
import org.sagebionetworks.challenge.model.dto.User;
import org.sagebionetworks.challenge.model.dto.UserUpdateRequest;
import org.sagebionetworks.challenge.service.KeycloakUserService;
import org.sagebionetworks.challenge.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping(value = "/api/v1/users")
@RequiredArgsConstructor
public class UserController {

private final KeycloakUserService keycloakUserService;
private final UserService userService;

@PostMapping(value = "/register")
public ResponseEntity createUser(@RequestBody User request) {
log.info("Creating user with {}", request.toString());
return ResponseEntity.ok(userService.createUser(request));
}

@PatchMapping(value = "/update/{id}")
public ResponseEntity updateUser(@PathVariable("id") Long userId, @RequestBody UserUpdateRequest userUpdateRequest) {
log.info("Updating user with {}", userUpdateRequest.toString());
return ResponseEntity.ok(userService.updateUser(userId, userUpdateRequest));
}

@GetMapping
public ResponseEntity readUsers(Pageable pageable) {
log.info("Reading all users from API");
return ResponseEntity.ok(userService.readUsers(pageable));
}

@GetMapping(value = "/{id}")
public ResponseEntity readUser(@PathVariable("id") Long id) {
log.info("Reading user by id {}", id);
return ResponseEntity.ok(userService.readUser(id));
}

}
Loading