diff --git a/cloudbank-v4/customer-helidon/.dockerignore b/cloudbank-v4/customer-helidon/.dockerignore
new file mode 100644
index 000000000..c8b241f22
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/.dockerignore
@@ -0,0 +1 @@
+target/*
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/.gitignore b/cloudbank-v4/customer-helidon/.gitignore
new file mode 100644
index 000000000..626464def
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/.gitignore
@@ -0,0 +1,36 @@
+# Compiled class file
+*.class
+
+# Maven
+target/
+.m2/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# IntelliJ Idea
+.idea/*
+!.idea/runConfigurations
+*.iws
+*.ipr
+*.iml
+*.releaseBackup
+atlassian-ide-plugin.xml
+
+# Netbeans
+nbactions.xml
+nb-configuration.xml
+
+# Eclipse
+.settings
+.settings/
+.project
+.classpath
+.factorypath
+
diff --git a/cloudbank-v4/customer-helidon/.helidon b/cloudbank-v4/customer-helidon/.helidon
new file mode 100644
index 000000000..10bc0857a
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/.helidon
@@ -0,0 +1,6 @@
+#Helidon Project Configuration
+#Wed Jun 18 10:11:56 EDT 2025
+schema.version=1.1.0
+helidon.version=4.2.3
+project.flavor=mp
+project.archetype=quickstart
diff --git a/cloudbank-v4/customer-helidon/Dockerfile b/cloudbank-v4/customer-helidon/Dockerfile
new file mode 100644
index 000000000..054037697
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/Dockerfile
@@ -0,0 +1,39 @@
+
+# 1st stage, build the app
+FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build
+
+# Install maven
+WORKDIR /usr/share
+RUN set -x && \
+ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \
+ tar -xvf apache-maven-*-bin.tar.gz && \
+ rm apache-maven-*-bin.tar.gz && \
+ mv apache-maven-* maven && \
+ ln -s /usr/share/maven/bin/mvn /bin/
+
+WORKDIR /helidon
+
+# Create a first layer to cache the "Maven World" in the local repository.
+# Incremental docker builds will always resume after that, unless you update
+# the pom
+ADD pom.xml .
+RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip -Declipselink.weave.skip -DskipOpenApiGenerate
+
+# Do the Maven build!
+# Incremental docker builds will resume here when you change sources
+ADD src src
+RUN mvn package -DskipTests
+
+RUN echo "done!"
+
+# 2nd stage, build the runtime image
+FROM container-registry.oracle.com/java/jdk-no-fee-term:21
+WORKDIR /helidon
+
+# Copy the binary built in the 1st stage
+COPY --from=build /helidon/target/customer-helidon.jar ./
+COPY --from=build /helidon/target/libs ./libs
+
+CMD ["java", "-jar", "customer-helidon.jar"]
+
+EXPOSE 8080
diff --git a/cloudbank-v4/customer-helidon/README.md b/cloudbank-v4/customer-helidon/README.md
new file mode 100644
index 000000000..04ecc52aa
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/README.md
@@ -0,0 +1,94 @@
+# customer-helidon
+
+Helidon MP version of the "customer" microservice.
+
+## Build and run
+
+
+With JDK21
+```bash
+mvn package
+java -jar target/customer-helidon.jar
+```
+
+## Exercise the application
+
+Basic:
+```
+curl -X GET http://localhost:8080/simple-greet
+Hello World!
+```
+
+
+JSON:
+```
+curl -X GET http://localhost:8080/greet
+{"message":"Hello World!"}
+
+curl -X GET http://localhost:8080/greet/Joe
+{"message":"Hello Joe!"}
+
+curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
+
+curl -X GET http://localhost:8080/greet/Jose
+{"message":"Hola Jose!"}
+```
+
+
+
+## Try health
+
+```
+curl -s -X GET http://localhost:8080/health
+{"outcome":"UP",...
+
+```
+
+
+## Building a Native Image
+
+The generation of native binaries requires an installation of GraalVM 22.1.0+.
+
+You can build a native binary using Maven as follows:
+
+```
+mvn -Pnative-image install -DskipTests
+```
+
+The generation of the executable binary may take a few minutes to complete depending on
+your hardware and operating system. When completed, the executable file will be available
+under the `target` directory and be named after the artifact ID you have chosen during the
+project generation phase.
+
+
+
+## Try metrics
+
+```
+# Prometheus Format
+curl -s -X GET http://localhost:8080/metrics
+# TYPE base:gc_g1_young_generation_count gauge
+. . .
+
+# JSON Format
+curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics
+{"base":...
+. . .
+```
+
+
+
+## Building the Docker Image
+
+```
+docker build -t customer-helidon .
+```
+
+## Running the Docker Image
+
+```
+docker run --rm -p 8080:8080 customer-helidon:latest
+```
+
+Exercise the application as described above.
+
diff --git a/cloudbank-v4/customer-helidon/pom.xml b/cloudbank-v4/customer-helidon/pom.xml
new file mode 100644
index 000000000..510839125
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/pom.xml
@@ -0,0 +1,206 @@
+
+
+ 4.0.0
+
+ io.helidon.applications
+ helidon-mp
+ 4.2.3
+
+
+ com.example
+ customer-helidon
+ 1.0-SNAPSHOT
+
+
+
+ io.helidon.microprofile.bundles
+ helidon-microprofile-core
+
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-datasource-ucp
+ runtime
+
+
+ io.helidon.integrations.db
+ ojdbc
+ runtime
+
+
+ jakarta.transaction
+ jakarta.transaction-api
+
+
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-jta-weld
+ runtime
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+
+
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-jpa
+ runtime
+
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-hibernate
+ runtime
+
+
+ org.hibernate
+ hibernate-core
+ ${version.lib.hibernate}
+
+
+ io.helidon.microprofile.openapi
+ helidon-microprofile-openapi
+
+
+ io.helidon.microprofile.health
+ helidon-microprofile-health
+
+
+ jakarta.json.bind
+ jakarta.json.bind-api
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-binding
+ runtime
+
+
+ io.helidon.logging
+ helidon-logging-jul
+ runtime
+
+
+ io.smallrye
+ jandex
+ runtime
+
+
+ org.eclipse.microprofile.metrics
+ microprofile-metrics-api
+
+
+ io.helidon.microprofile.metrics
+ helidon-microprofile-metrics
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-junit5
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.5.0
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.5.0
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-libs
+
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+
+
+
+ org.hibernate.orm.tooling
+ hibernate-enhance-maven-plugin
+
+
+ Statically enhance JPA entities for Hibernate
+ compile
+
+ enhance
+
+
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+ default-compile
+
+
+
+ org.hibernate.orm
+ hibernate-jpamodelgen
+ ${version.lib.hibernate}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cloudbank-v4/customer-helidon/src/main/java/com/example/Customer.java b/cloudbank-v4/customer-helidon/src/main/java/com/example/Customer.java
new file mode 100644
index 000000000..35093cf4e
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/java/com/example/Customer.java
@@ -0,0 +1,149 @@
+package com.example;
+
+import java.util.Date;
+import org.hibernate.annotations.CreationTimestamp;
+import jakarta.persistence.Access;
+import jakarta.persistence.AccessType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.NamedQueries;
+import jakarta.persistence.NamedQuery;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "CUSTOMER")
+@Access(AccessType.PROPERTY)
+@NamedQueries({
+ @NamedQuery(name = "getCustomers",
+ query = "SELECT c FROM Customer c"),
+ @NamedQuery(name = "getCustomerById",
+ query = "SELECT c FROM Customer c WHERE c.customerId = :id"),
+ @NamedQuery(name = "getCustomerByCustomerNameContaining",
+ query = "SELECT c FROM Customer c WHERE c.customerName LIKE :customerName"),
+ @NamedQuery(name = "getCustomerByCustomerEmailContaining",
+ query = "SELECT c FROM Customer c WHERE c.customerEmail LIKE :customerEmail")
+})
+public class Customer {
+
+ private String customerId;
+ private String customerName;
+ private String customerEmail;
+ private Date dateBecameCustomer;
+ private String customerOtherDetails;
+ private String customerPassword;
+
+ // Default constructor required by JPA
+ public Customer() {}
+
+ /**
+ * Creates a Customer object.
+ * @param customerId The Customer ID
+ * @param customerName The Customer Name
+ * @param customerEmail The Customer Email
+ * @param customerOtherDetails Other details about the customer
+ */
+ public Customer(String customerId, String customerName, String customerEmail, String customerOtherDetails) {
+ this.customerId = customerId;
+ this.customerName = customerName;
+ this.customerEmail = customerEmail;
+ this.customerOtherDetails = customerOtherDetails;
+ }
+
+ /**
+ * Full constructor including password
+ * @param customerId The Customer ID
+ * @param customerName The Customer Name
+ * @param customerEmail The Customer Email
+ * @param customerOtherDetails Other details about the customer
+ * @param customerPassword The Customer Password
+ */
+ public Customer(String customerId, String customerName, String customerEmail,
+ String customerOtherDetails, String customerPassword) {
+ this.customerId = customerId;
+ this.customerName = customerName;
+ this.customerEmail = customerEmail;
+ this.customerOtherDetails = customerOtherDetails;
+ this.customerPassword = customerPassword;
+ }
+
+ @Id
+ @Column(name = "CUSTOMER_ID", nullable = false, updatable = false)
+ public String getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+
+ @Column(name = "CUSTOMER_NAME")
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ public void setCustomerName(String customerName) {
+ this.customerName = customerName;
+ }
+
+ @Column(name = "CUSTOMER_EMAIL")
+ public String getCustomerEmail() {
+ return customerEmail;
+ }
+
+ public void setCustomerEmail(String customerEmail) {
+ this.customerEmail = customerEmail;
+ }
+
+ @CreationTimestamp
+ @Column(name = "DATE_BECAME_CUSTOMER", updatable = false, insertable = false)
+ public Date getDateBecameCustomer() {
+ return dateBecameCustomer;
+ }
+
+ public void setDateBecameCustomer(Date dateBecameCustomer) {
+ this.dateBecameCustomer = dateBecameCustomer;
+ }
+
+ @Column(name = "CUSTOMER_OTHER_DETAILS")
+ public String getCustomerOtherDetails() {
+ return customerOtherDetails;
+ }
+
+ public void setCustomerOtherDetails(String customerOtherDetails) {
+ this.customerOtherDetails = customerOtherDetails;
+ }
+
+ @Column(name = "PASSWORD")
+ public String getCustomerPassword() {
+ return customerPassword;
+ }
+
+ public void setCustomerPassword(String customerPassword) {
+ this.customerPassword = customerPassword;
+ }
+
+ @Override
+ public String toString() {
+ return "Customer{" +
+ "customerId='" + customerId + '\'' +
+ ", customerName='" + customerName + '\'' +
+ ", customerEmail='" + customerEmail + '\'' +
+ ", dateBecameCustomer=" + dateBecameCustomer +
+ ", customerOtherDetails='" + customerOtherDetails + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Customer customer = (Customer) o;
+ return customerId != null ? customerId.equals(customer.customerId) : customer.customerId == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return customerId != null ? customerId.hashCode() : 0;
+ }
+}
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/java/com/example/CustomerResource.java b/cloudbank-v4/customer-helidon/src/main/java/com/example/CustomerResource.java
new file mode 100644
index 000000000..cf7f96a29
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/java/com/example/CustomerResource.java
@@ -0,0 +1,236 @@
+package com.example;
+
+import java.util.List;
+import java.net.URI;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.TypedQuery;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.UriInfo;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * Helidon MP Customer REST Resource with all features from original Spring API
+ */
+@RequestScoped
+@Path("/api/v1/customer")
+public class CustomerResource {
+
+ private static final Logger LOGGER = Logger.getLogger(CustomerResource.class.getName());
+
+ @PersistenceContext(unitName = "customer")
+ private EntityManager entityManager;
+
+ @Context
+ private UriInfo uriInfo;
+
+
+ /**
+ * Get all customers
+ *
+ * @return List of all customers
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomers() {
+ try {
+ List customers = entityManager.createNamedQuery("getCustomers", Customer.class).getResultList();
+ return Response.ok(customers).build();
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error finding all customers", e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Find customers by name (containing)
+ *
+ * @param customerName The customer name to search for
+ * @return List of customers with matching names
+ */
+ @GET
+ @Path("name/{customerName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomerByName(@PathParam("customerName") String customerName) {
+ try {
+ TypedQuery query = entityManager.createNamedQuery("getCustomerByCustomerNameContaining", Customer.class);
+ query.setParameter("customerName", "%" + customerName + "%");
+ List customers = query.getResultList();
+ return Response.ok(customers).build();
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error finding customers by name: " + customerName, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Get Customer with specific ID.
+ *
+ * @param id The CustomerId
+ * @return If the customer is found, a customer and HTTP Status code.
+ */
+ @GET
+ @Path("{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomerById(@PathParam("id") String id) {
+ try {
+ Customer customer = entityManager.find(Customer.class, id);
+ if (customer != null) {
+ return Response.ok(customer).build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error getting customer by ID: " + id, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Get customer that contains an email.
+ *
+ * @param email of the customer
+ * @return Returns a list of customers if found
+ */
+ @GET
+ @Path("byemail/{email}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomerByEmail(@PathParam("email") String email) {
+ try {
+ TypedQuery query = entityManager.createNamedQuery("getCustomerByCustomerEmailContaining", Customer.class);
+ query.setParameter("customerEmail", "%" + email + "%");
+ List customers = query.getResultList();
+ return Response.ok(customers).build();
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error finding customers by email: " + email, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Create a customer.
+ *
+ * @param customer Customer object with the customer details.
+ * @return Returns HTTP Status code or the URI of the created object.
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Response createCustomer(Customer customer) {
+ try {
+ // Check if customer already exists
+ Customer existingCustomer = entityManager.find(Customer.class, customer.getCustomerId());
+
+ if (existingCustomer == null) {
+ entityManager.persist(customer);
+ entityManager.flush(); // Ensure the entity is persisted
+
+ // Build the location URI for the created resource
+ URI location = UriBuilder.fromResource(CustomerResource.class)
+ .path("{id}")
+ .build(customer.getCustomerId());
+
+ return Response.created(location).build();
+ } else {
+ return Response.status(Response.Status.CONFLICT).entity(customer).build();
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error creating customer", e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Update a specific Customer (ID).
+ *
+ * @param id The id of the customer
+ * @param customer A customer object
+ * @return A Http Status code and updated customer
+ */
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Response updateCustomer(@PathParam("id") String id, Customer customer) {
+ try {
+ Customer existingCustomer = entityManager.find(Customer.class, id);
+ if (existingCustomer != null) {
+ // Update the existing customer with new values
+ existingCustomer.setCustomerName(customer.getCustomerName());
+ existingCustomer.setCustomerEmail(customer.getCustomerEmail());
+ existingCustomer.setCustomerOtherDetails(customer.getCustomerOtherDetails());
+
+ Customer updatedCustomer = entityManager.merge(existingCustomer);
+ return Response.ok(updatedCustomer).build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error updating customer with ID: " + id, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Delete a specific customer (ID).
+ *
+ * @param customerId the Id of the customer to be deleted
+ * @return A Http Status code
+ */
+ @DELETE
+ @Path("{customerId}")
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Response deleteCustomer(@PathParam("customerId") String customerId) {
+ try {
+ Customer customer = entityManager.find(Customer.class, customerId);
+ if (customer != null) {
+ entityManager.remove(customer);
+ return Response.status(Response.Status.NO_CONTENT).build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error deleting customer with ID: " + customerId, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ /**
+ * Apply for loan - Method isn't fully implemented.
+ *
+ * @param amount Loan amount
+ * @return A Http Status
+ */
+ @POST
+ @Path("applyLoan/{amount}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response applyForLoan(@PathParam("amount") long amount) {
+ try {
+ // Check Credit Rating
+ // Amount vs Rating approval?
+ // Create Account
+ // Update Account Balance
+ // Notify
+ return Response.status(418).build(); // I_AM_A_TEAPOT equivalent
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error processing loan application for amount: " + amount, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/META-INF/beans.xml b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/beans.xml
new file mode 100644
index 000000000..b0993408c
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/META-INF/init-script.sql b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/init-script.sql
new file mode 100644
index 000000000..4c0f1f8cf
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/init-script.sql
@@ -0,0 +1,11 @@
+create table customer if not exists (
+ CUSTOMER_ID varchar2(256),
+ CUSTOMER_NAME varchar2(256),
+ CUSTOMER_EMAIL varchar2(256),
+ DATE_BECAME_CUSTOMER date,
+ CUSTOMER_OTHER_DETAILS varchar2(256),
+ PASSWORD varchar2(256)
+);
+insert into customer (customer_id, customer_name, customer_email, customer_other_details, password)
+values ('abc123', 'Bob Drake', 'bob@drake.com', '', 'Welcome-12345');
+commit;
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/META-INF/microprofile-config.properties b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/microprofile-config.properties
new file mode 100644
index 000000000..c64ac051c
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/microprofile-config.properties
@@ -0,0 +1,20 @@
+# Microprofile server properties
+server.port=8080
+server.host=0.0.0.0
+
+# Change the following to true to enable the optional MicroProfile Metrics REST.request metrics
+metrics.rest-request.enabled=false
+
+# Application properties. This is the default greeting
+app.greeting=Hello
+
+# need to set these through env vars to inject vals
+javax.sql.DataSource.customer.connectionFactoryClassName = oracle.jdbc.pool.OracleDataSource
+javax.sql.DataSource.customer.URL = jdbc:oracle:thin:@//localhost:1521/freepdb1
+javax.sql.DataSource.customer.user = customer
+javax.sql.DataSource.customer.password = Welcome12345
+
+# Enable Table Creation
+hibernate.hbm2ddl.auto=create
+hibernate.show_sql=true
+hibernate.format_sql=true
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/META-INF/native-image/com/example/customer-helidon/native-image.properties b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/native-image/com/example/customer-helidon/native-image.properties
new file mode 100644
index 000000000..b450206b5
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/native-image/com/example/customer-helidon/native-image.properties
@@ -0,0 +1 @@
+Args=--initialize-at-build-time=com.example
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/META-INF/persistence.xml b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 000000000..df1145500
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ customer
+ com.example.Customer
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/main/resources/logging.properties b/cloudbank-v4/customer-helidon/src/main/resources/logging.properties
new file mode 100644
index 000000000..ffad43dbf
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/main/resources/logging.properties
@@ -0,0 +1,20 @@
+
+# Example Logging Configuration File
+# For more information see $JAVA_HOME/jre/lib/logging.properties
+
+# Send messages to the console
+handlers=io.helidon.logging.jul.HelidonConsoleHandler
+
+# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
+java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n
+
+# Global logging level. Can be overridden by specific loggers
+.level=INFO
+
+# Quiet Weld
+org.jboss.level=WARNING
+
+# Component specific log levels
+#io.helidon.config.level=INFO
+#io.helidon.security.level=INFO
+#io.helidon.common.level=INFO
diff --git a/cloudbank-v4/customer-helidon/src/test/java/com/example/CustomerResourceUnitTest.java b/cloudbank-v4/customer-helidon/src/test/java/com/example/CustomerResourceUnitTest.java
new file mode 100644
index 000000000..1631150d7
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/test/java/com/example/CustomerResourceUnitTest.java
@@ -0,0 +1,428 @@
+package com.example;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.TypedQuery;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for CustomerResource using Mockito
+ * These tests run without any containers or databases
+ */
+@ExtendWith(MockitoExtension.class)
+public class CustomerResourceUnitTest {
+
+ @Mock
+ private EntityManager entityManager;
+
+ @Mock
+ private UriInfo uriInfo;
+
+ @Mock
+ private TypedQuery typedQuery;
+
+ @InjectMocks
+ private CustomerResource customerResource;
+
+ private Customer testCustomer;
+ private List testCustomers;
+
+ @BeforeEach
+ void setUp() {
+ // Create test data
+ testCustomer = new Customer("CUST001", "John Doe", "john.doe@example.com", "Premium customer");
+
+ Customer customer2 = new Customer("CUST002", "Jane Smith", "jane.smith@gmail.com", "Standard customer");
+ Customer customer3 = new Customer("CUST003", "Bob Johnson", "bob.johnson@yahoo.com", "VIP customer");
+
+ testCustomers = Arrays.asList(testCustomer, customer2, customer3);
+ }
+
+ // ==============================================
+ // GET ALL CUSTOMERS TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should get all customers successfully")
+ void testGetAllCustomers_Success() {
+ // Arrange
+ when(entityManager.createNamedQuery("getCustomers", Customer.class)).thenReturn(typedQuery);
+ when(typedQuery.getResultList()).thenReturn(testCustomers);
+
+ // Act
+ Response response = customerResource.getCustomers();
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(testCustomers, response.getEntity());
+
+ verify(entityManager).createNamedQuery("getCustomers", Customer.class);
+ verify(typedQuery).getResultList();
+ }
+
+ @Test
+ @DisplayName("Should handle exception when getting all customers")
+ void testGetAllCustomers_Exception() {
+ // Arrange
+ when(entityManager.createNamedQuery("getCustomers", Customer.class))
+ .thenThrow(new RuntimeException("Database error"));
+
+ // Act
+ Response response = customerResource.getCustomers();
+
+ // Assert
+ assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ @Test
+ @DisplayName("Should return empty list when no customers exist")
+ void testGetAllCustomers_EmptyList() {
+ // Arrange
+ when(entityManager.createNamedQuery("getCustomers", Customer.class)).thenReturn(typedQuery);
+ when(typedQuery.getResultList()).thenReturn(Collections.emptyList());
+
+ // Act
+ Response response = customerResource.getCustomers();
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(Collections.emptyList(), response.getEntity());
+ }
+
+ // ==============================================
+ // GET CUSTOMER BY ID TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should get customer by ID successfully")
+ void testGetCustomerById_Success() {
+ // Arrange
+ String customerId = "CUST001";
+ when(entityManager.find(Customer.class, customerId)).thenReturn(testCustomer);
+
+ // Act
+ Response response = customerResource.getCustomerById(customerId);
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(testCustomer, response.getEntity());
+
+ verify(entityManager).find(Customer.class, customerId);
+ }
+
+ @Test
+ @DisplayName("Should return 404 when customer not found by ID")
+ void testGetCustomerById_NotFound() {
+ // Arrange
+ String customerId = "NONEXISTENT";
+ when(entityManager.find(Customer.class, customerId)).thenReturn(null);
+
+ // Act
+ Response response = customerResource.getCustomerById(customerId);
+
+ // Assert
+ assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+ assertNull(response.getEntity());
+ }
+
+ @Test
+ @DisplayName("Should handle exception when getting customer by ID")
+ void testGetCustomerById_Exception() {
+ // Arrange
+ String customerId = "CUST001";
+ when(entityManager.find(Customer.class, customerId))
+ .thenThrow(new RuntimeException("Database error"));
+
+ // Act
+ Response response = customerResource.getCustomerById(customerId);
+
+ // Assert
+ assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ // ==============================================
+ // SEARCH BY NAME TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should search customers by name successfully")
+ void testGetCustomerByName_Success() {
+ // Arrange
+ String searchName = "John";
+ when(entityManager.createNamedQuery("getCustomerByCustomerNameContaining", Customer.class))
+ .thenReturn(typedQuery);
+ when(typedQuery.getResultList()).thenReturn(Arrays.asList(testCustomer));
+
+ // Act
+ Response response = customerResource.getCustomerByName(searchName);
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(Arrays.asList(testCustomer), response.getEntity());
+
+ verify(typedQuery).setParameter("customerName", "%" + searchName + "%");
+ }
+
+ @Test
+ @DisplayName("Should return empty list when no customers match name")
+ void testGetCustomerByName_NoMatches() {
+ // Arrange
+ String searchName = "NonExistent";
+ when(entityManager.createNamedQuery("getCustomerByCustomerNameContaining", Customer.class))
+ .thenReturn(typedQuery);
+ when(typedQuery.getResultList()).thenReturn(Collections.emptyList());
+
+ // Act
+ Response response = customerResource.getCustomerByName(searchName);
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(Collections.emptyList(), response.getEntity());
+ }
+
+ // ==============================================
+ // SEARCH BY EMAIL TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should search customers by email successfully")
+ void testGetCustomerByEmail_Success() {
+ // Arrange
+ String searchEmail = "gmail";
+ Customer gmailCustomer = new Customer("CUST002", "Jane Smith", "jane.smith@gmail.com", "Standard customer");
+
+ when(entityManager.createNamedQuery("getCustomerByCustomerEmailContaining", Customer.class))
+ .thenReturn(typedQuery);
+ when(typedQuery.getResultList()).thenReturn(Arrays.asList(gmailCustomer));
+
+ // Act
+ Response response = customerResource.getCustomerByEmail(searchEmail);
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(Arrays.asList(gmailCustomer), response.getEntity());
+
+ verify(typedQuery).setParameter("customerEmail", "%" + searchEmail + "%");
+ }
+
+ // ==============================================
+ // CREATE CUSTOMER TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should create customer successfully")
+ void testCreateCustomer_Success() {
+ // Arrange
+ Customer newCustomer = new Customer("CUST004", "New Customer", "new@example.com", "Details");
+ when(entityManager.find(Customer.class, newCustomer.getCustomerId())).thenReturn(null);
+
+ // Act
+ Response response = customerResource.createCustomer(newCustomer);
+
+ // Assert
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ verify(entityManager).persist(newCustomer);
+ verify(entityManager).flush();
+ }
+
+ @Test
+ @DisplayName("Should return conflict when customer already exists")
+ void testCreateCustomer_Conflict() {
+ // Arrange
+ when(entityManager.find(Customer.class, testCustomer.getCustomerId())).thenReturn(testCustomer);
+
+ // Act
+ Response response = customerResource.createCustomer(testCustomer);
+
+ // Assert
+ assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
+ assertEquals(testCustomer, response.getEntity());
+
+ verify(entityManager, never()).persist(any());
+ }
+
+ @Test
+ @DisplayName("Should handle exception when creating customer")
+ void testCreateCustomer_Exception() {
+ // Arrange
+ Customer newCustomer = new Customer("CUST004", "New Customer", "new@example.com", "Details");
+ when(entityManager.find(Customer.class, newCustomer.getCustomerId())).thenReturn(null);
+ doThrow(new RuntimeException("Database error")).when(entityManager).persist(newCustomer);
+
+ // Act
+ Response response = customerResource.createCustomer(newCustomer);
+
+ // Assert
+ assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ // ==============================================
+ // UPDATE CUSTOMER TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should update customer successfully")
+ void testUpdateCustomer_Success() {
+ // Arrange
+ String customerId = "CUST001";
+ Customer updatedData = new Customer(customerId, "John Doe Updated", "john.updated@example.com", "Updated details");
+ Customer existingCustomer = new Customer(customerId, "John Doe", "john.doe@example.com", "Original details");
+
+ when(entityManager.find(Customer.class, customerId)).thenReturn(existingCustomer);
+ when(entityManager.merge(existingCustomer)).thenReturn(existingCustomer);
+
+ // Act
+ Response response = customerResource.updateCustomer(customerId, updatedData);
+
+ // Assert
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(existingCustomer, response.getEntity());
+
+ // Verify the customer was updated with new values
+ assertEquals("John Doe Updated", existingCustomer.getCustomerName());
+ assertEquals("john.updated@example.com", existingCustomer.getCustomerEmail());
+ assertEquals("Updated details", existingCustomer.getCustomerOtherDetails());
+
+ verify(entityManager).merge(existingCustomer);
+ }
+
+ @Test
+ @DisplayName("Should return 404 when updating non-existent customer")
+ void testUpdateCustomer_NotFound() {
+ // Arrange
+ String customerId = "NONEXISTENT";
+ Customer updatedData = new Customer(customerId, "Updated Name", "updated@example.com", "Updated details");
+
+ when(entityManager.find(Customer.class, customerId)).thenReturn(null);
+
+ // Act
+ Response response = customerResource.updateCustomer(customerId, updatedData);
+
+ // Assert
+ assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+
+ verify(entityManager, never()).merge(any());
+ }
+
+ // ==============================================
+ // DELETE CUSTOMER TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should delete customer successfully")
+ void testDeleteCustomer_Success() {
+ // Arrange
+ String customerId = "CUST001";
+ when(entityManager.find(Customer.class, customerId)).thenReturn(testCustomer);
+
+ // Act
+ Response response = customerResource.deleteCustomer(customerId);
+
+ // Assert
+ assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+ verify(entityManager).remove(testCustomer);
+ }
+
+ @Test
+ @DisplayName("Should return 404 when deleting non-existent customer")
+ void testDeleteCustomer_NotFound() {
+ // Arrange
+ String customerId = "NONEXISTENT";
+ when(entityManager.find(Customer.class, customerId)).thenReturn(null);
+
+ // Act
+ Response response = customerResource.deleteCustomer(customerId);
+
+ // Assert
+ assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+
+ verify(entityManager, never()).remove(any());
+ }
+
+ // ==============================================
+ // LOAN APPLICATION TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should return 418 for loan application")
+ void testApplyForLoan_TeapotResponse() {
+ // Arrange
+ long loanAmount = 10000L;
+
+ // Act
+ Response response = customerResource.applyForLoan(loanAmount);
+
+ // Assert
+ assertEquals(418, response.getStatus()); // I'm a Teapot
+ }
+
+ @Test
+ @DisplayName("Should handle different loan amounts")
+ void testApplyForLoan_DifferentAmounts() {
+ // Test multiple loan amounts
+ long[] amounts = {1000L, 50000L, 100000L};
+
+ for (long amount : amounts) {
+ Response response = customerResource.applyForLoan(amount);
+ assertEquals(418, response.getStatus());
+ }
+ }
+
+ // ==============================================
+ // CUSTOMER ENTITY TESTS
+ // ==============================================
+
+ @Test
+ @DisplayName("Should create customer with all constructors")
+ void testCustomerConstructors() {
+ // Test default constructor
+ Customer customer1 = new Customer();
+ assertNull(customer1.getCustomerId());
+
+ // Test 4-parameter constructor
+ Customer customer2 = new Customer("ID", "Name", "email@test.com", "Details");
+ assertEquals("ID", customer2.getCustomerId());
+ assertEquals("Name", customer2.getCustomerName());
+ assertEquals("email@test.com", customer2.getCustomerEmail());
+ assertEquals("Details", customer2.getCustomerOtherDetails());
+
+ // Test 5-parameter constructor
+ Customer customer3 = new Customer("ID", "Name", "email@test.com", "Details", "password");
+ assertEquals("password", customer3.getCustomerPassword());
+ }
+
+ @Test
+ @DisplayName("Should test customer equals and hashCode")
+ void testCustomerEqualsAndHashCode() {
+ Customer customer1 = new Customer("CUST001", "John", "john@test.com", "Details");
+ Customer customer2 = new Customer("CUST001", "Jane", "jane@test.com", "Other details");
+ Customer customer3 = new Customer("CUST002", "John", "john@test.com", "Details");
+
+ // Same ID should be equal
+ assertEquals(customer1, customer2);
+ assertEquals(customer1.hashCode(), customer2.hashCode());
+
+ // Different ID should not be equal
+ assertNotEquals(customer1, customer3);
+
+ // toString should not contain password
+ assertFalse(customer1.toString().contains("password"));
+ }
+}
\ No newline at end of file
diff --git a/cloudbank-v4/customer-helidon/src/test/resources/META-INF/microprofile-config.properties b/cloudbank-v4/customer-helidon/src/test/resources/META-INF/microprofile-config.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/cloudbank-v4/customer-helidon/src/test/resources/application-test.yaml b/cloudbank-v4/customer-helidon/src/test/resources/application-test.yaml
new file mode 100644
index 000000000..94f345144
--- /dev/null
+++ b/cloudbank-v4/customer-helidon/src/test/resources/application-test.yaml
@@ -0,0 +1,2 @@
+security:
+ enabled: false
\ No newline at end of file
diff --git a/docs-source/spring/content/platform/intellij-plugin/_index.md b/docs-source/spring/content/platform/intellij-plugin/_index.md
index 86f85c1dc..361edb2a5 100644
--- a/docs-source/spring/content/platform/intellij-plugin/_index.md
+++ b/docs-source/spring/content/platform/intellij-plugin/_index.md
@@ -4,6 +4,10 @@ description: "IntelliJ Plugin for Oracle Backend for Microservices and AI"
keywords: "intellij plugin ide springboot spring development microservices development oracle backend"
---
+{{< hint type=[warning] icon=gdoc_fire title="Deprecation Notice" >}}
+This component is deprecated in the 1.4.0 release and will be removed in a future release.
+{{< /hint >}}
+
## GA 1.3.1 - October, 2024
General updates.
diff --git a/docs-source/spring/content/platform/soc/_index.md b/docs-source/spring/content/platform/soc/_index.md
index 732f203bc..af770dfb7 100644
--- a/docs-source/spring/content/platform/soc/_index.md
+++ b/docs-source/spring/content/platform/soc/_index.md
@@ -120,7 +120,9 @@ Oracle Backend for Microservices and AI includes "ServiceOps Center". The Servic
- Manage users and roles defined in the Authorization Server included in the environment
- Collect diagnostic data for support
-**Note:** More capabilities will be added to this feature in future releases.
+{{< hint type=[warning] icon=gdoc_fire title="Deprecation Notice" >}}
+This component is deprecated in the 1.4.0 release and will be removed in a future release.
+{{< /hint >}}
- [Accessing ServiceOps Center](#accessing-serviceops-center)
- [Login Screen](#login-screen)
diff --git a/docs-source/spring/content/platform/vscode-plugin/_index.md b/docs-source/spring/content/platform/vscode-plugin/_index.md
index b116e0a8a..e898977f3 100644
--- a/docs-source/spring/content/platform/vscode-plugin/_index.md
+++ b/docs-source/spring/content/platform/vscode-plugin/_index.md
@@ -8,6 +8,10 @@ keywords: "vscode code visualstudio extension springboot spring development micr
## GA 1.4.0 - Dec, 2024
"Oracle Backend for Microservices and AI" is a VS Code extension to browse and deploy applications on the Oracle Backend for Microservices and AI platform. This plugin allows to inspect the content of an Oracle Backend for Microservices and AI deployment, in terms of applications, services and related configurations. It simplifies the access to the Platform Services installed, like SigNoz, Spring, Apisix and Eureka creating ssh tunnels on-demand, and providing access to their respective web admin consoles. It allows to add credentials to access and bind services to the Oracle Autonomous DB included into Oracle Backend for Microservices and AI deployment. This plug-in replicates the functionalities available in [OBaas CLI](../../development/cli) and simplify the access to Oracle Backend for Microservices and AI deployments from a development IDE like VS Code.
+{{< hint type=[warning] icon=gdoc_fire title="Deprecation Notice" >}}
+This component is deprecated in the 1.4.0 release and will be removed in a future release.
+{{< /hint >}}
+
In this release has been removed the support for GraalVM compilation since a native app could be compiled externally and uploaded as possible in the past releases, and it has been added the datastore management and visualization.
See the Oracle Free Use Terms and Conditions [License](https://oss.oracle.com/licenses/upl/)
diff --git a/docs-source/spring/content/release-notes/_index.md b/docs-source/spring/content/release-notes/_index.md
index 54c54c183..7ffe61778 100644
--- a/docs-source/spring/content/release-notes/_index.md
+++ b/docs-source/spring/content/release-notes/_index.md
@@ -6,7 +6,43 @@ keywords: "release notes additions updates deprecations removals springboot spri
## Version 1.4.0, July, 2025
-TBD
+This release includes the following changes:
+
+- Helidon 4.2.x is added as a first-class citizen for applications, alongside Spring Boot.
+- SigNoz replaces the Grafana observability stack. SigNoz is OpenTelemetry native and has an enterprise-friendly open-source license. It provides observability capabilities including metrics, logs, tracing, dashboards, and alerts.
+- Spring Boot upgraded to 3.4.x and Spring Cloud x.x.x for internal components.
+
+The following components were added:
+
+| Component | Version | Description |
+|-------------------------------------------------------|------------|--------------|
+| SigNoz | 0.75.0 | Provides OpenTelemetry native observability. |
+| External Secrets Operator | x | x |
+
+The following components were updated:
+
+| Component | New Version | Replaced Version |
+|-------------------------------------------------------|---------------|---------------------|
+| TBA | x | x |
+
+{{< hint type=[warning] icon=gdoc_fire title="Deprecation Notice" >}}
+The following components are deprecated and were removed in this release:
+
+- Grafana
+- Prometheus
+- Prometheus Operator
+- Loki
+- Promtail
+- Alert Manager
+- Jaeger Tracing
+- Hashicorp Vault
+
+The following components are deprecated and will be removed in a future this release:
+
+- ServiceOps Center UI (SOC UI)
+- Oracle Backend for Microservices and AI Visual Studio Code extension
+- Oracle Backend for Microservices and AI IntelliJ plugin
+{{< /hint >}}
## Version 1.3.1, October 23rd, 2024
diff --git a/docs-source/spring/content/setup/_index.md b/docs-source/spring/content/setup/_index.md
index 6b9246357..adbabe5c6 100644
--- a/docs-source/spring/content/setup/_index.md
+++ b/docs-source/spring/content/setup/_index.md
@@ -224,7 +224,6 @@ Oracle Backend for Microservices and AI setup installs the following components.
| Component | Version | Description |
|------------------------------|---------------|---------------------------------------------------------------------------------------------|
-| Alertmanager | v0.067.1 | Alertmanager |
| Apache APISIX | 3.9.1 | Provides full lifecycle API management. |
| Apache Kafka | 3.8.0 | Provides distributed event streaming. |
| cert-manager | 1.12.3 | Automates the management of certificates. |
@@ -237,7 +236,7 @@ Oracle Backend for Microservices and AI setup installs the following components.
| Oracle Database Observability Exporter | 1.3.1 | Exposes Oracle Database metrics in standard Prometheus format. |
| Oracle Database Operator | 1.1.0 | Helps reduce the time and complexity of deploying and managing Oracle databases. |
| Oracle Transaction Manager for Microservices | 24.2.1 | Manages distributed transactions to ensure consistency across Microservices. |
-| SigNoz | 0.73.0 | Observability stack and Dashboards for logs, metrics and tracing. |
+| SigNoz | 0.75.0 | Observability stack and Dashboards for logs, metrics and tracing. |
| Spring Authorization Server | 3.3.3 | Provides authentication and authorization for applications. |
| Spring Boot Admin server | 3.3.3 | Manages and monitors Spring Cloud applications. |
| Spring Cloud Config server | 4.1.3 | Provides server-side support for an externalized configuration. |
@@ -298,8 +297,7 @@ To set up the OCI environment, process these steps:
- `SigNoz Administrator Password` (optional) : Leave blank to auto-generate.
- `ORACTL Administrator Password` optional) : Leave blank to auto-generate. This is the password for the `obaas-admin` user.
- `ORACTL User Password` (optional) : Leave blank to auto-generate. This is the password for the `obaas-user` user.
- - `Alertmanager Administrator Password` (optional) : Leave blank to auto-generate. This is the admin password for the alertmanager.
-
+
{{< img name="oci-stack-passwords" size="large" lazy=false >}}
@@ -371,22 +369,6 @@ To set up the OCI environment, process these steps:
> For more information on the *Bring Your Own Database* option for the Oracle Backend for Microservices and AI including the required values, please review the [Database](../infrastructure/database) documentation.
-1. (*Standard Edition Only*) If you check the checkbox *Enable Vault in Production Mode* in the section **Vault Options** you will be installing HashiCorp in **Production** mode otherwise the HashiCorp Vault be installed in **Development** mode.
-
- Fill in the following Vault options. You have the option of creating a new OCI Vault or using an existing OCI Vault. The OCI Vault is only used in **Production** mode to auto-unseal the HashiCorp Vault (see documentation ...) Fill in the following information if you want to use an existing OCI Vault:
-
- - `Vault Compartment (Optional)` : Select a compartment for the OCI Vault.
- - `Existing Vault (Optional)` : Select an existing OCI Vault. If not selected a new OCI Vault be created.
- - `Existing Vault Key (Optional)` : Select an existing OCI Vault key. If not selected a new OCI Vault Key will be created.
-
-
- {{< img name="oci-stack-vault-options" size="large" lazy=false >}}
-
-
- {{< hint type=[warning] icon=gdoc_check title=Warning >}}
- **Never** run a **Development** mode HashiCorp Vault Server in a production environment. It is insecure and will lose data on every restart (since it stores data in-memory). It is only intended for development or experimentation.
- {{< /hint >}}
-
1. (*Standard Edition Only*) If you check the checkbox *Enable Container Registry Vulnerability Scanning* in the section **Additional Options** you will enable the automatic Vulnerability Scanning on images stored in the Oracle Container Registry.