[FEAT] some intrinsec upgrade and add basic test in back

This commit is contained in:
Edouard DUPIN 2025-03-30 23:34:45 +02:00
parent ba7b6e4755
commit 15dfdd8605
13 changed files with 209 additions and 66 deletions

View File

@ -4,12 +4,6 @@
<groupId>org.kar</groupId> <groupId>org.kar</groupId>
<artifactId>karusic</artifactId> <artifactId>karusic</artifactId>
<version>1.1.1-SNAPSHOT</version> <version>1.1.1-SNAPSHOT</version>
<properties>
<maven.compiler.version>3.13.0</maven.compiler.version>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
</properties>
<repositories> <repositories>
<repository> <repository>
<id>gitea</id> <id>gitea</id>
@ -20,7 +14,7 @@
<dependency> <dependency>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.25.4</version> <version>0.25.6</version>
</dependency> </dependency>
<!-- Loopback of logger JDK logging API to SLF4J --> <!-- Loopback of logger JDK logging API to SLF4J -->
<dependency> <dependency>
@ -47,7 +41,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.1</version> <version>2.18.3</version>
</dependency> </dependency>
<!-- <!--
************************************************************ ************************************************************
@ -57,24 +51,24 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version> <version>5.12.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version> <version>5.12.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version> <version>2.25.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version> <version>3.6.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
@ -95,10 +89,10 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version> <version>3.14.0</version>
<configuration> <configuration>
<source>${maven.compiler.source}</source> <source>21</source>
<target>${maven.compiler.target}</target> <target>21</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@ -157,7 +157,7 @@ public class TrackResource {
LOGGER.info(" > title: " + title); LOGGER.info(" > title: " + title);
LOGGER.info(" > fileInputStream: " + fileInputStream); LOGGER.info(" > fileInputStream: " + fileInputStream);
LOGGER.info(" > fileMetaData: " + fileMetaData); LOGGER.info(" > fileMetaData: " + fileMetaData);
/* if (typeId == null) { return Response.status(406). entity("Missong Input 'type'"). type("text/plain"). build(); } */ /* if (typeId == null) { return Response.status(406). entity("Missing Input 'type'"). type("text/plain"). build(); } */
final long tmpUID = DataTools.getTmpDataId(); final long tmpUID = DataTools.getTmpDataId();
final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID); final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -2,9 +2,10 @@ package test.kar.karusic;
import java.util.Map; import java.util.Map;
import org.kar.archidata.filter.PartRight;
import org.kar.archidata.tools.JWTWrapper; import org.kar.archidata.tools.JWTWrapper;
public class Common { public class Common {
static String USER_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_user_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", Boolean.TRUE))); static String USER_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_user_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", PartRight.READ)));
static String ADMIN_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_admin_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", Boolean.TRUE, "ADMIN", Boolean.TRUE))); static String ADMIN_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_admin_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", PartRight.READ_WRITE, "ADMIN", PartRight.READ_WRITE)));
} }

View File

@ -0,0 +1,15 @@
package test.kar.karusic;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
public class ResourceUtils {
public static File getResourceFile(final String resourcePath) throws IOException {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final URL resource = Objects.requireNonNull(classLoader.getResource(resourcePath), "Fichier non trouvé : " + resourcePath);
return new File(resource.getFile());
}
}

View File

@ -1,21 +1,24 @@
package test.kar.karusic; package test.kar.karusic;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.exception.RESTErrorResponseException;
import org.kar.archidata.tools.ConfigBaseVariable; import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.RESTApi; import org.kar.archidata.tools.RESTApi;
import org.kar.karusic.api.HealthCheck.HealthResult;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ExtendWith(StepwiseExtension.class) @ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestBase { public class TestHealthCheck {
private final static Logger LOGGER = LoggerFactory.getLogger(TestBase.class); private final static Logger LOGGER = LoggerFactory.getLogger(TestHealthCheck.class);
public final static String ENDPOINT_NAME = "species/";
static WebLauncherTest webInterface = null; static WebLauncherTest webInterface = null;
static RESTApi api = null; static RESTApi api = null;
@ -40,9 +43,17 @@ public class TestBase {
ConfigureDb.clear(); ConfigureDb.clear();
} }
@Order(1)
@Test @Test
public static void TestEmpty() throws Exception { public void checkHealthCheck() throws Exception {
final HealthResult result = api.get(HealthResult.class, "health_check");
Assertions.assertEquals(result.value(), "alive and kicking");
}
@Order(2)
@Test
public void checkHealthCheckWrongAPI() throws Exception {
Assertions.assertThrows(RESTErrorResponseException.class, () -> api.get(HealthResult.class, "health_checks"));
} }
} }

View File

@ -0,0 +1,116 @@
package test.kar.karusic;
import java.io.File;
import java.io.IOException;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.RESTApi;
import org.kar.karusic.model.Track;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestTrack {
private final static Logger LOGGER = LoggerFactory.getLogger(TestTrack.class);
public final static String ENDPOINT_NAME = "track/";
public final static String ENDPOINT_DATA_NAME = "data/";
static WebLauncherTest webInterface = null;
static RESTApi api = null;
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();
LOGGER.info("configure server ...");
webInterface = new WebLauncherTest();
LOGGER.info("Create BDD");
webInterface.migrateDB();
LOGGER.info("Start REST (BEGIN)");
webInterface.process();
LOGGER.info("Start REST (DONE)");
api = new RESTApi(ConfigBaseVariable.apiAdress);
api.setToken(Common.ADMIN_TOKEN);
}
@AfterAll
public static void stopWebServer() throws Exception {
LOGGER.info("Kill the web server");
webInterface.stop();
webInterface = null;
ConfigureDb.clear();
}
public static boolean compareResponseWithFile(final byte[] responseBody, final File file) throws IOException {
final byte[] fileBytes = Files.readAllBytes(file.toPath());
if (responseBody == null && fileBytes == null) {
return true;
}
if (responseBody == null || fileBytes == null) {
return false;
}
if (responseBody.length != fileBytes.length) {
LOGGER.error("The data have not the same size: {} != {}(ref)", responseBody.length, fileBytes.length);
return false;
}
for (int iii = 0; iii < responseBody.length; iii++) {
if (responseBody[iii] != fileBytes[iii]) {
LOGGER.error("Detect error at the {}/{} element", iii, responseBody.length);
return false;
}
}
return true;
}
@Order(1)
@Test
public void createTrack() throws Exception {
final File dataToUpload = ResourceUtils.getResourceFile("icon-192x192.png");
final Track data = new Track();
data.name = "test track";
data.description = "My track description";
final Map<String, Object> multipart = new HashMap<>();
multipart.put("title", data.name);
multipart.put("genderId", null);
multipart.put("artistId", null);
multipart.put("albumId", null);
multipart.put("trackId", 9);
multipart.put("file", dataToUpload);
final Track inserted = api.postMultipart(Track.class, TestTrack.ENDPOINT_NAME + "upload", multipart);
Assertions.assertNotNull(inserted);
Assertions.assertNotNull(inserted.id);
Assertions.assertTrue(inserted.id >= 0);
Assertions.assertNull(inserted.updatedAt);
Assertions.assertNull(inserted.createdAt);
Assertions.assertNull(inserted.deleted);
Assertions.assertNotNull(inserted.name);
Assertions.assertEquals(data.name, inserted.name);
Assertions.assertNotNull(inserted.dataId);
// Retrieve Data:
final HttpResponse<byte[]> retreiveData = api.getRaw(TestTrack.ENDPOINT_DATA_NAME + inserted.dataId);
Assertions.assertNotNull(retreiveData);
Assertions.assertEquals(200, retreiveData.statusCode());
Assertions.assertEquals("11999", retreiveData.headers().firstValue("content-length").orElse(null));
final String contentType = retreiveData.headers().firstValue("content-type").orElse(null);
Assertions.assertEquals("image/png", contentType);
Assertions.assertTrue(compareResponseWithFile(retreiveData.body(), dataToUpload));
}
}

View File

@ -5,7 +5,7 @@ export const EmptyEnd = () => {
<Box <Box
width="full" width="full"
height="25%" height="25%"
minHeight="250px" minHeight="calc(max(50vh,500px))"
// borderWidth="1px" // borderWidth="1px"
// borderColor="red" // borderColor="red"
></Box> ></Box>

View File

@ -171,7 +171,7 @@ export const TopBar = ({
<LuAlignJustify /> <LuAlignJustify />
{isVisible && ( {isVisible && (
<Text paddingLeft="3px" fontWeight="bold"> <Text paddingLeft="3px" fontWeight="bold">
Karusic {environment.applName}
</Text> </Text>
)} )}
</HStack> </HStack>
@ -303,7 +303,7 @@ export const TopBar = ({
> >
<HStack {...BUTTON_TOP_BAR_PROPERTY} cursor="pointer"> <HStack {...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
<LuArrowBigLeft /> <LuArrowBigLeft />
<Span paddingLeft="3px">Karusic</Span> <Span paddingLeft="3px">{environment.applName}</Span>
</HStack> </HStack>
</DrawerHeader> </DrawerHeader>
<DrawerBody paddingX="0px"> <DrawerBody paddingX="0px">

View File

@ -48,39 +48,6 @@ const environment_local: Environment = {
replaceDataToRealServer: true, replaceDataToRealServer: true,
}; };
const environment_full_local: Environment = {
production: false,
// URL of development API
applName: 'karusic',
defaultServer: 'karusic',
server: {
karusic: 'http://localhost:19080/karusic/api',
karso: 'http://localhost:15080/karso/api',
},
ssoSite: `${serverSSOAddress}/karso/`,
ssoSignIn: 'http://localhost:4200/signin/karusic-dev/',
ssoSignUp: 'http://localhost:4200/signup/karusic-dev/',
ssoSignOut: 'http://localhost:4200/signout/karusic-dev/',
tokenStoredInPermanentStorage: false,
};
const environment_hybrid: Environment = {
production: false,
// URL of development API
applName: 'karusic',
defaultServer: 'karusic',
server: {
karusic: `${serverSSOAddress}/karusic/api`,
karso: `${serverSSOAddress}/karso/api`,
},
ssoSite: `${serverSSOAddress}/karso/`,
ssoSignIn: 'http://localhost:4200/signin/karusic-dev/',
ssoSignUp: 'http://localhost:4200/signup/karusic-dev/',
ssoSignOut: 'http://localhost:4200/signout/karusic-dev/',
tokenStoredInPermanentStorage: false,
};
/** /**
* Check if the current environment is for development * Check if the current environment is for development
* @returns true if development is active. * @returns true if development is active.

View File

@ -37,7 +37,7 @@ export const USERS = {
export const getUserToken = () => { export const getUserToken = () => {
return localStorage.getItem(TOKEN_KEY); return localStorage.getItem(TOKEN_KEY);
}; };
export type RightPart = 'ADMIN' | 'USER'; export type RightGroup = 'ADMIN' | 'USER';
export function getRestConfig(): RESTConfig { export function getRestConfig(): RESTConfig {
return { return {
@ -86,8 +86,8 @@ export type SessionServiceProps = {
setToken: (token: string) => void; setToken: (token: string) => void;
clearToken: () => void; clearToken: () => void;
login?: string; login?: string;
hasReadRight: (part: RightPart) => boolean; hasReadRight: (part: RightGroup) => boolean;
hasWriteRight: (part: RightPart) => boolean; hasWriteRight: (part: RightGroup) => boolean;
getRestConfig: () => RESTConfig; getRestConfig: () => RESTConfig;
}; };
@ -214,7 +214,7 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
}, [clearToken]); }, [clearToken]);
const hasReadRight = useCallback( const hasReadRight = useCallback(
(part: RightPart) => { (part: RightGroup) => {
//console.log(`right = ${JSON.stringify(token?.payload?.right?.[environment.applName], null, 2)}`); //console.log(`right = ${JSON.stringify(token?.payload?.right?.[environment.applName], null, 2)}`);
const right = token?.payload?.right?.[environment.applName]; const right = token?.payload?.right?.[environment.applName];
if (right === undefined) { if (right === undefined) {
@ -225,7 +225,7 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
[token] [token]
); );
const hasWriteRight = useCallback( const hasWriteRight = useCallback(
(part: RightPart) => { (part: RightGroup) => {
const right = token?.payload?.right?.[environment.applName]; const right = token?.payload?.right?.[environment.applName];
if (right === undefined) { if (right === undefined) {
return false; return false;
@ -248,7 +248,7 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
}; };
}; };
export const useHasRight = (part: RightPart) => { export const useHasRight = (part: RightGroup) => {
const { token, hasReadRight, hasWriteRight } = useSessionService(); const { token, hasReadRight, hasWriteRight } = useSessionService();
const isReadable = useMemo(() => { const isReadable = useMemo(() => {
//console.log(`get is read for: ${part} ==> ${hasReadRight(part)}`); //console.log(`get is read for: ${part} ==> ${hasReadRight(part)}`);

View File

@ -0,0 +1,39 @@
import { useCallback, useEffect, useRef, useState } from 'react';
export const useDebouncedCallback = (
callback: (...args: any[]) => void,
delay: number = 2000
) => {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const debouncedFunction = useCallback(
(...args: any[]) => {
// Cancel timeout if it previously exist
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// start a new timer
timeoutRef.current = setTimeout(() => {
callback(...args);
}, delay);
},
[callback, delay]
);
return debouncedFunction;
};
export const useDebounce = <T>(value: T, delay: number = 2000): T => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
};

View File

@ -51,11 +51,11 @@ export type useQueryCallProps<RETURN_TYPE, PARAMETER_TYPE> = {
onSuccess?: (data: RETURN_TYPE) => Promise<RETURN_TYPE>; onSuccess?: (data: RETURN_TYPE) => Promise<RETURN_TYPE>;
onFail?: (error: RestErrorResponse) => void; onFail?: (error: RestErrorResponse) => void;
}; };
export const useQueryCall = <TYPE, PARAMETERS>({ export const useQueryCall = <RETURN_TYPE, PARAMETERS>({
queryFunction, queryFunction,
onSuccess, onSuccess,
onFail, onFail,
}: useQueryCallProps<TYPE, PARAMETERS>) => { }: useQueryCallProps<RETURN_TYPE, PARAMETERS>) => {
const { clearToken } = useSessionService(); const { clearToken } = useSessionService();
const [isCalling, setIsCalling] = useState<boolean>(true); const [isCalling, setIsCalling] = useState<boolean>(true);
const [error, setError] = useState<RestErrorResponse | undefined>(undefined); const [error, setError] = useState<RestErrorResponse | undefined>(undefined);