Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(chore): RestCountries Client SDK #14

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion primekit-essentials/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<assertj-core.version>3.25.3</assertj-core.version>
<postgresql.version>42.7.3</postgresql.version>
<junit-jupiter.version>5.10.2</junit-jupiter.version>
<jjwt.version>0.12.6</jjwt.version>
<jacoco.version>0.8.9</jacoco.version>
</properties>

Expand Down Expand Up @@ -163,8 +164,20 @@
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml-jackson.version}</version>
</dependency>


<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) ShortThirdMan 2025.
package com.shortthirdman.primekit.essentials.common.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.userdetails.UserDetails;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public final class JwtUtils {

private JwtUtils() {}

/**
* @return the decoded JWT secret in
*/
public static SecretKey getSecretKey() {
var jwtSecret = System.getProperty("jwt.secret");
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
return Keys.hmacShaKeyFor(keyBytes);
}

public static String generateToken(UserDetails userDetails, Map<String, Object> extraClaims) {
var jwtSecret = System.getProperty("jwt.secret");
byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8);
SecretKey secretKey = Keys.hmacShaKeyFor(keyBytes);

return Jwts.builder()
.claims()
.add(extraClaims)
.subject(userDetails.getUsername())
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.and()
.signWith(secretKey, Jwts.SIG.HS512)
.compact();
}

/**
* @param token the token to validate
* @return true if valid, else false
*/
public static boolean validateTokenExpiration(String token) {
var jwtSecret = System.getProperty("jwt.secret");
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
Claims claims = Jwts.parser().verifyWith(getSecretKey())
.build()
.parseSignedClaims(token)
.getPayload();
return claims.getExpiration().after(new Date());
}

/**
* @param token the token to validate
* @return true if valid, else false
*/
public static boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(getSecretKey())
.build()
.parseSignedClaims(token);
return true;
} catch(SecurityException | MalformedJwtException e) {
throw new AuthenticationCredentialsNotFoundException("JWT was expired or incorrect");
} catch (ExpiredJwtException e) {
throw new AuthenticationCredentialsNotFoundException("Expired JWT token.");
} catch (UnsupportedJwtException e) {
throw new AuthenticationCredentialsNotFoundException("Unsupported JWT token.");
} catch (IllegalArgumentException e) {
throw new AuthenticationCredentialsNotFoundException("JWT token compact of handler are invalid.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.shortthirdman.primekit.essentials.common.util;

public class LinkedListUtils {

private LinkedListUtils() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) ShortThirdMan 2025.
package com.shortthirdman.primekit.essentials.common.util;

/**
* Utility methods for payments
*/
public final class PaymentUtils {

private PaymentUtils() {}

/**
* Validates a credit card number using Luhn's algorithm.
*
* @param cardNumber The credit/debit card number as a string.
* @return true if the number is valid, false otherwise.
*/
public static boolean isValidCardNumber(String cardNumber) {
// Remove any non-digit characters
String sanitizedNumber = cardNumber.replaceAll("\\D", "");

int length = sanitizedNumber.length();
if (length == 0) {
return false;
}

int sum = 0;
boolean shouldDouble = false;

// Iterate over the digits of the number, starting from the end
for (int i = length - 1; i >= 0; i--) {
int digit = Character.getNumericValue(sanitizedNumber.charAt(i));

if (shouldDouble) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}

sum += digit;
shouldDouble = !shouldDouble;
}

return (sum % 10 == 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) ShortThirdMan 2025.
package com.shortthirdman.primekit.essentials.common.util;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PaymentUtilsTest {

@Test
void test_validCardNumbers() {
assertTrue(PaymentUtils.isValidCardNumber("4532015112830366"), "Valid credit card number");
assertTrue(PaymentUtils.isValidCardNumber("6011514435546211"), "Valid credit card number");
assertTrue(PaymentUtils.isValidCardNumber("378282246310005"), "Valid American Express number.");
}

@Test
void test_invalidCardNumbers() {
assertFalse(PaymentUtils.isValidCardNumber("123456789012345"), "Invalid card number");
assertFalse(PaymentUtils.isValidCardNumber("0000000000000000"), "Invalid card number");
assertFalse(PaymentUtils.isValidCardNumber("4532015112830367"), "Slightly altered from a valid number to become invalid.");
assertFalse(PaymentUtils.isValidCardNumber("6011514435546210"), "Slightly altered from a valid number to become invalid.");
}
}
Empty file.
Empty file.
93 changes: 93 additions & 0 deletions primekit-restcountries-sdk/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?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.shortthirdman.primekit</groupId>
<artifactId>primekit-restcountries-sdk</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>primekit-restcountries-sdk</name>
<descripiton>PrimeKit RestCountries SDK</descripiton>

<properties>
<java.version>21</java.version>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.release>${java.version}</maven.compiler.release>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<!-- Optionally: parameterized tests support -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.4.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.6.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.shortthirdman.primekit.restcountries.clientsdk;

import com.shortthirdman.primekit.restcountries.clientsdk.config.CountryLayerAPIConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CountryLayerEndpointManager {

@Autowired
private CountryLayerAPIConfig apiConfig;

public Object allCountries() {
return null;
}

public Object countryByName(String name) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.shortthirdman.primekit.restcountries.clientsdk;

import com.shortthirdman.primekit.restcountries.clientsdk.config.RestCountriesAPIConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RestCountriesEndpointManager {

@Autowired
private RestCountriesAPIConfig apiConfig;

public Object allCountries() {
return null;
}

public Object countryByName(String name, boolean fullName) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.shortthirdman.primekit.restcountries.clientsdk.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:countrylayer.properties")
public class CountryLayerAPIConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.shortthirdman.primekit.restcountries.clientsdk.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:restcountries.properties")
public class RestCountriesAPIConfig {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.shortthirdman.primekit.restcountries.clientsdk.core;

public class EndpointConstants {

public final String REST_COUNTRIES_PREFIX = "primekit.restcountries.";
public final String COUNTRY_LAYER_PREFIX = "primekit.countrylayer.";

public static final String BASE_URL = "base-url";
public static final String ALL_COUNTRIES = "all-countries";
public static final String BY_NAME = "name";
public static final String BY_CAPITAL = "capital";
public static final String BY_LANGUAGE = "language";
public static final String BY_CURRENCY = "currency";
public static final String BY_REGION = "region";
public static final String BY_REGION_BLOC = "region-block";
public static final String BY_CALLING_CODE = "calling-code";
public static final String BY_ALPHA_CODE = "alpha-code";
}
Loading
Loading