Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
ndefokou authored Apr 22, 2024
2 parents 3afe72d + 1a3c49f commit fe0c83e
Show file tree
Hide file tree
Showing 34 changed files with 1,021 additions and 104 deletions.
13 changes: 9 additions & 4 deletions .github/workflows/build-deploy-and-docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ on:
- 'v*'
paths:
- 'power-pay-frontend/**'
- '.github/workflows/build-deploy-and-docker-build.yml'

pull_request:
branches:
- '**'
paths:
- 'power-pay-frontend/**'

- '.github/workflows/build-deploy-and-docker-build.yml'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-frontend
jobs:
# Build Job
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: power-pay-frontend
steps:
- name: Checkout Code
uses: actions/checkout@v3
Expand All @@ -30,10 +34,10 @@ jobs:
with:
node-version: 20
- name: Install Dependencies
run: cd power-pay-frontend && npm ci
run: npm ci

- name: Build Project
run: cd power-pay-frontend && npm run build-for-gh
run: npm run build-for-gh

- name: Upload artifact to enable deployment
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -72,7 +76,8 @@ jobs:
packages: write

steps:
- uses: actions/checkout@v4
- name: Checkout Code
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
Expand Down
134 changes: 90 additions & 44 deletions docs/useStorage.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,100 @@ One of the main benefits of Context API is its ability to simplify state managem

* Context API has two core concepts:

- Providers
- Consumers
#

- Providers
- Consumers
#


**Providers** is to define and keep track of specific pieces of state. This state can then be accessed by all the child components nested inside the Provider. These child components, known as **Consumers**, are responsible for accessing or modifying the state provided by the Context Provider.


## React Context with useStorage

To make the most use of the useStorage hook we are going to be using the Context library here this is because of its simplicity and also its ability to pass data from parent to children components without the use of props.<br>
Here is a basic form of how the react Context and usestorage hook function in other for us to have persistent and reusable data across our components.<br>
the react context works by creating a context object that holds the data we want to share between the components, once this context object is being created it is used in our components by first wrapping the component in a context provider then any component that imports(inherits) from this component can have access to the data, then now comes the useStorage hook which to persist that data to the localstorage or any other storage of our web browser to make the data persist accross page refresh or any other storage. <br>
The usestorage hook performs its task by taking the key and associated value converting it to a json string then using the ```setItem()``` method to store it in the storage with its associated key. And also has a ```getItem()``` for retrieving the value stored based on the corresponding key and a ```removeItem()``` method which will be used to remove key value pair from the storage. <br>
To conclude the react Context and useStorage helps our application to have reusable and persist data accross components.<br>
The flow is as shown

```mermaid
graph TD;
subgraph "ContextObject"
contextobject[key = value]
end
subgraph "Components"
A[component1]
B[component2]
end
subgraph "useStorage"
usestoragehook[useStoragehook]
end
subgraph "BrowserStorage"
localstorage[LocalStorage]
end
subgraph "ContextProvider"
provider[Provider]
end
ContextObject --> ContextProvider
ContextProvider --> Components
A -->|setItem,removeItem| useStorage
B --> |setItem,removeItem| useStorage
useStorage--> |getItem| A
useStorage --> |getItem|B
useStorage --> |settingItem/ removingItem|BrowserStorage
BrowserStorage --> |gettingItem|useStorage
TO implement the React context with the useStorage, we first start by creating and interface from which we define our data
and methods to read and write data across components by implementing the interface.<br>
Once this interface created we then create a context provider by importing context from react the we create a context provider from
it were all the child components to that component will consume its value.<br>
Once this done we have a reusable and a none tree shaking code, Now then comes the useStorage hook, the usestorage hook then
has then accepts the data provided by the consumer components and uses method such and getitem('key') and returns a value , setitem('key', value)
to store and item in the browser's storage using a key and value depending from which value and key may come from that provider by the
context provider for example.
So This is how we can obtain reusable , non-tree shaking and persistent data across our components.<br>

## Transactions using the browser's IndexDB storage
indexDB is a low level API for client-side storage of significant anounts of structured
including files and blobs

### KeyConcepts
+ Asynchronous, therefore it won't block any main thread operations.
+ lets you access data offline.
+ can store a large amount of data ( more than the local storage or session storage) of complex value types.
+ A noSQL database which makes it very flexible and dangerous.

### keywords
+ **Object stores** : the mechanism by which data is stored in the database.
+ **Database** : A repository of information , typically comprising of one or more object stores
+ **Index** : and index is a specialized object store for looking up records in another object store called the reference object store.
+ **request** : The operation by which reading and writing on a database is done. Every request represents one or more read or write operations

IndexDB just as the localStorage and sessionStorage stores data on client side and provides methods for easy retrieval , adding , and removing data from this storage
depending on whether the store object uses a ```keypath``` or a ``` key generator``` and you can also use and index for retrieving the data based on the key in the index data store which
maps the value in the object store.

### Basic Pattern
1. open the database.
2. create an object in the database.
3. start a transaction and make a request to do some database operations.
4. wait for the operation to be completed by listening to the right kind od DOM events.
5. do something with the result.

#### Possible Errors
One of the common possible errors when opening a databaseis ```VER_ERR``` it indicates that the version of the database stored on the
disk is greater than the version that you are trying to open. this an error case that must be handled by the
error handler.

Source, [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
)
```mermaid
classDiagram
class Storage {
<<interface>>
+ getItem<T>(key: string): Promise<T>
+ setItem<T>(key: string, value: T): Promise<void>
+ removeItem(key: string): Promise<void>
+ clear(): Promise<void>
}
class LocalstorageImpl {
}
class IndexDBImpl{
}
class MemoryStorageImp{
- dic: Map<string, any>
}
class IndexStorageImpl{
- dic: Map<any, any>
}
class Hook{
+ item: value
+ setItem(item: value): void
}
class ContextData {
+ data Map<string, any>
+ setData(data: Map<string, any>)
}
class Context {
+ storage: storage
+ data: connection
}
Context --> ContextData: use
Hook --> Context: use
MemoryStorageImp --> Storage: implements
LocalstorageImpl --> Storage: implements
IndexStorageImpl --> Storage: implements
IndexDBImpl --> Storage: implements
Context --> Storage: use
```

1 change: 1 addition & 0 deletions power-pay-backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package com.adorsys.gis.powerpay.powerpaybackend.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

@Entity
@Table(name = "Transaction")
public class Transaction extends Procedure {
private String receiverPhoneNumber;
private Double amount;
private String currency;


{
currency = "XAF";
}


public String getReceiverPhoneNumber() {
return receiverPhoneNumber;
}
Expand All @@ -35,4 +39,5 @@ public String getCurrency() {
public void setCurrency(String currency) {
this.currency = currency;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.adorsys.gis.powerpay.powerpaybackend.errorhandling;

public class InsufficientFundsException extends RuntimeException {

private final String message;

public InsufficientFundsException(String message) {
super(message);
this.message = message;
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.adorsys.gis.powerpay.powerpaybackend.repository;

import com.adorsys.gis.powerpay.powerpaybackend.domain.Transaction;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MoneyTransferRepository extends JpaRepository<Transaction, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.adorsys.gis.powerpay.powerpaybackend.repository;

import com.adorsys.gis.powerpay.powerpaybackend.domain.Transaction;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.Query;

@Repository
public interface TransactionRepository extends JpaRepository<Transaction, Long> {

@Query("SELECT SUM(CASE WHEN t.receiverPhoneNumber = :phoneNumber THEN t.amount ELSE -t.amount END) " +
"FROM Transaction t WHERE t.receiverPhoneNumber = :phoneNumber OR t.phoneNumber = :phoneNumber")

Double calculateBalaceByPhoneNumber(String phoneNumber);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.adorsys.gis.powerpay.powerpaybackend.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.adorsys.gis.powerpay.powerpaybackend.domain.UserRegistration;

public interface UserRegistrationRepository extends JpaRepository<UserRegistration, Integer> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.adorsys.gis.powerpay.powerpaybackend.repository;

import com.adorsys.gis.powerpay.powerpaybackend.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, String> {
Optional<User> findByPhoneNumber(String phoneNumber);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.adorsys.gis.powerpay.powerpaybackend.services;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

//implementing an empty interface
public interface CheckBalance {
Double checkBalance(String userId) throws UsernameNotFoundException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.adorsys.gis.powerpay.powerpaybackend.services;

import com.adorsys.gis.powerpay.powerpaybackend.domain.User;
import com.adorsys.gis.powerpay.powerpaybackend.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
@RequestMapping("/api/balance")
public class CheckBalanceController {
@Autowired
private final CheckBalance checkBalanceService;
private final UserRepository userRepository;

@Autowired
public CheckBalanceController(CheckBalance checkBalanceService, UserRepository userRepository) {
this.checkBalanceService = checkBalanceService;
this.userRepository = userRepository;
}

@GetMapping("/{userId}")
public ResponseEntity<String> checkBalance(@PathVariable String userId) {
Optional<User> user = userRepository.findByPhoneNumber(userId);
if (user.isPresent()) {
Double balance = checkBalanceService.checkBalance(userId);
return ResponseEntity.ok("Balance for user ID" + " : " + balance);
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User not found");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
package com.adorsys.gis.powerpay.powerpaybackend.services;

import com.adorsys.gis.powerpay.powerpaybackend.repository.TransactionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service // annotation to ensure classes are recognized as Spring Beans
public class CheckBalanceImpl implements CheckBalance {

private final TransactionRepository transactionRepository;

@Autowired
public CheckBalanceImpl(TransactionRepository transactionRepository){

this.transactionRepository = transactionRepository;
}

@Override
public Double checkBalance(String phoneNumber) throws UsernameNotFoundException{
Double balance = transactionRepository.calculateBalaceByPhoneNumber(phoneNumber);
return balance != null ? balance : 0.0;
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.adorsys.gis.powerpay.powerpaybackend.services;
import com.adorsys.gis.powerpay.powerpaybackend.domain.Transaction;

//implementing an empty interface
public interface SendMoney {
Transaction send(String phoneNumber, String receiverPhoneNumber, Double amount, String currency, Integer id);
}
Loading

0 comments on commit fe0c83e

Please sign in to comment.