@proconnect-gouv/proconnect.identite 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/data/organization/domains-whitelist.d.ts +2 -0
  3. package/dist/data/organization/domains-whitelist.d.ts.map +1 -0
  4. package/dist/data/organization/domains-whitelist.js +7 -0
  5. package/dist/data/organization/index.d.ts +2 -0
  6. package/dist/data/organization/index.d.ts.map +1 -0
  7. package/dist/data/organization/index.js +2 -0
  8. package/dist/errors/index.d.ts +22 -0
  9. package/dist/errors/index.d.ts.map +1 -0
  10. package/dist/errors/index.js +43 -0
  11. package/dist/managers/certification/distance.d.ts +4 -0
  12. package/dist/managers/certification/distance.d.ts.map +1 -0
  13. package/dist/managers/certification/distance.js +16 -0
  14. package/dist/managers/certification/index.d.ts +3 -0
  15. package/dist/managers/certification/index.d.ts.map +1 -0
  16. package/dist/managers/certification/index.js +3 -0
  17. package/dist/managers/certification/is-organization-dirigeant.d.ts +15 -0
  18. package/dist/managers/certification/is-organization-dirigeant.d.ts.map +1 -0
  19. package/dist/managers/certification/is-organization-dirigeant.js +60 -0
  20. package/dist/managers/franceconnect/index.d.ts +2 -0
  21. package/dist/managers/franceconnect/index.d.ts.map +1 -0
  22. package/dist/managers/franceconnect/index.js +2 -0
  23. package/dist/managers/franceconnect/openid-client.d.ts +37 -0
  24. package/dist/managers/franceconnect/openid-client.d.ts.map +1 -0
  25. package/dist/managers/franceconnect/openid-client.js +54 -0
  26. package/dist/managers/organization/force-join-organization.d.ts +29 -0
  27. package/dist/managers/organization/force-join-organization.d.ts.map +1 -0
  28. package/dist/managers/organization/force-join-organization.js +33 -0
  29. package/dist/managers/organization/get-organization-info.d.ts +5 -0
  30. package/dist/managers/organization/get-organization-info.d.ts.map +1 -0
  31. package/dist/managers/organization/get-organization-info.js +33 -0
  32. package/dist/managers/organization/index.d.ts +4 -0
  33. package/dist/managers/organization/index.d.ts.map +1 -0
  34. package/dist/managers/organization/index.js +4 -0
  35. package/dist/managers/organization/mark-domain-as-verified.d.ts +20 -0
  36. package/dist/managers/organization/mark-domain-as-verified.d.ts.map +1 -0
  37. package/dist/managers/organization/mark-domain-as-verified.js +63 -0
  38. package/dist/managers/user/assign-user-verification-type-to-domain.d.ts +10 -0
  39. package/dist/managers/user/assign-user-verification-type-to-domain.d.ts.map +1 -0
  40. package/dist/managers/user/assign-user-verification-type-to-domain.js +21 -0
  41. package/dist/managers/user/index.d.ts +2 -0
  42. package/dist/managers/user/index.d.ts.map +1 -0
  43. package/dist/managers/user/index.js +1 -0
  44. package/dist/mappers/certification/index.d.ts +4 -0
  45. package/dist/mappers/certification/index.d.ts.map +1 -0
  46. package/dist/mappers/certification/index.js +11 -0
  47. package/dist/mappers/index.d.ts +2 -0
  48. package/dist/mappers/index.d.ts.map +1 -0
  49. package/dist/mappers/index.js +2 -0
  50. package/dist/mappers/organization/from-siret.d.ts +5 -0
  51. package/dist/mappers/organization/from-siret.d.ts.map +1 -0
  52. package/dist/mappers/organization/from-siret.js +51 -0
  53. package/dist/mappers/organization/index.d.ts +2 -0
  54. package/dist/mappers/organization/index.d.ts.map +1 -0
  55. package/dist/mappers/organization/index.js +2 -0
  56. package/dist/repositories/email-domain/add-domain.d.ts +8 -0
  57. package/dist/repositories/email-domain/add-domain.d.ts.map +1 -0
  58. package/dist/repositories/email-domain/add-domain.js +25 -0
  59. package/dist/repositories/email-domain/delete-email-domains-by-verification-types.d.ts +8 -0
  60. package/dist/repositories/email-domain/delete-email-domains-by-verification-types.d.ts.map +1 -0
  61. package/dist/repositories/email-domain/delete-email-domains-by-verification-types.js +21 -0
  62. package/dist/repositories/email-domain/find-email-domains-by-organization-id.d.ts +4 -0
  63. package/dist/repositories/email-domain/find-email-domains-by-organization-id.d.ts.map +1 -0
  64. package/dist/repositories/email-domain/find-email-domains-by-organization-id.js +11 -0
  65. package/dist/repositories/email-domain/index.d.ts +12 -0
  66. package/dist/repositories/email-domain/index.d.ts.map +1 -0
  67. package/dist/repositories/email-domain/index.js +3 -0
  68. package/dist/repositories/organization/find-by-id.d.ts +4 -0
  69. package/dist/repositories/organization/find-by-id.d.ts.map +1 -0
  70. package/dist/repositories/organization/find-by-id.js +11 -0
  71. package/dist/repositories/organization/find-by-user-id.d.ts +12 -0
  72. package/dist/repositories/organization/find-by-user-id.d.ts.map +1 -0
  73. package/dist/repositories/organization/find-by-user-id.js +22 -0
  74. package/dist/repositories/organization/get-by-id.d.ts +4 -0
  75. package/dist/repositories/organization/get-by-id.d.ts.map +1 -0
  76. package/dist/repositories/organization/get-by-id.js +15 -0
  77. package/dist/repositories/organization/get-users-by-organization.d.ts +12 -0
  78. package/dist/repositories/organization/get-users-by-organization.d.ts.map +1 -0
  79. package/dist/repositories/organization/get-users-by-organization.js +23 -0
  80. package/dist/repositories/organization/index.d.ts +7 -0
  81. package/dist/repositories/organization/index.d.ts.map +1 -0
  82. package/dist/repositories/organization/index.js +7 -0
  83. package/dist/repositories/organization/link-user-to-organization.d.ts +16 -0
  84. package/dist/repositories/organization/link-user-to-organization.d.ts.map +1 -0
  85. package/dist/repositories/organization/link-user-to-organization.js +22 -0
  86. package/dist/repositories/organization/upsert.d.ts +7 -0
  87. package/dist/repositories/organization/upsert.d.ts.map +1 -0
  88. package/dist/repositories/organization/upsert.js +103 -0
  89. package/dist/repositories/user/create.d.ts +7 -0
  90. package/dist/repositories/user/create.d.ts.map +1 -0
  91. package/dist/repositories/user/create.js +25 -0
  92. package/dist/repositories/user/find-by-email.d.ts +4 -0
  93. package/dist/repositories/user/find-by-email.d.ts.map +1 -0
  94. package/dist/repositories/user/find-by-email.js +12 -0
  95. package/dist/repositories/user/find-by-id.d.ts +4 -0
  96. package/dist/repositories/user/find-by-id.d.ts.map +1 -0
  97. package/dist/repositories/user/find-by-id.js +13 -0
  98. package/dist/repositories/user/get-by-id.d.ts +4 -0
  99. package/dist/repositories/user/get-by-id.d.ts.map +1 -0
  100. package/dist/repositories/user/get-by-id.js +15 -0
  101. package/dist/repositories/user/get-franceconnect-user-info.d.ts +15 -0
  102. package/dist/repositories/user/get-franceconnect-user-info.d.ts.map +1 -0
  103. package/dist/repositories/user/get-franceconnect-user-info.js +13 -0
  104. package/dist/repositories/user/index.d.ts +9 -0
  105. package/dist/repositories/user/index.d.ts.map +1 -0
  106. package/dist/repositories/user/index.js +9 -0
  107. package/dist/repositories/user/update-user-organization-link.d.ts +16 -0
  108. package/dist/repositories/user/update-user-organization-link.d.ts.map +1 -0
  109. package/dist/repositories/user/update-user-organization-link.js +20 -0
  110. package/dist/repositories/user/update.d.ts +4 -0
  111. package/dist/repositories/user/update.d.ts.map +1 -0
  112. package/dist/repositories/user/update.js +19 -0
  113. package/dist/repositories/user/upsert-franceconnect-userinfo.d.ts +15 -0
  114. package/dist/repositories/user/upsert-franceconnect-userinfo.d.ts.map +1 -0
  115. package/dist/repositories/user/upsert-franceconnect-userinfo.js +23 -0
  116. package/dist/services/organization/index.d.ts +3 -0
  117. package/dist/services/organization/index.d.ts.map +1 -0
  118. package/dist/services/organization/index.js +3 -0
  119. package/dist/services/organization/is-domain-allowed-for-organization.d.ts +2 -0
  120. package/dist/services/organization/is-domain-allowed-for-organization.d.ts.map +1 -0
  121. package/dist/services/organization/is-domain-allowed-for-organization.js +8 -0
  122. package/dist/services/organization/is-entreprise-unipersonnelle.d.ts +9 -0
  123. package/dist/services/organization/is-entreprise-unipersonnelle.d.ts.map +1 -0
  124. package/dist/services/organization/is-entreprise-unipersonnelle.js +18 -0
  125. package/dist/services/postgres/hash-to-postgres-params.d.ts +6 -0
  126. package/dist/services/postgres/hash-to-postgres-params.d.ts.map +1 -0
  127. package/dist/services/postgres/hash-to-postgres-params.js +21 -0
  128. package/dist/services/postgres/index.d.ts +2 -0
  129. package/dist/services/postgres/index.d.ts.map +1 -0
  130. package/dist/services/postgres/index.js +2 -0
  131. package/dist/types/claims.d.ts +16 -0
  132. package/dist/types/claims.d.ts.map +1 -0
  133. package/dist/types/claims.js +16 -0
  134. package/dist/types/contexts.d.ts +5 -0
  135. package/dist/types/contexts.d.ts.map +1 -0
  136. package/dist/types/contexts.js +2 -0
  137. package/dist/types/dirigeant.d.ts +9 -0
  138. package/dist/types/dirigeant.d.ts.map +1 -0
  139. package/dist/types/dirigeant.js +9 -0
  140. package/dist/types/email-domain.d.ts +25 -0
  141. package/dist/types/email-domain.d.ts.map +1 -0
  142. package/dist/types/email-domain.js +14 -0
  143. package/dist/types/franceconnect.d.ts +28 -0
  144. package/dist/types/franceconnect.d.ts.map +1 -0
  145. package/dist/types/franceconnect.js +22 -0
  146. package/dist/types/index.d.ts +10 -0
  147. package/dist/types/index.d.ts.map +1 -0
  148. package/dist/types/index.js +10 -0
  149. package/dist/types/organization-info.d.ts +27 -0
  150. package/dist/types/organization-info.d.ts.map +1 -0
  151. package/dist/types/organization-info.js +26 -0
  152. package/dist/types/organization.d.ts +26 -0
  153. package/dist/types/organization.d.ts.map +1 -0
  154. package/dist/types/organization.js +2 -0
  155. package/dist/types/user-organization-link.d.ts +92 -0
  156. package/dist/types/user-organization-link.d.ts.map +1 -0
  157. package/dist/types/user-organization-link.js +44 -0
  158. package/dist/types/user.d.ts +28 -0
  159. package/dist/types/user.d.ts.map +1 -0
  160. package/dist/types/user.js +2 -0
  161. package/package.json +68 -0
  162. package/src/data/organization/domains-whitelist.ts +8 -0
  163. package/src/data/organization/index.ts +3 -0
  164. package/src/errors/index.ts +50 -0
  165. package/src/managers/certification/distance.test.ts +109 -0
  166. package/src/managers/certification/distance.ts +41 -0
  167. package/src/managers/certification/index.ts +4 -0
  168. package/src/managers/certification/is-organization-dirigeant.test.ts +125 -0
  169. package/src/managers/certification/is-organization-dirigeant.ts +136 -0
  170. package/src/managers/franceconnect/index.ts +3 -0
  171. package/src/managers/franceconnect/openid-client.ts +131 -0
  172. package/src/managers/organization/force-join-organization.test.ts +94 -0
  173. package/src/managers/organization/force-join-organization.ts +90 -0
  174. package/src/managers/organization/get-organization-info.test.ts +47 -0
  175. package/src/managers/organization/get-organization-info.test.ts.snapshot +68 -0
  176. package/src/managers/organization/get-organization-info.ts +58 -0
  177. package/src/managers/organization/index.ts +5 -0
  178. package/src/managers/organization/mark-domain-as-verified.test.ts +90 -0
  179. package/src/managers/organization/mark-domain-as-verified.test.ts.snapshot +52 -0
  180. package/src/managers/organization/mark-domain-as-verified.ts +140 -0
  181. package/src/managers/user/assign-user-verification-type-to-domain.ts +50 -0
  182. package/src/managers/user/index.ts +1 -0
  183. package/src/mappers/certification/index.ts +18 -0
  184. package/src/mappers/index.ts +3 -0
  185. package/src/mappers/organization/from-siret.test.ts +26 -0
  186. package/src/mappers/organization/from-siret.test.ts.snapshot +68 -0
  187. package/src/mappers/organization/from-siret.ts +75 -0
  188. package/src/mappers/organization/index.ts +3 -0
  189. package/src/repositories/email-domain/add-domain.test.ts +43 -0
  190. package/src/repositories/email-domain/add-domain.ts +48 -0
  191. package/src/repositories/email-domain/delete-email-domains-by-verification-types.test.ts +49 -0
  192. package/src/repositories/email-domain/delete-email-domains-by-verification-types.ts +45 -0
  193. package/src/repositories/email-domain/find-email-domains-by-organization-id.test.ts +55 -0
  194. package/src/repositories/email-domain/find-email-domains-by-organization-id.ts +28 -0
  195. package/src/repositories/email-domain/index.ts +13 -0
  196. package/src/repositories/organization/find-by-id.test.ts +51 -0
  197. package/src/repositories/organization/find-by-id.ts +22 -0
  198. package/src/repositories/organization/find-by-user-id.test.ts +70 -0
  199. package/src/repositories/organization/find-by-user-id.test.ts.snapshot +102 -0
  200. package/src/repositories/organization/find-by-user-id.ts +38 -0
  201. package/src/repositories/organization/get-by-id.test.ts +60 -0
  202. package/src/repositories/organization/get-by-id.ts +21 -0
  203. package/src/repositories/organization/get-users-by-organization.test.ts +50 -0
  204. package/src/repositories/organization/get-users-by-organization.test.ts.snapshot +38 -0
  205. package/src/repositories/organization/get-users-by-organization.ts +46 -0
  206. package/src/repositories/organization/index.ts +8 -0
  207. package/src/repositories/organization/link-user-to-organization.test.ts +65 -0
  208. package/src/repositories/organization/link-user-to-organization.test.ts.snapshot +31 -0
  209. package/src/repositories/organization/link-user-to-organization.ts +45 -0
  210. package/src/repositories/organization/upsert.ts +142 -0
  211. package/src/repositories/organization/upset.test.ts +95 -0
  212. package/src/repositories/user/create.test.ts +49 -0
  213. package/src/repositories/user/create.ts +44 -0
  214. package/src/repositories/user/find-by-email.test.ts +62 -0
  215. package/src/repositories/user/find-by-email.ts +22 -0
  216. package/src/repositories/user/find-by-id.test.ts +64 -0
  217. package/src/repositories/user/find-by-id.ts +23 -0
  218. package/src/repositories/user/get-by-id.test.ts +63 -0
  219. package/src/repositories/user/get-by-id.ts +21 -0
  220. package/src/repositories/user/get-franceconnect-user-info.test.ts +58 -0
  221. package/src/repositories/user/get-franceconnect-user-info.ts +25 -0
  222. package/src/repositories/user/index.ts +10 -0
  223. package/src/repositories/user/update-user-organization-link.test.ts +46 -0
  224. package/src/repositories/user/update-user-organization-link.ts +42 -0
  225. package/src/repositories/user/update.test.ts +26 -0
  226. package/src/repositories/user/update.ts +34 -0
  227. package/src/repositories/user/upsert-franceconnect-userinfo.test.ts +100 -0
  228. package/src/repositories/user/upsert-franceconnect-userinfo.ts +41 -0
  229. package/src/services/organization/index.ts +4 -0
  230. package/src/services/organization/is-domain-allowed-for-organization.ts +10 -0
  231. package/src/services/organization/is-entreprise-unipersonnelle.test.ts +32 -0
  232. package/src/services/organization/is-entreprise-unipersonnelle.ts +31 -0
  233. package/src/services/postgres/hash-to-postgres-params.ts +34 -0
  234. package/src/services/postgres/index.ts +3 -0
  235. package/src/types/claims.ts +21 -0
  236. package/src/types/contexts.ts +9 -0
  237. package/src/types/dirigeant.ts +13 -0
  238. package/src/types/email-domain.ts +48 -0
  239. package/src/types/franceconnect.ts +37 -0
  240. package/src/types/index.ts +11 -0
  241. package/src/types/organization-info.ts +32 -0
  242. package/src/types/organization.ts +30 -0
  243. package/src/types/user-organization-link.ts +71 -0
  244. package/src/types/user.ts +29 -0
  245. package/testing/index.ts +31 -0
  246. package/testing/seed/franceconnect/index.ts +40 -0
  247. package/testing/seed/insee/index.ts +22 -0
  248. package/testing/seed/mandataires/index.ts +32 -0
  249. package/testing/seed/organizations/index.ts +41 -0
  250. package/tsconfig.json +17 -0
  251. package/tsconfig.lib.json +9 -0
  252. package/tsconfig.lib.tsbuildinfo +1 -0
@@ -0,0 +1,58 @@
1
+ //
2
+
3
+ import { InvalidSiretError, NotFoundError } from "#src/errors";
4
+ import { OrganizationInfoMapper } from "#src/mappers";
5
+ import { type OrganizationInfo } from "#src/types";
6
+ import type { EntrepriseApiInseeRepository } from "@proconnect-gouv/proconnect.entreprise/api";
7
+ import {
8
+ EntrepriseApiConnectionError,
9
+ EntrepriseApiInvalidSiret,
10
+ } from "@proconnect-gouv/proconnect.entreprise/types";
11
+
12
+ //
13
+
14
+ export function getOrganizationInfoFactory(
15
+ config: EntrepriseApiInseeRepository,
16
+ ) {
17
+ const { findBySiren, findBySiret } = config;
18
+
19
+ return async function getOrganizationInfo(
20
+ siretOrSiren: string,
21
+ ): Promise<OrganizationInfo> {
22
+ try {
23
+ let establishment: OrganizationInfo;
24
+
25
+ if (siretOrSiren.match(/^\d{14}$/)) {
26
+ establishment = OrganizationInfoMapper.fromSiret(
27
+ await findBySiret(siretOrSiren),
28
+ );
29
+ } else if (siretOrSiren.match(/^\d{9}$/)) {
30
+ establishment = OrganizationInfoMapper.fromSiret(
31
+ await findBySiren(siretOrSiren),
32
+ );
33
+ } else {
34
+ throw new InvalidSiretError();
35
+ }
36
+
37
+ const { statutDiffusion } = establishment;
38
+
39
+ if (statutDiffusion === "non_diffusible") {
40
+ throw new NotFoundError();
41
+ }
42
+
43
+ return establishment;
44
+ } catch (e) {
45
+ if (EntrepriseApiInvalidSiret.isInvalidSiret(e))
46
+ throw new InvalidSiretError();
47
+
48
+ throw new EntrepriseApiConnectionError(
49
+ "unknown error while fetching entreprise.api.gouv.fr",
50
+ { cause: e },
51
+ );
52
+ }
53
+ };
54
+ }
55
+
56
+ export type GetOrganizationInfoHandler = ReturnType<
57
+ typeof getOrganizationInfoFactory
58
+ >;
@@ -0,0 +1,5 @@
1
+ //
2
+
3
+ export * from "./force-join-organization.js";
4
+ export * from "./get-organization-info.js";
5
+ export * from "./mark-domain-as-verified.js";
@@ -0,0 +1,90 @@
1
+ import { NotFoundError } from "#src/errors";
2
+ import {
3
+ type AddDomainHandler,
4
+ type DeleteEmailDomainsByVerificationTypesHandler,
5
+ } from "#src/repositories/email-domain";
6
+ import type {
7
+ FindByIdHandler,
8
+ GetUsersByOrganizationHandler,
9
+ } from "#src/repositories/organization";
10
+ import type { UpdateUserOrganizationLinkHandler } from "#src/repositories/user";
11
+ import type { BaseUserOrganizationLink, Organization, User } from "#src/types";
12
+ import assert from "node:assert/strict";
13
+ import { describe, it, mock } from "node:test";
14
+ import { markDomainAsVerifiedFactory } from "./mark-domain-as-verified.js";
15
+
16
+ //
17
+
18
+ describe("markDomainAsVerified", () => {
19
+ it("should update organization members", async (t) => {
20
+ const addDomain = mock.fn<AddDomainHandler>(() =>
21
+ Promise.resolve({} as any),
22
+ );
23
+ const deleteEmailDomainsByVerificationTypes =
24
+ mock.fn<DeleteEmailDomainsByVerificationTypesHandler>(() =>
25
+ Promise.resolve({} as any),
26
+ );
27
+ const updateUserOrganizationLink =
28
+ mock.fn<UpdateUserOrganizationLinkHandler>(() =>
29
+ Promise.resolve({} as any),
30
+ );
31
+
32
+ const markDomainAsVerified = markDomainAsVerifiedFactory({
33
+ addDomain,
34
+ deleteEmailDomainsByVerificationTypes,
35
+ findOrganizationById: mock.fn<FindByIdHandler>(() =>
36
+ Promise.resolve({ id: 42 } as Organization),
37
+ ),
38
+ getUsers: mock.fn<GetUsersByOrganizationHandler>(() =>
39
+ Promise.resolve([
40
+ {
41
+ id: 42,
42
+ email: "lion.eljonson@darkangels.world",
43
+ verification_type: null,
44
+ } as User & BaseUserOrganizationLink,
45
+ ]),
46
+ ),
47
+ updateUserOrganizationLink,
48
+ });
49
+
50
+ await markDomainAsVerified({
51
+ domain: "darkangels.world",
52
+ domain_verification_type: "verified",
53
+ organization_id: 42,
54
+ });
55
+
56
+ await t.test("should call updateUserOrganizationLink with", async (t) => {
57
+ t.assert.snapshot(updateUserOrganizationLink.mock.calls);
58
+ });
59
+
60
+ await t.test(
61
+ "should call deleteEmailDomainsByVerificationTypes with",
62
+ async (t) => {
63
+ t.assert.snapshot(deleteEmailDomainsByVerificationTypes.mock.calls);
64
+ },
65
+ );
66
+
67
+ await t.test("should call addDomain with", async (t) => {
68
+ t.assert.snapshot(addDomain.mock.calls);
69
+ });
70
+ });
71
+
72
+ it("❎ throws NotFoundError for unknown organization", async () => {
73
+ const markDomainAsVerified = markDomainAsVerifiedFactory({
74
+ addDomain: () => Promise.reject(),
75
+ deleteEmailDomainsByVerificationTypes: () => Promise.reject(),
76
+ findOrganizationById: () => Promise.resolve(undefined),
77
+ getUsers: () => Promise.reject(),
78
+ updateUserOrganizationLink: () => Promise.reject(),
79
+ });
80
+
81
+ await assert.rejects(
82
+ markDomainAsVerified({
83
+ domain: "darkangels.world",
84
+ domain_verification_type: "verified",
85
+ organization_id: 42,
86
+ }),
87
+ new NotFoundError(""),
88
+ );
89
+ });
90
+ });
@@ -0,0 +1,52 @@
1
+ exports[`markDomainAsVerified > should update organization members > should call addDomain with 1`] = `
2
+ [
3
+ {
4
+ "arguments": [
5
+ {
6
+ "organization_id": 42,
7
+ "domain": "darkangels.world",
8
+ "verification_type": "verified"
9
+ }
10
+ ],
11
+ "result": {},
12
+ "stack": {}
13
+ }
14
+ ]
15
+ `;
16
+
17
+ exports[`markDomainAsVerified > should update organization members > should call deleteEmailDomainsByVerificationTypes with 1`] = `
18
+ [
19
+ {
20
+ "arguments": [
21
+ {
22
+ "organization_id": 42,
23
+ "domain": "darkangels.world",
24
+ "domain_verification_types": [
25
+ "official_contact",
26
+ "trackdechets_postal_mail",
27
+ "verified",
28
+ null
29
+ ]
30
+ }
31
+ ],
32
+ "result": {},
33
+ "stack": {}
34
+ }
35
+ ]
36
+ `;
37
+
38
+ exports[`markDomainAsVerified > should update organization members > should call updateUserOrganizationLink with 1`] = `
39
+ [
40
+ {
41
+ "arguments": [
42
+ 42,
43
+ 42,
44
+ {
45
+ "verification_type": "domain"
46
+ }
47
+ ],
48
+ "result": {},
49
+ "stack": {}
50
+ }
51
+ ]
52
+ `;
@@ -0,0 +1,140 @@
1
+ //
2
+
3
+ import { NotFoundError } from "#src/errors";
4
+ import { assignUserVerificationTypeToDomainFactory } from "#src/managers/user";
5
+ import type { GetUsersByOrganizationHandler } from "#src/repositories/organization";
6
+ import type { UpdateUserOrganizationLinkHandler } from "#src/repositories/user";
7
+ import type {
8
+ AddDomainHandler,
9
+ DeleteEmailDomainsByVerificationTypesHandler,
10
+ } from "@proconnect-gouv/proconnect.identite/repositories/email-domain";
11
+ import type { FindByIdHandler } from "@proconnect-gouv/proconnect.identite/repositories/organization";
12
+ import {
13
+ EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES,
14
+ EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES,
15
+ type EmailDomainApprovedVerificationType,
16
+ type EmailDomainRejectedVerificationType,
17
+ type EmailDomainVerificationType,
18
+ } from "@proconnect-gouv/proconnect.identite/types";
19
+ import { isEmpty } from "lodash-es";
20
+ import { match } from "ts-pattern";
21
+
22
+ //
23
+
24
+ type FactoryDependencies = {
25
+ addDomain: AddDomainHandler;
26
+ deleteEmailDomainsByVerificationTypes: DeleteEmailDomainsByVerificationTypesHandler;
27
+ findOrganizationById: FindByIdHandler;
28
+ getUsers: GetUsersByOrganizationHandler;
29
+ updateUserOrganizationLink: UpdateUserOrganizationLinkHandler;
30
+ };
31
+
32
+ export function markDomainAsVerifiedFactory({
33
+ addDomain,
34
+ deleteEmailDomainsByVerificationTypes,
35
+ findOrganizationById,
36
+ getUsers,
37
+ updateUserOrganizationLink,
38
+ }: FactoryDependencies) {
39
+ const assignUserVerificationTypeToDomain =
40
+ assignUserVerificationTypeToDomainFactory({
41
+ getUsers,
42
+ updateUserOrganizationLink,
43
+ });
44
+ return async function markDomainAsVerified({
45
+ organization_id,
46
+ domain,
47
+ domain_verification_type,
48
+ }: {
49
+ organization_id: number;
50
+ domain: string;
51
+ domain_verification_type: NonNullable<EmailDomainVerificationType>;
52
+ }) {
53
+ const organization = await findOrganizationById(organization_id);
54
+
55
+ if (isEmpty(organization)) {
56
+ throw new NotFoundError();
57
+ }
58
+
59
+ return match(domain_verification_type)
60
+ .with(
61
+ EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES.enum.official_contact,
62
+ EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES.enum.trackdechets_postal_mail,
63
+ EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES.enum.verified,
64
+ ...EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES.options,
65
+ async (approved_verification_type) => {
66
+ await assignUserVerificationTypeToDomain(organization_id, domain);
67
+ return markDomainAsApproved({
68
+ organization_id,
69
+ domain,
70
+ domain_verification_type: approved_verification_type,
71
+ });
72
+ },
73
+ )
74
+ .with(
75
+ EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES.enum.blacklisted,
76
+ EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES.enum.external,
77
+ EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES.enum.refused,
78
+ ...EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES.options,
79
+ (rejected_verification_type) =>
80
+ markDomainAsRejected({
81
+ organization_id,
82
+ domain,
83
+ domain_verification_type: rejected_verification_type,
84
+ }),
85
+ )
86
+ .exhaustive();
87
+ };
88
+
89
+ async function markDomainAsApproved({
90
+ organization_id,
91
+ domain,
92
+ domain_verification_type,
93
+ }: {
94
+ organization_id: number;
95
+ domain: string;
96
+ domain_verification_type: EmailDomainApprovedVerificationType;
97
+ }) {
98
+ await deleteEmailDomainsByVerificationTypes({
99
+ organization_id,
100
+ domain,
101
+ domain_verification_types: [
102
+ ...EMAIL_DOMAIN_APPROVED_VERIFICATION_TYPES.options,
103
+ null,
104
+ ],
105
+ });
106
+ return addDomain({
107
+ organization_id,
108
+ domain,
109
+ verification_type: domain_verification_type,
110
+ });
111
+ }
112
+
113
+ async function markDomainAsRejected({
114
+ organization_id,
115
+ domain,
116
+ domain_verification_type,
117
+ }: {
118
+ organization_id: number;
119
+ domain: string;
120
+ domain_verification_type: EmailDomainRejectedVerificationType;
121
+ }) {
122
+ await deleteEmailDomainsByVerificationTypes({
123
+ organization_id,
124
+ domain,
125
+ domain_verification_types: [
126
+ null,
127
+ ...EMAIL_DOMAIN_REJECTED_VERIFICATION_TYPES.options,
128
+ ],
129
+ });
130
+ return addDomain({
131
+ organization_id,
132
+ domain,
133
+ verification_type: domain_verification_type,
134
+ });
135
+ }
136
+ }
137
+
138
+ export type MarkDomainAsVerifiedHandler = ReturnType<
139
+ typeof markDomainAsVerifiedFactory
140
+ >;
@@ -0,0 +1,50 @@
1
+ //
2
+
3
+ import type { GetUsersByOrganizationHandler } from "#src/repositories/organization";
4
+ import type { UpdateUserOrganizationLinkHandler } from "#src/repositories/user";
5
+ import { getEmailDomain } from "@proconnect-gouv/proconnect.core/services/email";
6
+
7
+ //
8
+
9
+ type FactoryDependencies = {
10
+ getUsers: GetUsersByOrganizationHandler;
11
+ updateUserOrganizationLink: UpdateUserOrganizationLinkHandler;
12
+ };
13
+
14
+ export function assignUserVerificationTypeToDomainFactory({
15
+ getUsers,
16
+ updateUserOrganizationLink,
17
+ }: FactoryDependencies) {
18
+ return async function assignUserVerificationTypeToDomain(
19
+ organization_id: number,
20
+ domain: string,
21
+ ) {
22
+ const usersInOrganization = await getUsers(organization_id);
23
+
24
+ await Promise.all(
25
+ usersInOrganization.map(
26
+ ({ id, email, verification_type: link_verification_type }) => {
27
+ const userDomain = getEmailDomain(email);
28
+ if (
29
+ userDomain === domain &&
30
+ [
31
+ null,
32
+ "no_verification_means_available",
33
+ "no_verification_means_for_entreprise_unipersonnelle",
34
+ ].includes(link_verification_type)
35
+ ) {
36
+ return updateUserOrganizationLink(organization_id, id, {
37
+ verification_type: "domain",
38
+ });
39
+ }
40
+
41
+ return null;
42
+ },
43
+ ),
44
+ );
45
+ };
46
+ }
47
+
48
+ export type AssignUserVerificationTypeToDomainFactoryHandler = ReturnType<
49
+ typeof assignUserVerificationTypeToDomainFactory
50
+ >;
@@ -0,0 +1 @@
1
+ export * from "./assign-user-verification-type-to-domain.js";
@@ -0,0 +1,18 @@
1
+ //
2
+
3
+ import type { IdentityVector } from "#src/types";
4
+ import type { InfogreffeSirenMandatairesSociaux } from "@proconnect-gouv/proconnect.entreprise/types";
5
+
6
+ //
7
+
8
+ export function fromInfogreffe(
9
+ mandataire: InfogreffeSirenMandatairesSociaux,
10
+ ): IdentityVector {
11
+ const birthdate = new Date(mandataire.date_naissance_timestamp || NaN);
12
+ return {
13
+ birthplace: mandataire.lieu_naissance ?? null,
14
+ birthdate: isNaN(birthdate.getTime()) ? null : birthdate,
15
+ family_name: mandataire.nom ?? null,
16
+ given_name: mandataire.prenom ?? null,
17
+ };
18
+ }
@@ -0,0 +1,3 @@
1
+ //
2
+
3
+ export * as OrganizationInfoMapper from "./organization/index.js";
@@ -0,0 +1,26 @@
1
+ //
2
+
3
+ import {
4
+ AppleEuropeInc,
5
+ MaireClamart,
6
+ RogalDornEntrepreneur,
7
+ } from "@proconnect-gouv/proconnect.entreprise/testing/seed/insee/siret";
8
+ import { suite, test } from "node:test";
9
+ import { fromSiret } from "./from-siret.js";
10
+
11
+ suite("fromSiret", () => {
12
+ test("AppleEuropeInc", (t) => {
13
+ const organization = fromSiret(AppleEuropeInc);
14
+ t.assert.snapshot(organization);
15
+ });
16
+
17
+ test("Commune de clamart - Mairie", (t) => {
18
+ const organization = fromSiret(MaireClamart);
19
+ t.assert.snapshot(organization);
20
+ });
21
+
22
+ test("RogalDornEntrepreneur", (t) => {
23
+ const organization = fromSiret(RogalDornEntrepreneur);
24
+ t.assert.snapshot(organization);
25
+ });
26
+ });
@@ -0,0 +1,68 @@
1
+ exports[`fromSiret > AppleEuropeInc 1`] = `
2
+ {
3
+ "activitePrincipale": "70.22Z",
4
+ "adresse": "100 west ten wilmington delawa, 99404, ETATS-UNIS",
5
+ "categorieJuridique": "3120",
6
+ "codeOfficielGeographique": "",
7
+ "codePostal": null,
8
+ "enseigne": "",
9
+ "estActive": false,
10
+ "estDiffusible": true,
11
+ "etatAdministratif": "A",
12
+ "libelle": "Apple europe inc.",
13
+ "libelleActivitePrincipale": "70.22Z - Conseil pour les affaires et autres conseils de gestion",
14
+ "libelleCategorieJuridique": "Société commerciale étrangère immatriculée au RCS",
15
+ "libelleTrancheEffectif": "",
16
+ "nomComplet": "Apple europe inc.",
17
+ "siret": "32122785200019",
18
+ "statutDiffusion": "diffusible",
19
+ "trancheEffectifs": null,
20
+ "trancheEffectifsUniteLegale": "31"
21
+ }
22
+ `;
23
+
24
+ exports[`fromSiret > Commune de clamart - Mairie 1`] = `
25
+ {
26
+ "activitePrincipale": "84.11Z",
27
+ "adresse": "1 place maurice gunsbourg, 92140 Clamart",
28
+ "categorieJuridique": "7210",
29
+ "codeOfficielGeographique": "92023",
30
+ "codePostal": "92140",
31
+ "enseigne": "MAIRIE",
32
+ "estActive": true,
33
+ "estDiffusible": true,
34
+ "etatAdministratif": "A",
35
+ "libelle": "Commune de clamart - Mairie",
36
+ "libelleActivitePrincipale": "84.11Z - Administration publique générale",
37
+ "libelleCategorieJuridique": "Commune et commune nouvelle",
38
+ "libelleTrancheEffectif": "1 000 à 1 999 salariés, en 2022",
39
+ "nomComplet": "Commune de clamart",
40
+ "siret": "21920023500014",
41
+ "statutDiffusion": "diffusible",
42
+ "trancheEffectifs": "42",
43
+ "trancheEffectifsUniteLegale": "42"
44
+ }
45
+ `;
46
+
47
+ exports[`fromSiret > RogalDornEntrepreneur 1`] = `
48
+ {
49
+ "activitePrincipale": "62.02A",
50
+ "adresse": "06155 Vallauris",
51
+ "categorieJuridique": "1000",
52
+ "codeOfficielGeographique": "06155",
53
+ "codePostal": "06155",
54
+ "enseigne": "",
55
+ "estActive": true,
56
+ "estDiffusible": false,
57
+ "etatAdministratif": "A",
58
+ "libelle": "Nom inconnu",
59
+ "libelleActivitePrincipale": "62.02A - Conseil en systèmes et logiciels informatiques",
60
+ "libelleCategorieJuridique": "Entrepreneur individuel",
61
+ "libelleTrancheEffectif": "",
62
+ "nomComplet": "Nom inconnu",
63
+ "siret": "94957325700019",
64
+ "statutDiffusion": "partiellement_diffusible",
65
+ "trancheEffectifs": null,
66
+ "trancheEffectifsUniteLegale": null
67
+ }
68
+ `;
@@ -0,0 +1,75 @@
1
+ //
2
+
3
+ import "#src/types";
4
+ import { OrganizationInfoSchema, type OrganizationInfo } from "#src/types";
5
+ import {
6
+ formatAddress,
7
+ formatMainActivity,
8
+ formatNomComplet,
9
+ libelleFromCodeEffectif,
10
+ } from "@proconnect-gouv/proconnect.entreprise/formatters";
11
+ import type { InseeSireneEstablishmentSiretResponseData } from "@proconnect-gouv/proconnect.entreprise/types";
12
+ import { capitalize } from "lodash-es";
13
+
14
+ //
15
+
16
+ export function fromSiret(
17
+ siretData: InseeSireneEstablishmentSiretResponseData,
18
+ ): OrganizationInfo {
19
+ const isPartiallyNonDiffusible =
20
+ siretData.status_diffusion === "partiellement_diffusible";
21
+ const enseigne = siretData.enseigne ?? "";
22
+ const nomComplet = isPartiallyNonDiffusible
23
+ ? "Nom inconnu"
24
+ : formatNomComplet({
25
+ denominationUniteLegale:
26
+ siretData.unite_legale.personne_morale_attributs.raison_sociale ?? "",
27
+ nomUniteLegale:
28
+ siretData.unite_legale.personne_physique_attributs.nom_naissance,
29
+ nomUsageUniteLegale:
30
+ siretData.unite_legale.personne_physique_attributs.nom_usage,
31
+ prenomUsuelUniteLegale:
32
+ siretData.unite_legale.personne_physique_attributs.prenom_usuel,
33
+ sigleUniteLegale:
34
+ siretData.unite_legale.personne_morale_attributs.sigle,
35
+ });
36
+ const libelle = isPartiallyNonDiffusible
37
+ ? "Nom inconnu"
38
+ : `${nomComplet}${enseigne ? ` - ${capitalize(enseigne)}` : ""}`;
39
+
40
+ const trancheEffectifs = isPartiallyNonDiffusible
41
+ ? null
42
+ : siretData.tranche_effectif_salarie.code;
43
+ const trancheEffectifsUniteLegale = isPartiallyNonDiffusible
44
+ ? null
45
+ : siretData.unite_legale.tranche_effectif_salarie.code;
46
+ const codePostal = isPartiallyNonDiffusible
47
+ ? siretData.adresse.code_commune
48
+ : siretData.adresse.code_postal;
49
+
50
+ return OrganizationInfoSchema.parse({
51
+ activitePrincipale: siretData.activite_principale.code ?? "",
52
+ adresse: formatAddress(siretData.adresse),
53
+ categorieJuridique: siretData.unite_legale.forme_juridique.code ?? "",
54
+ codeOfficielGeographique: siretData.adresse.code_commune ?? "",
55
+ codePostal,
56
+ enseigne,
57
+ estActive: siretData.etat_administratif === "A",
58
+ estDiffusible: siretData.status_diffusion === "diffusible",
59
+ etatAdministratif: siretData.unite_legale.etat_administratif,
60
+ libelle,
61
+ libelleActivitePrincipale: formatMainActivity(
62
+ siretData.activite_principale,
63
+ ),
64
+ libelleCategorieJuridique: siretData.unite_legale.forme_juridique.libelle,
65
+ libelleTrancheEffectif: libelleFromCodeEffectif(
66
+ siretData.tranche_effectif_salarie.intitule,
67
+ siretData.tranche_effectif_salarie.date_reference,
68
+ ),
69
+ nomComplet,
70
+ siret: siretData.siret,
71
+ statutDiffusion: siretData.status_diffusion,
72
+ trancheEffectifs,
73
+ trancheEffectifsUniteLegale,
74
+ } satisfies OrganizationInfo);
75
+ }
@@ -0,0 +1,3 @@
1
+ //
2
+
3
+ export * from "./from-siret.js";
@@ -0,0 +1,43 @@
1
+ //
2
+
3
+ import { emptyDatabase, migrate, pg } from "#testing";
4
+ import assert from "node:assert/strict";
5
+ import { before, beforeEach, mock, suite, test } from "node:test";
6
+ import { addDomainFactory } from "./add-domain.js";
7
+
8
+ //
9
+
10
+ const addDomain = addDomainFactory({ pg: pg as any });
11
+
12
+ suite("addDomainFactory", () => {
13
+ before(migrate);
14
+ beforeEach(emptyDatabase);
15
+
16
+ test("should add domain", async () => {
17
+ await pg.sql`
18
+ INSERT INTO organizations
19
+ (id, siret, created_at, updated_at)
20
+ VALUES
21
+ (1, '66204244933106', '3333-03-03', '3333-04-04')
22
+ ;
23
+ `;
24
+ mock.timers.enable({ apis: ["Date"], now: new Date("4444-04-04") });
25
+
26
+ const emailDomain = await addDomain({
27
+ domain: "darkangels.world",
28
+ organization_id: 1,
29
+ verification_type: "verified",
30
+ });
31
+
32
+ assert.deepEqual(emailDomain, {
33
+ can_be_suggested: true,
34
+ created_at: new Date("4444-04-04"),
35
+ domain: "darkangels.world",
36
+ id: 1,
37
+ organization_id: 1,
38
+ updated_at: new Date("4444-04-04"),
39
+ verification_type: "verified",
40
+ verified_at: new Date("4444-04-04"),
41
+ });
42
+ });
43
+ });