initial commit
This commit is contained in:
37
dev/tekton/examples/example-bank/bank-app-backend/.gitignore
vendored
Executable file
37
dev/tekton/examples/example-bank/bank-app-backend/.gitignore
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
**/target
|
||||
!.keep
|
||||
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
|
||||
## Local configuration files
|
||||
/local/config/*
|
||||
|
||||
*.swo
|
||||
*.swp
|
||||
*.~
|
||||
115
dev/tekton/examples/example-bank/bank-app-backend/README.md
Executable file
115
dev/tekton/examples/example-bank/bank-app-backend/README.md
Executable file
@@ -0,0 +1,115 @@
|
||||
|
||||
## Building individual microservices
|
||||
|
||||
### User service
|
||||
|
||||
```
|
||||
mvn -pl :user-service -am package
|
||||
docker build -t user-service:1.0-SNAPSHOT user-service
|
||||
```
|
||||
|
||||
### Transaction service
|
||||
```
|
||||
mvn -pl :transaction-service -am package
|
||||
docker build -t transaction-service:1.0-SNAPSHOT transaction-service
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
### Secrets
|
||||
|
||||
```
|
||||
kubectl create secret generic bank-db-secret --from-literal=DB_SERVERNAME=<host> --from-literal=DB_PORTNUMBER=<port> --from-literal=DB_DATABASENAME=ibmclouddb --from-literal=DB_USER=<user> --from-literal=DB_PASSWORD=<password>
|
||||
kubectl create secret generic bank-oidc-secret --from-literal=OIDC_JWKENDPOINTURL=<oauthServerUrl>/publickeys --from-literal=OIDC_ISSUERIDENTIFIER=<issuer> --from-literal=OIDC_AUDIENCES=<audience>
|
||||
```
|
||||
|
||||
|
||||
## Curl commands
|
||||
|
||||
### Users
|
||||
|
||||
```
|
||||
curl -X POST -H "Authorization: Bearer <access-token>" -H "Content-Type: application/json" -d "{\"consentGiven\": \"true\"}" -k https://localhost:9443/bank/v1/users
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k https://localhost:9443/bank/v1/users/self
|
||||
|
||||
curl -X PUT "Authorization: Bearer <access-token>" -H "Content-Type: application/json" -d "{\"consentGiven\": \"false\"}" -k https://localhost:9443/bank/v1/users/self
|
||||
|
||||
curl -X DELETE "Authorization: Bearer <access-token>" -k https://localhost:9443/bank/v1/users/self
|
||||
```
|
||||
|
||||
|
||||
### User Events
|
||||
|
||||
```
|
||||
curl -X POST "Authorization: Bearer <access-token>" -H "Content-Type: application/json" -d "{\"eventId\": \"871859e4-9fca-4bcf-adb5-e7d063d0747e\"}" -k https://localhost:9443/bank/v1/userEvents
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k https://localhost:9443/bank/v1/userEvents/self
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k https://localhost:9443/bank/v1/userEvents/self/info
|
||||
```
|
||||
|
||||
|
||||
### Events
|
||||
|
||||
```
|
||||
curl -X POST "Authorization: Bearer <access-token>" -H "Content-Type: application/json" -d "{\"eventName\": \"Event name\", \"pointValue\": 100}" -k https://localhost:9444/bank/v1/events
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k https://localhost:9444/bank/v1/events/{eventId}
|
||||
|
||||
curl -X PUT "Authorization: Bearer <access-token>" -H "Content-Type: application/json" -d "{\"eventName\": \"Event name\", \"pointValue\": 100}" -k https://localhost:9444/bank/v1/events/{eventId}
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k https://localhost:9444/bank/v1/events
|
||||
|
||||
curl -X GET "Authorization: Bearer <access-token>" -k "https://localhost:9444/bank/v1/events?id=&id=&id="
|
||||
|
||||
```
|
||||
|
||||
## Running the integration tests
|
||||
|
||||
### Set environment variables
|
||||
|
||||
Base URL where users and events services are deployed
|
||||
```
|
||||
export USERS_BASEURL=http://<host>:<port>
|
||||
export EVENTS_BASEURL=http://<host>:<port>
|
||||
```
|
||||
|
||||
Prefix for test user names and the password they should use. These users are created dynamically by the tests.
|
||||
```
|
||||
export TEST_USER_PREFIX=<testUsername>
|
||||
export TEST_PASSWORD=<testUserPassword>
|
||||
```
|
||||
|
||||
Admin user name and password. This user name must exist in App Id prior to running the test and must have the admin role.
|
||||
```
|
||||
export TEST_ADMIN_USER=<adminUsername>
|
||||
export TEST_ADMIN_PASSWORD=<adminUserPassword>
|
||||
```
|
||||
|
||||
App Id service URL. Change to correct URL for the region where your App Id instance is deployed.
|
||||
```
|
||||
export APPID_SERVICE_URL=https://us-south.appid.cloud.ibm.com
|
||||
```
|
||||
|
||||
App Id tenant id, client id, and client password (secret)
|
||||
```
|
||||
export APPID_TENANTID=<tenant id>
|
||||
export OIDC_CLIENTID=<client id>
|
||||
export OIDC_CLIENTPASSWORD=<client secret>
|
||||
export OIDC_ISSUERIDENTIFIER=%APPID_SERVICE_URL%/%APPID_TENANTID%
|
||||
```
|
||||
|
||||
IAM API key (needed for authentication to App Id)
|
||||
```
|
||||
export IAM_APIKEY=<apikey>
|
||||
export IAM_SERVICE_URL=https://iam.cloud.ibm.com/identity/token
|
||||
```
|
||||
|
||||
|
||||
### Run the tests
|
||||
|
||||
```
|
||||
mvn -pl :integration-tests -am verify
|
||||
```
|
||||
34
dev/tekton/examples/example-bank/bank-app-backend/common/pom.xml
Executable file
34
dev/tekton/examples/example-bank/bank-app-backend/common/pom.xml
Executable file
@@ -0,0 +1,34 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Open Liberty Features -->
|
||||
<dependency>
|
||||
<groupId>io.openliberty.features</groupId>
|
||||
<artifactId>microProfile-3.0</artifactId>
|
||||
<type>esa</type>
|
||||
</dependency>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.16</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.eclipse.microprofile.jwt.Claim;
|
||||
import org.eclipse.microprofile.jwt.Claims;
|
||||
|
||||
public class BaseResource {
|
||||
|
||||
@Inject
|
||||
@Claim("sub")
|
||||
private String subject;
|
||||
|
||||
@Inject
|
||||
@Claim(standard = Claims.raw_token)
|
||||
private String rawToken;
|
||||
|
||||
protected String getCallerSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
protected String getCallerCredentials() {
|
||||
return "Bearer " + rawToken;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ibm.codey.bank.accounts.json;
|
||||
|
||||
import javax.json.bind.annotation.JsonbProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter @Setter @ToString
|
||||
public class UserRegistration {
|
||||
|
||||
@JsonbProperty
|
||||
private boolean consentGiven;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ibm.codey.bank.accounts.json;
|
||||
|
||||
import javax.json.bind.annotation.JsonbProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter @Setter @ToString
|
||||
public class UserRegistrationInfo {
|
||||
|
||||
@JsonbProperty
|
||||
private String userId;
|
||||
|
||||
@JsonbProperty
|
||||
private boolean consentGiven;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ibm.codey.bank.catalog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@Dependent
|
||||
@RegisterRestClient
|
||||
public interface KnativeService {
|
||||
|
||||
@POST
|
||||
@Path("process")
|
||||
public CompletionStage<String> processTransaction(@QueryParam("transactionId") String transactionId, @QueryParam("category") String category, @QueryParam("amount") String amount);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ibm.codey.bank.catalog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistration;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistrationInfo;
|
||||
|
||||
@Dependent
|
||||
@RegisterRestClient
|
||||
public interface UserService {
|
||||
|
||||
@GET
|
||||
@Path("self")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserRegistrationInfo getUserConsent(@HeaderParam("Authorization") String authorizationHeader);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ibm.codey.bank.catalog.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.json.bind.annotation.JsonbProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter @Setter @ToString
|
||||
public class CreateTransactionDefinition {
|
||||
|
||||
@JsonbProperty
|
||||
private String transactionName;
|
||||
|
||||
@JsonbProperty
|
||||
private String category;
|
||||
|
||||
@JsonbProperty
|
||||
private BigDecimal amount;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ibm.codey.bank.catalog.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.json.bind.annotation.JsonbProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter @Setter @ToString
|
||||
public class RewardTransactionDefinition {
|
||||
|
||||
@JsonbProperty
|
||||
private BigDecimal pointsEarned;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.ibm.codey.bank.interceptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.interceptor.AroundInvoke;
|
||||
import javax.interceptor.InvocationContext;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObjectBuilder;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.jwt.Claim;
|
||||
|
||||
/*
|
||||
* This interceptor is used with the JAXRS resource classes to log any exception and return a 500 status code to the client.
|
||||
* This could have been accomplished with an ExceptionMapper as well but an interceptor lets us also log information about
|
||||
* the failing method and input parameters.
|
||||
*/
|
||||
public class LoggingInterceptor {
|
||||
|
||||
private static final Logger log = Logger.getLogger(LoggingInterceptor.class.getName());
|
||||
|
||||
@Inject
|
||||
@Claim("sub")
|
||||
private String subject;
|
||||
|
||||
@AroundInvoke
|
||||
public Object logInvocation(InvocationContext ctx) {
|
||||
try {
|
||||
Object result = ctx.proceed();
|
||||
logRequestAndResult(ctx, result);
|
||||
return result;
|
||||
} catch(Throwable e) {
|
||||
String clz = ctx.getMethod().getDeclaringClass().getName();
|
||||
String method = ctx.getMethod().getName();
|
||||
Object[] params = ctx.getParameters();
|
||||
if (params != null && params.length > 0) {
|
||||
log.log(Level.SEVERE, "***** Exception in " + clz + "." + method, params);
|
||||
} else {
|
||||
log.log(Level.SEVERE, "***** Exception in " + clz + "." + method);
|
||||
}
|
||||
e.printStackTrace();
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
private void logRequestAndResult(InvocationContext ctx, Object result) {
|
||||
String methodName = ctx.getMethod().getName();
|
||||
Object[] params = ctx.getParameters();
|
||||
JsonObjectBuilder requestBuilder = Json.createObjectBuilder()
|
||||
.add("subject", subject)
|
||||
.add("action", methodName);
|
||||
if (params != null && params.length > 0) {
|
||||
requestBuilder.add("input", Arrays.toString(params));
|
||||
}
|
||||
if (result instanceof Response) {
|
||||
Response response = (Response)result;
|
||||
requestBuilder.add("statuscode", response.getStatus());
|
||||
}
|
||||
log.log(Level.INFO, "API REQUEST", requestBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ibm.codey.bank.interceptor;
|
||||
|
||||
import javax.annotation.Priority;
|
||||
import javax.inject.Inject;
|
||||
import javax.interceptor.AroundInvoke;
|
||||
import javax.interceptor.Interceptor;
|
||||
import javax.interceptor.InvocationContext;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.jwt.Claim;
|
||||
|
||||
import com.ibm.codey.bank.interceptor.binding.RequiresAuthorization;
|
||||
|
||||
/*
|
||||
* This interceptor is used with the JAXRS resource classes to enforce a client scope for authorization purposes.
|
||||
*/
|
||||
@RequiresAuthorization @Interceptor
|
||||
@Priority(Interceptor.Priority.APPLICATION)
|
||||
public class SecurityInterceptor {
|
||||
|
||||
@Inject
|
||||
@Claim("scope")
|
||||
private String scope;
|
||||
|
||||
@AroundInvoke
|
||||
public Object checkScope(InvocationContext ctx) throws Exception {
|
||||
String[] scopeList = scope.split(" ");
|
||||
for(String hasScope : scopeList) {
|
||||
if (hasScope.equals("admin")) {
|
||||
Object result = ctx.proceed();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Response.status(Response.Status.FORBIDDEN).entity("admin permission required").build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ibm.codey.bank.interceptor.binding;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.interceptor.InterceptorBinding;
|
||||
|
||||
@Inherited
|
||||
@InterceptorBinding
|
||||
@Target({TYPE, METHOD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface RequiresAuthorization {
|
||||
}
|
||||
43
dev/tekton/examples/example-bank/bank-app-backend/integration-tests/pom.xml
Executable file
43
dev/tekton/examples/example-bank/bank-app-backend/integration-tests/pom.xml
Executable file
@@ -0,0 +1,43 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>integration-tests</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,175 @@
|
||||
package it.com.ibm.codey.loyalty;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.json.bind.Jsonb;
|
||||
import javax.json.bind.JsonbBuilder;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MultivaluedHashMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.ibm.codey.loyalty.accounts.json.UserRegistration;
|
||||
|
||||
import it.com.ibm.codey.loyalty.util.TestSecurityHelper;
|
||||
|
||||
public class EndpointTestBase {
|
||||
|
||||
protected static String USERS_BASEURL;
|
||||
protected static String EVENTS_BASEURL;
|
||||
|
||||
protected static String TEST_USER_PREFIX;
|
||||
protected static String TEST_USER;
|
||||
protected static String TEST_PASSWORD;
|
||||
protected static String userAccessToken;
|
||||
|
||||
protected static String TEST_ADMIN_USER;
|
||||
protected static String TEST_ADMIN_PASSWORD;
|
||||
protected static String adminAccessToken;
|
||||
|
||||
protected static final String USERS_ENDPOINT = "/loyalty/v1/users";
|
||||
protected static final String USERS_SELF_ENDPOINT = "/loyalty/v1/users/self";
|
||||
protected static final String USER_EVENTS_ENDPOINT = "/loyalty/v1/userEvents";
|
||||
protected static final String USER_EVENTS_SELF_ENDPOINT = "/loyalty/v1/userEvents/self";
|
||||
protected static final String USER_EVENTS_SELF_INFO_ENDPOINT = "/loyalty/v1/userEvents/self/info";
|
||||
protected static final String EVENTS_ENDPOINT = "/loyalty/v1/events";
|
||||
|
||||
protected static boolean CONSENT_GIVEN = true;
|
||||
protected static boolean CONSENT_NOT_GIVEN = false;
|
||||
|
||||
static {
|
||||
USERS_BASEURL = System.getenv("USERS_BASEURL");
|
||||
EVENTS_BASEURL = System.getenv("EVENTS_BASEURL");
|
||||
TEST_USER_PREFIX = System.getenv("TEST_USER_PREFIX");
|
||||
TEST_PASSWORD = System.getenv("TEST_PASSWORD");
|
||||
TEST_ADMIN_USER = System.getenv("TEST_ADMIN_USER");
|
||||
TEST_ADMIN_PASSWORD = System.getenv("TEST_ADMIN_PASSWORD");
|
||||
}
|
||||
|
||||
private Client client;
|
||||
|
||||
protected void setup() {
|
||||
client = ClientBuilder.newClient();
|
||||
TEST_USER = TEST_USER_PREFIX + (int) ((Math.random() * 999999) + 1);
|
||||
}
|
||||
|
||||
protected void teardown() {
|
||||
client.close();
|
||||
}
|
||||
|
||||
protected <T> T get(String baseUrl, String endpoint, Map<String, Object> queryParams, String accessToken, Response.Status expectedStatusCode, Type returnType) {
|
||||
String url = baseUrl + endpoint;
|
||||
WebTarget target = client.target(url);
|
||||
if (queryParams != null) {
|
||||
for (String key: queryParams.keySet()) {
|
||||
target = target.queryParam(key, queryParams.get(key));
|
||||
}
|
||||
}
|
||||
MultivaluedHashMap<String,Object> headers = new MultivaluedHashMap<String,Object>();
|
||||
if (accessToken != null) {
|
||||
String authHeader = "Bearer " + accessToken;
|
||||
headers.putSingle(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
try (Response response = target.request().headers(headers).get()) {
|
||||
checkStatusCode(url, response, expectedStatusCode);
|
||||
if (returnType == Void.class) {
|
||||
return null;
|
||||
}
|
||||
String jsonString = response.readEntity(String.class);
|
||||
if (returnType.equals(String.class)) {
|
||||
return (T)jsonString;
|
||||
}
|
||||
Jsonb jsonb = JsonbBuilder.create();
|
||||
return jsonb.fromJson(jsonString, returnType);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T put(String baseUrl, String endpoint, Object body, String accessToken, Response.Status expectedStatusCode, Class<T> returnType) {
|
||||
String url = baseUrl + endpoint;
|
||||
Jsonb jsonb = JsonbBuilder.create();
|
||||
String jsonBody = jsonb.toJson(body);
|
||||
MultivaluedHashMap<String,Object> headers = new MultivaluedHashMap<String,Object>();
|
||||
if (accessToken != null) {
|
||||
String authHeader = "Bearer " + accessToken;
|
||||
headers.putSingle(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
try (Response response = client.target(url).request().headers(headers).buildPut(Entity.json(jsonBody)).invoke()) {
|
||||
checkStatusCode(url, response, expectedStatusCode);
|
||||
if (returnType == Void.class) {
|
||||
return null;
|
||||
}
|
||||
String jsonString = response.readEntity(String.class);
|
||||
if (returnType.equals(String.class)) {
|
||||
return (T)jsonString;
|
||||
}
|
||||
return jsonb.fromJson(jsonString, returnType);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T post(String baseUrl, String endpoint, Object body, String accessToken, Response.Status expectedStatusCode, Class<T> returnType) {
|
||||
String url = baseUrl + endpoint;
|
||||
Jsonb jsonb = JsonbBuilder.create();
|
||||
String jsonBody = jsonb.toJson(body);
|
||||
MultivaluedHashMap<String,Object> headers = new MultivaluedHashMap<String,Object>();
|
||||
if (accessToken != null) {
|
||||
String authHeader = "Bearer " + accessToken;
|
||||
headers.putSingle(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
try (Response response = client.target(url).request().headers(headers).buildPost(Entity.json(jsonBody)).invoke()) {
|
||||
checkStatusCode(url, response, expectedStatusCode);
|
||||
if (returnType == Void.class) {
|
||||
return null;
|
||||
}
|
||||
String jsonString = response.readEntity(String.class);
|
||||
if (returnType.equals(String.class)) {
|
||||
return (T)jsonString;
|
||||
}
|
||||
return jsonb.fromJson(jsonString, returnType);
|
||||
}
|
||||
}
|
||||
|
||||
protected void delete(String baseUrl, String endpoint, String accessToken, Response.Status expectedStatusCode) {
|
||||
String url = baseUrl + endpoint;
|
||||
MultivaluedHashMap<String,Object> headers = new MultivaluedHashMap<String,Object>();
|
||||
if (accessToken != null) {
|
||||
String authHeader = "Bearer " + accessToken;
|
||||
headers.putSingle(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
try (Response response = client.target(url).request().headers(headers).buildDelete().invoke()) {
|
||||
checkStatusCode(url, response, expectedStatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupUser() {
|
||||
// Create a user in the user registry.
|
||||
TestSecurityHelper.createUser(TEST_USER, TEST_PASSWORD);
|
||||
|
||||
// Log the user in and obtain an access token for invoking the API.
|
||||
userAccessToken = TestSecurityHelper.signOn(TEST_USER, TEST_PASSWORD);
|
||||
|
||||
// Create user registration
|
||||
UserRegistration userRegistration = new UserRegistration();
|
||||
userRegistration.setConsentGiven(CONSENT_GIVEN);
|
||||
post(USERS_BASEURL, USERS_ENDPOINT, userRegistration, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
}
|
||||
|
||||
protected void removeUser() {
|
||||
// Use DELETE to remove user registration.
|
||||
delete(USERS_BASEURL, USERS_SELF_ENDPOINT, userAccessToken, Response.Status.NO_CONTENT);
|
||||
}
|
||||
|
||||
private void checkStatusCode(String url, Response response, Response.Status expectedStatusCode) {
|
||||
if (expectedStatusCode.getStatusCode() != response.getStatus()) {
|
||||
fail("Unexpected response code " + response.getStatus() +
|
||||
" (expected " + expectedStatusCode.getStatusCode() +
|
||||
") from " + url + " Response=" + response.readEntity(String.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package it.com.ibm.codey.loyalty.accounts;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.codey.loyalty.accounts.json.UserRegistration;
|
||||
|
||||
import it.com.ibm.codey.loyalty.EndpointTestBase;
|
||||
import it.com.ibm.codey.loyalty.util.TestSecurityHelper;
|
||||
|
||||
public class UserEndpointTest extends EndpointTestBase {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
super.teardown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserRegistrationAndDeletion() {
|
||||
try {
|
||||
setupUser();
|
||||
// Use GET to get the user registration.
|
||||
UserRegistration checkUserRegistration = get(USERS_BASEURL, USERS_SELF_ENDPOINT, null, userAccessToken, Response.Status.OK, UserRegistration.class);
|
||||
assertEquals("Consent flag is incorrect", CONSENT_GIVEN, checkUserRegistration.isConsentGiven());
|
||||
} finally {
|
||||
removeUser();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserRegistrationModificationAndDeletion() {
|
||||
try {
|
||||
setupUser();
|
||||
// Use PUT to change the user registration.
|
||||
UserRegistration userRegistration = new UserRegistration();
|
||||
userRegistration.setConsentGiven(CONSENT_NOT_GIVEN);
|
||||
put(USERS_BASEURL, USERS_SELF_ENDPOINT, userRegistration, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
// Use GET to get the user registration.
|
||||
UserRegistration checkUserRegistration = get(USERS_BASEURL, USERS_SELF_ENDPOINT, null, userAccessToken, Response.Status.OK, UserRegistration.class);
|
||||
assertEquals("Consent flag is incorrect", CONSENT_NOT_GIVEN, checkUserRegistration.isConsentGiven());
|
||||
} finally {
|
||||
removeUser();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticationFailure() {
|
||||
// Make calls without an authentication header and verify they are rejected.
|
||||
UserRegistration userRegistration = new UserRegistration();
|
||||
userRegistration.setConsentGiven(CONSENT_GIVEN);
|
||||
post(USERS_BASEURL, USERS_ENDPOINT, userRegistration, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
get(USERS_BASEURL, USERS_SELF_ENDPOINT, null, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
put(USERS_BASEURL, USERS_SELF_ENDPOINT, userRegistration, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
delete(USERS_BASEURL, USERS_SELF_ENDPOINT, null, Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package it.com.ibm.codey.loyalty.accounts;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.codey.loyalty.accounts.json.UserEventCheckIn;
|
||||
import com.ibm.codey.loyalty.accounts.json.UserEventInfo;
|
||||
import com.ibm.codey.loyalty.accounts.json.UserRegistration;
|
||||
import com.ibm.codey.loyalty.catalog.json.EventDefinition;
|
||||
|
||||
import it.com.ibm.codey.loyalty.EndpointTestBase;
|
||||
import it.com.ibm.codey.loyalty.util.TestSecurityHelper;
|
||||
|
||||
public class UserEventsEndpointTest extends EndpointTestBase {
|
||||
|
||||
private static String normalPointsEventId, doublePointsEventId;
|
||||
|
||||
private static final int NORMAL_POINTS = 10;
|
||||
private static final int DOUBLE_POINTS = NORMAL_POINTS*2;
|
||||
|
||||
private static final String NORMAL_POINTS_EVENT_NAME = "test event normal points";
|
||||
private static final String DOUBLE_POINTS_EVENT_NAME = "test event double points";
|
||||
|
||||
private static boolean eventsCreated = false;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
super.setup();
|
||||
// Create events. These are reused for all tests.
|
||||
// This isn't done in a BeforeClass method because it depends on the non-static post() method in the superclass.
|
||||
if (!eventsCreated) {
|
||||
adminAccessToken = TestSecurityHelper.signOn(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
|
||||
normalPointsEventId = createEvent(NORMAL_POINTS_EVENT_NAME, NORMAL_POINTS);
|
||||
doublePointsEventId = createEvent(DOUBLE_POINTS_EVENT_NAME, DOUBLE_POINTS);
|
||||
eventsCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
super.teardown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventCheckin() {
|
||||
try {
|
||||
setupUser();
|
||||
|
||||
// Verify no events attended or points earned yet
|
||||
UserEventInfo userEventInfo = get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, userAccessToken, Response.Status.OK, UserEventInfo.class);
|
||||
assertEquals("initial event count is incorrect", 0, userEventInfo.getEventCount());
|
||||
assertEquals("initial points earned is incorrect", 0, userEventInfo.getPointsEarned());
|
||||
|
||||
// Check in to first event
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(normalPointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
|
||||
// Verify check in to first event
|
||||
String[] eventIds = get(USERS_BASEURL, USER_EVENTS_SELF_ENDPOINT, null, userAccessToken, Response.Status.OK, String[].class);
|
||||
assertEquals("GET returned incorrect number of events checked in", 1, eventIds.length);
|
||||
assertEquals("Event id is incorrect", normalPointsEventId, eventIds[0]);
|
||||
|
||||
// Verify points earned
|
||||
UserEventInfo userEventInfo2 = get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, userAccessToken, Response.Status.OK, UserEventInfo.class);
|
||||
assertEquals("event count is incorrect", 1, userEventInfo2.getEventCount());
|
||||
assertEquals("points earned is incorrect", NORMAL_POINTS, userEventInfo2.getPointsEarned());
|
||||
|
||||
// Check in to second event
|
||||
UserEventCheckIn checkIn2 = new UserEventCheckIn();
|
||||
checkIn2.setEventId(doublePointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn2, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
|
||||
// Verify check in to both events
|
||||
String[] eventIds2 = get(USERS_BASEURL, USER_EVENTS_SELF_ENDPOINT, null, userAccessToken, Response.Status.OK, String[].class);
|
||||
assertEquals("GET returned incorrect number of events checked in", 2, eventIds2.length);
|
||||
if (eventIds2[0].equals(normalPointsEventId)) {
|
||||
assertEquals("Event id [1] is incorrect", doublePointsEventId, eventIds2[1]);
|
||||
} else {
|
||||
assertEquals("Event id [0] is incorrect", doublePointsEventId, eventIds2[0]);
|
||||
assertEquals("Event id [1] is incorrect", normalPointsEventId, eventIds2[1]);
|
||||
}
|
||||
|
||||
// Verify points earned
|
||||
UserEventInfo userEventInfo3 = get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, userAccessToken, Response.Status.OK, UserEventInfo.class);
|
||||
assertEquals("event count is incorrect", 2, userEventInfo3.getEventCount());
|
||||
assertEquals("points earned is incorrect", NORMAL_POINTS+DOUBLE_POINTS, userEventInfo3.getPointsEarned());
|
||||
} finally {
|
||||
removeUser();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateEventCheckin() {
|
||||
try {
|
||||
setupUser();
|
||||
// Check in to first event
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(normalPointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
// Check in to first event again
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
} finally {
|
||||
removeUser();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonConsentedUser() {
|
||||
try {
|
||||
setupUser();
|
||||
// Use PUT to change user registration to withdraw consent
|
||||
UserRegistration userRegistration = new UserRegistration();
|
||||
userRegistration.setConsentGiven(CONSENT_NOT_GIVEN);
|
||||
put(USERS_BASEURL, USERS_SELF_ENDPOINT, userRegistration, userAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
// Try to check into an event or get information
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(normalPointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.CONFLICT, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_ENDPOINT, null, userAccessToken, Response.Status.CONFLICT, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, userAccessToken, Response.Status.CONFLICT, Void.class);
|
||||
} finally {
|
||||
removeUser();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithUnregisteredUser() {
|
||||
setupUser();
|
||||
removeUser();
|
||||
// Try to check into an event or get information
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(normalPointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_ENDPOINT, null, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticationFailure() {
|
||||
// Make calls without an authentication header
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(normalPointsEventId);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_ENDPOINT, null, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
get(USERS_BASEURL, USER_EVENTS_SELF_INFO_ENDPOINT, null, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadEventId() {
|
||||
String badEventId1 = "1";
|
||||
String badEventId2 = "/deadbeef-0000-0000-0000-badbadbadbad";
|
||||
UserEventCheckIn checkIn1 = new UserEventCheckIn();
|
||||
checkIn1.setEventId(badEventId1);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn1, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
UserEventCheckIn checkIn2 = new UserEventCheckIn();
|
||||
checkIn2.setEventId(badEventId2);
|
||||
post(USERS_BASEURL, USER_EVENTS_ENDPOINT, checkIn2, userAccessToken, Response.Status.BAD_REQUEST, Void.class);
|
||||
}
|
||||
|
||||
private String createEvent(String eventName, int pointValue) {
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
String eventId = post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, adminAccessToken, Response.Status.CREATED, String.class);
|
||||
return eventId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package it.com.ibm.codey.loyalty.catalog;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.ws.rs.core.GenericType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.codey.loyalty.catalog.json.EventDefinition;
|
||||
|
||||
import it.com.ibm.codey.loyalty.EndpointTestBase;
|
||||
import it.com.ibm.codey.loyalty.util.TestSecurityHelper;
|
||||
|
||||
public class EventsEndpointTest extends EndpointTestBase {
|
||||
|
||||
private String eventName;
|
||||
private int pointValue;
|
||||
private String eventDescription;
|
||||
private String eventLocation;
|
||||
private OffsetDateTime startTime;
|
||||
private OffsetDateTime endTime;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
super.setup();
|
||||
// Set up a normal user to test methods which don't require admin.
|
||||
setupUser();
|
||||
// Set up an admin user.
|
||||
adminAccessToken = TestSecurityHelper.signOn(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
|
||||
// Set up event attributes.
|
||||
String suffix = RandomStringUtils.randomAlphabetic(8);
|
||||
eventName = "test event " + suffix;
|
||||
eventDescription = "all about " + suffix;
|
||||
eventLocation = "at " + suffix;
|
||||
startTime = OffsetDateTime.now();
|
||||
endTime = OffsetDateTime.now().plusHours(1);
|
||||
pointValue = (int) ((Math.random() * 99) + 1);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
removeUser();
|
||||
super.teardown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateEvent() {
|
||||
// Use POST to create an event.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
eventDefinition.setEventDescription(eventDescription);
|
||||
eventDefinition.setEventLocation(eventLocation);
|
||||
eventDefinition.setStartTime(startTime);
|
||||
eventDefinition.setEndTime(endTime);
|
||||
String eventId = post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, adminAccessToken, Response.Status.CREATED, String.class);
|
||||
// Use GET to get the event. This method does not require admin.
|
||||
EventDefinition checkEventDefinition = get(EVENTS_BASEURL, EVENTS_ENDPOINT + '/' + eventId, null, userAccessToken, Response.Status.OK, EventDefinition.class);
|
||||
assertEquals("Event name is incorrect", eventName, checkEventDefinition.getEventName());
|
||||
assertEquals("Point value is incorrect", pointValue, checkEventDefinition.getPointValue());
|
||||
assertEquals("Event description is incorrect", eventDescription, checkEventDefinition.getEventDescription());
|
||||
assertEquals("Event location is incorrect", eventLocation, checkEventDefinition.getEventLocation());
|
||||
assertEquals("Event start time is incorrect", startTime.toInstant(), checkEventDefinition.getStartTime().toInstant()); // Use toInstant to normalize timezones
|
||||
assertEquals("Event end time is incorrect", endTime.toInstant(), checkEventDefinition.getEndTime().toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllEvents() {
|
||||
// Use POST to create an event. An admin user must do this.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
eventDefinition.setEventDescription(eventDescription);
|
||||
eventDefinition.setEventLocation(eventLocation);
|
||||
eventDefinition.setStartTime(startTime);
|
||||
eventDefinition.setEndTime(endTime);
|
||||
String eventId = post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, adminAccessToken, Response.Status.CREATED, String.class);
|
||||
// Use GET to get all events. This method does not require admin.
|
||||
GenericType<Map<String, EventDefinition>> eventDefinitionMapType = new GenericType<Map<String, EventDefinition>>() {};
|
||||
Map<String, EventDefinition> eventDefinitionsMap = get(EVENTS_BASEURL, EVENTS_ENDPOINT, null, userAccessToken, Response.Status.OK, eventDefinitionMapType.getType());
|
||||
assertNotNull("GET did not return any events", eventDefinitionsMap);
|
||||
EventDefinition checkEventDefinition = eventDefinitionsMap.get(eventId);
|
||||
assertNotNull("GET did not return the event that was just created", checkEventDefinition);
|
||||
assertEquals("Event name is incorrect", eventName, checkEventDefinition.getEventName());
|
||||
assertEquals("Point value is incorrect", pointValue, checkEventDefinition.getPointValue());
|
||||
assertEquals("Event description is incorrect", eventDescription, checkEventDefinition.getEventDescription());
|
||||
assertEquals("Event location is incorrect", eventLocation, checkEventDefinition.getEventLocation());
|
||||
assertEquals("Event start time is incorrect", startTime.toInstant(), checkEventDefinition.getStartTime().toInstant()); // Use toInstant to normalize timezones
|
||||
assertEquals("Event end time is incorrect", endTime.toInstant(), checkEventDefinition.getEndTime().toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchEvent() {
|
||||
// Use POST to create an event. An admin user must do this.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
eventDefinition.setEventDescription(eventDescription);
|
||||
eventDefinition.setEventLocation(eventLocation);
|
||||
eventDefinition.setStartTime(startTime);
|
||||
eventDefinition.setEndTime(endTime);
|
||||
String eventId = post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, adminAccessToken, Response.Status.CREATED, String.class);
|
||||
// Use GET to search for this event. This method does not require admin.
|
||||
Map<String,Object> queryParams = Collections.singletonMap("id", eventId);
|
||||
GenericType<Map<String, EventDefinition>> eventDefinitionMapType = new GenericType<Map<String, EventDefinition>>() {};
|
||||
Map<String, EventDefinition> eventDefinitionsMap = get(EVENTS_BASEURL, EVENTS_ENDPOINT, queryParams, userAccessToken, Response.Status.OK, eventDefinitionMapType.getType());
|
||||
assertNotNull("GET did not return any events", eventDefinitionsMap);
|
||||
EventDefinition checkEventDefinition = eventDefinitionsMap.get(eventId);
|
||||
assertNotNull("GET did not return the event that was just created", checkEventDefinition);
|
||||
assertEquals("Event name is incorrect", eventName, checkEventDefinition.getEventName());
|
||||
assertEquals("Point value is incorrect", pointValue, checkEventDefinition.getPointValue());
|
||||
assertEquals("Event description is incorrect", eventDescription, checkEventDefinition.getEventDescription());
|
||||
assertEquals("Event location is incorrect", eventLocation, checkEventDefinition.getEventLocation());
|
||||
assertEquals("Event start time is incorrect", startTime.toInstant(), checkEventDefinition.getStartTime().toInstant()); // Use toInstant to normalize timezones
|
||||
assertEquals("Event end time is incorrect", endTime.toInstant(), checkEventDefinition.getEndTime().toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndUpdateEvent() {
|
||||
// Use POST to create an event. An admin user must do this.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
eventDefinition.setEventDescription(eventDescription);
|
||||
eventDefinition.setEventLocation(eventLocation);
|
||||
eventDefinition.setStartTime(startTime);
|
||||
eventDefinition.setEndTime(endTime);
|
||||
String eventId = post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, adminAccessToken, Response.Status.CREATED, String.class);
|
||||
// Use PUT to modify the event. An admin user must do this.
|
||||
eventDefinition.setEventName(eventName + eventName);
|
||||
eventDefinition.setPointValue(pointValue*2);
|
||||
put(EVENTS_BASEURL, EVENTS_ENDPOINT + '/' + eventId, eventDefinition, adminAccessToken, Response.Status.NO_CONTENT, Void.class);
|
||||
// Use GET to get the event. This method does not require admin.
|
||||
EventDefinition checkEventDefinition = get(EVENTS_BASEURL, EVENTS_ENDPOINT + '/' + eventId, null, userAccessToken, Response.Status.OK, EventDefinition.class);
|
||||
assertEquals("Event name is incorrect", eventDefinition.getEventName(), checkEventDefinition.getEventName());
|
||||
assertEquals("Point value is incorrect", eventDefinition.getPointValue(), checkEventDefinition.getPointValue());
|
||||
assertEquals("Event description is incorrect", eventDescription, checkEventDefinition.getEventDescription());
|
||||
assertEquals("Event location is incorrect", eventLocation, checkEventDefinition.getEventLocation());
|
||||
assertEquals("Event start time is incorrect", startTime.toInstant(), checkEventDefinition.getStartTime().toInstant()); // Use toInstant to normalize timezones
|
||||
assertEquals("Event end time is incorrect", endTime.toInstant(), checkEventDefinition.getEndTime().toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticationFailure() {
|
||||
// Make calls without an authentication header.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
put(EVENTS_BASEURL, EVENTS_ENDPOINT + "/deadbeef-0000-0000-0000-badbadbadbad", eventDefinition, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
get(EVENTS_BASEURL, EVENTS_ENDPOINT, null, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
get(EVENTS_BASEURL, EVENTS_ENDPOINT + "/deadbeef-0000-0000-0000-badbadbadbad", null, null, Response.Status.UNAUTHORIZED, Void.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizationFailure() {
|
||||
// Normal users do not have access to POST or PUT.
|
||||
EventDefinition eventDefinition = new EventDefinition();
|
||||
eventDefinition.setEventName(eventName);
|
||||
eventDefinition.setPointValue(pointValue);
|
||||
post(EVENTS_BASEURL, EVENTS_ENDPOINT, eventDefinition, userAccessToken, Response.Status.FORBIDDEN, Void.class);
|
||||
put(EVENTS_BASEURL, EVENTS_ENDPOINT + "/deadbeef-0000-0000-0000-badbadbadbad", eventDefinition, userAccessToken, Response.Status.FORBIDDEN, Void.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package it.com.ibm.codey.loyalty.util;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider;
|
||||
|
||||
public class TestSecurityHelper {
|
||||
|
||||
private static String APPID_SERVICE_URL;
|
||||
|
||||
private static String APPID_TENANTID;
|
||||
|
||||
private static String IAM_APIKEY;
|
||||
|
||||
private static String IAM_SERVICE_URL;
|
||||
|
||||
private static String OIDC_ISSUERIDENTIFIER;
|
||||
|
||||
private static String OIDC_CLIENTID;
|
||||
|
||||
private static String OIDC_CLIENTPASSWORD;
|
||||
|
||||
private static String iamAuthHeader;
|
||||
|
||||
private static String oidcAuthHeader;
|
||||
|
||||
static {
|
||||
APPID_SERVICE_URL = System.getenv("APPID_SERVICE_URL");
|
||||
APPID_TENANTID = System.getenv("APPID_TENANTID");
|
||||
IAM_APIKEY = System.getenv("IAM_APIKEY");
|
||||
IAM_SERVICE_URL = System.getenv("IAM_SERVICE_URL");
|
||||
OIDC_ISSUERIDENTIFIER = System.getenv("OIDC_ISSUERIDENTIFIER");
|
||||
OIDC_CLIENTID = System.getenv("OIDC_CLIENTID");
|
||||
OIDC_CLIENTPASSWORD = System.getenv("OIDC_CLIENTPASSWORD");
|
||||
String oidcClientCredentials = OIDC_CLIENTID + ":" + OIDC_CLIENTPASSWORD;
|
||||
oidcAuthHeader = "Basic " + Base64.getEncoder().encodeToString(oidcClientCredentials.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static void createUser(String user, String password) {
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(JsrJsonpProvider.class);
|
||||
// Get IAM bearer token when creating the first user. The token can be reused after that.
|
||||
if (iamAuthHeader == null) {
|
||||
Form form = new Form();
|
||||
form.param("grant_type", "urn:ibm:params:oauth:grant-type:apikey");
|
||||
form.param("apikey", IAM_APIKEY);
|
||||
String iamToken;
|
||||
try (Response response = client.target(IAM_SERVICE_URL).request(MediaType.APPLICATION_JSON).buildPost(Entity.form(form)).invoke()) {
|
||||
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
|
||||
throw new RuntimeException("TEST CASE FAILURE. Cannot obtain IAM access token. Status code " + response.getStatus() + " Response =" + response.readEntity(JsonObject.class));
|
||||
}
|
||||
JsonObject obj = response.readEntity(JsonObject.class);
|
||||
iamToken = obj.getString("access_token");
|
||||
}
|
||||
iamAuthHeader = "Bearer " + iamToken;
|
||||
}
|
||||
// Create the user
|
||||
JsonObject request = Json.createObjectBuilder()
|
||||
.add("userName", user)
|
||||
.add("password", password)
|
||||
.add("active", true)
|
||||
.add("emails", Json.createArrayBuilder()
|
||||
.add(Json.createObjectBuilder()
|
||||
.add("value", "ibmtestloyalty@yopmail.com")
|
||||
.add("primary", true))
|
||||
).build();
|
||||
String createUserURL = APPID_SERVICE_URL + "/management/v4/" + APPID_TENANTID + "/cloud_directory/Users";
|
||||
try (Response response = client.target(createUserURL).request(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, iamAuthHeader).buildPost(Entity.json(request)).invoke()) {
|
||||
if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
|
||||
throw new RuntimeException("TEST CASE FAILURE. Cannot create user. Status code " + response.getStatus() + " Response =" + response.readEntity(JsonObject.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String signOn(String user, String password) {
|
||||
String url = OIDC_ISSUERIDENTIFIER + "/token";
|
||||
Form form = new Form();
|
||||
form.param("grant_type", "password");
|
||||
form.param("username", user);
|
||||
form.param("password", password);
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(JsrJsonpProvider.class);
|
||||
try (Response response = client.target(url).request(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, oidcAuthHeader).buildPost(Entity.form(form)).invoke()) {
|
||||
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
|
||||
throw new RuntimeException("TEST CASE FAILURE. Cannot obtain access token. Status code " + response.getStatus() + " Response =" + response.readEntity(JsonObject.class));
|
||||
}
|
||||
JsonObject obj = response.readEntity(JsonObject.class);
|
||||
return obj.getString("access_token");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
216
dev/tekton/examples/example-bank/bank-app-backend/pom.xml
Executable file
216
dev/tekton/examples/example-bank/bank-app-backend/pom.xml
Executable file
@@ -0,0 +1,216 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<!-- Plugins -->
|
||||
<version.maven-war-plugin>3.2.2</version.maven-war-plugin>
|
||||
<version.maven-surefire-plugin>3.0.0-M1</version.maven-surefire-plugin>
|
||||
<version.maven-failsafe-plugin>3.0.0-M1</version.maven-failsafe-plugin>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.16</version>
|
||||
</dependency>
|
||||
<!-- For tests -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-rs-client</artifactId>
|
||||
<version>3.2.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-rs-extension-providers</artifactId>
|
||||
<version>3.2.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Support for JDK 9 and above -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>2.3.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- JSON-B provider for integration tests -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse</groupId>
|
||||
<artifactId>yasson</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.openliberty.features</groupId>
|
||||
<artifactId>features-bom</artifactId>
|
||||
<version>19.0.0.12</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${version.maven-war-plugin}</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<packagingExcludes>pom.xml</packagingExcludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Plugin to run unit tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${version.maven-surefire-plugin}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>test</phase>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/it/**</exclude>
|
||||
</excludes>
|
||||
<reportsDirectory>
|
||||
${project.build.directory}/test-reports/unit
|
||||
</reportsDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skipTests>${skipTests}</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${version.maven-failsafe-plugin}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>integration-test</phase>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/it/**</include>
|
||||
</includes>
|
||||
<systemPropertyVariables>
|
||||
<liberty.test.port>${http.port}</liberty.test.port>
|
||||
<war.name>${app.name}</war.name>
|
||||
</systemPropertyVariables>
|
||||
<trimStackTrace>false</trimStackTrace>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>verify-results</id>
|
||||
<goals>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<summaryFile>
|
||||
${project.build.directory}/test-reports/it/failsafe-summary.xml
|
||||
</summaryFile>
|
||||
<reportsDirectory>
|
||||
${project.build.directory}/test-reports/it
|
||||
</reportsDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>16</source>
|
||||
<target>16</target>
|
||||
<fork>true</fork>
|
||||
<compilerArgs>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.16</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<module>common</module>
|
||||
<module>transaction-service</module>
|
||||
<module>user-service</module>
|
||||
<module>integration-tests</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
FROM open-liberty:19.0.0.12-kernel-java8-openj9
|
||||
|
||||
USER root
|
||||
RUN apt-get update && apt-get upgrade -y e2fsprogs libgnutls30 libgcrypt20 libsasl2-2
|
||||
USER 1001
|
||||
|
||||
COPY --chown=1001:0 src/main/liberty/config/ /config/
|
||||
COPY --chown=1001:0 src/main/resources/security/ /config/resources/security/
|
||||
COPY --chown=1001:0 target/*.war /config/apps/
|
||||
COPY --chown=1001:0 target/jdbc/* /config/jdbc/
|
||||
RUN configure.sh
|
||||
@@ -0,0 +1,60 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: transaction-service
|
||||
labels:
|
||||
app: transaction-service
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: transaction-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: transaction-service
|
||||
annotations:
|
||||
sidecar.istio.io/inject: "false"
|
||||
spec:
|
||||
containers:
|
||||
- name: transaction-service
|
||||
image: ykoyfman/bank-transaction-service:1.0
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http-server
|
||||
containerPort: 9080
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: bank-db-secret
|
||||
- secretRef:
|
||||
name: bank-oidc-secret
|
||||
env:
|
||||
- name: USER_SERVICE_URL
|
||||
value: "http://user-service:9080/bank/v1/users"
|
||||
- name: KNATIVE_SERVICE_URL
|
||||
value: "http://process-transaction.example-bank.svc.cluster.local"
|
||||
- name: WLP_LOGGING_CONSOLE_LOGLEVEL
|
||||
value: INFO
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: transaction-service
|
||||
labels:
|
||||
app: transaction-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 9080
|
||||
targetPort: 9080
|
||||
selector:
|
||||
app: transaction-service
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: transaction-service
|
||||
spec:
|
||||
to:
|
||||
kind: Service
|
||||
name: transaction-service
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>transaction-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Open Liberty Features -->
|
||||
<dependency>
|
||||
<groupId>io.openliberty.features</groupId>
|
||||
<artifactId>microProfile-3.0</artifactId>
|
||||
<type>esa</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Add JDBC driver to package -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jdbc-driver</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.8</version>
|
||||
<outputDirectory>${project.build.directory}/jdbc</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Plugin to run unit tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
|
||||
@Liveness
|
||||
@ApplicationScoped
|
||||
public class LivenessCheck implements HealthCheck {
|
||||
|
||||
private boolean isAlive() {
|
||||
// perform health checks here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isAlive();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/bank")
|
||||
public class LoyaltyApplication extends Application {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
|
||||
@Readiness
|
||||
@ApplicationScoped
|
||||
public class ReadinessCheck implements HealthCheck {
|
||||
|
||||
private boolean isReady() {
|
||||
// perform readiness checks, e.g. database connection, etc.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isReady();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.ibm.codey.bank.catalog;
|
||||
|
||||
import java.net.URL;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.interceptor.Interceptors;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
|
||||
import com.ibm.codey.bank.BaseResource;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistration;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistrationInfo;
|
||||
import com.ibm.codey.bank.catalog.dao.TransactionDao;
|
||||
import com.ibm.codey.bank.catalog.json.CreateTransactionDefinition;
|
||||
import com.ibm.codey.bank.catalog.json.RewardTransactionDefinition;
|
||||
import com.ibm.codey.bank.catalog.models.Category;
|
||||
import com.ibm.codey.bank.catalog.models.Transaction;
|
||||
import com.ibm.codey.bank.interceptor.LoggingInterceptor;
|
||||
import com.ibm.codey.bank.interceptor.binding.RequiresAuthorization;
|
||||
|
||||
@RequestScoped
|
||||
@Interceptors(LoggingInterceptor.class)
|
||||
@Path("v1/transactions")
|
||||
public class TransactionResource extends BaseResource {
|
||||
|
||||
@Inject
|
||||
private TransactionDao transactionDao;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "USER_SERVICE_URL")
|
||||
private URL userServiceURL;
|
||||
|
||||
/**
|
||||
* This method creates a transaction.
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response createTransaction(CreateTransactionDefinition createTransactionDefinition) {
|
||||
|
||||
Transaction newTransaction = new Transaction();
|
||||
// create new uuid for new transaction
|
||||
String transactionId = UUID.randomUUID().toString();
|
||||
|
||||
// get subject
|
||||
String subject = this.getCallerSubject();
|
||||
// get user
|
||||
UserService userService = RestClientBuilder.newBuilder().baseUrl(userServiceURL).build(UserService.class);
|
||||
try {
|
||||
UserRegistrationInfo userRegistration = userService.getUserConsent(this.getCallerCredentials());
|
||||
if (!userRegistration.isConsentGiven()) {
|
||||
return Response.status(Response.Status.CONFLICT).entity("User has not consented to program").build();
|
||||
}
|
||||
|
||||
newTransaction.setTransactionId(transactionId);
|
||||
newTransaction.setUserId(userRegistration.getUserId());
|
||||
newTransaction.setTransactionName(createTransactionDefinition.getTransactionName());
|
||||
newTransaction.setCategory(createTransactionDefinition.getCategory());
|
||||
newTransaction.setAmount(createTransactionDefinition.getAmount());
|
||||
newTransaction.setProcessed(false);
|
||||
newTransaction.setDate(OffsetDateTime.now());
|
||||
transactionDao.createTransaction(newTransaction);
|
||||
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
} catch(WebApplicationException wae) {
|
||||
int status = wae.getResponse().getStatus();
|
||||
if (status == Response.Status.NOT_FOUND.getStatusCode()) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User not registered").build();
|
||||
} else {
|
||||
wae.printStackTrace();
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets the transactions of a user.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response getTransactions() {
|
||||
// get subject
|
||||
String subject = this.getCallerSubject();
|
||||
// get user
|
||||
UserService userService = RestClientBuilder.newBuilder().baseUrl(userServiceURL).build(UserService.class);
|
||||
try {
|
||||
UserRegistrationInfo userRegistration = userService.getUserConsent(this.getCallerCredentials());
|
||||
if (!userRegistration.isConsentGiven()) {
|
||||
return Response.status(Response.Status.CONFLICT).entity("User has not consented to program").build();
|
||||
}
|
||||
|
||||
List<Transaction> transactions = transactionDao.findTransactionsByUser(userRegistration.getUserId());
|
||||
return Response.status(Response.Status.OK).entity(transactions).build();
|
||||
} catch(WebApplicationException wae) {
|
||||
int status = wae.getResponse().getStatus();
|
||||
if (status == Response.Status.NOT_FOUND.getStatusCode()) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User not registered").build();
|
||||
} else {
|
||||
wae.printStackTrace();
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets the spending categories of a user.
|
||||
*/
|
||||
@GET
|
||||
@Path("spending")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response getCategory() {
|
||||
// get subject
|
||||
String subject = this.getCallerSubject();
|
||||
// get user
|
||||
UserService userService = RestClientBuilder.newBuilder().baseUrl(userServiceURL).build(UserService.class);
|
||||
try {
|
||||
UserRegistrationInfo userRegistration = userService.getUserConsent(this.getCallerCredentials());
|
||||
if (!userRegistration.isConsentGiven()) {
|
||||
return Response.status(Response.Status.CONFLICT).entity("User has not consented to program").build();
|
||||
}
|
||||
|
||||
List<Category> categories = transactionDao.groupCategoriesForUser(userRegistration.getUserId());
|
||||
return Response.status(Response.Status.OK).entity(categories).build();
|
||||
} catch(WebApplicationException wae) {
|
||||
int status = wae.getResponse().getStatus();
|
||||
if (status == Response.Status.NOT_FOUND.getStatusCode()) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User not registered").build();
|
||||
} else {
|
||||
wae.printStackTrace();
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: require admin scope
|
||||
/**
|
||||
* This method updates a transaction.
|
||||
*/
|
||||
@PUT
|
||||
@Path("reward/{transactionId}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
@RequiresAuthorization
|
||||
public Response updateTransaction(@PathParam("transactionId") String transactionId, RewardTransactionDefinition rewardTransactionDefinition) {
|
||||
// Validate UUID is formatted correctly.
|
||||
try {
|
||||
UUID.fromString(transactionId);
|
||||
} catch(IllegalArgumentException iae) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("Invalid transaction id").build();
|
||||
}
|
||||
|
||||
Transaction transaction = transactionDao.findTransactionById(transactionId);
|
||||
if (transaction == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("Transaction not found").build();
|
||||
}
|
||||
|
||||
if (transaction.isProcessed()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("Transaction already processed").build();
|
||||
}
|
||||
|
||||
transaction.setPointsEarned(rewardTransactionDefinition.getPointsEarned());
|
||||
transaction.setProcessed(true);
|
||||
transactionDao.updateTransaction(transaction);
|
||||
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.ibm.codey.bank.catalog.dao;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.ibm.codey.bank.catalog.models.Category;
|
||||
import com.ibm.codey.bank.catalog.models.Transaction;
|
||||
|
||||
@RequestScoped
|
||||
public class TransactionDao {
|
||||
|
||||
@PersistenceContext(name = "jpa-unit")
|
||||
private EntityManager em;
|
||||
|
||||
public void createTransaction(Transaction transaction) {
|
||||
em.persist(transaction);
|
||||
}
|
||||
|
||||
public void updateTransaction(Transaction transaction) {
|
||||
em.merge(transaction);
|
||||
}
|
||||
|
||||
public List<Transaction> findTransactions() {
|
||||
return em.createNamedQuery("Transaction.findTransactions", Transaction.class)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<Transaction> findTransactionsByUser(String userId) {
|
||||
return em.createNamedQuery("Transaction.findTransactionsByUser", Transaction.class)
|
||||
.setParameter("userId", userId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public Transaction findTransactionById(String transactionId) {
|
||||
try {
|
||||
return em.createNamedQuery("Transaction.findTransactionByIdOnly", Transaction.class)
|
||||
.setParameter("transactionId", transactionId)
|
||||
.getSingleResult();
|
||||
} catch(NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Transaction findTransactionById(String transactionId, String userId) {
|
||||
try {
|
||||
return em.createNamedQuery("Transaction.findTransactionById", Transaction.class)
|
||||
.setParameter("transactionId", transactionId)
|
||||
.setParameter("userId", userId)
|
||||
.getSingleResult();
|
||||
} catch(NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Category> groupCategoriesForUser(String userId) {
|
||||
try {
|
||||
List<Object[][]> rows = em.createNamedQuery("Transaction.groupCategoriesForUser", Object[][].class)
|
||||
.setParameter("userId", userId)
|
||||
.getResultList();
|
||||
List<Category> response = new ArrayList<>();
|
||||
for (Object[] row: rows) {
|
||||
if (row.length == 2) {
|
||||
response.add(new Category(row[0].toString(), new BigDecimal(row[1].toString())));
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch(NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ibm.codey.bank.catalog.models;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter @Setter
|
||||
public class Category {
|
||||
|
||||
private String category;
|
||||
private BigDecimal amount;
|
||||
|
||||
public Category(String category, BigDecimal amount) {
|
||||
this.category = category;
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.ibm.codey.bank.catalog.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "transactions")
|
||||
@IdClass(TransactionPK.class)
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "Transaction.findTransactions", query = "SELECT t FROM Transaction t"),
|
||||
@NamedQuery(name = "Transaction.findTransactionsByUser", query = "SELECT t FROM Transaction t WHERE t.userId = :userId"),
|
||||
@NamedQuery(name = "Transaction.findTransactionById", query = "SELECT t FROM Transaction t WHERE t.transactionId = :transactionId AND t.userId = :userId"),
|
||||
@NamedQuery(name = "Transaction.findTransactionByIdOnly", query = "SELECT t FROM Transaction t WHERE t.transactionId = :transactionId"),
|
||||
@NamedQuery(name = "Transaction.groupCategoriesForUser", query = "SELECT COALESCE(t.category, 'Uncategorized'), SUM (t.amount) FROM Transaction t WHERE t.userId = :userId GROUP BY t.category")
|
||||
})
|
||||
@Getter @Setter
|
||||
@EntityListeners(TransactionListener.class)
|
||||
public class Transaction implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "transaction_id")
|
||||
@Id
|
||||
private String transactionId;
|
||||
|
||||
@Id
|
||||
@Column(name = "usr")
|
||||
private String userId;
|
||||
|
||||
@Column(name = "transaction_name")
|
||||
private String transactionName;
|
||||
|
||||
@Column(name = "amount")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "category")
|
||||
private String category;
|
||||
|
||||
@Column(name = "points_earned")
|
||||
private BigDecimal pointsEarned;
|
||||
|
||||
@Column(name = "processed")
|
||||
private boolean processed;
|
||||
|
||||
@Column(name = "date")
|
||||
private OffsetDateTime date;
|
||||
|
||||
public Transaction() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.ibm.codey.bank.catalog.models;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.PostPersist;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
|
||||
import com.ibm.codey.bank.catalog.KnativeService;
|
||||
|
||||
@RequestScoped
|
||||
public class TransactionListener {
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "KNATIVE_SERVICE_URL")
|
||||
private URL knativeServiceURL;
|
||||
|
||||
@PostPersist
|
||||
public void sendToProcessing(Transaction transaction) {
|
||||
KnativeService knativeService = RestClientBuilder.newBuilder().baseUrl(knativeServiceURL).build(KnativeService.class);
|
||||
|
||||
try {
|
||||
knativeService.processTransaction(transaction.getTransactionId(), transaction.getCategory(), transaction.getAmount().toString());
|
||||
} catch (WebApplicationException wae) {
|
||||
System.out.print("web app exception");
|
||||
int status = wae.getResponse().getStatus();
|
||||
if (status == Response.Status.NOT_FOUND.getStatusCode()) {
|
||||
// TODO: ..
|
||||
} else {
|
||||
wae.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ibm.codey.bank.catalog.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter @Setter
|
||||
public class TransactionPK implements Serializable {
|
||||
|
||||
private String transactionId;
|
||||
|
||||
private String userId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
default.http.port=9080
|
||||
default.https.port=9443
|
||||
@@ -0,0 +1,2 @@
|
||||
# This option is needed when using an IBM JRE to avoid a handshake failure when making a secure JDBC connection.
|
||||
-Dcom.ibm.jsse2.overrideDefaultTLS=true
|
||||
@@ -0,0 +1,53 @@
|
||||
<server description="Liberty server">
|
||||
|
||||
<featureManager>
|
||||
<feature>jpa-2.2</feature>
|
||||
<feature>microProfile-3.0</feature>
|
||||
<feature>mpJwt-1.1</feature>
|
||||
</featureManager>
|
||||
|
||||
<logging traceSpecification="eclipselink=all" maxFileSize="20" maxFiles="10"/>
|
||||
|
||||
<keyStore id="digicertRootCA" password="digicert" location="${server.config.dir}/resources/security/digicert-root-ca.jks"/>
|
||||
<ssl id="defaultSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="digicertRootCA" />
|
||||
|
||||
<httpEndpoint host="*" httpPort="${default.http.port}"
|
||||
httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
|
||||
|
||||
<mpJwt
|
||||
id="jwt"
|
||||
issuer="${OIDC_ISSUERIDENTIFIER}"
|
||||
jwksUri="${OIDC_JWKENDPOINTURL}"
|
||||
audiences="${OIDC_AUDIENCES}"
|
||||
userNameAttribute="sub"
|
||||
/>
|
||||
|
||||
<library id="PostgresLib">
|
||||
<fileset dir="${server.config.dir}/jdbc"/>
|
||||
</library>
|
||||
|
||||
<dataSource id="AccountsDataSource" jndiName="jdbc/AccountsDataSource">
|
||||
<jdbcDriver libraryRef="PostgresLib" />
|
||||
<!-- Idle connections to this server are timing out after 5 minutes.
|
||||
It is recommended to set maxIdleTime to half of that value to avoid jdbc failures (e.g. broken pipe).
|
||||
Reap time is reduced from default of 3 minutes to close idle connections in time. -->
|
||||
<connectionManager maxIdleTime="2m30s" reapTime="60s"/>
|
||||
<properties.postgresql
|
||||
serverName="${DB_SERVERNAME}"
|
||||
portNumber="${DB_PORTNUMBER}"
|
||||
databaseName="${DB_DATABASENAME}"
|
||||
user="${DB_USER}"
|
||||
password="${DB_PASSWORD}"
|
||||
ssl="false"
|
||||
/>
|
||||
</dataSource>
|
||||
|
||||
<webApplication location="transaction-service.war" contextRoot="/">
|
||||
<application-bnd>
|
||||
<security-role name="authenticated">
|
||||
<special-subject type="ALL_AUTHENTICATED_USERS"/>
|
||||
</security-role>
|
||||
</application-bnd>
|
||||
</webApplication>
|
||||
|
||||
</server>
|
||||
@@ -0,0 +1,10 @@
|
||||
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
|
||||
version="2.0">
|
||||
<persistence-unit-metadata>
|
||||
<persistence-unit-defaults>
|
||||
<schema>bank</schema>
|
||||
</persistence-unit-defaults>
|
||||
</persistence-unit-metadata>
|
||||
</entity-mappings>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.2"
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
|
||||
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
|
||||
<persistence-unit name="jpa-unit" transaction-type="JTA">
|
||||
<jta-data-source>jdbc/AccountsDataSource</jta-data-source>
|
||||
<shared-cache-mode>NONE</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="eclipselink.target-database" value="PostgreSQL"/>
|
||||
<property name="eclipselink.logging.level" value="ALL"/>
|
||||
<property name="eclipselink.logging.parameters" value="true"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
||||
@@ -0,0 +1,27 @@
|
||||
<web-app
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<display-name>transaction-service</display-name>
|
||||
|
||||
<security-role>
|
||||
<role-name>authenticated</role-name>
|
||||
</security-role>
|
||||
|
||||
<security-constraint>
|
||||
<display-name>Security Constraints</display-name>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>ProtectedArea</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>authenticated</role-name>
|
||||
</auth-constraint>
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>NONE</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
|
||||
</web-app>
|
||||
11
dev/tekton/examples/example-bank/bank-app-backend/user-service/Dockerfile
Executable file
11
dev/tekton/examples/example-bank/bank-app-backend/user-service/Dockerfile
Executable file
@@ -0,0 +1,11 @@
|
||||
FROM open-liberty:19.0.0.12-kernel-java8-openj9
|
||||
|
||||
USER root
|
||||
RUN apt-get update && apt-get upgrade -y e2fsprogs libgnutls30 libgcrypt20 libsasl2-2
|
||||
USER 1001
|
||||
|
||||
COPY --chown=1001:0 src/main/liberty/config/ /config/
|
||||
COPY --chown=1001:0 src/main/resources/security/ /config/resources/security/
|
||||
COPY --chown=1001:0 target/*.war /config/apps/
|
||||
COPY --chown=1001:0 target/jdbc/* /config/jdbc/
|
||||
RUN configure.sh
|
||||
@@ -0,0 +1,51 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: user-service
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: user-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
containers:
|
||||
- name: user-service
|
||||
image: anthonyamanse/user-service:example-bank-1.0
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http-server
|
||||
containerPort: 9080
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: bank-db-secret
|
||||
- secretRef:
|
||||
name: bank-oidc-secret
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: user-service
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 9080
|
||||
targetPort: 9080
|
||||
selector:
|
||||
app: user-service
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: user-service
|
||||
spec:
|
||||
to:
|
||||
kind: Service
|
||||
name: user-service
|
||||
|
||||
78
dev/tekton/examples/example-bank/bank-app-backend/user-service/pom.xml
Executable file
78
dev/tekton/examples/example-bank/bank-app-backend/user-service/pom.xml
Executable file
@@ -0,0 +1,78 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>user-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Open Liberty Features -->
|
||||
<dependency>
|
||||
<groupId>io.openliberty.features</groupId>
|
||||
<artifactId>microProfile-3.0</artifactId>
|
||||
<type>esa</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Add JDBC driver to package -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jdbc-driver</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.8</version>
|
||||
<outputDirectory>${project.build.directory}/jdbc</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Plugin to run unit tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
|
||||
@Liveness
|
||||
@ApplicationScoped
|
||||
public class LivenessCheck implements HealthCheck {
|
||||
|
||||
private boolean isAlive() {
|
||||
// perform health checks here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isAlive();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/bank")
|
||||
public class LoyaltyApplication extends Application {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
|
||||
@Readiness
|
||||
@ApplicationScoped
|
||||
public class ReadinessCheck implements HealthCheck {
|
||||
|
||||
private boolean isReady() {
|
||||
// perform readiness checks, e.g. database connection, etc.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isReady();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.ibm.codey.bank.accounts;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.interceptor.Interceptors;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import com.ibm.codey.bank.BaseResource;
|
||||
import com.ibm.codey.bank.accounts.dao.UserDao;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistration;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistrationInfo;
|
||||
import com.ibm.codey.bank.accounts.models.User;
|
||||
import com.ibm.codey.bank.interceptor.LoggingInterceptor;
|
||||
|
||||
@RequestScoped
|
||||
@Interceptors(LoggingInterceptor.class)
|
||||
@Path("v1/users")
|
||||
public class UserResource extends BaseResource {
|
||||
|
||||
@Inject
|
||||
private UserDao userDAO;
|
||||
|
||||
/**
|
||||
* This method creates a new user.
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response registerUser(UserRegistration userRegistration) {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
if (userDAO.findUserByRegistryId(subject) != null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("User is already registered").build();
|
||||
}
|
||||
User newUser = new User();
|
||||
newUser.setSubject(subject);
|
||||
newUser.setConsentGiven(userRegistration.isConsentGiven());
|
||||
userDAO.createUser(newUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the user registration data for a user.
|
||||
*/
|
||||
@GET
|
||||
@Path("self")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response getUser() {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
UserRegistrationInfo userRegistration = new UserRegistrationInfo();
|
||||
userRegistration.setUserId(prevUser.getUserId());
|
||||
userRegistration.setConsentGiven(prevUser.isConsentGiven());
|
||||
return Response.status(Response.Status.OK).entity(userRegistration).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method updates the user registration data for a user.
|
||||
*/
|
||||
@PUT
|
||||
@Path("self")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response updateUser(UserRegistration userRegistration) {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
if (prevUser.isDeleteRequested()) {
|
||||
return Response.status(Response.Status.CONFLICT).entity("User has requested deletion").build();
|
||||
}
|
||||
prevUser.setConsentGiven(userRegistration.isConsentGiven());
|
||||
userDAO.updateUser(prevUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method schedules an asynchronous process to remove the user from the system.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("self")
|
||||
@Transactional
|
||||
public Response deleteUser() {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
prevUser.setDeleteRequested(true);
|
||||
prevUser.setSubject(null);
|
||||
userDAO.updateUser(prevUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ibm.codey.bank.accounts.dao;
|
||||
|
||||
import java.util.List;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.ibm.codey.bank.accounts.models.User;
|
||||
|
||||
@RequestScoped
|
||||
public class UserDao {
|
||||
|
||||
@PersistenceContext(name = "jpa-unit")
|
||||
private EntityManager em;
|
||||
|
||||
public void createUser(User user) {
|
||||
em.persist(user);
|
||||
}
|
||||
|
||||
public void updateUser(User user) {
|
||||
em.merge(user);
|
||||
}
|
||||
|
||||
public User findUserByRegistryId(String subject) {
|
||||
try {
|
||||
return em.createNamedQuery("User.findUserByRegistryId", User.class)
|
||||
.setParameter("subject", subject).getSingleResult();
|
||||
} catch(NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ibm.codey.bank.accounts.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "User.findUserByRegistryId", query = "SELECT e FROM User e WHERE e.subject = :subject"),
|
||||
})
|
||||
@Getter @Setter
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "user_id")
|
||||
@Id
|
||||
@Setter(AccessLevel.NONE)
|
||||
private String userId;
|
||||
|
||||
@Column(name = "subject", unique=true)
|
||||
private String subject;
|
||||
|
||||
@Column(name = "consent_given")
|
||||
private boolean consentGiven;
|
||||
|
||||
@Column(name = "delete_requested")
|
||||
private boolean deleteRequested;
|
||||
|
||||
public User() {
|
||||
this.userId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
default.http.port=9080
|
||||
default.https.port=9443
|
||||
@@ -0,0 +1,2 @@
|
||||
# This option is needed when using an IBM JRE to avoid a handshake failure when making a secure JDBC connection.
|
||||
-Dcom.ibm.jsse2.overrideDefaultTLS=true
|
||||
@@ -0,0 +1,53 @@
|
||||
<server description="Liberty server">
|
||||
|
||||
<featureManager>
|
||||
<feature>jpa-2.2</feature>
|
||||
<feature>microProfile-3.0</feature>
|
||||
<feature>mpJwt-1.1</feature>
|
||||
</featureManager>
|
||||
|
||||
<logging traceSpecification="eclipselink=all" maxFileSize="20" maxFiles="10"/>
|
||||
|
||||
<keyStore id="digicertRootCA" password="digicert" location="${server.config.dir}/resources/security/digicert-root-ca.jks"/>
|
||||
<ssl id="defaultSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="digicertRootCA" />
|
||||
|
||||
<httpEndpoint host="*" httpPort="${default.http.port}"
|
||||
httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
|
||||
|
||||
<mpJwt
|
||||
id="jwt"
|
||||
issuer="${OIDC_ISSUERIDENTIFIER}"
|
||||
jwksUri="${OIDC_JWKENDPOINTURL}"
|
||||
audiences="${OIDC_AUDIENCES}"
|
||||
userNameAttribute="sub"
|
||||
/>
|
||||
|
||||
<library id="PostgresLib">
|
||||
<fileset dir="${server.config.dir}/jdbc"/>
|
||||
</library>
|
||||
|
||||
<dataSource id="AccountsDataSource" jndiName="jdbc/AccountsDataSource">
|
||||
<jdbcDriver libraryRef="PostgresLib" />
|
||||
<!-- Idle connections to this server are timing out after 5 minutes.
|
||||
It is recommended to set maxIdleTime to half of that value to avoid jdbc failures (e.g. broken pipe).
|
||||
Reap time is reduced from default of 3 minutes to close idle connections in time. -->
|
||||
<connectionManager maxIdleTime="2m30s" reapTime="60s"/>
|
||||
<properties.postgresql
|
||||
serverName="${DB_SERVERNAME}"
|
||||
portNumber="${DB_PORTNUMBER}"
|
||||
databaseName="${DB_DATABASENAME}"
|
||||
user="${DB_USER}"
|
||||
password="${DB_PASSWORD}"
|
||||
ssl="false"
|
||||
/>
|
||||
</dataSource>
|
||||
|
||||
<webApplication location="user-service.war" contextRoot="/">
|
||||
<application-bnd>
|
||||
<security-role name="authenticated">
|
||||
<special-subject type="ALL_AUTHENTICATED_USERS"/>
|
||||
</security-role>
|
||||
</application-bnd>
|
||||
</webApplication>
|
||||
|
||||
</server>
|
||||
@@ -0,0 +1,10 @@
|
||||
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
|
||||
version="2.0">
|
||||
<persistence-unit-metadata>
|
||||
<persistence-unit-defaults>
|
||||
<schema>bank</schema>
|
||||
</persistence-unit-defaults>
|
||||
</persistence-unit-metadata>
|
||||
</entity-mappings>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.2"
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
|
||||
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
|
||||
<persistence-unit name="jpa-unit" transaction-type="JTA">
|
||||
<jta-data-source>jdbc/AccountsDataSource</jta-data-source>
|
||||
<shared-cache-mode>NONE</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="eclipselink.target-database" value="PostgreSQL"/>
|
||||
<property name="eclipselink.logging.level" value="ALL"/>
|
||||
<property name="eclipselink.logging.parameters" value="true"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
||||
@@ -0,0 +1,27 @@
|
||||
<web-app
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<display-name>user-service</display-name>
|
||||
|
||||
<security-role>
|
||||
<role-name>authenticated</role-name>
|
||||
</security-role>
|
||||
|
||||
<security-constraint>
|
||||
<display-name>Security Constraints</display-name>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>ProtectedArea</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>authenticated</role-name>
|
||||
</auth-constraint>
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>NONE</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
|
||||
</web-app>
|
||||
Reference in New Issue
Block a user