diff --git a/back/.project b/back/.project
index 182e0e3..bdecf66 100644
--- a/back/.project
+++ b/back/.project
@@ -15,10 +15,16 @@
+
+ edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder
+
+
+
org.eclipse.jdt.core.javanature
org.eclipse.m2e.core.maven2Nature
+ edu.umd.cs.findbugs.plugin.eclipse.findbugsNature
diff --git a/back/pom.xml b/back/pom.xml
index bc40d14..e93e74b 100644
--- a/back/pom.xml
+++ b/back/pom.xml
@@ -20,7 +20,7 @@
kangaroo-and-rabbit
archidata
- 0.10.2
+ 0.11.0
org.slf4j
@@ -40,13 +40,13 @@
org.junit.jupiter
junit-jupiter-api
- 5.11.0-M1
+ 5.11.0-M2
test
org.junit.jupiter
junit-jupiter-engine
- 5.11.0-M1
+ 5.11.0-M2
test
diff --git a/back/src/org/kar/karso/WebLauncher.java b/back/src/org/kar/karso/WebLauncher.java
index 7cea58c..0a1633e 100755
--- a/back/src/org/kar/karso/WebLauncher.java
+++ b/back/src/org/kar/karso/WebLauncher.java
@@ -12,11 +12,7 @@ import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.backup.BackupEngine;
import org.kar.archidata.backup.BackupEngine.StoreMode;
-import org.kar.archidata.catcher.ExceptionCatcher;
-import org.kar.archidata.catcher.FailException404API;
-import org.kar.archidata.catcher.FailExceptionCatcher;
-import org.kar.archidata.catcher.InputExceptionCatcher;
-import org.kar.archidata.catcher.SystemExceptionCatcher;
+import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
@@ -121,11 +117,7 @@ public class WebLauncher {
// global authentication system
rc.register(KarsoAuthenticationFilter.class);
// register exception catcher
- rc.register(InputExceptionCatcher.class);
- rc.register(SystemExceptionCatcher.class);
- rc.register(FailExceptionCatcher.class);
- rc.register(FailException404API.class);
- rc.register(ExceptionCatcher.class);
+ GenericCatcher.addAll(rc);
// add default resource:
rc.register(DataResource.class);
rc.register(ApplicationResource.class);
diff --git a/back/src/org/kar/karso/api/UserResource.java b/back/src/org/kar/karso/api/UserResource.java
index 619bb02..1810680 100755
--- a/back/src/org/kar/karso/api/UserResource.java
+++ b/back/src/org/kar/karso/api/UserResource.java
@@ -52,7 +52,7 @@ public class UserResource {
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
@JsonInclude(JsonInclude.Include.NON_NULL)
- public class UserOut {
+ public static class UserOut {
public long id;
public String login;
@@ -276,8 +276,9 @@ public class UserResource {
"FAIL Authentiocate-wrong email/login '" + login + "')");
}
// Check the password:
- final String passwodCheck = getSHA512("login='" + login + "';pass='" + user.password + "';date='" + time + "'");
- if (!passwodCheck.contentEquals(password)) {
+ final String passwordCheck = getSHA512(
+ "login='" + login + "';pass='" + user.password + "';date='" + time + "'");
+ if (!passwordCheck.contentEquals(password)) {
throw new FailException(Response.Status.PRECONDITION_FAILED, "Password error ...");
}
LOGGER.debug(" ==> pass nearly all test : admin={} blocked={} removed={}", user.admin, user.blocked,
diff --git a/back/src/org/kar/karso/migration/Initialization.java b/back/src/org/kar/karso/migration/Initialization.java
index ced1e88..7202ebd 100644
--- a/back/src/org/kar/karso/migration/Initialization.java
+++ b/back/src/org/kar/karso/migration/Initialization.java
@@ -2,20 +2,25 @@ package org.kar.karso.migration;
import java.util.List;
+import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.migration.MigrationSqlStep;
+import org.kar.archidata.tools.UuidUtils;
import org.kar.karso.model.Application;
import org.kar.karso.model.ApplicationToken;
import org.kar.karso.model.Right;
import org.kar.karso.model.RightDescription;
import org.kar.karso.model.Settings;
import org.kar.karso.model.UserAuth;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class Initialization extends MigrationSqlStep {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Initialization.class);
public static final int KARSO_INITIALISATION_ID = 1;
- public static final List> CLASSES_BASE = List.of(Settings.class, UserAuth.class, Application.class,
- ApplicationToken.class, RightDescription.class, Right.class);
+ public static final List> LIST_OF_COMMON_CLASSES = List.of(Settings.class, UserAuth.class,
+ Application.class, ApplicationToken.class, RightDescription.class, Right.class);
@Override
public String getName() {
@@ -28,7 +33,7 @@ public class Initialization extends MigrationSqlStep {
@Override
public void generateStep() throws Exception {
- for (final Class> clazz : CLASSES_BASE) {
+ for (final Class> clazz : LIST_OF_COMMON_CLASSES) {
addClass(clazz);
}
@@ -44,10 +49,15 @@ public class Initialization extends MigrationSqlStep {
(1, 'karadmin', '0ddcac5ede3f1300a1ce5948ab15112f2810130531d578ab8bc4dc131652d7cf7a3ff6e827eb957bff43bc2c65a6a1d46722e5b3a2343ac3176a33ea7250080b',
'admin@admin.ZZZ', 1);
""");
+ final String data = UuidUtils.nextUUID().toString();
addAction("""
- INSERT INTO `user_link_application` (`object1Id`, `object2Id`)
- VALUES ('1', '1');
- """);
+ INSERT INTO `user_link_application` (`uuid`, `object1Id`, `object2Id`)
+ VALUES (UUID_TO_BIN('%s'), '1', '1');
+ """.formatted(data), "mysql");
+ addAction("""
+ INSERT INTO `user_link_application` (`uuid`, `object1Id`, `object2Id`)
+ VALUES ('%s', '1', '1');
+ """.formatted(data), "sqlite");
addAction("""
INSERT INTO `settings` (`key`, `right`, `type`, `value`) VALUES
('SIGN_UP_ENABLE', 'rwr-r-', 'BOOLEAN', 'false'),
@@ -83,4 +93,25 @@ public class Initialization extends MigrationSqlStep {
display();
}
+ public static void dropAll() {
+ for (final Class> element : LIST_OF_COMMON_CLASSES) {
+ try {
+ DataAccess.drop(element);
+ } catch (final Exception ex) {
+ LOGGER.error("Fail to drop table !!!!!!");
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ public static void cleanAll() {
+ for (final Class> element : LIST_OF_COMMON_CLASSES) {
+ try {
+ DataAccess.cleanAll(element);
+ } catch (final Exception ex) {
+ LOGGER.error("Fail to clean table !!!!!!");
+ ex.printStackTrace();
+ }
+ }
+ }
}
diff --git a/back/src/org/kar/karso/migration/Migration20231015.java b/back/src/org/kar/karso/migration/Migration20231015.java
index 2c244a8..0813335 100644
--- a/back/src/org/kar/karso/migration/Migration20231015.java
+++ b/back/src/org/kar/karso/migration/Migration20231015.java
@@ -18,7 +18,7 @@ public class Migration20231015 extends MigrationSqlStep {
}
@Override
- public void generateStep() throws Exception {
+ public void generateStep() {
for (final String elem : List.of("application", "applicationToken", "right", "rightDescription", "settings",
"user", "user_link_application")) { //, "user_link_cover")) {
@@ -30,7 +30,6 @@ public class Migration20231015 extends MigrationSqlStep {
RENAME COLUMN `modify_date` TO `updatedAt`;
""");
}
- display();
}
}
diff --git a/back/src/org/kar/karso/migration/Migration20231126.java b/back/src/org/kar/karso/migration/Migration20231126.java
index 44a0e2c..e4cf1db 100644
--- a/back/src/org/kar/karso/migration/Migration20231126.java
+++ b/back/src/org/kar/karso/migration/Migration20231126.java
@@ -16,7 +16,7 @@ public class Migration20231126 extends MigrationSqlStep {
}
@Override
- public void generateStep() throws Exception {
+ public void generateStep() {
// update migration update (last one)
addAction(
"""
diff --git a/back/src/org/kar/karso/migration/Migration20240515.java b/back/src/org/kar/karso/migration/Migration20240515.java
index 5397ff3..156b6d4 100644
--- a/back/src/org/kar/karso/migration/Migration20240515.java
+++ b/back/src/org/kar/karso/migration/Migration20240515.java
@@ -23,7 +23,7 @@ public class Migration20240515 extends MigrationSqlStep {
}
@Override
- public void generateStep() throws Exception {
+ public void generateStep() {
// update migration update (last one)
addAction("""
ALTER TABLE `user_link_application` ADD `uuid` binary(16) AFTER `id`;
@@ -35,9 +35,13 @@ public class Migration20240515 extends MigrationSqlStep {
elem.uuid = UuidUtils.nextUUID();
}
for (final UUIDConversion elem : datas) {
- DataAccess.update(elem, elem.id, List.of("uuid"), new OverrideTableName("data"));
+ DataAccess.update(elem, elem.id, List.of("uuid"), new OverrideTableName("user_link_application"));
}
});
+ addAction("""
+ ALTER TABLE `user_link_application`
+ CHANGE `id` `id` bigint;
+ """);
addAction("""
ALTER TABLE `user_link_application` DROP PRIMARY KEY;
""");
@@ -52,7 +56,7 @@ public class Migration20240515 extends MigrationSqlStep {
addAction("""
ALTER TABLE `user_link_application`
- ADD `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Deleted state' AFTER `uuid`;
+ DROP `id`;
""");
addAction("""
diff --git a/back/test/src/test/kar/karso/TestUsers.java b/back/test/src/test/kar/karso/TestUsers.java
new file mode 100644
index 0000000..afbc19f
--- /dev/null
+++ b/back/test/src/test/kar/karso/TestUsers.java
@@ -0,0 +1,89 @@
+package test.kar.karso;
+
+import java.io.IOException;
+import java.util.List;
+
+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.db.DBEntry;
+import org.kar.archidata.exception.RESTErrorResponseExeption;
+import org.kar.archidata.model.GetToken;
+import org.kar.archidata.tools.ConfigBaseVariable;
+import org.kar.archidata.tools.RESTApi;
+import org.kar.karso.migration.Initialization;
+import org.kar.karso.model.DataGetToken;
+import org.kar.karso.model.UserAuthGet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ExtendWith(StepwiseExtension.class)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class TestUsers {
+ private final static Logger LOGGER = LoggerFactory.getLogger(TestUsers.class);
+ public final static String ENDPOINT_NAME = "users/";
+
+ static WebLauncherTest webInterface = null;
+ static RESTApi api = null;
+
+ private static long idTest;
+
+ @BeforeAll
+ public static void configureWebServer() throws InterruptedException, RESTErrorResponseExeption, IOException {
+ LOGGER.info("configure server ...");
+ webInterface = new WebLauncherTest();
+ LOGGER.info("Clean previous table");
+
+ try {
+ Initialization.dropAll();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ LOGGER.error("plop: {}", ex.getLocalizedMessage());
+ throw ex;
+ }
+
+ LOGGER.info("Create DB");
+ try {
+ webInterface.migrateDB();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ LOGGER.error("Detect an error: {}", ex.getMessage());
+ }
+ LOGGER.info("Start REST (BEGIN)");
+ webInterface.process();
+ LOGGER.info("Start REST (DONE)");
+ api = new RESTApi(ConfigBaseVariable.apiAdress);
+ final GetToken result = api.post(GetToken.class, "users/get_token",
+ DataGetToken.generate("karadmin", "v1", "202515252", "adminA@666"));
+ api.setToken(result.jwt);
+ }
+
+ @AfterAll
+ public static void stopWebServer() throws IOException {
+ LOGGER.info("Kill the web server");
+ webInterface.stop();
+ webInterface = null;
+ LOGGER.info("Remove the test db");
+ DBEntry.closeAllForceMode();
+ ConfigBaseVariable.clearAllValue();
+ }
+
+ @Order(1)
+ @Test
+ public void getsValue() throws RESTErrorResponseExeption, IOException, InterruptedException {
+ final List listUsers = api.gets(UserAuthGet.class, TestUsers.ENDPOINT_NAME);
+ Assertions.assertNotNull(listUsers);
+ Assertions.assertEquals(1, listUsers.size());
+ Assertions.assertEquals(1, listUsers.get(0).id);
+ Assertions.assertEquals(true, listUsers.get(0).admin);
+ Assertions.assertEquals(false, listUsers.get(0).blocked);
+ Assertions.assertEquals(false, listUsers.get(0).avatar);
+ Assertions.assertEquals("karadmin", listUsers.get(0).login);
+ Assertions.assertEquals("admin@admin.ZZZ", listUsers.get(0).email);
+ }
+}
diff --git a/front/package.json b/front/package.json
index 94804a1..188ce5c 100644
--- a/front/package.json
+++ b/front/package.json
@@ -21,31 +21,31 @@
},
"private": true,
"dependencies": {
- "@angular/animations": "^18.0.0",
- "@angular/cdk": "^18.0.0",
- "@angular/common": "^18.0.0",
- "@angular/compiler": "^18.0.0",
- "@angular/core": "^18.0.0",
- "@angular/forms": "^18.0.0",
- "@angular/material": "^18.0.0",
- "@angular/platform-browser": "^18.0.0",
- "@angular/platform-browser-dynamic": "^18.0.0",
- "@angular/router": "^18.0.0",
+ "@angular/animations": "^18.0.1",
+ "@angular/cdk": "^18.0.1",
+ "@angular/common": "^18.0.1",
+ "@angular/compiler": "^18.0.1",
+ "@angular/core": "^18.0.1",
+ "@angular/forms": "^18.0.1",
+ "@angular/material": "^18.0.1",
+ "@angular/platform-browser": "^18.0.1",
+ "@angular/platform-browser-dynamic": "^18.0.1",
+ "@angular/router": "^18.0.1",
"rxjs": "^7.8.1",
"zone.js": "^0.14.6",
"zod": "3.23.8",
"@kangaroo-and-rabbit/kar-cw": "^0.4.0"
},
"devDependencies": {
- "@angular-devkit/build-angular": "^18.0.1",
- "@angular-eslint/builder": "17.5.2",
- "@angular-eslint/eslint-plugin": "17.5.2",
- "@angular-eslint/eslint-plugin-template": "17.5.2",
- "@angular-eslint/schematics": "17.5.2",
- "@angular-eslint/template-parser": "17.5.2",
- "@angular/cli": "^18.0.1",
- "@angular/compiler-cli": "^18.0.0",
- "@angular/language-service": "^18.0.0",
+ "@angular-devkit/build-angular": "^18.0.2",
+ "@angular-eslint/builder": "18.0.1",
+ "@angular-eslint/eslint-plugin": "18.0.1",
+ "@angular-eslint/eslint-plugin-template": "18.0.1",
+ "@angular-eslint/schematics": "18.0.1",
+ "@angular-eslint/template-parser": "18.0.1",
+ "@angular/cli": "^18.0.2",
+ "@angular/compiler-cli": "^18.0.1",
+ "@angular/language-service": "^18.0.1",
"@playwright/test": "^1.44.1",
"@types/jest": "^29.5.12",
"jasmine": "^5.1.0",
@@ -57,7 +57,7 @@
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.1.0",
"karma-spec-reporter": "^0.0.36",
- "prettier": "^3.2.5",
+ "prettier": "^3.3.0",
"npm-check-updates": "^16.14.20",
"tslib": "^2.6.2"
}
diff --git a/front/src/assets/images/ikon_red.svg b/front/src/assets/images/ikon_red.svg
index cc0fb69..0b1243e 100644
--- a/front/src/assets/images/ikon_red.svg
+++ b/front/src/assets/images/ikon_red.svg
@@ -6,7 +6,7 @@
version="1.1"
id="svg18"
sodipodi:docname="ikon_red.svg"
- inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
@@ -72,9 +72,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="5.6"
- inkscape:cx="42.410714"
- inkscape:cy="110.625"
+ inkscape:zoom="7.9195959"
+ inkscape:cx="89.966711"
+ inkscape:cy="177.91312"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
@@ -85,10 +85,18 @@
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="1"
- inkscape:pagecheckerboard="0">
+ inkscape:pagecheckerboard="0"
+ inkscape:showpageshadow="2"
+ inkscape:deskcolor="#d1d1d1">
+ id="grid4504"
+ originx="0"
+ originy="0"
+ spacingy="1"
+ spacingx="1"
+ units="px"
+ visible="true" />
@@ -114,11 +122,9 @@
transform="matrix(0.8407653,0,0,0.83753055,-37.28971,3.4402954)"
aria-label="K">
+ d="M 65.200545 279.95309 L 65.200545 341.55532 L 74.14964 341.55532 L 74.14964 321.02125 L 80.541851 314.17676 L 99.718483 341.55532 L 106.11069 336.07998 L 85.655619 308.7008 L 106.11069 286.7982 L 99.718483 279.95309 L 74.14964 307.33227 L 74.14964 279.95309 L 65.200545 279.95309 z M 69.586585 307.95792 C 71.270821 307.95521 72.163105 308.76519 72.164982 309.94037 C 72.166858 311.11555 71.276967 311.92813 69.592731 311.93085 C 67.946259 311.9335 67.061695 311.12357 67.059818 309.94839 C 67.057941 308.77322 67.940113 307.96057 69.586585 307.95792 z M 69.588429 309.10309 C 68.568824 309.10473 68.017826 309.4316 68.01865 309.94716 C 68.019473 310.46272 68.571283 310.78793 69.590887 310.78629 C 70.655808 310.78458 71.206973 310.45717 71.20615 309.94161 C 71.205327 309.42604 70.653349 309.10138 69.588429 309.10309 z M 70.651134 317.13779 C 71.466818 317.13648 72.177771 317.82535 72.179733 319.0536 C 72.180798 319.7208 71.940317 320.4034 71.472902 320.93487 L 70.70891 320.29194 C 71.010421 319.91995 71.221049 319.45722 71.220287 318.97956 C 71.219524 318.50191 71.038049 318.28194 70.788812 318.28234 C 70.441392 318.2829 70.351355 318.5789 70.155738 319.04928 L 69.884683 319.68666 C 69.681646 320.24045 69.259204 320.7412 68.541704 320.74236 C 67.726021 320.74367 67.075662 320.0017 67.073955 318.93267 C 67.072998 318.33371 67.298319 317.73449 67.720551 317.28649 L 68.424309 317.85414 C 68.175617 318.19572 68.032626 318.50667 68.033401 318.9919 C 68.033994 319.36341 68.192626 319.61369 68.479626 319.61323 C 68.774179 319.61276 68.886895 319.27856 69.059842 318.80063 L 69.315531 318.20151 C 69.556173 317.54909 69.956292 317.13891 70.651134 317.13779 z M 72.09983 325.98324 L 72.102289 327.23453 L 70.32845 328.18534 L 70.32968 328.76904 L 72.104747 328.76595 L 72.106591 329.88027 L 67.18213 329.88829 L 67.179057 328.13722 C 67.177386 327.09093 67.538843 326.22575 68.7095 326.22387 C 69.434552 326.2227 69.895571 326.57849 70.130538 327.10126 L 72.09983 325.98324 z M 68.711344 327.32338 C 68.227976 327.32416 68.061959 327.6353 68.062903 328.22668 L 68.064133 328.77274 L 69.445833 328.77027 L 69.445219 328.22422 C 69.444274 327.63284 69.194712 327.3226 68.711344 327.32338 z M 72.114581 335.0286 L 72.116425 336.2114 L 70.946159 336.5088 L 70.948618 338.00258 L 72.119499 338.30368 L 72.121342 339.44145 L 67.195038 337.91003 L 67.192579 336.57544 L 72.114581 335.0286 z M 70.078294 336.73771 L 69.625307 336.85248 C 69.134592 336.98216 68.560497 337.13471 68.039547 337.24921 L 68.039547 337.27945 C 68.560872 337.39992 69.13541 337.53513 69.626536 337.66323 L 70.079523 337.77614 L 70.078294 336.73771 z " />
void;
export interface RESTAbort {
- abort?: () => boolean;
+ abort?: () => boolean;
}
// Rest generic callback have a basic model to upload and download advancement.
export interface RESTCallbacks {
- progressUpload?: ProgressCallback;
- progressDownload?: ProgressCallback;
- abortHandle?: RESTAbort;
+ progressUpload?: ProgressCallback;
+ progressDownload?: ProgressCallback;
+ abortHandle?: RESTAbort;
}
export interface RESTRequestType {
- restModel: RESTModel;
- restConfig: RESTConfig;
- data?: any;
- params?: object;
- queries?: object;
- callback?: RESTCallbacks;
+ restModel: RESTModel;
+ restConfig: RESTConfig;
+ data?: any;
+ params?: object;
+ queries?: object;
+ callback?: RESTCallbacks;
}
function replaceAll(input, searchValue, replaceValue) {
- return input.split(searchValue).join(replaceValue);
+ return input.split(searchValue).join(replaceValue);
}
function removeTrailingSlashes(input: string): string {
- if (isNullOrUndefined(input)) {
- return "undefined";
- }
- return input.replace(/\/+$/, "");
+ if (isNullOrUndefined(input)) {
+ return "undefined";
+ }
+ return input.replace(/\/+$/, "");
}
function removeLeadingSlashes(input: string): string {
- if (isNullOrUndefined(input)) {
- return "";
- }
- return input.replace(/^\/+/, "");
+ if (isNullOrUndefined(input)) {
+ return "";
+ }
+ return input.replace(/^\/+/, "");
}
export function RESTUrl({
- restModel,
- restConfig,
- params,
- queries,
+ restModel,
+ restConfig,
+ params,
+ queries,
}: RESTRequestType): string {
- // Create the URL PATH:
- let generateUrl = `${removeTrailingSlashes(
- restConfig.server
- )}/${removeLeadingSlashes(restModel.endPoint)}`;
- if (params !== undefined) {
- for (let key of Object.keys(params)) {
- generateUrl = replaceAll(generateUrl, `{${key}}`, `${params[key]}`);
+ // Create the URL PATH:
+ let generateUrl = `${removeTrailingSlashes(
+ restConfig.server
+ )}/${removeLeadingSlashes(restModel.endPoint)}`;
+ if (params !== undefined) {
+ for (let key of Object.keys(params)) {
+ generateUrl = replaceAll(generateUrl, `{${key}}`, `${params[key]}`);
+ }
+ }
+ if (
+ queries === undefined &&
+ (restConfig.token === undefined || restModel.tokenInUrl !== true)
+ ) {
+ return generateUrl;
+ }
+ const searchParams = new URLSearchParams();
+ if (queries !== undefined) {
+ for (let key of Object.keys(queries)) {
+ const value = queries[key];
+ if (Array.isArray(value)) {
+ for (const element of value) {
+ searchParams.append(`${key}`, `${element}`);
}
+ } else {
+ searchParams.append(`${key}`, `${value}`);
+ }
}
- if (
- queries === undefined &&
- (restConfig.token === undefined || restModel.tokenInUrl !== true)
- ) {
- return generateUrl;
- }
- const searchParams = new URLSearchParams();
- if (queries !== undefined) {
- for (let key of Object.keys(queries)) {
- const value = queries[key];
- if (Array.isArray(value)) {
- for (const element of value) {
- searchParams.append(`${key}`, `${element}`);
- }
- } else {
- searchParams.append(`${key}`, `${value}`);
- }
- }
- }
- if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
- searchParams.append("Authorization", `Bearer ${restConfig.token}`);
- }
- return generateUrl + "?" + searchParams.toString();
+ }
+ if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
+ searchParams.append("Authorization", `Bearer ${restConfig.token}`);
+ }
+ return generateUrl + "?" + searchParams.toString();
}
export function fetchProgress(
- generateUrl: string,
- {
- method,
- headers,
- body,
- }: {
- method: HTTPRequestModel;
- headers: any;
- body: any;
- },
- { progressUpload, progressDownload, abortHandle }: RESTCallbacks
+ generateUrl: string,
+ {
+ method,
+ headers,
+ body,
+ }: {
+ method: HTTPRequestModel;
+ headers: any;
+ body: any;
+ },
+ { progressUpload, progressDownload, abortHandle }: RESTCallbacks
): Promise {
- const xhr: {
- io?: XMLHttpRequest;
- } = {
- io: new XMLHttpRequest(),
- };
- return new Promise((resolve, reject) => {
- // Stream the upload progress
- if (progressUpload) {
- xhr.io?.upload.addEventListener("progress", (dataEvent) => {
- if (dataEvent.lengthComputable) {
- progressUpload(dataEvent.loaded, dataEvent.total);
- }
- });
+ const xhr: {
+ io?: XMLHttpRequest;
+ } = {
+ io: new XMLHttpRequest(),
+ };
+ return new Promise((resolve, reject) => {
+ // Stream the upload progress
+ if (progressUpload) {
+ xhr.io?.upload.addEventListener("progress", (dataEvent) => {
+ if (dataEvent.lengthComputable) {
+ progressUpload(dataEvent.loaded, dataEvent.total);
}
- // Stream the download progress
- if (progressDownload) {
- xhr.io?.addEventListener("progress", (dataEvent) => {
- if (dataEvent.lengthComputable) {
- progressDownload(dataEvent.loaded, dataEvent.total);
- }
- });
+ });
+ }
+ // Stream the download progress
+ if (progressDownload) {
+ xhr.io?.addEventListener("progress", (dataEvent) => {
+ if (dataEvent.lengthComputable) {
+ progressDownload(dataEvent.loaded, dataEvent.total);
}
- if (abortHandle) {
- abortHandle.abort = () => {
- if (xhr.io) {
- console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`);
- xhr.io.abort();
- return true;
- }
- console.log(
- `Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`
- );
- return false;
- };
+ });
+ }
+ if (abortHandle) {
+ abortHandle.abort = () => {
+ if (xhr.io) {
+ console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`);
+ xhr.io.abort();
+ return true;
}
- // Check if we have an internal Fail:
- xhr.io?.addEventListener("error", () => {
- xhr.io = undefined;
- reject(new TypeError("Failed to fetch"));
- });
-
- // Capture the end of the stream
- xhr.io?.addEventListener("loadend", () => {
- if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
- return;
- }
- if (xhr.io?.status === 0) {
- //the stream has been aborted
- reject(new TypeError("Fetch has been aborted"));
- return;
- }
- // Stream is ended, transform in a generic response:
- const response = new Response(xhr.io.response, {
- status: xhr.io.status,
- statusText: xhr.io.statusText,
- });
- const headersArray = replaceAll(
- xhr.io.getAllResponseHeaders().trim(),
- "\r\n",
- "\n"
- ).split("\n");
- headersArray.forEach(function (header) {
- const firstColonIndex = header.indexOf(":");
- if (firstColonIndex !== -1) {
- const key = header.substring(0, firstColonIndex).trim();
- const value = header.substring(firstColonIndex + 1).trim();
- response.headers.set(key, value);
- } else {
- response.headers.set(header, "");
- }
- });
- xhr.io = undefined;
- resolve(response);
- });
- xhr.io?.open(method, generateUrl, true);
- if (!isNullOrUndefined(headers)) {
- for (const [key, value] of Object.entries(headers)) {
- xhr.io?.setRequestHeader(key, value as string);
- }
- }
- xhr.io?.send(body);
+ console.log(
+ `Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`
+ );
+ return false;
+ };
+ }
+ // Check if we have an internal Fail:
+ xhr.io?.addEventListener("error", () => {
+ xhr.io = undefined;
+ reject(new TypeError("Failed to fetch"));
});
+
+ // Capture the end of the stream
+ xhr.io?.addEventListener("loadend", () => {
+ if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
+ return;
+ }
+ if (xhr.io?.status === 0) {
+ //the stream has been aborted
+ reject(new TypeError("Fetch has been aborted"));
+ return;
+ }
+ // Stream is ended, transform in a generic response:
+ const response = new Response(xhr.io.response, {
+ status: xhr.io.status,
+ statusText: xhr.io.statusText,
+ });
+ const headersArray = replaceAll(
+ xhr.io.getAllResponseHeaders().trim(),
+ "\r\n",
+ "\n"
+ ).split("\n");
+ headersArray.forEach(function (header) {
+ const firstColonIndex = header.indexOf(":");
+ if (firstColonIndex !== -1) {
+ const key = header.substring(0, firstColonIndex).trim();
+ const value = header.substring(firstColonIndex + 1).trim();
+ response.headers.set(key, value);
+ } else {
+ response.headers.set(header, "");
+ }
+ });
+ xhr.io = undefined;
+ resolve(response);
+ });
+ xhr.io?.open(method, generateUrl, true);
+ if (!isNullOrUndefined(headers)) {
+ for (const [key, value] of Object.entries(headers)) {
+ xhr.io?.setRequestHeader(key, value as string);
+ }
+ }
+ xhr.io?.send(body);
+ });
}
export function RESTRequest({
- restModel,
- restConfig,
- data,
- params,
- queries,
- callback,
+ restModel,
+ restConfig,
+ data,
+ params,
+ queries,
+ callback,
}: RESTRequestType): Promise {
- // Create the URL PATH:
- let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
- let headers: any = {};
- if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
- headers["Authorization"] = `Bearer ${restConfig.token}`;
+ // Create the URL PATH:
+ let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
+ let headers: any = {};
+ if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
+ headers["Authorization"] = `Bearer ${restConfig.token}`;
+ }
+ if (restModel.accept !== undefined) {
+ headers["Accept"] = restModel.accept;
+ }
+ if (restModel.requestType !== HTTPRequestModel.GET) {
+ // if Get we have not a content type, the body is empty
+ if (restModel.contentType !== HTTPMimeType.MULTIPART) {
+ // special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
+ headers["Content-Type"] = restModel.contentType;
}
- if (restModel.accept !== undefined) {
- headers["Accept"] = restModel.accept;
+ }
+ let body = data;
+ if (restModel.contentType === HTTPMimeType.JSON) {
+ body = JSON.stringify(data);
+ } else if (restModel.contentType === HTTPMimeType.MULTIPART) {
+ const formData = new FormData();
+ for (const name in data) {
+ formData.append(name, data[name]);
}
- if (restModel.requestType !== HTTPRequestModel.GET) {
- // if Get we have not a content type, the body is empty
- if (restModel.contentType !== HTTPMimeType.MULTIPART) {
- // special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
- headers["Content-Type"] = restModel.contentType;
- }
+ body = formData;
+ }
+ return new Promise((resolve, reject) => {
+ let action: undefined | Promise = undefined;
+ if (
+ isNullOrUndefined(callback) ||
+ (isNullOrUndefined(callback.progressDownload) &&
+ isNullOrUndefined(callback.progressUpload) &&
+ isNullOrUndefined(callback.abortHandle))
+ ) {
+ // No information needed: call the generic fetch interface
+ action = fetch(generateUrl, {
+ method: restModel.requestType,
+ headers,
+ body,
+ });
+ } else {
+ // need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x
+ action = fetchProgress(
+ generateUrl,
+ {
+ method: restModel.requestType ?? HTTPRequestModel.GET,
+ headers,
+ body,
+ },
+ callback
+ );
}
- let body = data;
- if (restModel.contentType === HTTPMimeType.JSON) {
- body = JSON.stringify(data);
- } else if (restModel.contentType === HTTPMimeType.MULTIPART) {
- const formData = new FormData();
- for (const name in data) {
- formData.append(name, data[name]);
- }
- body = formData;
- }
- return new Promise((resolve, reject) => {
- let action: undefined | Promise = undefined;
- if (
- isNullOrUndefined(callback) ||
- (isNullOrUndefined(callback.progressDownload) &&
- isNullOrUndefined(callback.progressUpload) &&
- isNullOrUndefined(callback.abortHandle))
- ) {
- // No information needed: call the generic fetch interface
- action = fetch(generateUrl, {
- method: restModel.requestType,
- headers,
- body,
- });
- } else {
- // need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x
- action = fetchProgress(
- generateUrl,
- {
- method: restModel.requestType ?? HTTPRequestModel.GET,
- headers,
- body,
- },
- callback
- );
- }
- action
- .then((response: Response) => {
- if (response.status >= 200 && response.status <= 299) {
- const contentType = response.headers.get("Content-Type");
- if (
- !isNullOrUndefined(restModel.accept) &&
- restModel.accept !== contentType
- ) {
- reject({
- name: "Model accept type incompatible",
- time: Date().toString(),
- status: 901,
- error: `REST check wrong type: ${restModel.accept} != ${contentType}`,
- statusMessage: "Fetch error",
- message: "rest-tools.ts Wrong type in the message return type",
- } as RestErrorResponse);
- } else if (contentType === HTTPMimeType.JSON) {
- response
- .json()
- .then((value: any) => {
- resolve({ status: response.status, data: value });
- })
- .catch((reason: any) => {
- reject({
- name: "API serialization error",
- time: Date().toString(),
- status: 902,
- error: `REST parse json fail: ${reason}`,
- statusMessage: "Fetch parse error",
- message: "rest-tools.ts Wrong message model to parse",
- } as RestErrorResponse);
- });
- } else {
- resolve({ status: response.status, data: response.body });
- }
- } else {
- reject({
- name: "REST return no OK status",
- time: Date().toString(),
- status: response.status,
- error: `${response.body}`,
- statusMessage: "Fetch code error",
- message: "rest-tools.ts Wrong return code",
- } as RestErrorResponse);
- }
- })
- .catch((error: any) => {
+ action
+ .then((response: Response) => {
+ if (response.status >= 200 && response.status <= 299) {
+ const contentType = response.headers.get("Content-Type");
+ if (
+ !isNullOrUndefined(restModel.accept) &&
+ restModel.accept !== contentType
+ ) {
+ reject({
+ name: "Model accept type incompatible",
+ time: Date().toString(),
+ status: 901,
+ message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
+ statusMessage: "Fetch error",
+ error: "rest-tools.ts Wrong type in the message return type",
+ } as RestErrorResponse);
+ } else if (contentType === HTTPMimeType.JSON) {
+ response
+ .json()
+ .then((value: any) => {
+ resolve({ status: response.status, data: value });
+ })
+ .catch((reason: Error) => {
reject({
- name: "Request fail",
- time: Date(),
- status: 999,
- error: error,
- statusMessage: "Fetch catch error",
- message: "rest-tools.ts detect an error in the fetch request",
+ name: "API serialization error",
+ time: Date().toString(),
+ status: 902,
+ message: `REST parse json fail: ${reason}`,
+ statusMessage: "Fetch parse error",
+ error: "rest-tools.ts Wrong message model to parse",
+ } as RestErrorResponse);
+ });
+ } else {
+ resolve({ status: response.status, data: response.body });
+ }
+ } else {
+ // the answer is not correct not a 2XX
+ // clone the response to keep the raw data if case of error:
+ response
+ .clone()
+ .json()
+ .then((value: any) => {
+ if (isRestErrorResponse(value)) {
+ reject(value);
+ } else {
+ response
+ .text()
+ .then((dataError: string) => {
+ reject({
+ name: "API serialization error",
+ time: Date().toString(),
+ status: 903,
+ message: `REST parse error json with wrong type fail. ${dataError}`,
+ statusMessage: "Fetch parse error",
+ error: "rest-tools.ts Wrong message model to parse",
+ } as RestErrorResponse);
+ })
+ .catch((reason: any) => {
+ reject({
+ name: "API serialization error",
+ time: Date().toString(),
+ status: response.status,
+ message: `unmanaged error model: ??? with error: ${reason}`,
+ statusMessage: "Fetch ERROR parse error",
+ error: "rest-tools.ts Wrong message model to parse",
+ } as RestErrorResponse);
+ });
+ }
+ })
+ .catch((reason: Error) => {
+ response
+ .text()
+ .then((dataError: string) => {
+ reject({
+ name: "API serialization error",
+ time: Date().toString(),
+ status: response.status,
+ message: `unmanaged error model: ${dataError} with error: ${reason}`,
+ statusMessage: "Fetch ERROR TEXT parse error",
+ error: "rest-tools.ts Wrong message model to parse",
+ } as RestErrorResponse);
+ })
+ .catch((reason: any) => {
+ reject({
+ name: "API serialization error",
+ time: Date().toString(),
+ status: response.status,
+ message: `unmanaged error model: ??? with error: ${reason}`,
+ statusMessage: "Fetch ERROR TEXT FAIL",
+ error: "rest-tools.ts Wrong message model to parse",
+ } as RestErrorResponse);
});
});
- });
+ }
+ })
+ .catch((error: Error) => {
+ if (isRestErrorResponse(error)) {
+ reject(error);
+ } else {
+ reject({
+ name: "Request fail",
+ time: Date(),
+ status: 999,
+ message: error,
+ statusMessage: "Fetch catch error",
+ error: "rest-tools.ts detect an error in the fetch request",
+ });
+ }
+ });
+ });
}
export function RESTRequestJson(
- request: RESTRequestType,
- checker?: (data: any) => data is TYPE
+ request: RESTRequestType,
+ checker?: (data: any) => data is TYPE
): Promise {
- return new Promise((resolve, reject) => {
- RESTRequest(request)
- .then((value: ModelResponseHttp) => {
- if (isNullOrUndefined(checker)) {
- console.log(`Have no check of MODEL in API: ${RESTUrl(request)}`);
- resolve(value.data);
- } else if (checker === undefined || checker(value.data)) {
- resolve(value.data);
- } else {
- reject({
- name: "Model check fail",
- time: Date().toString(),
- status: 950,
- error: "REST Fail to verify the data",
- statusMessage: "API cast ERROR",
- message: "api.ts Check type as fail",
- } as RestErrorResponse);
- }
- })
- .catch((reason: RestErrorResponse) => {
- reject(reason);
- });
- });
+ return new Promise((resolve, reject) => {
+ RESTRequest(request)
+ .then((value: ModelResponseHttp) => {
+ if (isNullOrUndefined(checker)) {
+ console.log(`Have no check of MODEL in API: ${RESTUrl(request)}`);
+ resolve(value.data);
+ } else if (checker === undefined || checker(value.data)) {
+ resolve(value.data);
+ } else {
+ reject({
+ name: "Model check fail",
+ time: Date().toString(),
+ status: 950,
+ error: "REST Fail to verify the data",
+ statusMessage: "API cast ERROR",
+ message: "api.ts Check type as fail",
+ } as RestErrorResponse);
+ }
+ })
+ .catch((reason: RestErrorResponse) => {
+ reject(reason);
+ });
+ });
}
export function RESTRequestVoid(request: RESTRequestType): Promise {
- return new Promise((resolve, reject) => {
- RESTRequest(request)
- .then((value: ModelResponseHttp) => {
- resolve();
- })
- .catch((reason: RestErrorResponse) => {
- reject(reason);
- });
- });
+ return new Promise((resolve, reject) => {
+ RESTRequest(request)
+ .then((value: ModelResponseHttp) => {
+ resolve();
+ })
+ .catch((reason: RestErrorResponse) => {
+ reject(reason);
+ });
+ });
}
diff --git a/front/src/styles.less b/front/src/styles.less
index 8337a90..f32235d 100644
--- a/front/src/styles.less
+++ b/front/src/styles.less
@@ -2,12 +2,15 @@
.xdesktop {
display: block;
}
+
.xtablette {
display: none;
}
+
.xphone {
display: none;
}
+
.xmobile {
display: none;
}
@@ -17,12 +20,15 @@
.xdesktop {
display: none;
}
+
.xtablette {
display: block;
}
+
.xphone {
display: none;
}
+
.xmobile {
display: block;
}
@@ -32,12 +38,15 @@
.xdesktop {
display: none;
}
+
.xtablette {
display: none;
}
+
.xphone {
display: block;
}
+
.xmobile {
display: block;
}
@@ -206,6 +215,7 @@ html {
vertical-align: middle;
}
}
+
.square-button {
// background: #b3d4fc;
text-shadow: none;
@@ -264,12 +274,15 @@ html {
text-align: center;
line-height: 36px;
vertical-align: middle;
+
&:disabled {
cursor: not-allowed;
}
+
.material-icons {
vertical-align: middle;
}
+
background: rgba(0, 0, 0, 0);
}
@@ -302,6 +315,7 @@ label {
height: 100%;
}
+
.full-back {
position: absolute;
width: 100%;
@@ -312,12 +326,9 @@ label {
background-image: url('assets/images/ikon_red.svg');
background-repeat: no-repeat;
- /*
background-size: contain;
- */
- background-size: 80%;
background-attachment: fixed;
background-position: 50% 50%;
z-index: -1;
-}
+}
\ No newline at end of file