diff --git a/api/api-iam/iam-external-client/src/main/java/fr/gouv/vitamui/iam/external/client/IamExternalRestClientFactory.java b/api/api-iam/iam-external-client/src/main/java/fr/gouv/vitamui/iam/external/client/IamExternalRestClientFactory.java index db384ecf2ab1b671619331440fea43049bdeaace..87c8b2d5d7b6dc85b3ccc8277e98468f2a3067f8 100644 --- a/api/api-iam/iam-external-client/src/main/java/fr/gouv/vitamui/iam/external/client/IamExternalRestClientFactory.java +++ b/api/api-iam/iam-external-client/src/main/java/fr/gouv/vitamui/iam/external/client/IamExternalRestClientFactory.java @@ -107,5 +107,4 @@ public class IamExternalRestClientFactory extends BaseRestClientFactory { public LogbookExternalRestClient getLogbookExternalRestClient() { return new LogbookExternalRestClient(getRestTemplate(), getBaseUrl()); } - } diff --git a/api/api-iam/iam-internal/src/main/config/customer-init.yml b/api/api-iam/iam-internal/src/main/config/customer-init.yml index b0945657df4424796b47eb2dbad2d6c2dba816b6..b1c7d38f07acc542043c93edecc21859811f9406 100644 --- a/api/api-iam/iam-internal/src/main/config/customer-init.yml +++ b/api/api-iam/iam-internal/src/main/config/customer-init.yml @@ -118,6 +118,15 @@ customer-init: - ROLE_CREATE_INGEST - ROLE_GET_ALL_INGEST + - name: Profil Arbres et Plans + description: Gestion des application d'import d'arbres de positionnement et plans de classement + app-name: HOLDING_FILLING_SCHEME_APP + level: + roles: + - ROLE_CREATE_HOLDING_FILLING_SCHEME + - ROLE_GET_HOLDING_FILLING_SCHEME + - ROLE_GET_ALL_HOLDING_FILLING_SCHEME + #- name: profileName # description: desc # level: 1 diff --git a/api/api-ingest/ingest-internal/src/main/config/ingest-internal-application-dev.yml b/api/api-ingest/ingest-internal/src/main/config/ingest-internal-application-dev.yml index 07722852e87ce356959ad3c4c3e8e935cb373ac3..df95618eb5bd98c9a28d47af95d9883b8b7ad83d 100644 --- a/api/api-ingest/ingest-internal/src/main/config/ingest-internal-application-dev.yml +++ b/api/api-ingest/ingest-internal/src/main/config/ingest-internal-application-dev.yml @@ -11,6 +11,10 @@ spring: enabled: false register: false + data: + mongodb: + uri: mongodb://mongod_dbuser_iam:mongod_dbpwd_iam@localhost:27018/iam?connectTimeoutMS=2000 + multipart: enabled: true diff --git a/api/api-ingest/ingest-internal/src/main/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfig.java b/api/api-ingest/ingest-internal/src/main/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfig.java index fe17c2510d95ca1774d35891625b2a1a69f5821a..d6fc14610b2e71bd7685c287a3122036beea2f12 100644 --- a/api/api-ingest/ingest-internal/src/main/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfig.java +++ b/api/api-ingest/ingest-internal/src/main/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfig.java @@ -38,6 +38,7 @@ package fr.gouv.vitamui.ingest.internal.server.config; import fr.gouv.vitam.ingest.external.client.IngestExternalClient; import fr.gouv.vitamui.commons.api.application.AbstractContextConfiguration; +import fr.gouv.vitamui.commons.mongo.config.MongoConfig; import fr.gouv.vitamui.commons.rest.RestExceptionHandler; import fr.gouv.vitamui.commons.rest.client.configuration.RestClientConfiguration; import fr.gouv.vitamui.commons.rest.configuration.SwaggerConfiguration; @@ -64,7 +65,7 @@ import org.springframework.context.annotation.Import; import com.fasterxml.jackson.databind.ObjectMapper; @Configuration -@Import({RestExceptionHandler.class, SwaggerConfiguration.class, WebSecurityConfig.class, VitamAccessConfig.class, VitamIngestConfig.class, +@Import({RestExceptionHandler.class, MongoConfig.class, SwaggerConfiguration.class, WebSecurityConfig.class, VitamAccessConfig.class, VitamIngestConfig.class, VitamAdministrationConfig.class}) public class ApiIngestInternalServerConfig extends AbstractContextConfiguration { diff --git a/api/api-referential/referential-commons/src/test/java/fr/gouv/vitamui/referential/common/utils/ReferentialDtoBuilder.java b/api/api-referential/referential-commons/src/test/java/fr/gouv/vitamui/referential/common/utils/ReferentialDtoBuilder.java index dcf52e3804041adeb0d76ccf4b2f612b95afbcf7..8f1ac3adb944ba831061ff97f39eb23228926d25 100644 --- a/api/api-referential/referential-commons/src/test/java/fr/gouv/vitamui/referential/common/utils/ReferentialDtoBuilder.java +++ b/api/api-referential/referential-commons/src/test/java/fr/gouv/vitamui/referential/common/utils/ReferentialDtoBuilder.java @@ -56,7 +56,6 @@ public class ReferentialDtoBuilder { contextDto.setStatus(ContextStatus.ACTIVE.toString()); contextDto.setEnableControl(true); contextDto.setSecurityProfile("securityProfile"); - // contextDto.setPermissions(buildPermissions()); return contextDto; } diff --git a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java index 4729bf3199cd99398f8ac404749725a30db33354..e9f942f7dd26bd88fd483ac4bdfaf934ee4b4905 100644 --- a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java +++ b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java @@ -353,6 +353,16 @@ public class ServicesData { public static final String ROLE_GET_INGEST = GET_ROLE_PREFIX + SERVICE_INGEST; public static final String ROLE_GET_ALL_INGEST = GET_ROLE_PREFIX + "ALL_" + SERVICE_INGEST; + + //------------------------------------ API TREES & PLANS ----------------------------------------- + + public static final String SERVICE_HOLDING_FILLING_SCHEME_ROLE = "HOLDING_FILLING_SCHEME"; + + public static final String ROLE_CREATE_HOLDING_FILLING_SCHEME_ROLE = CREATE_ROLE_PREFIX + SERVICE_HOLDING_FILLING_SCHEME_ROLE; + public static final String ROLE_GET_HOLDING_FILLING_SCHEME_ROLE = GET_ROLE_PREFIX + SERVICE_HOLDING_FILLING_SCHEME_ROLE; + public static final String ROLE_GET_ALL_HOLDING_FILLING_SCHEME_ROLE = GET_ROLE_PREFIX + "ALL_" + SERVICE_HOLDING_FILLING_SCHEME_ROLE; + + //@formatter:off /** @@ -438,7 +448,11 @@ public class ServicesData { ROLE_GET_ALL_INGEST, ROLE_GET_INGEST, - ROLE_CREATE_INGEST + ROLE_CREATE_INGEST, + + ROLE_CREATE_HOLDING_FILLING_SCHEME_ROLE, + ROLE_GET_HOLDING_FILLING_SCHEME_ROLE, + ROLE_GET_ALL_HOLDING_FILLING_SCHEME_ROLE ); /** @@ -548,7 +562,11 @@ public class ServicesData { ROLE_GET_ALL_INGEST, ROLE_GET_INGEST, - ROLE_CREATE_INGEST + ROLE_CREATE_INGEST, + + ROLE_CREATE_HOLDING_FILLING_SCHEME_ROLE, + ROLE_GET_HOLDING_FILLING_SCHEME_ROLE, + ROLE_GET_ALL_HOLDING_FILLING_SCHEME_ROLE ); //@formatter:on diff --git a/deployment/roles/vitamui/files/customer-init.yml b/deployment/roles/vitamui/files/customer-init.yml index c74fdf44335ffc939f329626b9570cb222ce8aec..72b763a8eab364b5ce3602428531835a74f6aa71 100644 --- a/deployment/roles/vitamui/files/customer-init.yml +++ b/deployment/roles/vitamui/files/customer-init.yml @@ -3,46 +3,6 @@ customer-init: # Default profiles for each customer created profiles: - - name: Profil pour la gestion des profiles de sécurité - description: Gestion des profiles de sécurité dans Vitam - app-name: SECURITY_PROFILES_APP - level: - roles: - - ROLE_GET_SECURITY_PROFILES - - ROLE_CREATE_SECURITY_PROFILES - - ROLE_UPDATE_SECURITY_PROFILES - - ROLE_DELETE_SECURITY_PROFILES - - - name: Profil pour la gestion des formats de fichiers - description: Gestion des formats de fichiers dans Vitam - app-name: FILE_FORMATS_APP - level: - roles: - - ROLE_GET_FILE_FORMATS - - ROLE_CREATE_FILE_FORMATS - - ROLE_UPDATE_FILE_FORMATS - - ROLE_DELETE_FILE_FORMATS - - ROLE_IMPORT_FILE_FORMATS - - - name: Profil pour la gestion des ontologies - description: Gestion des ontologies dans Vitam - app-name: ONTOLOGY_APP - level: - roles: - - ROLE_GET_ONTOLOGIES - - ROLE_CREATE_ONTOLOGIES - - ROLE_DELETE_ONTOLOGIES - - ROLE_IMPORT_ONTOLOGIES - - - name: Profile pour la gestion des contrats des contextes - description: Gestion des contrats des contextes dans Vitam - app-name: CONTEXTS_APP - level: - roles: - - ROLE_GET_CONTEXTS - - ROLE_CREATE_CONTEXTS - - ROLE_UPDATE_CONTEXTS - #- name: profileName # description: desc # level: 1 @@ -147,6 +107,25 @@ customer-init: - ROLE_CREATE_INGEST - ROLE_GET_ALL_INGEST + - name: Profil Arbres et Plans + description: Gestion des application d'import d'arbres de positionnement et plans de classement + app-name: HOLDING_FILLING_SCHEME_APP + level: + roles: + - ROLE_CREATE_HOLDING_FILLING_SCHEME + - ROLE_GET_HOLDING_FILLING_SCHEME + - ROLE_GET_ALL_HOLDING_FILLING_SCHEME + + - name: Profil pour la gestion des règles de gestion + description: Gestion des règles de gestion + app-name: RULES_APP + level: + roles: + - ROLE_GET_RULES + - ROLE_CREATE_RULES + - ROLE_UPDATE_RULES + - ROLE_DELETE_RULES + # Other Default profiles for admin group admin-profiles: #- name: profileName diff --git a/deployment/roles/vitamui/templates/ingest-internal/application.yml.j2 b/deployment/roles/vitamui/templates/ingest-internal/application.yml.j2 index 112b652b616afb04a3f84a1f8782f491b920554e..e06e55aa95ad78bf634079beac482ca7c94a0077 100644 --- a/deployment/roles/vitamui/templates/ingest-internal/application.yml.j2 +++ b/deployment/roles/vitamui/templates/ingest-internal/application.yml.j2 @@ -6,6 +6,9 @@ spring: discovery: preferIpAddress: true tags: {{ consul_tags }} + data: + mongodb: + uri: "mongodb://{{ mongodb.iam.user }}:{{ mongodb.iam.password }}@{{ mongodb.host }}:{{ mongodb.mongod_port }}/{{ mongodb.iam.db }}?replicaSet={{ mongod_replicaset_name }}&connectTimeoutMS={{ mongod_client_connect_timeout_ms }}" # should we fix some limit here ? spring.servlet.multipart.max-file-size: -1 diff --git a/deployment/roles/vitamui/templates/ui-identity-admin/application.yml.j2 b/deployment/roles/vitamui/templates/ui-identity-admin/application.yml.j2 index c77fd6891f0290f2178ea67fe1cf65abcbf0bcc6..b51dc0514ac091a2cb97416efa730d91d7966966 100644 --- a/deployment/roles/vitamui/templates/ui-identity-admin/application.yml.j2 +++ b/deployment/roles/vitamui/templates/ui-identity-admin/application.yml.j2 @@ -105,6 +105,13 @@ ui-identity: {% else %} identity: "{{ url_prefix }}/identity" # TODO OMA : revoir avec les redirections du RP {% endif %} + portal-categories: +{% for id, category in vitamui_defaults.portal_categories.iteritems() %} + {{ id }}: + title: "{{ category.title }}" + displayTitle: {{ category.displayTitle }} + order: {{ category.order }} +{% endfor %} ui: {% if vitamui_struct.base_url is defined %} diff --git a/deployment/scripts/mongod/1.0.0/101_iam_system_demo.js b/deployment/scripts/mongod/1.0.0/101_iam_system_demo.js index 9990f0ee6b2346e82cb810242b785d5d3016bfec..6f44db95efa34a4d686d737c6ff68b4968d88c1d 100644 --- a/deployment/scripts/mongod/1.0.0/101_iam_system_demo.js +++ b/deployment/scripts/mongod/1.0.0/101_iam_system_demo.js @@ -196,7 +196,8 @@ db.groups.insert({ "system_secure", "system_dsl", "system_probative_value", - "system_logbook_operation_profile" + "system_logbook_operation", + "system_holding_filling_scheme_profile" ], "readonly": false, "level": "", diff --git a/deployment/scripts/mongod/1.0.0/207_iam_ref.js.j2 b/deployment/scripts/mongod/1.0.0/207_iam_ref.js.j2 index c379920e5237de6f53ff288b4e7dfc89adf449d8..7b3e1d7abbbaa35bdcac7b35c9345d7e7b420b3b 100644 --- a/deployment/scripts/mongod/1.0.0/207_iam_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/207_iam_ref.js.j2 @@ -268,7 +268,7 @@ db.profiles.insert({ }); db.profiles.insert({ - "_id": "system_logbook_operation_profile", + "_id": "system_logbook_operation", "identifier" : NumberInt(maxIdProfile++), "name": "Journal des Opérations", "description": "Logbook Operation Profile", @@ -285,6 +285,30 @@ db.profiles.insert({ ] }); +db.profiles.insert({ + "_id": "system_holding_filling_scheme_profile", + "identifier" : NumberInt(maxIdProfile++), + "name": "Arbres et Plans", + "description": "Arbres et Plans", + "tenantIdentifier": NumberInt({{ vitamui_platform_informations.proof_tenant }}), + "applicationName": "HOLDING_FILLING_SCHEME_APP", + "level": "", + "enabled": true, + "readonly": false, + "customerId": "system_customer", + "roles": [ + { + "name": "ROLE_CREATE_HOLDING_FILLING_SCHEME" + }, + { + "name": "ROLE_GET_HOLDING_FILLING_SCHEME" + }, + { + "name": "ROLE_GET_ALL_HOLDING_FILLING_SCHEME" + } + ] +}); + db.sequences.updateOne({ "_id": "profile_identifier" }, { @@ -312,7 +336,8 @@ db.groups.updateOne( { "system_secure", "system_dsl", "system_probative_value", - "system_logbook_operation_profile" + "system_logbook_operation", + "system_holding_filling_scheme_profile" ] } } diff --git a/deployment/scripts/mongod/1.0.0/208_application_ref.js.j2 b/deployment/scripts/mongod/1.0.0/208_application_ref.js.j2 index e1fef1cafcb748f84de8079b7946c53399b25bd5..7a6d62e26b16735546d3b3678a549b1b2bc6024c 100644 --- a/deployment/scripts/mongod/1.0.0/208_application_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/208_application_ref.js.j2 @@ -220,4 +220,21 @@ db.applications.insert({ "target": "_self" }); +db.applications.insert({ + "identifier" : "HOLDING_FILLING_SCHEME_APP", +{% if vitamui.referential.base_url is defined %} + "url": "{{ vitamui.ingest.base_url }}/holding-filling-scheme", +{% else %} + "url": "{{ url_prefix }}/ingest/holding-filling-scheme", +{% endif %} + "icon" : "vitamui-icon vitamui-icon-filing", + "name" : "Arbres et Plans", + "category" : "referential", + "position" : NumberInt(16), + "hasCustomerList" : false, + "hasTenantList" : true, + "hasHighlight" : false, + "tooltip" : "Importer un arbre de positionnement ou un plan de classement", + "target" : "_self" +}); print("END 208_application_ref.js"); diff --git a/deployment/scripts/mongod/1.0.0/210_security_ref.js.j2 b/deployment/scripts/mongod/1.0.0/210_security_ref.js.j2 index 4985fe91feb084bb9745e53d716c8c7e7724b99f..f78c18914419d397cb6d55f5ca75319da66bf4c4 100644 --- a/deployment/scripts/mongod/1.0.0/210_security_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/210_security_ref.js.j2 @@ -9,12 +9,12 @@ db.contexts.insert({ "tenants" : [NumberInt({{ vitamui_platform_informations.proof_tenant }}), NumberInt({{ vitamui_platform_informations.cas_tenant }})], "roleNames" : [ "ROLE_GET_CUSTOMERS", - "ROLE_GET_USERS", + "ROLE_GET_USERS", "ROLE_GET_PROFILES", - "ROLE_GET_GROUPS", + "ROLE_GET_GROUPS", "ROLE_GET_PROFILES_ALL_TENANTS", "ROLE_GET_TENANTS", - "ROLE_GET_ALL_TENANTS", + "ROLE_GET_ALL_TENANTS", "ROLE_GET_ACCESS_CONTRACTS", "ROLE_CREATE_ACCESS_CONTRACTS", "ROLE_UPDATE_ACCESS_CONTRACTS", "ROLE_GET_INGEST_CONTRACTS", "ROLE_CREATE_INGEST_CONTRACTS", "ROLE_UPDATE_INGEST_CONTRACTS", "ROLE_GET_MANAGEMENT_CONTRACTS", "ROLE_GET_ARCHIVE_PROFILES", "ROLE_GET_ONTOLOGIES", "ROLE_CREATE_ONTOLOGIES", "ROLE_DELETE_ONTOLOGIES", diff --git a/deployment/scripts/mongod/1.0.0/214_security_ref.js.j2 b/deployment/scripts/mongod/1.0.0/214_security_ref.js.j2 index 3577876b0fb4f8d2501f75b6db5ad3f3419b1f28..58ae8560ce64999b68a8c8182c4c263bbc87433f 100644 --- a/deployment/scripts/mongod/1.0.0/214_security_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/214_security_ref.js.j2 @@ -8,7 +8,9 @@ db.contexts.insert({ "fullAccess" : true, "tenants" : [NumberInt({{ vitamui_platform_informations.proof_tenant }}), NumberInt({{ vitamui_platform_informations.cas_tenant }})], "roleNames" : [ - "ROLE_CREATE_INGEST", "ROLE_GET_INGEST", "ROLE_GET_ALL_INGEST" + "ROLE_CREATE_INGEST", "ROLE_GET_INGEST", "ROLE_GET_ALL_INGEST", + "ROLE_CREATE_HOLDING_FILLING_SCHEME", "ROLE_GET_HOLDING_FILLING_SCHEME", "ROLE_GET_ALL_HOLDING_FILLING_SCHEME" + ] }); diff --git a/deployment/scripts/mongod/1.0.0/307_iam_ref.js.j2 b/deployment/scripts/mongod/1.0.0/307_iam_ref.js.j2 index b98acde3d9155150bbcb49209bd5d8f45a78749b..53c77194e476eaa09f5e8cbd7b1c1c2a6426d6f9 100644 --- a/deployment/scripts/mongod/1.0.0/307_iam_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/307_iam_ref.js.j2 @@ -43,43 +43,6 @@ db.sequences.updateOne({ } }); -// ------------------------------------------- REFERENTIAL TENANT 2 -------------------------------- - -db.profiles.insert({ - "_id" : "auto_system_rules", - "identifier" : NumberInt(maxIdProfile++), - "name" : "Auto Rules Profile", - "description" : "Auto Rules Profile", - "tenantIdentifier": NumberInt(2), - "applicationName" : "RULES_APP", - "enabled" : true, - "readonly" : true, - "level" : "", - "customerId" : "system_customer", - "roles" : [ - { - "name": "ROLE_GET_RULES" - }, - { - "name": "ROLE_CREATE_RULES" - }, - { - "name": "ROLE_UPDATE_RULES" - }, - { - "name": "ROLE_DELETE_RULES" - } - ] -}); - -db.sequences.updateOne({ - "_id": "profile_identifier" -}, { - $set: { - "sequence": NumberInt(maxIdProfile) - } -}); - // ========================================= GROUPS ========================================= // ----------------------------------------- LEVEL "0" ----------------------------------------- @@ -89,8 +52,7 @@ db.groups.updateOne( { $addToSet: { "profileIds": { $each: [ - "system_rules", - "auto_system_rules" + "system_rules" ] } } diff --git a/deployment/scripts/mongod/1.0.0/307_iam_ref_fix.js.j2 b/deployment/scripts/mongod/1.0.0/307_iam_ref_fix.js.j2 deleted file mode 100644 index fb76f37fb1ab1ec04c53a6aa3bbbc2f1d35c59b0..0000000000000000000000000000000000000000 --- a/deployment/scripts/mongod/1.0.0/307_iam_ref_fix.js.j2 +++ /dev/null @@ -1,15 +0,0 @@ -db = db.getSiblingDB('iam') - -print("START 307_iam_ref_fix.js"); - -db.profiles.remove( {"_id": "auto_system_rules"}, true); - -// ----------------------------------------- LEVEL "0" ----------------------------------------- - -db.groups.updateOne( { - "_id": "admin_group" -}, { - $pull: { - "profileIds": "auto_system_rules" - } -} ); diff --git a/deployment/scripts/mongod/1.0.0/311_iam_ref.js.j2 b/deployment/scripts/mongod/1.0.0/311_iam_ref.js.j2 index 20e035d7285bf7f99d1f30649d87f73fc40d7dcd..9eb829eab1cb9fe6a4ed342365873b62a7de117e 100644 --- a/deployment/scripts/mongod/1.0.0/311_iam_ref.js.j2 +++ b/deployment/scripts/mongod/1.0.0/311_iam_ref.js.j2 @@ -53,30 +53,6 @@ db.profiles.update( ]} }); -db.profiles.update( -{"_id" : "auto_agencies"}, -{$set: {"roles" : [ - { - "name": "ROLE_GET_AGENCIES" - }, - { - "name": "ROLE_CREATE_AGENCIES" - }, - { - "name": "ROLE_UPDATE_AGENCIES" - }, - { - "name": "ROLE_DELETE_AGENCIES" - }, - { - "name": "ROLE_EXPORT_AGENCIES" - }, - { - "name": "ROLE_IMPORT_AGENCIES" - } -]} -}); - db.profiles.update( {"_id" : "system_file_format"}, {$set: {"roles" : [ diff --git a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCheckSteps.java b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCheckSteps.java index 55d645205fec29dd63ee1b3721d9f0c1a791cc82..1e3ba801deef107eccd8d7104ee0d7e384304de7 100644 --- a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCheckSteps.java +++ b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCheckSteps.java @@ -40,6 +40,7 @@ import io.cucumber.java.en.When; import fr.gouv.vitamui.commons.api.domain.CriterionOperator; import fr.gouv.vitamui.commons.api.domain.QueryDto; import fr.gouv.vitamui.cucumber.common.CommonSteps; +import fr.gouv.vitamui.referential.common.dto.ContextDto; import fr.gouv.vitamui.utils.TestConstants; /** @@ -52,8 +53,9 @@ public class ApiReferentialExternalContextCheckSteps extends CommonSteps { @When("^un utilisateur vérifie l'existence d'un contexte par son identifiant$") public void un_utilisateur_vérifie_l_existence_d_un_contexte_par_son_identifiant() { try { - final QueryDto criteria = QueryDto.criteria("id", TestConstants.CONTEXT_NAME, CriterionOperator.EQUALS); - testContext.bResponse = getContextRestClient().checkExist(getSystemTenantUserAdminContext(), TestConstants.CONTEXT_IDENTIFIER); + ContextDto dto = new ContextDto(); + dto.setIdentifier(TestConstants.CONTEXT_IDENTIFIER); + testContext.bResponse = getContextRestClient().check(getSystemTenantUserAdminContext(), dto); } catch (final RuntimeException e) { testContext.exception = e; @@ -63,10 +65,10 @@ public class ApiReferentialExternalContextCheckSteps extends CommonSteps { @When("^un utilisateur vérifie l'existence d'un contexte par son code et son nom$") public void un_utilisateur_vérifie_l_existence_d_un_contexte_par_son_code_et_son_nom() { try { - final QueryDto criteria = QueryDto.criteria( - "id", TestConstants.CONTEXT_IDENTIFIER, CriterionOperator.EQUALS).addCriterion( - "name",TestConstants.CONTEXT_NAME, CriterionOperator.EQUALS); - testContext.bResponse = getContextRestClient().checkExist(getSystemTenantUserAdminContext(), criteria.toJson()); + ContextDto dto = new ContextDto(); + dto.setIdentifier(TestConstants.CONTEXT_IDENTIFIER); + dto.setName(TestConstants.CONTEXT_NAME); + testContext.bResponse = getContextRestClient().check(getSystemTenantUserAdminContext(), dto); } catch (final RuntimeException e) { testContext.exception = e; diff --git a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCreationSteps.java b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCreationSteps.java index 6eb4905ec4e6ab78d777f1ea311ac81160b3b6cd..1fba55928c9326b61a1485c1a42a68303d1e5afc 100644 --- a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCreationSteps.java +++ b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextCreationSteps.java @@ -79,22 +79,11 @@ public class ApiReferentialExternalContextCreationSteps extends CommonSteps { final ContextDto dto = FactoryDto.buildDto(ContextDto.class); testContext.savedContextDto = getContextRestClient().create(getSystemTenantUserAdminContext(), dto); } - @Then("^le serveur retourne le contexte créé$") public void le_status_de_la_réponse_doit_etre() { assertThat(testContext.savedContextDto).overridingErrorMessage("la réponse retournée est null").isNotNull(); } - @Then("^(.*) permissions sont créés dans le contexte$") - public void des_permissions_sont_créés_dans_le_contexte(final Integer profilesNumber) { - assertThat(testContext.savedContextDto.getPermissions()).isNotNull().isNotEmpty(); - assertThat(testContext.savedContextDto.getPermissions().size()).isEqualTo(2); - assertThat(testContext.savedContextDto.getPermissions().stream().anyMatch(permission -> - (permission.getTenant().equals("tenant_1") || permission.getTenant().equals("tenant_2")) - )).isTrue(); - } - - @Then("^une trace de création d'un contexte est présente dans vitam$") public void une_trace_de_création_d_un_contexte_est_présente_dans_vitam() throws InterruptedException { super.testTrace(TestConstants.CONTEXT_ID, testContext.contextDto.getIdentifier(), "contexts", "EXT_VITAMUI_CREATE_CONTEXT"); diff --git a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextGetSteps.java b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextGetSteps.java index 51ad4b41ab01d989430117e71bfce06463fdd1b1..72f3fee34bf6842fd915ed3e831d3c59aef068a6 100644 --- a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextGetSteps.java +++ b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/back/steps/referential/context/ApiReferentialExternalContextGetSteps.java @@ -86,14 +86,12 @@ public class ApiReferentialExternalContextGetSteps extends CommonSteps { private List<ContextDto> contextDtos; private ContextDto contextDto; - private JsonNode contextHistory; @When("^un utilisateur avec le rôle ROLE_GET_CONTEXTS récupère tous les contextes applicatifs en utilisant un certificat full access avec le rôle ROLE_GET_CONTEXTS$") public void un_utilisateur_avec_le_rôle_ROLE_GET_CONTEXTS_récupère_tous_les_contextes_en_utilisant_un_certificat_full_access_avec_le_rôle_ROLE_GET_CONTEXTS() { contextDtos = getContextRestClient().getAll(getSystemTenantUserAdminContext()); } - @Then("^le serveur retourne tous les contextes$") public void le_serveur_retourne_tous_les_contextes() { assertThat(contextDtos).isNotNull(); @@ -101,7 +99,6 @@ public class ApiReferentialExternalContextGetSteps extends CommonSteps { final int size = contextDtos.size(); assertThat(size).isGreaterThanOrEqualTo(1); } - @When("^un utilisateur avec le rôle ROLE_GET_CONTEXTS récupère un contexte par son identifiant en utilisant un certificat full access avec le rôle ROLE_GET_CONTEXTS$") public void un_utilisateur_avec_le_rôle_ROLE_GET_CONTEXTS_récupère_un_contexte_par_son_identifiant_en_utilisant_un_certificat_full_access_avec_le_rôle_ROLE_GET_CONTEXTS() { contextDto = getContextRestClient().getOne(getSystemTenantUserAdminContext(), TestConstants.CONTEXT_IDENTIFIER, Optional.empty()); @@ -113,7 +110,6 @@ public class ApiReferentialExternalContextGetSteps extends CommonSteps { assertThat(contextDto.getIdentifier()).isEqualTo(TestConstants.CONTEXT_IDENTIFIER); assertThat(contextDto.getName()).isEqualTo(TestConstants.CONTEXT_NAME); } - @When("^un utilisateur avec le rôle ROLE_GET_CONTEXTS récupère tous les contextes par code et nom en utilisant un certificat full access avec le rôle ROLE_GET_CONTEXTS$") public void un_utilisateur_avec_le_rôle_ROLE_GET_CUSTOMERS_récupère_tous_les_contextes_par_code_et_nom_en_utilisant_un_certificat_full_access_avec_le_rôle_ROLE_GET_CONTEXTS() { QueryDto criteria = QueryDto.criteria( @@ -158,15 +154,12 @@ public class ApiReferentialExternalContextGetSteps extends CommonSteps { public void le_serveur_retourne_les_clients_paginés() { le_serveur_retourne_les_contextes_par_code_ou_nom(); } - @When("^un utilisateur avec le rôle ROLE_GET_CONTEXTS récupère l'historique d'un contexte à partir de son son identifiant en utilisant un certificat full access avec le rôle ROLE_GET_CONTEXTS$") public void un_utilisateur_avec_le_rôle_ROLE_GET_CONTEXTS_récupère_l_historique_d_un_contexte_à _partir_de_son_identifiant_en_utilisant_un_certificat_full_access_avec_le_rôle_ROLE_GET_CONTEXTS() { contextHistory = getContextRestClient().findHistoryById(getSystemTenantUserAdminContext(), TestConstants.CONTEXT_IDENTIFIER); } - @Then("^le serveur retourne l'historique du contexte$") public void le_serveur_retourne_l_historique_du_contexte() { assertThat(contextHistory).isNotNull(); } - } diff --git a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/common/BaseIntegration.java b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/common/BaseIntegration.java index 381e906d931bafa182e5b03694117ecfdf054a6c..3941b6085db44f9c99d54e1043345371629db1fc 100644 --- a/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/common/BaseIntegration.java +++ b/integration-tests/src/test/java/fr/gouv/vitamui/cucumber/common/BaseIntegration.java @@ -98,7 +98,7 @@ public abstract class BaseIntegration { private IamExternalRestClientFactory restClientFactory; private IamExternalWebClientFactory iamExternalWebClientFactory; - + private ReferentialExternalRestClientFactory restReferentialClientFactory; private ReferentialExternalWebClientFactory webReferentialClientFactory; @@ -477,8 +477,6 @@ public abstract class BaseIntegration { getCertificatesCollection().deleteOne(eq("_id", TESTS_CERTIFICATE_ID)); //@formatter:off try { - LOGGER.debug("Get certificate : {}", genericCert); - LOGGER.debug("Get passaword : {}", jksPassword); final String certificate = getCertificate("JKS", genericCert, jksPassword.toCharArray()); final Document itCertificate = new Document("_id", TESTS_CERTIFICATE_ID) @@ -507,9 +505,6 @@ public abstract class BaseIntegration { final Enumeration<?> enumeration = keyStore.aliases(); while (enumeration.hasMoreElements()) { final String alias = (String) enumeration.nextElement(); - - LOGGER.debug("Get certificate : {}", genericCert); - LOGGER.debug("Get passaword : {}", jksPassword); final Certificate certificate = keyStore.getCertificate(alias); final byte[] encodedCertKey = certificate.getEncoded(); result = Base64.getEncoder().encodeToString(encodedCertKey); diff --git a/ui/ui-frontend-common/package-lock.json b/ui/ui-frontend-common/package-lock.json index c6ae0c32af67954d7c5c029e3627251aee47a228..651e0cad91d692437a6951921fb61b7a92659088 100644 --- a/ui/ui-frontend-common/package-lock.json +++ b/ui/ui-frontend-common/package-lock.json @@ -2607,7 +2607,7 @@ }, "@types/normalize-package-data": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, @@ -3183,7 +3183,7 @@ "dependencies": { "bn.js": { "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true } @@ -3673,7 +3673,7 @@ "dependencies": { "readable-stream": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { @@ -3684,7 +3684,7 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true } @@ -4696,7 +4696,7 @@ "dependencies": { "bn.js": { "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true } @@ -5513,7 +5513,7 @@ "dependencies": { "bn.js": { "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true } @@ -8859,7 +8859,7 @@ }, "lines-and-columns": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/lines-and-columns/-/lines-and-columns-1.1.6.tgz", "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, @@ -10199,7 +10199,7 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, @@ -12381,7 +12381,7 @@ }, "read-pkg-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-5.0.0.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/read-pkg-up/-/read-pkg-up-5.0.0.tgz", "integrity": "sha512-XBQjqOBtTzyol2CpsQOw8LHV0XbDZVG7xMMjmXAJomlVY03WOBRmYgDJETlvcg0H63AJvPRwT7GFi5rvOzUOKg==", "dev": true, "requires": { @@ -15618,7 +15618,7 @@ "dependencies": { "anymatch": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { @@ -15628,7 +15628,7 @@ "dependencies": { "normalize-path": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { @@ -15639,7 +15639,7 @@ }, "binary-extensions": { "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, @@ -15674,7 +15674,7 @@ }, "chokidar": { "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/chokidar/-/chokidar-2.1.8.tgz", "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { @@ -15754,7 +15754,7 @@ }, "is-binary-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { @@ -15804,7 +15804,7 @@ }, "readdirp": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "resolved": "https://nexus.teamdlab.com/repository/npm/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { diff --git a/ui/ui-frontend-common/src/app/modules/application.service.ts b/ui/ui-frontend-common/src/app/modules/application.service.ts index 493a74a9c8678b5e079f5b019577c68ae9c8d362..a25fc895fd9b0258f946f5ffc3706c09cbc676c5 100644 --- a/ui/ui-frontend-common/src/app/modules/application.service.ts +++ b/ui/ui-frontend-common/src/app/modules/application.service.ts @@ -228,8 +228,8 @@ export class ApplicationService { return resultMap; } - private getLastUsedApps(categoriesByIds: { [categoryId: string]: Category }, - applications: Application[], max = 8): { category: Category, apps: Application[] } { + /* tslint:disable:max-line-length */ + private getLastUsedApps(categoriesByIds: { [categoryId: string]: Category }, applications: Application[], max = 8): { category: Category, apps: Application[] } { let dataSource: ApplicationAnalytics[]; if (this.applicationsAnalytics) { dataSource = this.applicationsAnalytics; diff --git a/ui/ui-frontend-common/src/app/modules/components/application-select-content/application-select-content.component.spec.ts b/ui/ui-frontend-common/src/app/modules/components/application-select-content/application-select-content.component.spec.ts index c78b23cfab76e883066660adac0a2eba13a1b307..0a2318a979085fd08c666acf369c8d565b8cdea4 100644 --- a/ui/ui-frontend-common/src/app/modules/components/application-select-content/application-select-content.component.spec.ts +++ b/ui/ui-frontend-common/src/app/modules/components/application-select-content/application-select-content.component.spec.ts @@ -34,6 +34,7 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ +/* tslint:disable:component-selector max-classes-per-file */ import { Component, Input } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; diff --git a/ui/ui-frontend/projects/ingest/src/app/app-routing.module.ts b/ui/ui-frontend/projects/ingest/src/app/app-routing.module.ts index f9a9ac599fd9cdab144d96b196f9eb5694937b74..0700d11fccebb189c5fa0b641ac64d98471f6bf6 100644 --- a/ui/ui-frontend/projects/ingest/src/app/app-routing.module.ts +++ b/ui/ui-frontend/projects/ingest/src/app/app-routing.module.ts @@ -65,6 +65,16 @@ const routes: Routes = [ canActivate: [AuthGuard, AppGuard], data: { appId: 'INGEST_MANAGEMENT_APP' } }, + + // ===================================================== + // TREES PLANS API + // ===================================================== + { + path: 'holding-filling-scheme', + loadChildren: () => import('./holding-filling-scheme/holding-filling-scheme.module').then(m => m.HoldingFillingSchemeModule), + canActivate: [AuthGuard, AppGuard], + data: { appId: 'HOLDING_FILLING_SCHEME_APP' } + }, // ===================================================== // unknown path // ===================================================== diff --git a/ui/ui-frontend/projects/ingest/src/app/app.module.ts b/ui/ui-frontend/projects/ingest/src/app/app.module.ts index 237f6e8846d3e64a09658a860ab3dcf51c470ae9..f0530b1688d3e449fd78de5ac9a74c2db5c9a5a2 100644 --- a/ui/ui-frontend/projects/ingest/src/app/app.module.ts +++ b/ui/ui-frontend/projects/ingest/src/app/app.module.ts @@ -44,6 +44,8 @@ import { VitamUICommonModule, WINDOW_LOCATION } from 'ui-frontend-common'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CoreModule } from './core/core.module'; +import { IngestModule } from './ingest'; +import { HoldingFillingSchemeModule } from './holding-filling-scheme/holding-filling-scheme.module'; registerLocaleData(localeFr, 'fr'); @@ -57,6 +59,8 @@ registerLocaleData(localeFr, 'fr'); BrowserModule, VitamUICommonModule, AppRoutingModule, + IngestModule, + HoldingFillingSchemeModule ], providers: [ Title, diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.html b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.html similarity index 57% rename from ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.html rename to ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.html index 944cf4aab705d953de22dd2b71af57acac07da77..c60cac44ac11fcdb52994d2873d2172353beaa40 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.html +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.html @@ -3,35 +3,33 @@ </div> <form [formGroup]="sipForm"> <div class="content"> - <h4>Nouveau versement</h4> - <h2>Verser un SIP</h2> + <h4>{{ messageLabelImportType }}</h4> + <h2>{{ messageImportType }}</h2> <div class="d-flex"> <div class="drag-and-drop-area" [ngClass]="{'on-over': hasDropZoneOver}" vitamuiCommonDragAndDrop - (fileToUploadEmitter)="onSipDropped($event)" (fileDragOverEmitter)="onSipDragOver($event)" - (fileDragLeaveEmitter)="onSipDragLeave($event)"> + (fileToUploadEmitter)="onDropped($event)" (fileDragOverEmitter)="onDragOver($event)" (fileDragLeaveEmitter)="onDragLeave($event)"> <div *ngIf="fileName && fileSize>0 && fileSizeString" class="drag-container"> - <div class="file-info-class">{{ fileName }} : {{ fileSizeString }} + <div class="file-info-class">{{ fileName }} + <span class="text-grey" class="font-class-file"> | {{ fileSizeString }} </span> <i class="material-icons success-icon" *ngIf="!hasError">check_circle</i> + <div> + <span class="text-red">{{ message }} </span> <i class="material-icons error-icon" + *ngIf="hasError">error</i> + </div> </div> - </div> <input type="file" #fileSearch class="input-file" (change)="handleFileInput($event.target.files)" /> <div class="drop-area"> <div *ngIf="!fileSize || !hasSip" class="sip-drop"> - Glisser-déposer<br> + Glisser-déposer ou <br> </div> <div *ngIf="!fileSize || !hasSip"> - <div class="upload-text"> - <div class="upload"><span class="underline" (click)="addSip()">Parcourir</span></div> - </div> - </div> - <div class="error-message drag-container" *ngIf="hasError"> - <div> - <i class="material-icons error-icon">error</i> - {{ message }} + <div class="sip-drop"> + <div class="upload"><span class="underline" (click)="addSip()">Parcourir</span> + </div> </div> </div> </div> @@ -40,7 +38,8 @@ </div> <div class="actions"> - <button type="button" class="btn primary" [disabled]="hasError" (click)="upload()">Confirmer le versement</button> + <button type="button" class="btn primary" [disabled]="isDisabled || hasError" (click)="upload()">Confirmer le + versement</button> <button type="button" class="btn cancel" (click)="onCancel()">Annuler</button> </div> </div> diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.scss b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.scss similarity index 98% rename from ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.scss rename to ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.scss index 4fcc791333297611684de674225236c5cc05c078..f5476b4c936c30e34ce368aa11befaa448f617d7 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.scss +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.scss @@ -178,3 +178,7 @@ button > small { color: #a5a5a5; } } + +.font-class-file { +font-size: 13px; +} diff --git a/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.spec.ts b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ed5eb8648249f6f4fd69cfe71f73cee66fc5ff2 --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.spec.ts @@ -0,0 +1,140 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { EMPTY, of } from 'rxjs'; +import { ConfirmDialogService, LoggerModule } from 'ui-frontend-common'; +import { UploadComponent } from './upload.component'; +import { UploadService } from './upload.service'; + +describe('UploadComponent', () => { + let component: UploadComponent; + let fixture: ComponentFixture<UploadComponent>; + + const matDialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['open']); + matDialogRefSpy.open.and.returnValue({ afterClosed: () => of(true) }); + + const uploadServiceSpy = jasmine.createSpyObj('UploadService', { uploadFile: of({}) }); + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + MatProgressBarModule, + MatSnackBarModule, + LoggerModule.forRoot() + ], + declarations: [UploadComponent], + providers: [ + FormBuilder, + { provide: MatDialogRef, useValue: matDialogRefSpy }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfirmDialogService, useValue: { listenToEscapeKeyPress: () => EMPTY } }, + { provide: UploadService, useValue: uploadServiceSpy } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('initContextIdentifier', () => { + + beforeEach(() => { + spyOn(console, 'error'); + }); + + it('should adapt the message with Holding scheme', () => { + component.initContextIdentifier('HOLDING_SCHEME'); + expect(component.messageImportType).toEqual('Importer un arbre de positionnement'); + expect(component.messageLabelImportType).toEqual('Nouvel arbre de positionnement'); + }); + + it('should adapt the message with Filling scheme', () => { + component.initContextIdentifier('FILING_SCHEME'); + expect(component.messageImportType).toEqual('Importer un plan de classement'); + expect(component.messageLabelImportType).toEqual('Nouveau plan de classement'); + }); + + it('should adapt the message with default ingest', () => { + component.initContextIdentifier('DEFAULT_WORKFLOW'); + expect(component.messageImportType).toEqual('Verser un SIP'); + expect(component.messageLabelImportType).toEqual('Nouveau versement'); + }); + + it('should log error for unknown context', () => { + component.initContextIdentifier('XYZ'); + expect(console.error).toHaveBeenCalled(); + }); + }); + + describe('checkFileExtension', () => { + + it('should return true when extension zip is correct', () => { + expect(component.checkFileExtension('correct.zip')).toBeTruthy(); + }); + + it('should return true when extension tar is correct', () => { + expect(component.checkFileExtension('correct.tar')).toBeTruthy(); + }); + + it('should return true when extension .tar.gz is correct', () => { + expect(component.checkFileExtension('correct.tar.gz')).toBeTruthy(); + }); + + it('should return true when extension .tar.bz2 is correct', () => { + expect(component.checkFileExtension('correct.tar.bz2')).toBeTruthy(); + }); + + it('should return true when extension is bad', () => { + expect(component.checkFileExtension('bad.json')).toBeFalsy(); + }); + }); +}); diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.ts b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.ts similarity index 72% rename from ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.ts rename to ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.ts index 6d914646c140f3f7a134fc7ba9ea5caf866817bb..3f2e54be2ae33d48996d22e7183ba5bf81e57eed 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.ts +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.component.ts @@ -38,43 +38,44 @@ import { Component, OnInit, ViewChild, Inject } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { BytesPipe, Logger } from 'ui-frontend-common'; -import { UploadSipService } from './upload-sip.service'; +import { UploadService } from './upload.service'; import { VitamUISnackBarComponent } from '../../shared/vitamui-snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'; +const action = 'RESUME'; @Component({ - selector: 'app-upload-sip', - templateUrl: './upload-sip.component.html', - styleUrls: ['./upload-sip.component.scss'] + selector: 'app-upload', + templateUrl: './upload.component.html', + styleUrls: ['./upload.component.scss'] }) -export class UploadSipComponent implements OnInit { +export class UploadComponent implements OnInit { sipForm: FormGroup; hasSip: boolean; hasDropZoneOver = false; - sipToUpload: File = null; + fileToUpload: File = null; hasError = false; message: string; fileName: string; fileSize = 0; fileSizeString: string; extensions: string[]; - contextId = 'DEFAULT_WORKFLOW'; - action = 'RESUME'; - + contextId: string; + messageImportType: string; + messageLabelImportType: string; tenantIdentifier: string; - uploadComplete = false; + isDisabled = true; @ViewChild('fileSearch', { static: false }) fileSearch: any; constructor( @Inject(MAT_DIALOG_DATA) public data: any, - public dialogRef: MatDialogRef<UploadSipComponent>, + public dialogRef: MatDialogRef<UploadComponent>, private formBuilder: FormBuilder, - private uploadService: UploadSipService, + private uploadService: UploadService, private snackBar: MatSnackBar, public logger: Logger ) { @@ -86,32 +87,53 @@ export class UploadSipComponent implements OnInit { } ngOnInit() { + this.contextId = this.data.givenContextId; + this.initContextIdentifier(this.data.givenContextId); this.extensions = ['.zip', '.tar', '.tar.gz', '.tar.bz2']; this.sipForm.get('hasSip').setValue(true); this.hasSip = this.sipForm.get('hasSip').value; } + initContextIdentifier(contextInput: string) { + switch (contextInput) { + case 'HOLDING_SCHEME': + this.messageImportType = 'Importer un arbre de positionnement'; + this.messageLabelImportType = 'Nouvel arbre de positionnement'; + break; + case 'FILING_SCHEME': + this.messageImportType = 'Importer un plan de classement'; + this.messageLabelImportType = 'Nouveau plan de classement'; + break; + case 'DEFAULT_WORKFLOW': + this.messageImportType = 'Verser un SIP'; + this.messageLabelImportType = 'Nouveau versement'; + break; + default: + console.error('unknown context identifier'); + } + } - onSipDragOver(inDropZone: boolean) { + onDragOver(inDropZone: boolean) { this.hasDropZoneOver = inDropZone; } - onSipDragLeave(inDropZone: boolean) { + onDragLeave(inDropZone: boolean) { this.hasDropZoneOver = inDropZone; } - onSipDropped(files: FileList) { + onDropped(files: FileList) { this.hasDropZoneOver = false; - this.handleSipFile(files); + this.handleFile(files); } - handleSipFile(files: FileList) { + handleFile(files: FileList) { + this.isDisabled = false; this.hasError = false; this.message = null; - this.sipToUpload = files.item(0); + this.fileToUpload = files.item(0); - this.fileName = this.sipToUpload.name; - this.fileSize = this.sipToUpload.size; + this.fileName = this.fileToUpload.name; + this.fileSize = this.fileToUpload.size; const transformer = new BytesPipe(this.logger); this.fileSizeString = transformer.transform(this.fileSize); @@ -128,13 +150,13 @@ export class UploadSipComponent implements OnInit { } handleFileInput(files: FileList) { - this.handleSipFile(files); + this.handleFile(files); } upload() { if (!this.isValidSIP) { return; } - this.uploadService.uploadFile(this.sipToUpload, this.contextId, this.action, this.tenantIdentifier) + this.uploadService.uploadFile(this.fileToUpload, this.contextId, action, this.tenantIdentifier) .subscribe( (res: boolean) => { this.uploadComplete = res; @@ -152,7 +174,7 @@ export class UploadSipComponent implements OnInit { displaySnackBar(uploadComplete: boolean) { this.snackBar.openFromComponent(VitamUISnackBarComponent, { panelClass: 'vitamui-snack-bar', - data: { type: 'sipUploaded', name: uploadComplete }, + data: { type: 'fileUploaded', name: uploadComplete }, duration: 10000 }); } diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.module.ts b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.module.ts similarity index 91% rename from ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.module.ts rename to ui/ui-frontend/projects/ingest/src/app/core/common/upload.module.ts index 6b0737a4dc640edc6865c00ac3a23132734b73b1..3058eca045574e8922b051cbf272421b8ac22ec8 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.module.ts +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.module.ts @@ -45,8 +45,8 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { VitamUICommonModule } from 'ui-frontend-common'; -import { UploadSipComponent } from './upload-sip.component'; -import { UploadSipService } from './upload-sip.service'; +import { UploadComponent } from './upload.component'; +import { UploadService } from './upload.service'; import { SharedModule } from '../../shared/shared.module'; @NgModule({ @@ -64,9 +64,9 @@ import { SharedModule } from '../../shared/shared.module'; MatProgressBarModule ], declarations: [ - UploadSipComponent, + UploadComponent, ], - entryComponents: [UploadSipComponent], - providers: [UploadSipService] + entryComponents: [UploadComponent], + providers: [UploadService] }) -export class UploadSipModule { } +export class UploadModule { } diff --git a/ui/ui-frontend/projects/ingest/src/app/core/common/upload.service.ts b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d5bf0bf535893844fe0cfa310d74432a9a5ac53 --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/core/common/upload.service.ts @@ -0,0 +1,160 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import {Injectable} from '@angular/core'; +import { HttpRequest, HttpHeaders, HttpResponse } from '@angular/common/http'; +import { IngestApiService } from '../api/ingest-api.service'; +import { retry } from 'rxjs/operators'; +import { Observable, Subject } from 'rxjs'; + + +const BYTES_PER_CHUNK = 1024 * 1024; // 1MB request +const tenantKey = 'X-Tenant-Id'; +const contextIdKey = 'X-Context-Id'; +const actionKey = 'X-Action'; +const chunkOffsetKey = 'X-Chunk-Offset'; +const totalSizeKey = 'X-Size-Total'; +const requestIdKey = 'X-Request-Id'; + +const MAX_RETRIES = 3; + +@Injectable() +export class UploadService { + + uploadComplete = new Subject<boolean>(); + + constructor( private ingestApiService: IngestApiService) { + } + + + uploadFile(file: File, contextId: string, action: string, tenantIdentifier: string): Observable<boolean> { + const totalSize = file.size; + let start = 0; + let end = (totalSize < BYTES_PER_CHUNK) ? totalSize : BYTES_PER_CHUNK; + + const request = this.generateIngestRequest(tenantIdentifier, contextId, action, start, end, totalSize, file); + + this.ingestApiService.upload(request) + .pipe( + retry(MAX_RETRIES)) + .subscribe( + (event) => { + if (event instanceof HttpResponse) { + // We get the requestId with the first request. + const requestId = event.headers.get(requestIdKey); + console.log('First API Request Id : ' + requestId); + start = end; + end = start + BYTES_PER_CHUNK; + + if (start >= totalSize) { + this.uploadComplete.next(true); + + return; + } + + for (let pointer = start; pointer < totalSize; pointer += BYTES_PER_CHUNK) { + this.uploadChunks(file, requestId, pointer, pointer + BYTES_PER_CHUNK, totalSize, tenantIdentifier, contextId, action); + } + + this.uploadComplete.next(true); + + } + }, + (error) => { + console.log(error); + this.uploadComplete.next(false); + } + ); + return this.uploadComplete; + } + + private uploadChunks( + file: File, + requestId: any, + start: number, + end: number, + totalSize: any, + tenantIdentifier: string, + contextId: string, + action: string + ) { + + const request = this.generateIngestRequest(tenantIdentifier, contextId, action, start, end, totalSize, file, requestId); + + this.ingestApiService.upload(request) + .pipe( + retry(MAX_RETRIES)) + .subscribe( + (event) => { + if (event instanceof HttpResponse) { + const requestIdd = event.headers.get(requestIdKey); + console.log('Request Id: ' + requestIdd); + } + }, + (error) => { + console.log(error); + this.uploadComplete.next(false); + } + ); + } + + private generateIngestRequest( + tenantIdentifier: string, + contextId: string, + action: string, + start: number, + end: number, + totalSize: number, + file: File, + requestId?: string + ): HttpRequest<FormData> { + let headers = new HttpHeaders(); + headers = headers.set(tenantKey, tenantIdentifier.toString()); + headers = headers.set(chunkOffsetKey, start.toString()); + headers = headers.set(totalSizeKey, totalSize.toString()); + headers = headers.set(contextIdKey, contextId); + headers = headers.set(actionKey, action); + if (requestId) { + headers = headers.set(requestIdKey, requestId); + } + + const formdata: FormData = new FormData(); + formdata.append('file', file.slice(start, end), file.name); + + return new HttpRequest('POST', this.ingestApiService.getBaseUrl() + '/ingest/upload', formdata, { headers }); + } + +} diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme-routing.module.ts b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme-routing.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..bf0ad1b75e355b11be134f2b36d3504819290f76 --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme-routing.module.ts @@ -0,0 +1,75 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Route, RouterModule } from '@angular/router'; +import { VitamUITenantSelectComponent, TenantSelectionGuard, ActiveTenantGuard } from 'ui-frontend-common'; +import { HoldingFillingSchemeComponent } from './holding-filling-scheme.component'; + + + +const routes: Route[] = [ + { + path: '', + redirectTo: 'tenant', + pathMatch: 'full' + }, + { + path: 'tenant', + component: VitamUITenantSelectComponent, + pathMatch: 'full', + canActivate: [TenantSelectionGuard] + }, + { + path: 'tenant/:tenantIdentifier', + component: HoldingFillingSchemeComponent, + canActivate: [ActiveTenantGuard] + } +]; + + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class HoldingFillingSchemeRoutingModule { } diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.html b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.html new file mode 100644 index 0000000000000000000000000000000000000000..f184f3e3b873157cbfc3b706cbbfd8e67f6d6662 --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.html @@ -0,0 +1,33 @@ +<mat-sidenav-container [autosize]="true" [hasBackdrop]="false"> + <mat-sidenav #panel mode="side" position="end" [fixedInViewport]="true"> + </mat-sidenav> + <mat-sidenav-content> + + <div class="vitamui-header no-background"> + <div class="vitamui-container"> + <vitamui-common-navbar [appId]="appId" (tenantSelect)="changeTenant($event)" [hideTenantMenu]="false" + [hideCustomerMenu]="true"></vitamui-common-navbar> + <h2><img src="assets/mini-logo-vitam.png">Importer ou modifier des <strong> arbres et plans</strong></h2> + + <div class="controls"> + <div class="holding-filling-scheme-search"> + <vitamui-common-search-bar #searchBar name="ingest-search" + placeholder="Noeud de l'arbre ou plan" i18n-placeholder="@@treePlanSearchPlaceholder"> + </vitamui-common-search-bar> + </div> + + <div class="actions"> + <button class="btn secondary" (click)="openImportTreePlanPopup('HOLDING_SCHEME')"> + <i class="vitamui-icon"></i> <span>Nouvel arbre de positionnement</span> + </button> + <button class="btn secondary" (click)="openImportTreePlanPopup('FILING_SCHEME')"> + <i class="vitamui-icon"></i> <span>Nouveau plan de classement</span> + </button> + </div> + </div> + + </div> + </div> + </mat-sidenav-content> + +</mat-sidenav-container> diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.scss b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..1ad1ac294d7917cf7e8f67008c58a0cbd5bf343f --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.scss @@ -0,0 +1,20 @@ +.holding-filling-scheme-search { + width: 100%; + .reset-filters { + padding-right: calc(100% - 700px); + margin-top: 10px; + display: inline-block; + font-family: Roboto; + font-style: normal; + font-weight: normal; + font-size: 11px; + line-height: 13px; + text-decoration-line: underline; + cursor: pointer; + } +} + +.no-background { + background-image:none; + background: #f7f8fb; +} diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.spec.ts b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b546d7afa3ed3b5cffc81c029ccff7913f023ddb --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.spec.ts @@ -0,0 +1,128 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { FormBuilder } from '@angular/forms'; + +import { MatNativeDateModule } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; +import { InjectorModule, LoggerModule, SearchBarModule } from 'ui-frontend-common'; +import { VitamUICommonTestModule } from 'ui-frontend-common/testing'; +import { environment } from '../../environments/environment'; +import { IngestService } from '../ingest/ingest.service'; +import { HoldingFillingSchemeComponent } from './holding-filling-scheme.component'; + +@Component({ selector: 'app-ingest-list', template: '' }) +class IngestListStubComponent { +} + +describe('HoldingFilingSchemeComponent', () => { + let component: HoldingFillingSchemeComponent; + let fixture: ComponentFixture<HoldingFillingSchemeComponent>; + + const ingestServiceMock = { + ingest: () => of('test ingest'), + search: () => of([]) + }; + + beforeEach(waitForAsync(() => { + const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); + matDialogSpy.open.and.returnValue({ afterClosed: () => of(true) }); + TestBed.configureTestingModule({ + imports: [ + MatDatepickerModule, + MatNativeDateModule, + MatMenuModule, + MatSidenavModule, + InjectorModule, + RouterTestingModule, + VitamUICommonTestModule, + BrowserAnimationsModule, + LoggerModule.forRoot(), + RouterTestingModule, + NoopAnimationsModule, + SearchBarModule, + MatDialogModule, + ], + declarations: [ + HoldingFillingSchemeComponent, + IngestListStubComponent + ], + providers: [ + FormBuilder, + { provide: MatDialog, useValue: matDialogSpy }, + { provide: IngestService, useValue: ingestServiceMock }, + { provide: ActivatedRoute, useValue: { params: of({ tenantIdentifier: 1 }), data: of({ appId: 'HOLDING_FILLING_SCHEME_APP' }) } }, + { provide: environment, useValue: environment } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HoldingFillingSchemeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call open', () => { + const matDialogSpy = TestBed.inject(MatDialog); + component.openImportTreePlanPopup('HOLDING_SCHEME'); + expect(matDialogSpy.open).toHaveBeenCalled(); + expect(matDialogSpy.open).toHaveBeenCalledTimes(1); + }); + + it('should open a modal with HoldingFillingSchemeComponent', () => { + const matDialogSpy = TestBed.inject(MatDialog); + component.openImportTreePlanPopup('FILING_SCHEME'); + expect(matDialogSpy.open).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.ts b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f43dc808d5db69af16de5dd30f65f79a0a39afb --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.component.ts @@ -0,0 +1,82 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { Component, OnInit } from '@angular/core'; +import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { GlobalEventService, SidenavPage } from 'ui-frontend-common'; +import { UploadComponent } from '../core/common/upload.component'; + +@Component({ + selector: 'app-holding-filling-scheme', + templateUrl: './holding-filling-scheme.component.html', + styleUrls: ['./holding-filling-scheme.component.scss'] +}) +export class HoldingFillingSchemeComponent extends SidenavPage<any> implements OnInit { + + tenantIdentifier: string; + + constructor(private router: Router, private route: ActivatedRoute, globalEventService: GlobalEventService, public dialog: MatDialog) { + super(route, globalEventService); + } + + ngOnInit() { + this.route.params.subscribe(params => { + this.tenantIdentifier = params.tenantIdentifier; + }); + } + + openImportTreePlanPopup(type: string) { + const dialogConfig = new MatDialogConfig(); + + dialogConfig.panelClass = 'vitamui-modal'; + dialogConfig.disableClose = false; + + dialogConfig.data = { + tenantIdentifier: this.tenantIdentifier, + givenContextId: type + }; + + const dialogRef = this.dialog.open(UploadComponent, dialogConfig); + + dialogRef.afterClosed().subscribe(); + } + + changeTenant(tenantIdentifier: number) { + this.router.navigate(['..', tenantIdentifier], { relativeTo: this.route }); + } + +} diff --git a/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.module.ts b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..89a5ef3fb089b74008cbb0f0563b04d0cefcb145 --- /dev/null +++ b/ui/ui-frontend/projects/ingest/src/app/holding-filling-scheme/holding-filling-scheme.module.ts @@ -0,0 +1,72 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatNativeDateModule } from '@angular/material/core'; +import { MatSidenavModule } from '@angular/material/sidenav'; + +import { VitamUICommonModule } from 'ui-frontend-common'; +import { SharedModule } from 'projects/identity/src/app/shared/shared.module'; +import { HoldingFillingSchemeComponent } from './holding-filling-scheme.component'; +import { HoldingFillingSchemeRoutingModule } from './holding-filling-scheme-routing.module'; +import { MatProgressBarModule } from '@angular/material'; +import { UploadModule } from '../core/common/upload.module'; + +@NgModule({ + imports: [ + CommonModule, + VitamUICommonModule, + MatDialogModule, + MatMenuModule, + MatSidenavModule, + SharedModule, + ReactiveFormsModule, + MatNativeDateModule, + HoldingFillingSchemeRoutingModule, + MatProgressBarModule, + UploadModule + ], + declarations: [ + HoldingFillingSchemeComponent + ], + providers: [ + ] +}) +export class HoldingFillingSchemeModule { } diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.html b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.html index 8cdaff1723ab1f539534825620e8fcebde508a5e..e4234a092155d63967bf8cadffeac0fbf835cf34 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.html +++ b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.html @@ -24,7 +24,9 @@ i18n-matTooltip="Proof safe info hint@@proofSafeInfo" matTooltipClass="vitamui-tooltip"> <i class="vitamui-icon vitamui-icon-refresh"></i> <span>Rafraîchir</span> </button> - <button class="btn more-actions" (click)="openImportSipDialog()"> + <button class="btn more-actions" (click)="openImportSipDialog('DEFAULT_WORKFLOW')" + matTooltip="Déposer un nouveau versement d'archives (SIP)" + i18n-matTooltip="Proof safe info hint@@proofSafeInfo" matTooltipClass="vitamui-tooltip"> <i class="vitamui-icon vitamui-icon-archive-ingest"></i> <span>Nouveau versement</span> </button> diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.spec.ts b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.spec.ts index 2ad50087a46dde158863b21ddcee87e17dc2f900..caf9565abcaaed948cbc3bfccf9bafe2b2ed5c94 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.spec.ts +++ b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.spec.ts @@ -110,4 +110,18 @@ describe('IngestComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call open', () => { + const matDialogSpy = TestBed.get(MatDialog); + component.openImportSipDialog('DEFULT_WORKFLOW'); + expect(matDialogSpy.open).toHaveBeenCalled(); + expect(matDialogSpy.open.calls.count()).toBe(1); + }); + + it('should open a modal with IngestComponent', () => { + const matDialogSpy = TestBed.get(MatDialog); + component.openImportSipDialog('DEFULT_WORKFLOW'); + expect(matDialogSpy.open.calls.count()).toBe(1); + expect(matDialogSpy.open).toHaveBeenCalled(); + }); }); diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.ts b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.ts index f891aad88e2a7cd7b2a577941d4565562763487d..24609811a56fe5a947d28f4639cfde5f98b0c2e4 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.ts +++ b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.component.ts @@ -40,9 +40,9 @@ import { ActivatedRoute, Router } from '@angular/router'; import { IngestListComponent } from './ingest-list/ingest-list.component'; -import { UploadSipComponent } from './upload-sip/upload-sip.component'; import { GlobalEventService, SidenavPage, SearchBarComponent } from 'ui-frontend-common'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { UploadComponent } from '../core/common/upload.component'; @Component({ selector: 'app-ingest', @@ -112,20 +112,20 @@ export class IngestComponent extends SidenavPage<any> implements OnInit { this.openPanel(item); } - openImportSipDialog() { + openImportSipDialog(type: string) { const dialogConfig = new MatDialogConfig(); dialogConfig.panelClass = 'vitamui-modal'; dialogConfig.disableClose = false; dialogConfig.data = { - tenantIdentifier: this.tenantIdentifier + tenantIdentifier: this.tenantIdentifier, + givenContextId: type }; - const dialogRef = this.dialog.open(UploadSipComponent, dialogConfig); + const dialogRef = this.dialog.open(UploadComponent, dialogConfig); dialogRef.afterClosed().subscribe((result) => { if (result) { - this.refresh(); } }); } diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.module.ts b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.module.ts index 15c964c903ee96c18e487fd42341869a07f4af69..c2d21800ce84e0f79d8abe7f22c3ff30170730c6 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.module.ts +++ b/ui/ui-frontend/projects/ingest/src/app/ingest/ingest.module.ts @@ -45,11 +45,11 @@ import { MatSidenavModule } from '@angular/material/sidenav'; import { VitamUICommonModule } from 'ui-frontend-common'; import { IngestComponent } from './ingest.component'; -import { UploadSipModule } from './upload-sip/upload-sip.module'; import { SharedModule } from 'projects/identity/src/app/shared/shared.module'; import { IngestListModule } from './ingest-list/ingest-list.module'; import { IngestRoutingModule } from './ingest-routing.module'; import { IngestPreviewModule } from './ingest-preview/ingest-preview.module'; +import { UploadModule } from '../core/common/upload.module'; @NgModule({ imports: [ @@ -59,7 +59,7 @@ import { IngestPreviewModule } from './ingest-preview/ingest-preview.module'; MatMenuModule, MatSidenavModule, IngestRoutingModule, - UploadSipModule, + UploadModule, SharedModule, IngestListModule, IngestPreviewModule, diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.service.ts b/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.service.ts index 8a7e45d0adbc217c71b5ecbcab65513084575ac6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.service.ts +++ b/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.service.ts @@ -1,160 +0,0 @@ -/* - * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) - * and the signatories of the "VITAM - Accord du Contributeur" agreement. - * - * contact@programmevitam.fr - * - * This software is a computer program whose purpose is to implement - * implement a digital archiving front-office system for the secure and - * efficient high volumetry VITAM solution. - * - * This software is governed by the CeCILL-C license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-C - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-C license and that you accept its terms. - */ -import {Injectable} from '@angular/core'; -import { HttpRequest, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { IngestApiService } from '../../core/api/ingest-api.service'; -import { retry } from 'rxjs/operators'; -import { Observable, Subject } from 'rxjs'; - - -const BYTES_PER_CHUNK = 1024 * 1024; // 1MB request -const tenantKey = 'X-Tenant-Id'; -const contextIdKey = 'X-Context-Id'; -const actionKey = 'X-Action'; -const chunkOffsetKey = 'X-Chunk-Offset'; -const totalSizeKey = 'X-Size-Total'; -const requestIdKey = 'X-Request-Id'; - -const MAX_RETRIES = 3; - -@Injectable() -export class UploadSipService { - - uploadComplete = new Subject<boolean>(); - - constructor( private ingestApiService: IngestApiService) { - } - - - uploadFile(file: File, contextId: string, action: string, tenantIdentifier: string): Observable<boolean> { - const totalSize = file.size; - let start = 0; - let end = (totalSize < BYTES_PER_CHUNK) ? totalSize : BYTES_PER_CHUNK; - - const request = this.generateIngestRequest(tenantIdentifier, contextId, action, start, end, totalSize, file); - - this.ingestApiService.upload(request) - .pipe( - retry(MAX_RETRIES)) - .subscribe( - (event) => { - if (event instanceof HttpResponse) { - // We get the requestId with the first request. - const requestId = event.headers.get(requestIdKey); - console.log('First API Request Id : ' + requestId); - start = end; - end = start + BYTES_PER_CHUNK; - - if (start >= totalSize) { - this.uploadComplete.next(true); - - return; - } - - for (let pointer = start; pointer < totalSize; pointer += BYTES_PER_CHUNK) { - this.uploadChunks(file, requestId, pointer, pointer + BYTES_PER_CHUNK, totalSize, tenantIdentifier, contextId, action); - } - - this.uploadComplete.next(true); - - } - }, - (error) => { - console.log(error); - this.uploadComplete.next(false); - } - ); - return this.uploadComplete; - } - - private uploadChunks( - file: File, - requestId: any, - start: number, - end: number, - totalSize: any, - tenantIdentifier: string, - contextId: string, - action: string - ) { - - const request = this.generateIngestRequest(tenantIdentifier, contextId, action, start, end, totalSize, file, requestId); - - this.ingestApiService.upload(request) - .pipe( - retry(MAX_RETRIES)) - .subscribe( - (event) => { - if (event instanceof HttpResponse) { - const requestIdd = event.headers.get(requestIdKey); - console.log('Request Id: ' + requestIdd); - } - }, - (error) => { - console.log(error); - this.uploadComplete.next(false); - } - ); - } - - private generateIngestRequest( - tenantIdentifier: string, - contextId: string, - action: string, - start: number, - end: number, - totalSize: number, - file: File, - requestId?: string - ): HttpRequest<FormData> { - let headers = new HttpHeaders(); - headers = headers.set(tenantKey, tenantIdentifier.toString()); - headers = headers.set(chunkOffsetKey, start.toString()); - headers = headers.set(totalSizeKey, totalSize.toString()); - headers = headers.set(contextIdKey, contextId); - headers = headers.set(actionKey, action); - if (requestId) { - headers = headers.set(requestIdKey, requestId); - } - - const formdata: FormData = new FormData(); - formdata.append('uploadedFile', file.slice(start, end), file.name); - - return new HttpRequest('POST', this.ingestApiService.getBaseUrl() + '/ingest/upload', formdata, { headers }); - } - -} diff --git a/ui/ui-frontend/projects/ingest/src/app/shared/vitamui-snack-bar/vitamui-snack-bar.component.html b/ui/ui-frontend/projects/ingest/src/app/shared/vitamui-snack-bar/vitamui-snack-bar.component.html index 5b596593c7439370d73ef93d28ac8ed6b5613c4a..b5e8d649af14072534e4c13bcffb76901d5d692d 100644 --- a/ui/ui-frontend/projects/ingest/src/app/shared/vitamui-snack-bar/vitamui-snack-bar.component.html +++ b/ui/ui-frontend/projects/ingest/src/app/shared/vitamui-snack-bar/vitamui-snack-bar.component.html @@ -2,10 +2,10 @@ <span class="vitamui-snack-bar-content" [ngSwitch]="data?.type"> - <ng-container *ngSwitchCase="'sipUploaded'"> - <i class="vitamui-icon vitamui-icon-archive-ingest"></i> Un ou plusieurs versement(s) sont en cours + <ng-container *ngSwitchCase="'fileUploaded'"> + <i class="vitamui-icon vitamui-icon-archive-ingest"></i> Un ou plusieurs versement(s) sont en cours de téléchargement. <br/> - <span>(Merci de ne pas éteindre votre navigateur pendant le processus)</span> + <span>(Merci de ne pas quitter cette page pendant le processus et d'ouvrir un nouvel onglet si besoin)</span> </ng-container> </span> diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.html b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.html index 39d55c64f78650678b38eebf6f79d1c5d5ef214c..33cb97350756a6cd060b0d50e1f8dcc510fe75b0 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.html +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.html @@ -74,12 +74,17 @@ <div class="form-group"> - <app-context-edit-permission formControlName="permissions"></app-context-edit-permission> + <app-context-edit-permission formControlName="permissions" + (changeOrganisations)="onChangeOrganisations($event)"></app-context-edit-permission> + <div *ngIf="isPermissionsOnMultipleOrganisations"> + <span class="error-message" i18n="Multiple organisation warning hint@@formWarningMultipleOrganisations">Attention vous avez renseigné deux organisations différentes</span> + </div> <ng-container> <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.permissionsTenant" i18n="Required tenant error hint@@formErrorRequiredField">Un tenant doit être sélectionné pour chaque permission</span> <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.noPermissions" i18n="Required permissions error hint@@formErrorRequiredField">Au moins une permission doit être renseignée</span> + </ng-container> </div> diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.ts b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.ts index 2ee163175bd4dd7f10ae55c7c3e0919ccb07be54..80e67d0a4fde48a8810560ccf322020e8c2f2052 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.component.ts @@ -54,11 +54,13 @@ const PROGRESS_BAR_MULTIPLICATOR = 100; export class ContextCreateComponent implements OnInit, OnDestroy { form: FormGroup; + statusControl = new FormControl(false); stepIndex = 0; accessContractInfo: { code: string, name: string, companyName: string } = {code: '', name: '', companyName: ''}; hasCustomGraphicIdentity = false; hasError = true; message: string; + isPermissionsOnMultipleOrganisations = false; // stepCount is the total number of steps and is used to calculate the advancement of the progress bar. // We could get the number of steps using ViewChildren(StepComponent) but this triggers a @@ -82,8 +84,6 @@ export class ContextCreateComponent implements OnInit, OnDestroy { ) { } - statusControl = new FormControl(false); - ngOnInit() { this.form = this.formBuilder.group({ status: ['INACTIVE'], @@ -152,6 +152,22 @@ export class ContextCreateComponent implements OnInit, OnDestroy { return this.form.get('permissions').invalid; } + onChangeOrganisations(organisations: string[]) { + this.isPermissionsOnMultipleOrganisations = false; + if (organisations && organisations.length > 1) { + let idx = 0; + let organisationId: string = null; + while (idx < organisations.length && !this.isPermissionsOnMultipleOrganisations) { + if (idx === 0) { + organisationId = organisations[0]; + } else if (organisations[idx] != null && organisations[idx] !== organisationId) { + this.isPermissionsOnMultipleOrganisations = true; + } + idx++; + } + } + } + get stepProgress() { return ((this.stepIndex + 1) / this.stepCount) * PROGRESS_BAR_MULTIPLICATOR; } diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.module.ts b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.module.ts index f6637a1121f683192dd2e4cc50739d6202f15d53..6258fbd6700148a07564b1006f68b60de6875be5 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.module.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-create.module.ts @@ -48,9 +48,9 @@ import {MatTooltipModule} from '@angular/material/tooltip'; import {VitamUICommonModule, VitamUIFieldErrorModule} from 'ui-frontend-common'; import {TenantService} from '../../../../../identity/src/app/customer/tenant.service'; import {SharedModule} from '../../../../../identity/src/app/shared/shared.module'; +import {ContextEditPermissionModule} from '../context-create/context-edit-permission/context-edit-permission.module'; import {ContextCreateComponent} from './context-create.component'; import {ContextCreateValidators} from './context-create.validators'; -import {ContextEditPermissionModule} from './context-edit-permission/context-edit-permission.module'; @NgModule({ imports: [ diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.html b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.html index ec3228ac7ca92bb339f022b2fd30dc94a9d40652..6df7233ab92d816970b96b43994bb01250c81c3b 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.html +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.html @@ -2,7 +2,8 @@ <table class="vitamui-table form-group"> <thead> <tr class="ng-tns-c10-3 "> - <th ></th><!-- Delete ? --> + <th class="delete"></th><!-- Delete ? --> + <th >Organisations</th> <th >Tenants</th> <th >Contrats d'accès</th> <th >Contrats d'entrée</th> @@ -11,16 +12,31 @@ </thead> <tbody > <tr class="dlab-table-row ng-tns-c10-3 ng-star-inserted no-hover" *ngFor="let permission of permissions; let i = index"> - <td style="padding-left: 25px" [ngClass]="editMode ? 'editTd' : ''"> + <td class="delete" [ngClass]="editMode ? 'editTd' : ''"> <i (click)="onDelete(i)" class="vitamui-icon vitamui-icon-cross clickable-icon"></i> </td> - <td [ngClass]="editMode ? 'editTd' : ''"> + <td [ngClass]="editMode ? 'editTd' : 'defaultTd'" *ngIf="selectedOrganisations?.length > i"> + <mat-form-field class="vitamui-mat-select customers selectorPosition" [ngClass]="editMode? 'editMode' : ''"> + <mat-select [(value)]="selectedOrganisations[i]" placeholder="Organisation" (selectionChange)="onCustomerSelect(permission)" + i18n-placeholder="Application@@customersFormApplicationPlaceholder" panelclass="vitamui-mat-select" + required> + <mat-option *ngFor='let customer of customers' [value]="customer.id"> + {{customer.name}} + </mat-option> + </mat-select> + <div class="select-arrow"> + <i class="material-icons">keyboard_arrow_down</i> + </div> + </mat-form-field> + </td> + <td *ngIf="selectedOrganisations?.length > i && selectedOrganisations[i] !== null" [ngClass]="editMode ? 'editTd' : 'defaultTd'"> <mat-form-field class="vitamui-mat-select tenants selectorPosition" [ngClass]="editMode? 'editMode' : ''"> - <mat-select [(value)]="permission.tenant" placeholder="Tenant" (selectionChange)="onTenantSelect()" + <mat-select [(value)]="permission.tenant" [compareWith]="compareTenantIds" + placeholder="Tenant" (selectionChange)="onTenantSelect(permission)" i18n-placeholder="Application@@tenantsFormApplicationPlaceholder" panelclass="vitamui-mat-select" required> - <mat-option *ngFor='let tenant of tenants' [value]="tenant.key"> - {{tenant.label}} + <mat-option *ngFor='let tenant of getTenantsForOrganisation(selectedOrganisations[i])' [value]="tenant.identifier"> + {{tenant.name}} </mat-option> </mat-select> <div class="select-arrow"> @@ -28,7 +44,7 @@ </div> </mat-form-field> </td> - <td *ngIf="permission.tenant" style="padding-left: 0" [ngClass]="editMode ? 'editTd' : ''"> + <td *ngIf="permission.tenant" [ngClass]="editMode ? 'editTd' : 'defaultTd'"> <mat-form-field class="vitamui-mat-select contractsSelect selectorPosition" [ngClass]="editMode? 'editMode editContract' : ''"> <mat-select [(value)]="permission.accessContracts" placeholder="Contrats d'accès" (selectionChange)="onContractSelect()" i18n-placeholder="Application@@accessContractsFormApplicationPlaceholder" panelclass="vitamui-mat-select" @@ -37,7 +53,7 @@ title="Tous les contrats d'accès" i18n-title="Application@@accessContractsFormApplicationSelectAll" (toggleSelection)="permission.accessContracts = $event"> </vitamui-select-all-option> - <mat-option *ngFor='let contract of accessContracts.get(permission.tenant)' [value]="contract.key"> + <mat-option *ngFor="let contract of accessContracts.get('' + permission.tenant)" [value]="contract.key"> {{contract.label}} </mat-option> </mat-select> @@ -46,7 +62,7 @@ </div> </mat-form-field> </td> - <td *ngIf="permission.tenant"> + <td *ngIf="permission.tenant" class="ingestContracts" [ngClass]="editMode ? 'editTd' : 'defaultTd'"> <mat-form-field class="vitamui-mat-select contractsSelect selectorPosition" [ngClass]="editMode? 'editMode' : ''"s> <mat-select [(value)]="permission.ingestContracts" placeholder="Contrats d'entrée" (selectionChange)="onContractSelect()" i18n-placeholder="Application@@accessContractsFormApplicationPlaceholder" panelclass="vitamui-mat-select" @@ -55,7 +71,7 @@ title="Tous les contrats d'entrée" i18n-title="Application@@ingestContractsFormApplicationSelectAll" (toggleSelection)="permission.ingestContracts = $event"> </vitamui-select-all-option> - <mat-option *ngFor='let contract of ingestContracts.get(permission.tenant)' [value]="contract.key"> + <mat-option *ngFor="let contract of ingestContracts.get('' + permission.tenant)" [value]="contract.key"> {{contract.label}} </mat-option> </mat-select> @@ -66,7 +82,7 @@ </td> <td *ngIf="!permission.tenant"></td> <td *ngIf="!permission.tenant"></td> - <td [ngClass]="editMode ? 'editTd' : ''"> + <td *ngIf="permission.tenant" [ngClass]="editMode ? 'editTd' : 'defaultTd'"> <i class="material-icons field-tooltip" matTooltip="Choisir pour chaque tenant les contrats qui vont s'appliquer" matTooltipClass="vitamui-tooltip">info</i> </td> </tr> diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.scss b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.scss index 7f1d7a750d3164f6b43616cc930ae751e4da7f09..eb332f7fff1f1ef204e666b12236147d334d7c88 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.scss +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.scss @@ -29,12 +29,26 @@ } .contractsSelect { - max-width: 190px; + max-width: 150px; +} + +.delete { + padding-left: 0px !important; + padding-right: 10px; +} + +.customers { + max-width: 150px; } + .tenants { max-width: 150px; } +.ingestContracts { + padding-right: 0px; +} + .editMode { max-width: 100px; margin-left: 5px; @@ -52,4 +66,8 @@ td{ mat-form-field{ padding-bottom: 0; } -} \ No newline at end of file +} + +.defaultTd { + padding-left: 0 !important; +} diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.ts b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.ts index 8d450b7b8899fa728dea8464e9ee024ffabc8bfa..289c22ff88d859dff35d3f5a7e20dfecc116e61f 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context-create/context-edit-permission/context-edit-permission.component.ts @@ -36,12 +36,14 @@ */ /* tslint:disable:no-use-before-declare */ -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ContextPermission } from 'projects/vitamui-library/src/public-api'; import { AuthService, Option } from 'ui-frontend-common'; -import { Tenant } from 'ui-frontend-common/app/modules/models/customer'; +import { Customer, Tenant } from 'ui-frontend-common/app/modules/models/customer'; import { AccessContractService } from '../../../access-contract/access-contract.service'; +import { CustomerApiService } from '../../../core/api/customer-api.service'; +import { TenantApiService } from '../../../core/api/tenant-api.service'; import { IngestContractService } from '../../../ingest-contract/ingest-contract.service'; export const CONTEXT_PERMISSION_VALUE_ACCESSOR: any = { @@ -59,17 +61,23 @@ export const CONTEXT_PERMISSION_VALUE_ACCESSOR: any = { export class ContextEditPermissionComponent implements ControlValueAccessor, OnInit { constructor( + private customerApiService: CustomerApiService, + private tenantApiService: TenantApiService, + private authService: AuthService, private accessService: AccessContractService, - private ingestService: IngestContractService, - private authService: AuthService) { + private ingestService: IngestContractService) { } permissions: ContextPermission[]; + selectedOrganisations: string[]; + @Input() editMode = false; + @Output() changeOrganisations: EventEmitter<string[]> = new EventEmitter<string[]>(); + tenants: Tenant[] = []; disabled: boolean; - tenants: Option[] = []; + customers: Customer[] = []; accessContracts: Map<string, Option[]> = new Map(); ingestContracts: Map<string, Option[]> = new Map(); // tslint:disable-next-line:variable-name @@ -79,14 +87,39 @@ export class ContextEditPermissionComponent implements ControlValueAccessor, OnI } ngOnInit(): void { + // Get the list of all the organisations + // and find for each permission the tenant organisation + this.tenantApiService.getAll().subscribe(tenants => { + this.tenants = tenants ? tenants : []; + + this.customerApiService.getAll().subscribe(customers => { + this.customers = customers ? customers : []; + this.customers.sort((c1, c2) => c1.name.localeCompare(c2.name)); + + if (this.permissions && this.permissions.length > 0) { + this.selectedOrganisations = new Array<string>(); + this.permissions.forEach(permission => { + let tenant: Tenant; + if (permission.tenant != null) { + tenant = this.tenants.find(t => '' + t.identifier === permission.tenant); + } + + if (tenant) { + this.selectedOrganisations.push(tenant.customerId); + } else { + this.selectedOrganisations.push(null); + } + }); + } + }); + }); + if (this.authService.user) { + // Get the access contracts const accessTenantsInfo = this.authService.user.tenantsByApp.find( appTenantInfo => appTenantInfo.name === 'ACCESS_APP'); const accessTenants: Tenant[] = accessTenantsInfo ? accessTenantsInfo.tenants : []; accessTenants.forEach((tenant) => { - if (!this.tenants.find(appTenant => appTenant.key === '' + tenant.identifier)) { - this.tenants.push({ key: '' + tenant.identifier, label: tenant.name }); - } this.accessService.getAllForTenant('' + tenant.identifier).subscribe( accessContracts => { this.accessContracts.set('' + tenant.identifier, accessContracts.map(x => ({ @@ -96,13 +129,11 @@ export class ContextEditPermissionComponent implements ControlValueAccessor, OnI }); }); + // Get the ingest contracts const ingestTenantsInfo = this.authService.user.tenantsByApp.find( appTenantInfo => appTenantInfo.name === 'INGEST_APP'); const ingestTenants: Tenant[] = ingestTenantsInfo ? ingestTenantsInfo.tenants : []; ingestTenants.forEach((tenant) => { - if (!this.tenants.find(appTenant => appTenant.key === '' + tenant.identifier)) { - this.tenants.push({ key: '' + tenant.identifier, label: tenant.name }); - } this.ingestService.getAllForTenant('' + tenant.identifier).subscribe( ingestContracts => { this.ingestContracts.set('' + tenant.identifier, ingestContracts.map(x => ({ @@ -111,23 +142,44 @@ export class ContextEditPermissionComponent implements ControlValueAccessor, OnI }))); }); }); - - this.tenants.sort((t1, t2) => t1.label.localeCompare(t2.label)); } } onDelete(index: number) { this.permissions.splice(index, 1); + this.selectedOrganisations.splice(index, 1); + this.changeOrganisations.emit(this.selectedOrganisations); if (this.onChange) { this.onChange(this.permissions); } + } onAdd() { this.permissions.push({ tenant: '', accessContracts: [], ingestContracts: [] }); + this.selectedOrganisations.push(null); + this.changeOrganisations.emit(this.selectedOrganisations); + if (this.onChange) { + this.onChange(this.permissions); + } + } + + compareTenantIds(a: any, b: any) { + return '' + a === '' + b; + } + + getTenantsForOrganisation(customerId: string): Tenant[] { + return this.tenants.filter(tenant => tenant.customerId === customerId); + } + + onCustomerSelect(permission: ContextPermission) { + permission.tenant = ''; + permission.accessContracts = []; + permission.ingestContracts = []; if (this.onChange) { this.onChange(this.permissions); } + this.changeOrganisations.emit(this.selectedOrganisations); } onTenantSelect() { @@ -165,13 +217,13 @@ export class ContextEditPermissionComponent implements ControlValueAccessor, OnI this.disabled = isDisabled; } - getAccessContractKeys(tenant: string): string[] { - const contracts: Option[] = this.accessContracts.get(tenant); - return contracts != null ? contracts.map(item => item.key) : []; + getAccessContractKeys(tenantId: number): string[] { + const contracts: Option[] = this.accessContracts.get('' + tenantId); + return contracts != null ? contracts.map(item => item.key) : []; } - getIngestContractKeys(tenant: string) { - const contracts: Option[] = this.ingestContracts.get(tenant); - return contracts != null ? contracts.map(item => item.key) : []; + getIngestContractKeys(tenantId: number): string[] { + const contracts: Option[] = this.ingestContracts.get('' + tenantId); + return contracts != null ? contracts.map(item => item.key) : []; } } diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.html b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e0b839176b2fb69d2852a7a2fcfd34f983c4d008 --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.html @@ -0,0 +1,36 @@ +<div class="header"> + <mat-progress-bar mode="determinate" [value]="stepProgress" class="stepper-progress-bar"></mat-progress-bar> + </div> + + <form [formGroup]="form" (ngSubmit)="onSubmit()"> + <vitamui-common-stepper (selectionChange)="stepIndex=$event.selectedIndex"> + <cdk-step> + <div class="content"> + <h2 i18n="Edit context title@@contextEditTitle1">Modification d'un contexte applicatif</h2> + <h3 i18n="Edit context title@@contextEditTitle2">Permissions</h3> + + + <div class="form-group"> + <app-context-edit-permission formControlName="permissions" + (changeOrganisations)="onChangeOrganisations($event)"></app-context-edit-permission> + <div *ngIf="isPermissionsOnMultipleOrganisations"> + <span class="error-message" i18n="Multiple organisation warning hint@@formWarningMultipleOrganisations">Attention vous avez renseigné deux organisations différentes</span> + </div> + <ng-container> + <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.permissionsTenant" + i18n="Required tenant error hint@@formErrorRequiredField">Un tenant doit être sélectionné pour chaque permission</span> + <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.noPermissions" + i18n="Required permissions error hint@@formErrorRequiredField">Au moins une permission doit être renseignée</span> + + </ng-container> + </div> + + <button type="submit" class="btn primary" [disabled]="isPermissionsInvalid()" + i18n="Finish context edition button@@contextEditFinishButton">Terminer</button> + <button type="button" class="btn cancel" (click)="onCancel()" + i18n="Cancel context creation@@contextEditCancelButton">Annuler</button> + </div> + </cdk-step> + </vitamui-common-stepper> + </form> + \ No newline at end of file diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.scss b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..7b7bac6d545997a02e9e98debf2c8d8634a7c4df --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.scss @@ -0,0 +1,41 @@ +@import '~ui-frontend-common/sass/variables/colors'; + +$field-spacing: 15px; + +.header { + padding: 30px 60px 0 60px; +} + +.content { + padding: 0px 60px 60px 60px; +} + +h2 { + font-size: 26px; + line-height: 46px; + margin-top: 23px; + margin-bottom: 37px; +} + +.actions { + margin-top: 20px; + display: flex; +} + +.actions > button:not(:last-child) { + margin-right: 20px; +} + +.form-group { + padding-bottom: 30px; +} + +button.primary { + margin-right: 20px; +} + +.error-message { + color: $red; + font-size: 14px; + margin-bottom: 30px; +} diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.ts b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0820c64ce142582d52f8867702f356af1b716903 --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.component.ts @@ -0,0 +1,148 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ContextPermission } from 'projects/vitamui-library/src/public-api'; +import { Subscription } from 'rxjs'; +import { ConfirmDialogService } from 'ui-frontend-common'; +import { ContextCreateValidators } from '../context-create/context-create.validators'; + +const PROGRESS_BAR_MULTIPLICATOR = 100; + +@Component({ + selector: 'app-context-edit', + templateUrl: './context-edit.component.html', + styleUrls: ['./context-edit.component.scss'] +}) + +export class ContextEditComponent implements OnInit, OnDestroy { + + form: FormGroup; + stepIndex = 0; + hasError = true; + message: string; + isPermissionsOnMultipleOrganisations = false; + + // stepCount is the total number of steps and is used to calculate the advancement of the progress bar. + // We could get the number of steps using ViewChildren(StepComponent) but this triggers a + // "Expression has changed after it was checked" error so we instead manually define the value. + // Make sure to update this value whenever you add or remove a step from the template. + private stepCount = 1; + private keyPressSubscription: Subscription; + + private permissions: ContextPermission[]; + + constructor( + public dialogRef: MatDialogRef<ContextEditComponent>, + @Inject(MAT_DIALOG_DATA) public data: ContextPermission[], + private formBuilder: FormBuilder, + private confirmDialogService: ConfirmDialogService, + private contextCreateValidators: ContextCreateValidators, + ) { + this.permissions = data; + + let permissions: any[] = new Array(); + if (this.permissions) { + permissions = this.permissions.map(permission => { + return { + tenant: permission.tenant, + accessContracts: permission.accessContracts, + ingestContracts: permission.ingestContracts + }; + }); + } + + this.form = this.formBuilder.group({ + permissions: [permissions, null, this.contextCreateValidators.permissionInvalid()] + }); + } + + ngOnInit() { + this.keyPressSubscription = this.confirmDialogService.listenToEscapeKeyPress(this.dialogRef).subscribe(() => this.onCancel()); + } + + ngOnDestroy() { + this.keyPressSubscription.unsubscribe(); + } + + onCancel() { + if (this.form.dirty) { + this.confirmDialogService.confirmBeforeClosing(this.dialogRef); + } else { + this.dialogRef.close(); + } + } + + isPermissionsInvalid(): boolean { + return this.form.get('permissions').invalid; + } + + onChangeOrganisations(organisations: string[]) { + this.isPermissionsOnMultipleOrganisations = false; + if (organisations && organisations.length > 1) { + let idx = 0; + let organisationId: string = null; + while (idx < organisations.length && !this.isPermissionsOnMultipleOrganisations) { + if (idx === 0) { + organisationId = organisations[0]; + } else if (organisations[idx] != null && organisations[idx] !== organisationId) { + this.isPermissionsOnMultipleOrganisations = true; + } + idx++; + } + } + } + + onSubmit() { + if (this.form.invalid) { + return; + } + + // Update the context permissions + this.permissions = this.form.value.permissions.map((item: + { tenant: string; accessContracts: string[]; ingestContracts: string[]; }) => + new ContextPermission('' + item.tenant, item.accessContracts, item.ingestContracts) + ); + + this.dialogRef.close({ permissions: this.permissions }); + } + + get stepProgress() { + return ((this.stepIndex + 1) / this.stepCount) * PROGRESS_BAR_MULTIPLICATOR; + } +} diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.module.ts b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4868c6e505ab3213faa95a1700cea8ea068cb66 --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/context/context-edit/context-edit.module.ts @@ -0,0 +1,77 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; +import {MatButtonToggleModule} from '@angular/material/button-toggle'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {MatSelectModule} from '@angular/material/select'; +import {MatSnackBarModule} from '@angular/material/snack-bar'; +import {MatTooltipModule} from '@angular/material/tooltip'; + +import {VitamUICommonModule, VitamUIFieldErrorModule} from 'ui-frontend-common'; +import {TenantService} from '../../../../../identity/src/app/customer/tenant.service'; +import {SharedModule} from '../../../../../identity/src/app/shared/shared.module'; +import {ContextEditPermissionModule} from '../context-create/context-edit-permission/context-edit-permission.module'; +import {ContextEditComponent} from './context-edit.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + MatButtonToggleModule, + MatFormFieldModule, + MatInputModule, + MatProgressBarModule, + MatSelectModule, + MatSnackBarModule, + MatTooltipModule, + ReactiveFormsModule, + VitamUICommonModule, + VitamUIFieldErrorModule, + ContextEditPermissionModule + ], + declarations: [ + ContextEditComponent + ], + entryComponents: [ContextEditComponent], + providers: [TenantService] +}) +export class ContextEditModule { +} diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.html b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.html index 0bb5f59568b739f13b7a03d13403f9f44699a714..aa2dc1f508449ab089a07387e20930924f74e59a 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.html +++ b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.html @@ -1,14 +1,35 @@ -<form [formGroup]="form" class="side-form" (ngSubmit)="onSubmit()"> +<div class="side-form" *ngIf="dataLoaded"> + <div class="permission" *ngFor="let permission of updatedPermissions"> + <ul> + <li> + <span class="label">Organisation : </span> + {{ getOrganisationName(permission.tenant) }} + </li> + <li> + <span class="label">Tenant : </span> + {{ tenants?.get(permission.tenant)?.name }} + </li> + <li *ngIf="permission.accessContracts?.length > 0"> + <span class="label">Contrat(s) d'entrée</span> + {{ getAccessContractNames(permission.accessContracts) }}</li> + <li *ngIf="permission.ingestContracts?.length > 0"> + <span class="label">Contrat(s) d'accès : </span> + {{ getIngestContractNames(permission.ingestContracts) }} + </li> + </ul> + <mat-divider></mat-divider> + </div> - <app-context-edit-permission formControlName="permissions" [editMode]="true"></app-context-edit-permission> - <ng-container> - <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.permissionsTenant" - i18n="Required tenant error hint@@formErrorRequiredField">Un tenant doit être sélectionné pour chaque permission</span> - <span class="error-message" *ngIf="!!form.get('permissions')?.errors?.noPermissions" - i18n="Required permissions error hint@@formErrorRequiredField">Au moins une permission doit être renseignée</span> - </ng-container> + <div class="actions" *ngIf="!readOnly"> + <button class="btn secondary" (click)="openEditContextDialog()"> + <i class="btn-create"></i> + <span i18n="Update context modify permission button label@@contextUpdatePermissionModifyButton">Modifier</span> + </button> + </div> - <div class="form-group bottom"> - <button type="submit" class="btn secondary" [disabled]="isInvalidOrUnchanged() || submited">Enregistrer</button> + <div class="bottom"> + <button type="submit" class="btn secondary" [disabled]="readOnly || unchanged || submited" (click)="onSubmit()"> + <span i18n="Update context save permission button label@@contextUpdatePermissionSaveButton">Enregistrer</span> + </button> </div> -</form> +</div> \ No newline at end of file diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.scss b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.scss index 4738f900e01e01459d12cf01db9a1097e58b37bc..422aa1a1764c79d8987bee17c5df5d7dd9487208 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.scss +++ b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.scss @@ -5,5 +5,15 @@ .bottom { margin-top: auto; + padding-top: 20px; } -} \ No newline at end of file +} + + +.permission { + padding-bottom: 10px; +} + +.actions { + padding-top: 10px; +} diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.ts b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.ts index a08fd88d004436c9b3f0d244316efb57da4e9be6..43c2cc62119eb72724f5e4b59f0e6801bc69d293 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-permission-tab/context-permission-tab.component.ts @@ -34,37 +34,49 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; import { Context, ContextPermission } from 'projects/vitamui-library/src/public-api'; -import { Observable, of } from 'rxjs'; -import { catchError, filter, map, switchMap } from 'rxjs/operators'; -import { diff } from 'ui-frontend-common'; +import { AccessContract } from 'projects/vitamui-library/src/public-api'; +import { IngestContract } from 'projects/vitamui-library/src/public-api'; +import { forkJoin, Observable, of } from 'rxjs'; +import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; +import { AuthService, Customer, diff, Tenant } from 'ui-frontend-common'; import { extend, isEmpty } from 'underscore'; - -import { ContextCreateValidators } from '../../context-create/context-create.validators'; +import { AccessContractService } from '../../../access-contract/access-contract.service'; +import { CustomerApiService } from '../../../core/api/customer-api.service'; +import { TenantApiService } from '../../../core/api/tenant-api.service'; +import { IngestContractService } from '../../../ingest-contract/ingest-contract.service'; +import { ContextEditComponent } from '../../context-edit/context-edit.component'; import { ContextService } from '../../context.service'; - @Component({ selector: 'app-context-permission-tab', templateUrl: './context-permission-tab.component.html', styleUrls: ['./context-permission-tab.component.scss'] }) -export class ContextPermissionTabComponent { +export class ContextPermissionTabComponent implements OnInit { @Output() updated: EventEmitter<boolean> = new EventEmitter<boolean>(); - form: FormGroup; - submited = false; + unchanged = true; + dataLoaded = false; + isPermissionsOnMultipleOrganisations = false; + // The initial context beforme modification // tslint:disable-next-line:variable-name private _context: Context; + // The updated permissions + private updatedPermissions: ContextPermission[] = new Array(); - previousValue = (): Context => { - return this._context; - } + tenants: Map<string, Tenant> = new Map(); + organisations: Map<string, Customer> = new Map(); + accessContracts: Map<string, AccessContract> = new Map(); + ingestContracts: Map<string, IngestContract> = new Map(); + + @Input() + readOnly: boolean; @Input() set context(context: Context) { @@ -85,25 +97,79 @@ export class ContextPermissionTabComponent { this.resetForm(this.context); this.updated.emit(false); } + get context(): Context { return this._context; } - @Input() - set readOnly(readOnly: boolean) { - if (readOnly && this.form.enabled) { - this.form.disable({ emitEvent: false }); - } else if (this.form.disabled) { - this.form.enable({ emitEvent: false }); - this.form.get('identifier').disable({ emitEvent: false }); - } + previousValue = (): Context => { + return this._context; } constructor( - private formBuilder: FormBuilder, + public dialog: MatDialog, private contextService: ContextService, - private contextCreateValidators: ContextCreateValidators - ) { - this.form = this.formBuilder.group({ - permissions: [[], Validators.required, this.contextCreateValidators.permissionInvalid()] + private customerApiService: CustomerApiService, + private tenantApiService: TenantApiService, + private authService: AuthService, + private accessService: AccessContractService, + private ingestService: IngestContractService + ) { } + + ngOnInit(): void { + let accessContractObservable: Observable<any> = of(); + let ingestContractObservable: Observable<any> = of(); + + // Build the forkJoins used to get the access and ingest contracts + if (this.authService.user) { + // Get the access contracts + const accessTenantsInfo = this.authService.user.tenantsByApp.find( + appTenantInfo => appTenantInfo.name === 'ACCESS_APP'); + if (accessTenantsInfo && accessTenantsInfo.tenants && accessTenantsInfo.tenants.length > 0) { + accessContractObservable = forkJoin(accessTenantsInfo.tenants.map(tenant => { + return this.accessService.getAllForTenant('' + tenant.identifier).pipe( + tap((accessContracts) => { + accessContracts.forEach(accessContract => { + this.accessContracts.set(accessContract.identifier, accessContract); + }); + }) + ); + })); + } + + // Get the ingest contracts + const ingestTenantsInfo = this.authService.user.tenantsByApp.find( + appTenantInfo => appTenantInfo.name === 'INGEST_APP'); + if (ingestTenantsInfo && ingestTenantsInfo.tenants && ingestTenantsInfo.tenants.length > 0) { + ingestContractObservable = forkJoin(ingestTenantsInfo.tenants.map(tenant => { + return this.ingestService.getAllForTenant('' + tenant.identifier).pipe( + tap((ingestContracts) => { + ingestContracts.forEach(ingestContract => { + this.ingestContracts.set(ingestContract.identifier, ingestContract); + }); + }) + ); + })); + } + } + + // Get the list of all the organisations and tenants + forkJoin([ + this.tenantApiService.getAll(), + this.customerApiService.getAll(), + accessContractObservable, + ingestContractObservable + ]).subscribe(([tenants, customers]) => { + if (tenants && tenants.length > 0) { + tenants.forEach(tenant => { + this.tenants.set('' + tenant.identifier, tenant); + }); + } + if (customers && customers.length > 0) { + customers.forEach(customer => { + this.organisations.set(customer.id, customer); + }); + } + + this.dataLoaded = true; }); } @@ -148,23 +214,13 @@ export class ContextPermissionTabComponent { return true; } - isInvalidOrUnchanged(): boolean { - const unchanged = this.samePermission(this.previousValue().permissions, this.form.getRawValue().permissions); - this.updated.emit(!unchanged); - - if (this.isInvalid()) { - return true; - } - - return unchanged; - } - - isInvalid(): boolean { - return this.form.controls.permissions.invalid; + hasChanged() { + this.unchanged = this.samePermission(this.previousValue().permissions, this.updatedPermissions); + this.updated.emit(!this.unchanged); } prepareSubmit(): Observable<Context> { - return of(diff(this.form.getRawValue(), this.previousValue())).pipe( + return of(diff({permissions: this.updatedPermissions}, this.previousValue())).pipe( filter((formData) => !isEmpty(formData)), map((formData) => extend({ id: this.previousValue().id, identifier: this.previousValue().identifier }, formData)), switchMap((formData: { id: string, [key: string]: any }) => this.contextService.patch(formData).pipe(catchError(() => of(null))))); @@ -172,12 +228,12 @@ export class ContextPermissionTabComponent { onSubmit() { this.submited = true; - if (this.isInvalid()) { return; } this.prepareSubmit().subscribe(() => { this.contextService.get(this._context.identifier).subscribe( response => { this.submited = false; this.context = response; + this.hasChanged(); } ); }, () => { @@ -186,13 +242,69 @@ export class ContextPermissionTabComponent { } resetForm(context: Context) { - this.form.reset(context, { emitEvent: false }); - const permissionCopy: ContextPermission[] = []; + this.updatedPermissions = new Array(); for (const permission of context.permissions) { - permissionCopy.push(new ContextPermission(permission.tenant, - [...(permission.accessContracts ? permission.accessContracts : [])], - [...(permission.ingestContracts ? permission.ingestContracts : [])])); + this.updatedPermissions.push( + new ContextPermission(permission.tenant, permission.accessContracts, permission.ingestContracts) + ); + } + } + + openEditContextDialog() { + const dialogRef = this.dialog.open(ContextEditComponent, { + panelClass: 'vitamui-modal', + disableClose: true, + data: this.updatedPermissions + }); + dialogRef.afterClosed().subscribe(result => { + if (result && result.permissions) { + this.updatedPermissions = result.permissions; + // Check if the permissions have been modified + this.hasChanged(); + } + }); + } + + getOrganisationName(tenantIdentifier: string): string { + let organisation: Customer = null; + if (this.tenants && this.organisations) { + const tenant = this.tenants.get(tenantIdentifier); + if (tenant) { + organisation = this.organisations.get(tenant.customerId); + } + } + return organisation ? organisation.name : ''; + } + + getAccessContractNames(accessContractIdentifiers: string[]): string { + let label = ''; + if (accessContractIdentifiers && accessContractIdentifiers.length > 0) { + accessContractIdentifiers.forEach((identifier, index) => { + const contract = this.accessContracts.get(identifier); + if (contract) { + label += contract.name; + } + if (index < accessContractIdentifiers.length - 1) { + label += ', '; + } + }); + } + return label; + } + + getIngestContractNames(ingestContractIdentifiers: string[]): string { + let label = ''; + if (ingestContractIdentifiers && ingestContractIdentifiers.length > 0) { + ingestContractIdentifiers.forEach((id, index) => { + const contract = this.ingestContracts.get(id); + if (contract) { + label += contract.name; + } + if (index < ingestContractIdentifiers.length - 1) { + label += ', '; + } + }); + return label; } - this.form.controls.permissions.setValue(permissionCopy); } } diff --git a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-preview.module.ts b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-preview.module.ts index 5d28d950629f010b8974eaf761323d8680a7e9d0..4d3ed990ee00738abcd604746d30bb27d316d56c 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-preview.module.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context-preview/context-preview.module.ts @@ -39,6 +39,7 @@ import {NgModule} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatOptionModule} from '@angular/material/core'; import {MatDialogModule} from '@angular/material/dialog'; +import { MatDividerModule } from '@angular/material/divider'; import {MatMenuModule} from '@angular/material/menu'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatSelectModule} from '@angular/material/select'; @@ -71,7 +72,8 @@ import {ContextPreviewComponent} from './context-preview.component'; MatProgressSpinnerModule, MatSelectModule, MatOptionModule, - MatTabsModule + MatTabsModule, + MatDividerModule ], declarations: [ ContextPreviewComponent, diff --git a/ui/ui-frontend/projects/referential/src/app/context/context.component.spec.ts b/ui/ui-frontend/projects/referential/src/app/context/context.component.spec.ts index 65f6b221e36d6ccb532a3f1b638ea96adfae9378..cd35613cdb8acb5e98e07013232c1c84ba6e4b69 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context.component.spec.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context.component.spec.ts @@ -4,9 +4,11 @@ import {MatDialogModule} from '@angular/material/dialog'; import {MatSidenavModule} from '@angular/material/sidenav'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {RouterTestingModule} from '@angular/router/testing'; -import {InjectorModule, LoggerModule} from 'ui-frontend-common'; +import {GlobalEventService, InjectorModule, LoggerModule} from 'ui-frontend-common'; import {VitamUICommonTestModule} from 'ui-frontend-common/testing'; +import { ActivatedRoute } from '@angular/router'; +import { EMPTY } from 'rxjs'; import {ContextComponent} from './context.component'; @Component({selector: 'app-agency-preview', template: ''}) @@ -32,6 +34,10 @@ describe('ContextComponent', () => { ContextListStub, ContextPreviewStub ], + providers: [ + { provide: ActivatedRoute, useValue: { paramMap: EMPTY, data: EMPTY } }, + { provide: GlobalEventService, useValue: { pageEvent: EMPTY, customerEvent: EMPTY, tenantEvent: EMPTY } } + ], imports: [ VitamUICommonTestModule, RouterTestingModule, diff --git a/ui/ui-frontend/projects/referential/src/app/context/context.module.ts b/ui/ui-frontend/projects/referential/src/app/context/context.module.ts index 01527be23a39724eb20113d14605d75d79f7ee9c..1a0284f80a8e6fe19cacee0389fb5a9bc1e481fc 100644 --- a/ui/ui-frontend/projects/referential/src/app/context/context.module.ts +++ b/ui/ui-frontend/projects/referential/src/app/context/context.module.ts @@ -46,6 +46,7 @@ import {TableFilterModule, VitamUICommonModule} from 'ui-frontend-common'; import {SharedModule} from '../shared/shared.module'; import {ContextCreateModule} from './context-create'; +import {ContextEditModule} from './context-edit/context-edit.module'; import {ContextListComponent} from './context-list/context-list.component'; import {ContextPreviewModule} from './context-preview/context-preview.module'; import {ContextRoutingModule} from './context-routing.module'; @@ -58,6 +59,7 @@ import {ContextComponent} from './context.component'; VitamUICommonModule, ContextRoutingModule, ContextCreateModule, + ContextEditModule, ContextPreviewModule, MatMenuModule, MatSnackBarModule, diff --git a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.spec.ts b/ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.spec.ts similarity index 58% rename from ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.spec.ts rename to ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.spec.ts index 068bf1845fc47de3c72c9008c2f00c341f214f4e..3fd83fc6b4821be4bc24c6c85e28be545478c5d6 100644 --- a/ui/ui-frontend/projects/ingest/src/app/ingest/upload-sip/upload-sip.component.spec.ts +++ b/ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.spec.ts @@ -34,53 +34,31 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed, waitForAsync } from '@angular/core/testing'; -import { UploadSipComponent } from './upload-sip.component'; -import { of } from 'rxjs'; -import { UploadSipService } from './upload-sip.service'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; import { LoggerModule } from 'ui-frontend-common'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -describe('UploadSipComponent', () => { - let component: UploadSipComponent; - let fixture: ComponentFixture<UploadSipComponent>; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { BASE_URL } from 'ui-frontend-common'; +import { CustomerApiService } from './customer-api.service'; - const matDialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['open']); - matDialogRefSpy.open.and.returnValue({ afterClosed: () => of(true) }); - - const uploadSipServiceSpy = jasmine.createSpyObj('UploadSipService', { create: of({}) }); +describe('CustomerApiService', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - MatProgressBarModule, - MatSnackBarModule, - LoggerModule.forRoot() + LoggerModule.forRoot(), + HttpClientTestingModule ], - declarations: [UploadSipComponent], providers: [ - FormBuilder, - { provide: MatDialogRef, useValue: matDialogRefSpy }, - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: UploadSipService, useValue: uploadSipServiceSpy } - ], - schemas: [NO_ERRORS_SCHEMA] + {provide: BASE_URL, useValue: ''} + ] }) .compileComponents(); })); - beforeEach(() => { - fixture = TestBed.createComponent(UploadSipComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); + it('should be created', () => { + const service: CustomerApiService = TestBed.inject(CustomerApiService); + expect(service).toBeTruthy(); }); }); diff --git a/ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.ts b/ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..d485ba20de3c37886d19ff8cc7391da6c6f320c5 --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/core/api/customer-api.service.ts @@ -0,0 +1,53 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { BASE_URL, BaseHttpClient, Customer } from 'ui-frontend-common'; + +@Injectable({ + providedIn: 'root' +}) +export class CustomerApiService extends BaseHttpClient<Customer> { + + constructor(http: HttpClient, @Inject(BASE_URL) baseUrl: string) { + super(http, baseUrl + '/customers'); + } + + getAll() { + return super.getAllByParams(new HttpParams()); + } +} diff --git a/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.spec.ts b/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9350128b428289caf13685f8e0430f02e96e3092 --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.spec.ts @@ -0,0 +1,59 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { async, TestBed } from '@angular/core/testing'; + +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { BASE_URL } from 'ui-frontend-common'; +import { TenantApiService } from './tenant-api.service'; + +describe('TenantApiService', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + ], + providers: [ + {provide: BASE_URL, useValue: ''}, + ], + }).compileComponents(); + })); + + it('should be created', () => { + const service: TenantApiService = TestBed.get(TenantApiService); + expect(service).toBeTruthy(); + }); +}); diff --git a/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.ts b/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a0c5dd19cf0152409773b9f640ce55fe7d8115b --- /dev/null +++ b/ui/ui-frontend/projects/referential/src/app/core/api/tenant-api.service.ts @@ -0,0 +1,53 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { BASE_URL, BaseHttpClient, Tenant } from 'ui-frontend-common'; + +@Injectable({ + providedIn: 'root' +}) +export class TenantApiService extends BaseHttpClient<Tenant> { + + constructor(http: HttpClient, @Inject(BASE_URL) baseUrl: string) { + super(http, baseUrl + '/tenants'); + } + + getAll() { + return super.getAllByParams(new HttpParams()); + } +} diff --git a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts index 5dc2bc2eb5f09b0b7d200bee2a4fddeb78810890..ec95cc56280c65a2de4105ddb4fc0c6035729aeb 100644 --- a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts +++ b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts @@ -35,7 +35,7 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ -import { ENVIRONMENT, GlobalEventService } from 'ui-frontend-common'; +import { ENVIRONMENT, GlobalEventService, InjectorModule, LoggerModule } from 'ui-frontend-common'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; @@ -56,7 +56,9 @@ describe('LogbookOperationComponent', () => { TestBed.configureTestingModule({ imports: [ MatMenuModule, - ReactiveFormsModule + ReactiveFormsModule, + InjectorModule, + LoggerModule.forRoot() ], declarations: [LogbookOperationComponent], providers: [ diff --git a/ui/ui-frontend/projects/referential/src/app/rule/rule-list/rule-list.component.ts b/ui/ui-frontend/projects/referential/src/app/rule/rule-list/rule-list.component.ts index 61c9810e8a840ae4d3e9529bc274c5acea67a7fe..4e9468331af52e7c0e1d398c4a34bc4f0314730d 100644 --- a/ui/ui-frontend/projects/referential/src/app/rule/rule-list/rule-list.component.ts +++ b/ui/ui-frontend/projects/referential/src/app/rule/rule-list/rule-list.component.ts @@ -48,7 +48,7 @@ import { import {MatDialog} from '@angular/material/dialog'; import {Rule} from 'projects/vitamui-library/src/lib/models/rule'; import {ConfirmActionComponent} from 'projects/vitamui-library/src/public-api'; -import {merge, Subject, Subscription} from 'rxjs'; +import {merge, Subject} from 'rxjs'; import {debounceTime, filter} from 'rxjs/operators'; import { AdminUserProfile, @@ -108,7 +108,6 @@ export class RuleListComponent extends InfiniteScrollTable<Rule> implements OnDe genericUserRole: Readonly<{appId: ApplicationId, tenantIdentifier: number, roles: Role[]}>; private groups: Array<{id: string, group: any}> = []; - private updatedUserSub: Subscription; private readonly filterChange = new Subject<string>(); private readonly searchChange = new Subject<string>(); private readonly orderChange = new Subject<string>(); @@ -170,7 +169,6 @@ export class RuleListComponent extends InfiniteScrollTable<Rule> implements OnDe } ngOnDestroy() { - this.updatedUserSub.unsubscribe(); this.updatedData.unsubscribe(); } diff --git a/ui/ui-frontend/projects/referential/src/app/shared/vitamui-import-dialog/vitamui-import-dialog.component.spec.ts b/ui/ui-frontend/projects/referential/src/app/shared/vitamui-import-dialog/vitamui-import-dialog.component.spec.ts index 907ae73d6c6e5e720cae4498b835bc3b470cabca..1d085443adc089a9ba9f1c2d9e1ca007bf287b51 100644 --- a/ui/ui-frontend/projects/referential/src/app/shared/vitamui-import-dialog/vitamui-import-dialog.component.spec.ts +++ b/ui/ui-frontend/projects/referential/src/app/shared/vitamui-import-dialog/vitamui-import-dialog.component.spec.ts @@ -46,8 +46,10 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { BASE_URL, LoggerModule, VitamUICommonModule } from 'ui-frontend-common'; +import { BASE_URL, LoggerModule } from 'ui-frontend-common'; import { VitamUIImportDialogComponent } from './vitamui-import-dialog.component'; +import { VitamUICommonTestModule } from 'ui-frontend-common/testing'; +import { CdkStepper, CdkStepperModule } from '@angular/cdk/stepper'; describe('VitamUIImportDialogComponent', () => { let component: VitamUIImportDialogComponent; @@ -62,16 +64,18 @@ describe('VitamUIImportDialogComponent', () => { LoggerModule.forRoot(), NoopAnimationsModule, HttpClientTestingModule, - VitamUICommonModule, + VitamUICommonTestModule, MatSidenavModule, MatSnackBarModule, MatDialogModule, MatProgressBarModule, MatMenuModule, - MatOptionModule + MatOptionModule, + CdkStepperModule ], providers: [ {provide: MatDialogRef, useValue: ''}, + {provide: CdkStepper}, {provide: MAT_DIALOG_DATA, useValue: {}}, {provide: BASE_URL, useValue: ''} ] diff --git a/ui/ui-frontend/projects/vitamui-library/src/lib/components/application-select-content/application-select-content.component.spec.ts b/ui/ui-frontend/projects/vitamui-library/src/lib/components/application-select-content/application-select-content.component.spec.ts index 90909a9b5877b9cf01582d4b83bb56c28474f0f4..bb5ebdafb54bf6f755201b41b2cb0aa6da31a2e5 100644 --- a/ui/ui-frontend/projects/vitamui-library/src/lib/components/application-select-content/application-select-content.component.spec.ts +++ b/ui/ui-frontend/projects/vitamui-library/src/lib/components/application-select-content/application-select-content.component.spec.ts @@ -36,6 +36,7 @@ */ import {Component, Input} from '@angular/core'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; + import {AuthService, WINDOW_LOCATION} from 'ui-frontend-common'; import {UpdatedApplicationSelectContentComponent} from './application-select-content.component'; diff --git a/ui/ui-frontend/projects/vitamui-library/src/lib/components/filing-plan/filing-plan.component.spec.ts b/ui/ui-frontend/projects/vitamui-library/src/lib/components/filing-plan/filing-plan.component.spec.ts index 681182061f5b4cd7dbeeed0781bba720fc535509..017f89904b45d87cd342ca5ed9cd95cf26870776 100644 --- a/ui/ui-frontend/projects/vitamui-library/src/lib/components/filing-plan/filing-plan.component.spec.ts +++ b/ui/ui-frontend/projects/vitamui-library/src/lib/components/filing-plan/filing-plan.component.spec.ts @@ -1,17 +1,54 @@ -import {Component, Input, NO_ERRORS_SCHEMA} from '@angular/core'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +/* tslint:disable:component-selector max-classes-per-file */ + +import { Component, Input, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed , waitForAsync } from '@angular/core/testing'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatTreeModule} from '@angular/material/tree'; import {EMPTY, of} from 'rxjs'; import {AuthService} from 'ui-frontend-common'; import {FileType} from '../../models/file-type.enum'; import {Node} from '../../models/node.interface'; -import {FilingPlanComponent} from './filing-plan.component'; -import {FilingPlanMode, FilingPlanService} from './filing-plan.service'; +import { FilingPlanComponent } from './filing-plan.component'; +import { FilingPlanMode, FilingPlanService } from './filing-plan.service'; - -@Component({selector: 'lib-vitamui-library-node', template: ''}) +@Component({ selector: 'lib-vitamui-library-node', template: '' }) class NodeStubComponent { @Input() tenantIdentifier: any; @Input() node: any; diff --git a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/config/ReferentialContextConfiguration.java b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/config/ReferentialContextConfiguration.java index 455f97cddf745652cb4cc3ebea5b26f7a770c152..6e03943f44e8f0a93829d4305aa481fd40050103 100644 --- a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/config/ReferentialContextConfiguration.java +++ b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/config/ReferentialContextConfiguration.java @@ -39,6 +39,7 @@ package fr.gouv.vitamui.referential.config; import fr.gouv.vitamui.commons.api.application.AbstractContextConfiguration; import fr.gouv.vitamui.commons.rest.RestExceptionHandler; import fr.gouv.vitamui.commons.rest.configuration.SwaggerConfiguration; +import fr.gouv.vitamui.iam.external.client.CustomerExternalRestClient; import fr.gouv.vitamui.iam.external.client.IamExternalRestClientFactory; import fr.gouv.vitamui.iam.external.client.IamExternalWebClientFactory; import fr.gouv.vitamui.iam.external.client.TenantExternalRestClient; @@ -119,6 +120,11 @@ public class ReferentialContextConfiguration extends AbstractContextConfiguratio public TenantExternalRestClient tenantCrudRestClient(final IamExternalRestClientFactory iamExternalRestClientFactory) { return iamExternalRestClientFactory.getTenantExternalRestClient(); } + + @Bean + public CustomerExternalRestClient customerCrudRestClient(final IamExternalRestClientFactory iamExternalRestClientFactory) { + return iamExternalRestClientFactory.getCustomerExternalRestClient(); + } @Bean public OperationExternalRestClient auditCrudRestClient(final ReferentialExternalRestClientFactory referentialExternalRestClientFactory) { diff --git a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/ContextController.java b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/ContextController.java index 2c95e1b386f1fba1a308d17c3027b491a0ebd035..4a89e7b6efd45244883cfae1fa860e2cf74f5c2a 100644 --- a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/ContextController.java +++ b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/ContextController.java @@ -147,6 +147,7 @@ public class ContextController extends AbstractUiRestController { public ContextDto patch(final @PathVariable("id") String id, @RequestBody final ContextDto partialDto) { LOGGER.debug("Patch User {} with {}", id, partialDto); Assert.isTrue(StringUtils.equals(id, partialDto.getId()), "Unable to patch context : the DTO id must match the path id."); + return service.patchWithDto(buildUiHttpContext(), partialDto, id); } diff --git a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/CustomerController.java b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/CustomerController.java new file mode 100644 index 0000000000000000000000000000000000000000..405b8d541adf2289072e64adc54ccd1c4a73adc2 --- /dev/null +++ b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/rest/CustomerController.java @@ -0,0 +1,80 @@ +/** + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package fr.gouv.vitamui.referential.rest; + +import java.util.Collection; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import fr.gouv.vitamui.commons.api.logger.VitamUILogger; +import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; +import fr.gouv.vitamui.commons.rest.AbstractUiRestController; +import fr.gouv.vitamui.commons.rest.util.RestUtils; +import fr.gouv.vitamui.iam.common.dto.CustomerDto; +import fr.gouv.vitamui.referential.service.CustomerService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +@Api(tags = "customers") +@RestController +@RequestMapping("${ui-referential.prefix}/customers") +public class CustomerController extends AbstractUiRestController { + + private final CustomerService service; + + private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(CustomerController.class); + + @Autowired + public CustomerController(final CustomerService service) { + this.service = service; + } + + @ApiOperation(value = "Get all customers") + @GetMapping + @ResponseStatus(HttpStatus.OK) + public Collection<CustomerDto> getAll(final Optional<String> criteria) { + LOGGER.debug("Get all with criteria={}", criteria); + RestUtils.checkCriteria(criteria); + return service.getAll(buildUiHttpContext(), criteria); + } +} diff --git a/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/service/CustomerService.java b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/service/CustomerService.java new file mode 100644 index 0000000000000000000000000000000000000000..5bdc114df8334dca32ad4a1b4335602f9e24f04b --- /dev/null +++ b/ui/ui-referential/src/main/java/fr/gouv/vitamui/referential/service/CustomerService.java @@ -0,0 +1,67 @@ +/** + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package fr.gouv.vitamui.referential.service; + +import java.util.Collection; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext; +import fr.gouv.vitamui.iam.common.dto.CustomerDto; +import fr.gouv.vitamui.iam.external.client.CustomerExternalRestClient; + +/** + * + * + */ +@Service +public class CustomerService { + + private final CustomerExternalRestClient client; + + @Autowired + public CustomerService(final CustomerExternalRestClient client) { + this.client = client; + } + + public Collection<CustomerDto> getAll(final ExternalHttpContext context, final Optional<String> criteria) { + return client.getAll(context, criteria); + } + +} \ No newline at end of file diff --git a/ui/ui-referential/src/test/java/fr/gouv/vitamui/referential/SwaggerJsonFileGenerationTest.java b/ui/ui-referential/src/test/java/fr/gouv/vitamui/referential/SwaggerJsonFileGenerationTest.java index 398715d4b6abced3e43fb459c7ca3ef3df4dcaf8..3a2e9391819233b947de7b22b50aaccadef31fce 100644 --- a/ui/ui-referential/src/test/java/fr/gouv/vitamui/referential/SwaggerJsonFileGenerationTest.java +++ b/ui/ui-referential/src/test/java/fr/gouv/vitamui/referential/SwaggerJsonFileGenerationTest.java @@ -36,22 +36,11 @@ */ package fr.gouv.vitamui.referential; -import org.junit.runner.RunWith; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; - -import fr.gouv.vitamui.commons.api.identity.ServerIdentityConfiguration; -import fr.gouv.vitamui.commons.rest.RestExceptionHandler; -import fr.gouv.vitamui.commons.rest.configuration.SwaggerConfiguration; -import fr.gouv.vitamui.commons.test.rest.AbstractSwaggerJsonFileGenerationTest; import fr.gouv.vitamui.referential.service.AccessContractService; import fr.gouv.vitamui.referential.service.AccessionRegisterService; import fr.gouv.vitamui.referential.service.AgencyService; import fr.gouv.vitamui.referential.service.ContextService; +import fr.gouv.vitamui.referential.service.CustomerService; import fr.gouv.vitamui.referential.service.FileFormatService; import fr.gouv.vitamui.referential.service.IngestContractService; import fr.gouv.vitamui.referential.service.ManagementContractService; @@ -62,6 +51,18 @@ import fr.gouv.vitamui.referential.service.RuleService; import fr.gouv.vitamui.referential.service.SecurityProfileService; import fr.gouv.vitamui.referential.service.TenantService; import fr.gouv.vitamui.referential.service.UnitService; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import fr.gouv.vitamui.commons.api.identity.ServerIdentityConfiguration; +import fr.gouv.vitamui.commons.rest.RestExceptionHandler; +import fr.gouv.vitamui.commons.rest.configuration.SwaggerConfiguration; +import fr.gouv.vitamui.commons.test.rest.AbstractSwaggerJsonFileGenerationTest; import fr.gouv.vitamui.ui.commons.security.SecurityConfig; /** @@ -120,4 +121,7 @@ public class SwaggerJsonFileGenerationTest extends AbstractSwaggerJsonFileGenera @MockBean private RuleService ruleService; + + @MockBean + private CustomerService customerService; }