@adobe/acc-js-sdk 1.1.41 → 1.1.43

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.
package/docs/_config.yml CHANGED
@@ -34,6 +34,11 @@ github_username: mkiki
34
34
  plugins:
35
35
  - jekyll-feed
36
36
 
37
+ collections:
38
+ posts:
39
+ output: true
40
+
41
+
37
42
  # Exclude from processing.
38
43
  # The following items will not be processed, by default.
39
44
  # Any item listed under the `exclude:` key here will be automatically added to
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  layout: post
3
3
  title: "Welcome to the ACC JS SDK"
4
- date: 2022-10-14 09:06:57 +0200
5
4
  author: Alexandre Morin
6
5
  categories: "SDK Update"
7
6
  excerpt: Some ideas about how to use the SDK and a short example illustrating how to build a REST proxy lambda function
@@ -0,0 +1,364 @@
1
+ ---
2
+ layout: post
3
+ title: "Manipulating schemas"
4
+ author: Alexandre Morin
5
+ tags: schemas soap
6
+ excerpt: Sequence of APIs to discover and create a FDA schema
7
+ ---
8
+
9
+
10
+ <p>
11
+ This article will illustrate how to use APIs to create and manipulate schemas
12
+ </p>
13
+
14
+ <h1>Schemas</h1>
15
+
16
+ <p>
17
+ There are 3 types of schemas in Campaign
18
+ <ul>
19
+ <li>Source schemas (xtk:srcSchema)</li>
20
+ <li>Schemas (xtk:schema)</li>
21
+ <li>SQL schemas (xtk:sqlSchema)</li>
22
+ </ul>
23
+ </p>
24
+
25
+ <p>
26
+ Users normally only create source schemas. Those are then "compiled" into schemas and SQL schemas.
27
+ The compilation process (called "build schemas") can take several seconds and is performed via an API call.
28
+ This means that the <b>xtk:builder#BuildSchema</b> (or similar API) must be called after schemas are
29
+ modified or created, to ensure overall consistency.
30
+ </p>
31
+
32
+ <p>
33
+ Once source schemas are created and compiled, you will usually use schemas and not source schemas because
34
+ source schemas are just the various elements that build up the final schemas. You will probably never need
35
+ to use sql schemas directly. They are used internally to map schemas to SQL tables.
36
+ </p>
37
+ <p>
38
+ Of course you can read and use source schemas as well but this is usually limited to schema authoring
39
+ use cases.
40
+ </p>
41
+
42
+ <table>
43
+ <thead>
44
+ <tr><th>Attribute/Method</th><th>Description</th></tr>
45
+ </thead>
46
+ <tbody>
47
+ <tr><td>Create or modify</td><td>Use source schemas and BuildSchema API</td></tr>
48
+ <tr><td>Delete</td><td></td></tr>
49
+ <tr><td>Use</td><td>Use schemas</td></tr>
50
+ </tbody>
51
+ </table>
52
+
53
+
54
+ <h1>Reading schemas</h1>
55
+
56
+ <h2>Get a schema as a JSON object</h2>
57
+ <p>
58
+ The simplest way to read a schema is to use the <b>getSchema</b> SDK APIs which also provide client-side caching.
59
+ </p>
60
+
61
+ <p>
62
+ The <b>client.getSchema()</b> API returns a JSON or XML object representing the (compiled) schema requested.
63
+ See the <a href="{{ site.baseurl }}/xtkSchema.html">Schema API</a> page for more details.
64
+ </p>
65
+
66
+ <pre class="code">
67
+ const schema = await client.getSchema("nms:recipient");
68
+ console.log(JSON.stringify(schema));
69
+ </pre>
70
+
71
+ <h2>Get a schema as a convenient JavaScript object</h2>
72
+ <p>
73
+ The <b>application.getSchema()</b> API is built on top of the previous one and returns a schema object which
74
+ is more convenient to use that a raw JSON object. See the <a href="{{ site.baseurl }}/application.html">Application</a> object for more details.
75
+ </p>
76
+
77
+ <pre class="code">
78
+ const schema = await client.application.getSchema("nms:recipient");
79
+ </pre>
80
+
81
+ <h2>List schemas using a query</h2>
82
+ <p>
83
+ It is also possible to use the <a href="{{ site.baseurl }}/xtkQueryDef.html">QueryDef</a> API to read schemas and source schemas.
84
+ </p>
85
+ <p>
86
+ Let's first get the list of source schemas in the custom namespace "cus". The following will return then
87
+ names and labels of 10 source schemas in the cus namespace.
88
+ </p>
89
+
90
+ <pre class="code">
91
+
92
+ const queryDef = {
93
+ schema: "xtk:srcSchema",
94
+ operation: "select",
95
+ lineCount: 10,
96
+ select: {
97
+ node: [
98
+ { expr: "@name" },
99
+ { expr: "@namespace" },
100
+ { expr: "@label" }
101
+ ]
102
+ },
103
+ where: {
104
+ condition: [
105
+ { expr:`@namespace = 'cus'` }
106
+ ]
107
+ }
108
+ };
109
+ const query = client.NLWS.xtkQueryDef.create(queryDef);
110
+ const srcSchemas = await query.executeQuery();
111
+ </pre>
112
+
113
+ <p>
114
+ To return schemas instead of source schemas, use "xtk:schema" instead of "xtk:srcSchema" in the QueryDef.
115
+ </p>
116
+
117
+ <p>
118
+ If you want both schemas and source schemas, you can query the "xtk:entity" schema instead. But this requires an
119
+ additional condition en the "@entitySchema" attribute. If not, you will also get custom forms, javascript, etc.
120
+ </p>
121
+
122
+ <pre class="code">
123
+
124
+ const queryDef = {
125
+ schema: "xtk:entity",
126
+ operation: "select",
127
+ lineCount: 10,
128
+ select: {
129
+ node: [
130
+ { expr: "@name" },
131
+ { expr: "@namespace" },
132
+ { expr: "@label" },
133
+ { expr: "@entitySchema" },
134
+ ]
135
+ },
136
+ where: {
137
+ condition: [
138
+ { expr:`@namespace = 'cus'` },
139
+ { expr:`@entitySchema IN ('xtk:schema', 'xtk:srcSchema')` }
140
+ ]
141
+ }
142
+ };
143
+ const query = client.NLWS.xtkQueryDef.create(queryDef);
144
+ const entities = await query.executeQuery();
145
+ </pre>
146
+
147
+
148
+ <h2>Use a query to get schema details</h2>
149
+
150
+ <p>
151
+ Schemas are stored into the xtk:entity table. The main fields (@name, @label, @namespace) are stored directly
152
+ as SQL columns, but the schema definition is actually stored as XML.
153
+ Getting the XML requires to query at least one of attibutes which is stored in XML, for example
154
+ the @library attribute.
155
+ </p>
156
+
157
+ <pre class="code">
158
+
159
+ const queryDef = {
160
+ schema: "xtk:schema",
161
+ operation: "get",
162
+ select: {
163
+ node: [
164
+ { expr: "@name" },
165
+ { expr: "@namespace" },
166
+ { expr: "@label" },
167
+ { expr: "@library" },
168
+ ]
169
+ },
170
+ where: {
171
+ condition: [
172
+ { expr:`@namespace = 'nms' and @name='recipient'` },
173
+ ]
174
+ }
175
+ };
176
+ const query = client.NLWS.xtkQueryDef.create(queryDef);
177
+ const schema = await query.executeQuery();
178
+ </pre>
179
+
180
+
181
+ <p>
182
+ Note that querying for schemas (as opposed to srcSchemas) the id is the id of the top most src schema (i.e. "nms:recipient" and not "cus:recipient").
183
+ The reason is that all source schemas (nms:recipient, cus:recipient, etc.) are merged into a single schema (nms:recipient) when compiled.
184
+ You can, however, query individual source schemas even if they are extension schemas
185
+ </p>
186
+
187
+ <pre class="code">
188
+ const queryDef = {
189
+ schema: "xtk:srcSchema",
190
+ operation: "get",
191
+ select: {
192
+ node: [
193
+ { expr: "@name" },
194
+ { expr: "@namespace" },
195
+ { expr: "@label" },
196
+ { expr: "@library" },
197
+ ]
198
+ },
199
+ where: {
200
+ condition: [
201
+ { expr:`@namespace = 'gov' and @name='recipient'` },
202
+ ]
203
+ }
204
+ };
205
+ const query = client.NLWS.xtkQueryDef.create(queryDef);
206
+ const srcSchema = await query.executeQuery();
207
+ </pre>
208
+
209
+ <h2>Use GetEntityIfMoreRecent</h2>
210
+ <p>
211
+ Last, but not least, you can use the <b>xtk:persist#GetEntityIfMoreRecent</b> method to return a (compiled) schema. This is the method internally
212
+ used by the SDK. This method is useful if you already have the schema but you are not sure it's up to date. Calling this method is usefull to
213
+ save network bandwidth as it will only return the schema if it has changed.
214
+ </p>
215
+ <p>
216
+ For this, you need to keep both the schema and its md5 hash (@md5 attribute). Then you can call the method as follows:
217
+ <ul>
218
+ <li>Pass an identifier to the schema. As GetEntityIfMoreRecent works with any type of entities, the id is formatted as "xtk:srcSchema:{schemaId}" or "xtk:schema:{schemaId}" </li>
219
+ <li>Pass the MD5 of the schema if you have it. If you pass undefined, the API call will return the full schema. If you pass a md5, the API will only return a schema if it has changed</li>
220
+ <li>Pass a boolean indicating if you expect the requested entity to exist. If false, then the API will return null for non existent entities</li>
221
+ </ul>
222
+ </p>
223
+
224
+ <pre class="code">
225
+ const schema = await client.NLWS.xtkSession.getEntityIfMoreRecent("xtk:srcSchema|nms:notFound", md5, mustExist);
226
+ </pre>
227
+
228
+
229
+
230
+ <h1>Creating and updating schemas</h1>
231
+
232
+ <p>
233
+ The following API calls can be used to either create or update schemas. Note the builtin schemas cannot be updated.
234
+ The operation is always a two-step process:
235
+ <ui>
236
+ <li>Create or modify a source schema</li>
237
+ <li>Call one of the <b>BuildSchema</b> methods</li>
238
+ </ui>
239
+ </p>
240
+
241
+ <p class="warning">Always work with source schemas for creation and update. Tampering with (compiled) schemas can lead to inconsistencies and unpredictable results.</p>
242
+
243
+ <h2>Create (or modify) a custom schema</h2>
244
+
245
+ <p>
246
+ In this example we'll create or update a simple custom schema. This is the simplest scenario, there's no schema extension, etc.
247
+ </p>
248
+
249
+ <pre class="code">
250
+ await client.NLWS.xtkSession.write({
251
+ xtkschema: "xtk:srcSchema",
252
+ namespace: "cus",
253
+ name: "foo",
254
+ label: "Foos",
255
+ labelSingular: "Foo",
256
+ desc: "This is a fooooo",
257
+ mappingType: "sql",
258
+ element: [
259
+ {
260
+ name: "foo",
261
+ key: [
262
+ {
263
+ name: "id",
264
+ keyfield: [
265
+ {
266
+ xpath: "@id"
267
+ }
268
+ ]
269
+ }
270
+ ],
271
+ attribute: [
272
+ {
273
+ name: "id",
274
+ label: "Id",
275
+ type: "string",
276
+ length: 100
277
+ }
278
+ ]
279
+ }
280
+ ]
281
+ });
282
+ </pre>
283
+
284
+ <p>
285
+ <ul>
286
+ <li>Make sure to pass the attribute xtkschema: "xtk:srcSchema". This means that we are creating or updating a source schema</li>
287
+ <li>Do not modify (compiled) schemas directly</li>
288
+ <li>The function does not return anything. You can a queryDef to verify that the source schema was properly created</li>
289
+ <li>By convention, the schema name is <i>singular</i>, in camel case, starting with a lower case character</li>
290
+ <li>The label is the human friendly name and is plural, whereas labelSingular is for ... singular</li>
291
+ <li>It's important to set the mapping type to SQL, indicating that we want a SQL table created</li>
292
+ <li>Create a root element with the <i>same name</i> as the schema</li>
293
+ <li>Although it's not mandatory to have a primary key, it's a best practice. Create a <i>key</i> element</li>
294
+ </ul>
295
+ </p>
296
+
297
+ <p>
298
+ Now, build the schema. This operation may take a few seconds, adjust the timeout accordingly.
299
+ </p>
300
+
301
+ <pre class="code">
302
+ await client.NLWS.pushDown({ timeout: 60000 }).xtkBuilder.BuildSchemaFromId("cus:foo");
303
+ </pre>
304
+ <p class="warning">
305
+ Do not forget to call one of the <b>xtk:builder#BuildSchema</b>
306
+ </p>
307
+
308
+
309
+
310
+ <h1>Deleting a schema</h1>
311
+
312
+ <p>
313
+ Before you can delete a source schema you need to get it's <b>@extendedSchema</b> attribute. The following will
314
+ return the id of the parent schema if cus:foo is an extension schema, or undefined if cus:foo is not an extension schema.
315
+ </p>
316
+
317
+ <pre class="code">
318
+ const queryDef = {
319
+ schema: "xtk:srcSchema",
320
+ operation: "get",
321
+ select: {
322
+ node: [
323
+ { expr: "@extendedSchema" },
324
+ ]
325
+ },
326
+ where: {
327
+ condition: [
328
+ { expr:`@namespace = 'cus' and @name='foo'` },
329
+ ]
330
+ }
331
+ };
332
+ const query = client.NLWS.xtkQueryDef.create(queryDef);
333
+ const srcSchema = await query.executeQuery();
334
+ const extendedSchema = srcSchema.extendedSchema;
335
+ </pre>
336
+
337
+ <p>
338
+ To remove a schema, a writer to delete the source schema entities you want to delete. Make sure to pass the extendedSchema if there is one.
339
+ </p>
340
+
341
+ <pre class="code">
342
+ await client.NLWS.xtkSession.writeCollection({
343
+ xtkschema: "xtk:srcSchema",
344
+ srcSchema: [
345
+ {
346
+ _operation: "delete",
347
+ extendedSchema: extendedSchema,
348
+ namespace: "cus",
349
+ name: "foo"
350
+ }
351
+ ]
352
+ });
353
+ </pre>
354
+
355
+ <p>
356
+ Now, call the <b>xtk:builder#RemoveSchemaLinkRef</b> method to remove the schema and reverse links. Make sure to pass the extendedSchema if there is one.
357
+ </p>
358
+ <pre class="code">
359
+ await client.NLWS.xtkBuilder.RemoveSchemaLinkRef("cus:foo", extendedSchema);
360
+ </pre>
361
+
362
+ <p>
363
+ If the schema you want to delete has extension schema
364
+ </p>
@@ -0,0 +1,429 @@
1
+ ---
2
+ layout: post
3
+ title: "Discover and create a FDA schema"
4
+ author: Alexandre Morin
5
+ tags: fda schemas soap
6
+ excerpt: Sequence of APIs to discover and create a FDA schema
7
+ ---
8
+
9
+
10
+ <p>
11
+ This article will illustrate the following flow and show the corresponding SOAP calls.
12
+ <ul>
13
+ <li>Create an external account corresponding to a FDA PostgreSQL database</li>
14
+ <li>Discover all the tables available in the FDA database</li>
15
+ <li>Choose a table and discover the schema</li>
16
+ <li>Annotate and save the schema</li>
17
+ </ul>
18
+ </p>
19
+
20
+ <h1>Create FDA external account</h1>
21
+
22
+ <p>
23
+ We'll create (or update) an external acount for a FDA database. You need the server, port, database and schema name, user and password.
24
+ </p>
25
+
26
+ <h2>Encrypt the password</h2>
27
+
28
+ <p>
29
+ The first step is to encrypt the password.
30
+ The <b>xtk:persist#EncryptPassword</b> API will by used to encrypt the password.
31
+ </p>
32
+
33
+ <p></p>
34
+
35
+ <pre class="code">
36
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
37
+ &lt;SOAP-ENV:Body&gt;
38
+
39
+ &lt;EncryptPassword xmlns='urn:xtk:persist' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
40
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
41
+
42
+ &lt;strDecrypted xsi:type='xsd:string'&gt;admin&lt;/strDecrypted&gt;
43
+
44
+ &lt;/EncryptPassword&gt;
45
+ &lt;/SOAP-ENV:Body&gt;
46
+ &lt;/SOAP-ENV:Envelope&gt;
47
+
48
+ </pre>
49
+
50
+ <p>
51
+ The API will return the encrypted password as follows.
52
+ </p>
53
+
54
+ <pre class="code">
55
+ &lt;?xml version='1.0'?&gt;
56
+ &lt;SOAP-ENV:Envelope
57
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
58
+ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
59
+ xmlns:ns='urn:xtk:session'
60
+ xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
61
+ &lt;SOAP-ENV:Body&gt;
62
+ &lt;EncryptPasswordResponse
63
+ xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
64
+ &lt;pstrEncrypted xsi:type='xsd:string'&gt;@S/U/D7xOdZfRAwf3+WRM3w==&lt;/pstrEncrypted&gt;
65
+ &lt;/EncryptPasswordResponse&gt;
66
+ &lt;/SOAP-ENV:Body&gt;
67
+ &lt;/SOAP-ENV:Envelope&gt;
68
+ </pre>
69
+
70
+
71
+ <h2>Create the external account</h2>
72
+
73
+ <p>
74
+ Now the external account can be created using the <b>xtk:persist#Write</b> API. Give it a name, make sure it's active and set all connection parameters as follows. Make sure you provide the encrypted password
75
+ </p>
76
+
77
+ <pre class="code">
78
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
79
+ &lt;SOAP-ENV:Body&gt;
80
+
81
+ &lt;Write xmlns='urn:xtk:persist' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
82
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
83
+ &lt;domDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
84
+
85
+ &lt;extAccount xtkschema="nms:extAccount"
86
+ name="pg2" label="Postgres (2)" provider="PostgreSQL" type="7" active="1"
87
+ server="localhost" port="" dbName="fda" dbSchema="public"
88
+ account="postgres" password="@S/U/D7xOdZfRAwf3+WRM3w=="
89
+ timezone="_server_" unicodeData="1"&gt;
90
+ &lt;/extAccount&gt;
91
+
92
+ &lt;/domDoc&gt;
93
+ &lt;/Write&gt;
94
+ &lt;/SOAP-ENV:Body&gt;
95
+ &lt;/SOAP-ENV:Envelope&gt;
96
+ </pre>
97
+
98
+ <p>
99
+ The API does not return anything. Note the the <b>name</b> attribute is a unique key in the nms:account schema. So the same API call can be used to update the account.
100
+ </p>
101
+
102
+ <h2>Test the connection</h2>
103
+
104
+ <p>
105
+ Campaign provides the <b>nms:extAccount#TestAccount</b> API to test the connection. Unfortunately, this API cannot take an extAccount id, but it needs to be passed all the credentials settings again.
106
+ Note that the password is still encrypted.
107
+ </p>
108
+
109
+ <pre class="code">
110
+
111
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
112
+ &lt;SOAP-ENV:Body&gt;
113
+
114
+ &lt;TestAccount xmlns='urn:xtk:persist' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
115
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
116
+
117
+ &lt;param xsi:type='xsd:byte'&gt;7&lt;/param&gt;
118
+ &lt;param xsi:type='xsd:boolean'&gt;1&lt;/param&gt;
119
+ &lt;param xsi:type='xsd:string'&gt;PostgreSQL:localhost&lt;/param&gt;
120
+ &lt;param xsi:type='xsd:string'&gt;postgres&lt;/param&gt;
121
+ &lt;param xsi:type='xsd:string'&gt;@S/U/D7xOdZfRAwf3+WRM3w==&lt;/param&gt;
122
+ &lt;param xsi:type='xsd:string'&gt;fda&lt;/param&gt;
123
+ &lt;param xsi:type='xsd:string'&gt;NChar=0;unicodeData=1;timezone=_server_;dbSchema=public;fileMethod=uploadFile;filePath=;&lt;/param&gt;
124
+ &lt;param xsi:type='xsd:string'&gt;pg2&lt;/param&gt;
125
+ &lt;param xsi:type='xsd:boolean'&gt;0&lt;/param&gt;
126
+ &lt;param xsi:type='xsd:string'&gt;&lt;/param&gt;
127
+ &lt;param xsi:type='xsd:string'&gt;&lt;/param&gt;
128
+ &lt;param xsi:type='xsd:string'&gt;&lt;/param&gt;
129
+ &lt;param xsi:type='xsd:string'&gt;&lt;/param&gt;
130
+
131
+ &lt;/TestAccount&gt;
132
+ &lt;/SOAP-ENV:Body&gt;
133
+ &lt;/SOAP-ENV:Envelope&gt;
134
+
135
+ </pre>
136
+
137
+ <p>
138
+ This should return a string with version information.
139
+ </p>
140
+
141
+ <pre class="code">
142
+ &lt;?xml version='1.0'?&gt;
143
+ &lt;SOAP-ENV:Envelope
144
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
145
+ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
146
+ xmlns:ns='urn:nms:extAccount'
147
+ xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
148
+ &lt;SOAP-ENV:Body&gt;
149
+ &lt;TestAccountResponse
150
+ xmlns='urn:nms:extAccount' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
151
+ &lt;pstrServer xsi:type='xsd:string'&gt;localhost&lt;/pstrServer&gt;
152
+ &lt;pstrDbmsVer xsi:type='xsd:string'&gt;Database server version &#39;PostgreSQL 12.2, compiled by Visual C++ build 1914, 64-bit&#39;.&lt;/pstrDbmsVer&gt;
153
+ &lt;pstrWarehouse xsi:type='xsd:string'&gt;&lt;/pstrWarehouse&gt;
154
+ &lt;pstrTestDuration xsi:type='xsd:string'&gt;Test connection took: 0 ms&lt;/pstrTestDuration&gt;
155
+ &lt;/TestAccountResponse&gt;
156
+ &lt;/SOAP-ENV:Body&gt;
157
+ &lt;/SOAP-ENV:Envelope&gt;
158
+ </pre>
159
+
160
+
161
+ <h2>Query the external account</h2>
162
+
163
+ <p>
164
+ You can also use a query to get information about the external account.
165
+ </p>
166
+
167
+ <pre class="code">
168
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
169
+ &lt;SOAP-ENV:Body&gt;
170
+
171
+ &lt;ExecuteQuery xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
172
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
173
+
174
+ &lt;entity xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
175
+ &lt;queryDef fullLoad="true" operation="get" schema="nms:extAccount" startPath="/" xtkschema="xtk:queryDef"&gt;
176
+ &lt;select&gt;
177
+ &lt;node expr="@id"/&gt;
178
+ &lt;node expr="@name"/&gt;
179
+ &lt;node expr="@label"/&gt;
180
+ &lt;node expr="@provider"/&gt;
181
+ &lt;node expr="@active"/&gt;
182
+ &lt;node expr="@server"/&gt;
183
+ &lt;node expr="@port"/&gt;
184
+ &lt;node expr="@dbName"/&gt;
185
+ &lt;node expr="@dbSchema"/&gt;
186
+ &lt;node expr="@account"/&gt;
187
+ &lt;node expr="@password"/&gt;
188
+ &lt;node expr="@timezone"/&gt;
189
+ &lt;node expr="@unicodeData"/&gt;
190
+ &lt;/select&gt;
191
+ &lt;where&gt;
192
+ &lt;condition expr="@name='pg2'"/&gt;
193
+ &lt;/where&gt;
194
+ &lt;/queryDef&gt;
195
+ &lt;/entity&gt;
196
+
197
+ &lt;/ExecuteQuery&gt;
198
+
199
+
200
+ &lt;/SOAP-ENV:Body&gt;
201
+ &lt;/SOAP-ENV:Envelope&gt;
202
+
203
+ </pre>
204
+
205
+ <p>
206
+ It will return something like this
207
+ </p>
208
+
209
+ <pre class="code">
210
+ &lt;?xml version='1.0'?&gt;
211
+ &lt;SOAP-ENV:Envelope
212
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
213
+ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
214
+ xmlns:ns='urn:xtk:queryDef'
215
+ xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
216
+ &lt;SOAP-ENV:Body&gt;
217
+ &lt;ExecuteQueryResponse
218
+ xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
219
+ &lt;pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
220
+ &lt;extAccount account="postgres" active="1" dbName="fda" dbSchema="public" id="4510" label="Postgres (2)" name="pg2" password="@S/U/D7xOdZfRAwf3+WRM3w==" port="" provider="PostgreSQL" server="localhost" timezone="_server_" unicodeData="1"&gt;&lt;/extAccount&gt;
221
+ &lt;/pdomOutput&gt;
222
+ &lt;/ExecuteQueryResponse&gt;
223
+ &lt;/SOAP-ENV:Body&gt;
224
+ &lt;/SOAP-ENV:Envelope&gt;
225
+
226
+ </pre>
227
+
228
+
229
+ <h1>Discover the tables</h1>
230
+
231
+ <p>
232
+ Now you can discover the tables available in the FDA database. The <b>xtk:sqlSchema#BuildTableList</b> API will return a list of tables.
233
+ </p>
234
+
235
+ <pre class="code">
236
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
237
+ &lt;SOAP-ENV:Body&gt;
238
+
239
+ &lt;BuildTableList xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
240
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
241
+
242
+ &lt;param xsi:type='xsd:string'&gt;nms:extAccount:pg2&lt;/param&gt;
243
+ &lt;param xsi:type='xsd:string'&gt;%%&lt;/param&gt;
244
+
245
+ &lt;/BuildTableList&gt;
246
+
247
+ &lt;/SOAP-ENV:Body&gt;
248
+ &lt;/SOAP-ENV:Envelope&gt;
249
+ </pre>
250
+
251
+ <p>
252
+ Which returns the following. In this example, I only have one table named <b>x</b>.
253
+ </p>
254
+
255
+ <pre class="code">
256
+ &lt;?xml version='1.0'?&gt;
257
+ &lt;SOAP-ENV:Envelope
258
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
259
+ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
260
+ xmlns:ns='urn:xtk:sqlSchema'
261
+ xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
262
+ &lt;SOAP-ENV:Body&gt;
263
+ &lt;BuildTableListResponse
264
+ xmlns='urn:xtk:sqlSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
265
+ &lt;pdomTableList xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
266
+ &lt;sqlSchema&gt;
267
+ &lt;table name="public.x"/&gt;
268
+ &lt;/sqlSchema&gt;
269
+ &lt;/pdomTableList&gt;
270
+ &lt;/BuildTableListResponse&gt;
271
+ &lt;/SOAP-ENV:Body&gt;
272
+ &lt;/SOAP-ENV:Envelope&gt;
273
+ </pre>
274
+
275
+
276
+ <h1>Create the schema</h1>
277
+
278
+ <p>
279
+ Schema creation is a 3-step process
280
+ <ul>
281
+ <li>First, create a sql schema by discovering the table structure in the FDA database</li>
282
+ <li>Second, modify the schema if necessary and save it as a source schema</li>
283
+ <li>Finally, build the final schema</li>
284
+ </ul>
285
+ </p>
286
+
287
+ <h2>Discover the table structure</h2>
288
+
289
+ <p>
290
+ We'll call the <b>xtk:builder#GenerateSchema</b> API. It's the API behind the schema creation wizard.
291
+ This API takes high level parameters and return an XML document which is the future schema. Note that
292
+ everything happens in memory. At this point, nothing is created in the database.
293
+ </p>
294
+ <p>
295
+ The rationale is that the discovery may not discover anything, so we return a source schema, give you
296
+ a chance to modify it before it is persisted.
297
+ </p>
298
+
299
+ <pre class="code">
300
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
301
+ &lt;SOAP-ENV:Body&gt;
302
+
303
+ &lt;GenerateSchema xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
304
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
305
+
306
+ &lt;param xsi:type='xsi:element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
307
+ &lt;tmp type="extView" sqltable="public.x" removePrefix="0" advanced="0"&gt;
308
+ &lt;/tmp&gt;
309
+ &lt;/param&gt;
310
+
311
+ &lt;param xsi:type='xsi:element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
312
+ &lt;srcSchema dataSource="nms:extAccount:pg2" img="xtk:schema.png" label="NewAPI" mappingType="sql" name="new2" namespace="cus" xtkschema="xtk:srcSchema"&gt;
313
+ &lt;/srcSchema&gt;
314
+ &lt;/param&gt;
315
+
316
+ &lt;/GenerateSchema&gt;
317
+
318
+ &lt;/SOAP-ENV:Body&gt;
319
+ &lt;/SOAP-ENV:Envelope&gt;
320
+ </pre>
321
+
322
+ <p>
323
+ Which returns
324
+ </p>
325
+
326
+ <pre class="code">
327
+ &lt;?xml version='1.0'?&gt;
328
+ &lt;SOAP-ENV:Envelope
329
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
330
+ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
331
+ xmlns:ns='urn:xtk:builder'
332
+ xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
333
+ &lt;SOAP-ENV:Body&gt;
334
+ &lt;GenerateSchemaResponse
335
+ xmlns='urn:xtk:builder' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
336
+ &lt;pdomSchema xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
337
+
338
+ &lt;srcSchema img="xtk:schema.png" label="NewAPI" mappingType="sql" name="new2" namespace="cus" view="true" xtkschema="xtk:srcSchema"&gt;
339
+ &lt;element dataSource="nms:extAccount:pg2" label="NewAPI" name="new2" sqltable="public.x"&gt;
340
+ &lt;attribute advanced="false" label="i" name="i" sqlname="i" type="long"/&gt;
341
+ &lt;/element&gt;
342
+ &lt;/srcSchema&gt;
343
+
344
+ &lt;/pdomSchema&gt;
345
+ &lt;/GenerateSchemaResponse&gt;
346
+ &lt;/SOAP-ENV:Body&gt;
347
+ &lt;/SOAP-ENV:Envelope&gt;
348
+ </pre>
349
+
350
+ <p>
351
+ As you can see a src schema is returned. In this case, my table only had one column named "i" which is a long.
352
+ </p>
353
+
354
+ <h2>Create the schema</h2>
355
+
356
+ <p>
357
+ Let's extract the srcSchema object from the result of the previous API call. We can modify this XML document
358
+ and, for example, define links, labels, keys, etc.
359
+
360
+ Typically, we'll create at least a key as follows
361
+ </p>
362
+ <pre class="code">
363
+ &lt;srcSchema img="xtk:schema.png" label="NewAPI" mappingType="sql" name="new2" namespace="cus" view="true" xtkschema="xtk:srcSchema"&gt;
364
+ &lt;element dataSource="nms:extAccount:pg2" label="NewAPI" name="new2" sqltable="public.x"&gt;
365
+ &lt;key name="pk"&gt;
366
+ &lt;keyfield xpath="@i"/&gt;
367
+ &lt;/key&gt;
368
+ &lt;attribute advanced="false" label="i" name="i" sqlname="i" type="long"/&gt;
369
+ &lt;/element&gt;
370
+ &lt;/srcSchema&gt;
371
+ </pre>
372
+ <p>
373
+ When happy with the document, it's time to save it and persist the src schema in the database. This
374
+ can be done with a simple <b>xtk:persist#Write</b> API call.
375
+ </p>
376
+
377
+ <pre class="code">
378
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
379
+ &lt;SOAP-ENV:Body&gt;
380
+
381
+ &lt;Write xmlns='urn:xtk:persist' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
382
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
383
+
384
+ &lt;domDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'&gt;
385
+
386
+ &lt;srcSchema img="xtk:schema.png" label="NewAPI" mappingType="sql" name="new2" namespace="cus" view="true" xtkschema="xtk:srcSchema"&gt;
387
+ &lt;element dataSource="nms:extAccount:pg2" label="NewAPI" name="new2" sqltable="public.x"&gt;
388
+ &lt;key name="pk"&gt;
389
+ &lt;keyfield xpath="@i"/&gt;
390
+ &lt;/key&gt;
391
+ &lt;attribute advanced="false" label="i" name="i" sqlname="i" type="long"/&gt;
392
+ &lt;/element&gt;
393
+ &lt;/srcSchema&gt;
394
+
395
+ &lt;/domDoc&gt;
396
+
397
+ &lt;/Write&gt;
398
+
399
+
400
+ &lt;/SOAP-ENV:Body&gt;
401
+ &lt;/SOAP-ENV:Envelope&gt;
402
+ </pre>
403
+
404
+ <p>
405
+ This API call does not return anything. But you can check in the database, the schema is there.
406
+ </p>
407
+
408
+ <h2>Build the schema</h2>
409
+
410
+ <p>
411
+ The last step is very important and consists of building a xtk:schema from the xtk:srcSchema which has
412
+ just been created. We can use the <b>xtk:builder#BuildSchemaFromId</b> API call.
413
+ </p>
414
+
415
+ <pre class="code">
416
+ &lt;SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:persist' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'&gt;
417
+ &lt;SOAP-ENV:Body&gt;
418
+
419
+ &lt;BuildSchemaFromId xmlns='urn:xtk:builder' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'&gt;
420
+ &lt;__sessiontoken xsi:type='xsd:string'&gt;{{SESSION_TOKEN}}&lt;/__sessiontoken&gt;
421
+
422
+ &lt;schemaId&gt;cus:new2&lt;/schemaId&gt;
423
+
424
+ &lt;/BuildSchemaFromId&gt;
425
+
426
+
427
+ &lt;/SOAP-ENV:Body&gt;
428
+ &lt;/SOAP-ENV:Envelope&gt;
429
+ </pre>
package/docs/blog.html CHANGED
@@ -8,7 +8,7 @@ title: Blog (latest posts)
8
8
  {% for post in site.posts %}
9
9
  <li>
10
10
  <div class="blog-expert">
11
- <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
11
+ <h2><a href="{{ site.baseurl }}/{{ post.url }}">{{ post.title }}</a></h2>
12
12
  <div class="blog-header">{{ post.author }} - {{ post.date | date_to_string }}</div>
13
13
  {{ post.excerpt }}
14
14
  </div>
@@ -2,6 +2,30 @@
2
2
  layout: page
3
3
  title: Change Log
4
4
  ---
5
+ <section class="changelog"><h1>Version 1.1.43</h1>
6
+ <h2>2023/12/21</h2>
7
+ <li>
8
+ Do not throw an error in case of duplicate enumeration value. Use the last one instead. This introduces a small behavior change
9
+ because this was causing an exception before.
10
+ </li>
11
+ </section>
12
+
13
+
14
+ <section class="changelog"><h1>Version 1.1.42</h1>
15
+ <h2>2023/12/20</h2>
16
+ <li>
17
+ Fixed a typo in documentation
18
+ </li>
19
+ <li>
20
+ Added 2 blog articles about schema creation (SOAP calls)
21
+ </li>
22
+ <li>
23
+ Fixed the documentation: blog articles links were broken
24
+ </li>
25
+ <li>
26
+ Upgraded Axios to fix a vulnerability
27
+ </li>
28
+ </section>
5
29
 
6
30
  <section class="changelog"><h1>Version 1.1.41</h1>
7
31
  <h2>2023/11/01</h2>
@@ -105,7 +105,7 @@ await client.logoff();
105
105
  </p>
106
106
 
107
107
  <h2>Login with user and password</h2>
108
- <p>This is the most common method to log in to Campaign. User the <b>ofUserAndPassword</b> function and pass it the user name and password</p>
108
+ <p>This is the most common method to log in to Campaign. Use the <b>ofUserAndPassword</b> function and pass it the user name and password</p>
109
109
  <pre class="code">
110
110
  const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
111
111
  "https://myInstance.campaign.adobe.com",
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.41",
3
+ "version": "1.1.43",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@adobe/acc-js-sdk",
9
- "version": "1.1.41",
9
+ "version": "1.1.43",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "axios": "^1.2.1",
@@ -1322,8 +1322,9 @@
1322
1322
  "license": "MIT"
1323
1323
  },
1324
1324
  "node_modules/axios": {
1325
- "version": "1.2.1",
1326
- "license": "MIT",
1325
+ "version": "1.6.2",
1326
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
1327
+ "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
1327
1328
  "dependencies": {
1328
1329
  "follow-redirects": "^1.15.0",
1329
1330
  "form-data": "^4.0.0",
@@ -5599,7 +5600,9 @@
5599
5600
  "version": "0.4.0"
5600
5601
  },
5601
5602
  "axios": {
5602
- "version": "1.2.1",
5603
+ "version": "1.6.2",
5604
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
5605
+ "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
5603
5606
  "requires": {
5604
5607
  "follow-redirects": "^1.15.0",
5605
5608
  "form-data": "^4.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.41",
3
+ "version": "1.1.43",
4
4
  "description": "ACC Javascript SDK",
5
5
  "main": "src/index.js",
6
6
  "homepage": "https://github.com/adobe/acc-js-sdk#readme",
package/src/util.js CHANGED
@@ -196,13 +196,17 @@ class ArrayMap {
196
196
 
197
197
  _push(key, value, withoutIndexing) {
198
198
  let isNumKey = false;
199
+ let updateIndex = -1;
200
+
199
201
  if (key) {
200
202
  // reserved keyworkds
201
203
  const isReserved = key === "_items" || key === "length" || key === "_push" || key === "forEach" || key === "map" || key === "_map" || key === "get" || key === "find" || key === "flatMap" || key === "filter";
202
204
 
203
205
  // already a child with the name => there's a problem with the schema
204
- if (!isReserved && this[key])
205
- throw new Error(`Failed to add element '${key}' to ArrayMap. There's already an item with the same name`);
206
+ // it seems the current behavior is to replace the existing item with the new one
207
+ if (!isReserved && this[key]) {
208
+ updateIndex = this._items.indexOf(this[key]);
209
+ }
206
210
 
207
211
  // Set key as a enumerable property, so that elements can be accessed by key,
208
212
  // but also iterated on with a for ... in loop
@@ -222,14 +226,19 @@ class ArrayMap {
222
226
  if (!withoutIndexing && !isNumKey) {
223
227
  // Set the index property so that items can be accessed by array index.
224
228
  // However, make it non-enumerable to make sure indexes do not show up in a for .. in loop
225
- Object.defineProperty(this, this._items.length, {
229
+ const numKey = updateIndex == -1 ? this._items.length : updateIndex;
230
+ Object.defineProperty(this, numKey, {
226
231
  value: value,
227
232
  writable: false,
228
233
  enumerable: false,
234
+ configurable: true, // Allow to redefine the property later in case there's a duplicate key
229
235
  });
230
236
  }
231
237
  // Add to array and set length
232
- this._items.push(value);
238
+ if (updateIndex == -1)
239
+ this._items.push(value);
240
+ else
241
+ this._items[updateIndex] = value;
233
242
  this.length = this._items.length;
234
243
  }
235
244
 
@@ -45,13 +45,37 @@ describe('Application', () => {
45
45
  });
46
46
 
47
47
  it("Duplicate root nodes", () => {
48
- expect(() => {
49
- var xml = DomUtil.parse(`<schema namespace='nms' name='recipient'>
50
- <element name='recipient' label='Recipients'/>
51
- <element name='recipient' label='Recipients2'/>
52
- </schema>`);
53
- newSchema(xml);
54
- }).toThrow("Failed to add element 'recipient' to ArrayMap. There's already an item with the same name");
48
+ var xml = DomUtil.parse(`<schema namespace='nms' name='recipient'>
49
+ <element name='recipient' label='Recipients'/>
50
+ <element name='recipient' label='Recipients2'/>
51
+ </schema>`);
52
+ var schema = newSchema(xml);
53
+ var root = schema.root;
54
+ expect(root).toBeTruthy();
55
+ expect(root.name).toBe("recipient");
56
+ expect(root.label).toBe("Recipients2");
57
+ });
58
+
59
+ it("Duplicate enumeration value", () => {
60
+ const xml = DomUtil.parse(`<schema namespace='nms' name='recipient'>
61
+ <enumeration basetype="string" name="AKA">
62
+ <value name="PC" value="1"/>
63
+ <value name="CL2" value="2"/>
64
+ <value name="CL3" value="3"/>
65
+ <value name="Null" value="4"/>
66
+ <value name="Null" value="NULL"/>
67
+ </enumeration>
68
+ <element name='recipient' label='Recipients'/>
69
+ </schema>`);
70
+ const schema = newSchema(xml);
71
+ const enumerations = schema.enumerations;
72
+ const enumeration = enumerations.AKA.values;
73
+ expect(enumeration.length).toBe(4);
74
+ expect(enumeration[0].name).toBe("PC");
75
+ expect(enumeration[1].name).toBe("CL2");
76
+ expect(enumeration[2].name).toBe("CL3");
77
+ expect(enumeration[3].name).toBe("Null");
78
+ expect(enumeration[3].value).toBe("NULL");
55
79
  });
56
80
 
57
81
  it("Should find root node", () => {
package/test/util.test.js CHANGED
@@ -371,10 +371,26 @@ describe('Util', function() {
371
371
  expect(cat).toBe("012");
372
372
  });
373
373
 
374
- it("Should not support adding the same key twice", () => {
374
+ it("Should support adding the same key twice. The last value overwrites", () => {
375
375
  const am = new ArrayMap();
376
376
  am._push("hello", "Hello");
377
- expect(() => { am._push("hello", "World"); }).toThrow("Failed to add element 'hello' to ArrayMap. There's already an item with the same name");
377
+ expect(am.length).toBe(1);
378
+ am._push("hello", "World");
379
+ expect(am.length).toBe(1);
380
+ expect(am.get(0)).toBe("World");
381
+ });
382
+
383
+ it("Should support adding the same key twice. The last value overwrites in the middle of the array", () => {
384
+ const am = new ArrayMap();
385
+ am._push("hello", "Hello");
386
+ am._push("cruel", "Cruel");
387
+ am._push("world", "World");
388
+ expect(am.length).toBe(3);
389
+ am._push("cruel", "*cruel*");
390
+ expect(am.length).toBe(3);
391
+ expect(am.get(0)).toBe("Hello");
392
+ expect(am.get(1)).toBe("*cruel*");
393
+ expect(am.get(2)).toBe("World");
378
394
  });
379
395
 
380
396
  it("Should support missing names", () => {