@platformatic/sql-graphql 0.0.23

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 (54) hide show
  1. package/.nyc_output/0681b2b7-587a-40f2-8242-257bc4c72456.json +1 -0
  2. package/.nyc_output/191054d4-6a1e-4a1b-87bb-f801ed78a223.json +1 -0
  3. package/.nyc_output/22ead0f6-e5a6-4647-a2ea-db2f42498081.json +1 -0
  4. package/.nyc_output/4e847e26-05cd-4063-9277-48aff86d6822.json +1 -0
  5. package/.nyc_output/5acfd416-02d1-497b-8dbd-69c0af167c62.json +1 -0
  6. package/.nyc_output/5ec04f76-9bcb-475d-b79e-f9304cc0026c.json +1 -0
  7. package/.nyc_output/5f3bb47a-0d2d-440a-a466-8ffa9ab3efaa.json +1 -0
  8. package/.nyc_output/71c2bd84-9a72-4e86-83c6-d587ce525fe4.json +1 -0
  9. package/.nyc_output/74cc1dab-7479-48e7-80b0-c97351d8856e.json +1 -0
  10. package/.nyc_output/9f5d12ba-bc3d-461c-a0e2-2a95d2020c19.json +1 -0
  11. package/.nyc_output/ac343b71-6f76-421c-a287-46d19bb48f89.json +1 -0
  12. package/.nyc_output/c1c0eecb-63a0-4258-81b6-50e7488bd67c.json +1 -0
  13. package/.nyc_output/c201a1cd-3105-4d18-bbbb-8d7fa674d439.json +1 -0
  14. package/.nyc_output/d6da0270-e9ed-41e2-bbf3-9262d1df683c.json +1 -0
  15. package/.nyc_output/processinfo/0681b2b7-587a-40f2-8242-257bc4c72456.json +1 -0
  16. package/.nyc_output/processinfo/191054d4-6a1e-4a1b-87bb-f801ed78a223.json +1 -0
  17. package/.nyc_output/processinfo/22ead0f6-e5a6-4647-a2ea-db2f42498081.json +1 -0
  18. package/.nyc_output/processinfo/4e847e26-05cd-4063-9277-48aff86d6822.json +1 -0
  19. package/.nyc_output/processinfo/5acfd416-02d1-497b-8dbd-69c0af167c62.json +1 -0
  20. package/.nyc_output/processinfo/5ec04f76-9bcb-475d-b79e-f9304cc0026c.json +1 -0
  21. package/.nyc_output/processinfo/5f3bb47a-0d2d-440a-a466-8ffa9ab3efaa.json +1 -0
  22. package/.nyc_output/processinfo/71c2bd84-9a72-4e86-83c6-d587ce525fe4.json +1 -0
  23. package/.nyc_output/processinfo/74cc1dab-7479-48e7-80b0-c97351d8856e.json +1 -0
  24. package/.nyc_output/processinfo/9f5d12ba-bc3d-461c-a0e2-2a95d2020c19.json +1 -0
  25. package/.nyc_output/processinfo/ac343b71-6f76-421c-a287-46d19bb48f89.json +1 -0
  26. package/.nyc_output/processinfo/c1c0eecb-63a0-4258-81b6-50e7488bd67c.json +1 -0
  27. package/.nyc_output/processinfo/c201a1cd-3105-4d18-bbbb-8d7fa674d439.json +1 -0
  28. package/.nyc_output/processinfo/d6da0270-e9ed-41e2-bbf3-9262d1df683c.json +1 -0
  29. package/.nyc_output/processinfo/index.json +1 -0
  30. package/.taprc +1 -0
  31. package/LICENSE +201 -0
  32. package/NOTICE +13 -0
  33. package/README.md +13 -0
  34. package/index.d.ts +40 -0
  35. package/index.js +117 -0
  36. package/lib/entity-to-type.js +248 -0
  37. package/lib/relationship.js +67 -0
  38. package/lib/utils.js +77 -0
  39. package/package.json +44 -0
  40. package/test/datatypes.test.js +708 -0
  41. package/test/federation.test.js +312 -0
  42. package/test/helper.js +70 -0
  43. package/test/hooks.test.js +660 -0
  44. package/test/ignore.test.js +117 -0
  45. package/test/insert.test.js +181 -0
  46. package/test/inserted_updated_at.test.js +517 -0
  47. package/test/nested.test.js +862 -0
  48. package/test/no-modification-of-entity.test.js +113 -0
  49. package/test/order_by.test.js +397 -0
  50. package/test/relations.test.js +60 -0
  51. package/test/simple.test.js +708 -0
  52. package/test/sqlite.test.js +276 -0
  53. package/test/types/index.test-d.ts +33 -0
  54. package/test/where.test.js +814 -0
@@ -0,0 +1,708 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const sqlGraphQL = require('..')
5
+ const sqlMapper = require('@platformatic/sql-mapper')
6
+ const fastify = require('fastify')
7
+ const { clear, connInfo, isPg, isMysql, isSQLite } = require('./helper')
8
+
9
+ test('[PG] simple db simple graphql schema', { skip: !isPg }, async ({ pass, teardown, same, equal }) => {
10
+ const app = fastify()
11
+ app.register(sqlMapper, {
12
+ ...connInfo,
13
+ async onDatabaseLoad (db, sql) {
14
+ pass('onDatabaseLoad called')
15
+
16
+ await clear(db, sql)
17
+
18
+ await db.query(sql`CREATE TABLE simple_types (
19
+ id SERIAL8 PRIMARY KEY,
20
+ published BOOL,
21
+ current DOUBLE PRECISION,
22
+ long_text TEXT,
23
+ born_at_date DATE,
24
+ born_at_time TIME,
25
+ born_at_timestamp TIMESTAMP,
26
+ uuid UUID UNIQUE,
27
+ a_real real,
28
+ a_smallint smallint,
29
+ a_decimal decimal
30
+ );`)
31
+ }
32
+ })
33
+ teardown(app.close.bind(app))
34
+
35
+ app.register(sqlGraphQL)
36
+
37
+ await app.ready()
38
+
39
+ const timestamp = new Date()
40
+
41
+ {
42
+ const res = await app.inject({
43
+ method: 'POST',
44
+ url: '/graphql',
45
+ body: {
46
+ query: `
47
+ mutation {
48
+ saveSimpleType(input: {
49
+ published: true,
50
+ current: 42,
51
+ longText: "abc",
52
+ bornAtDate: "2021-11-11",
53
+ bornAtTime: "12:42:00Z",
54
+ bornAtTimestamp: "${timestamp.toISOString()}",
55
+ uuid: "12345678-1234-1234-1234-123456789012",
56
+ aReal: 1.2,
57
+ aSmallint: 42,
58
+ aDecimal: 42 }) {
59
+ id
60
+ published
61
+ current
62
+ longText
63
+ bornAtDate
64
+ bornAtTime
65
+ bornAtTimestamp
66
+ uuid
67
+ aReal
68
+ aSmallint
69
+ aDecimal
70
+ }
71
+ }
72
+ `
73
+ }
74
+ })
75
+ equal(res.statusCode, 200, 'saveSimpleType status code')
76
+ same(res.json(), {
77
+ data: {
78
+ saveSimpleType: {
79
+ id: '1',
80
+ published: true,
81
+ current: 42,
82
+ longText: 'abc',
83
+ bornAtDate: '2021-11-11',
84
+ bornAtTime: '12:42:00',
85
+ bornAtTimestamp: timestamp.toISOString(),
86
+ uuid: '12345678-1234-1234-1234-123456789012',
87
+ aReal: 1.2,
88
+ aSmallint: 42,
89
+ aDecimal: 42
90
+ }
91
+ }
92
+ }, 'saveSimpleType response')
93
+ }
94
+
95
+ {
96
+ const res = await app.inject({
97
+ method: 'POST',
98
+ url: '/graphql',
99
+ body: {
100
+ query: `
101
+ query {
102
+ getSimpleTypeById(id: 1) {
103
+ id
104
+ published
105
+ current
106
+ longText
107
+ bornAtDate
108
+ bornAtTime
109
+ bornAtTimestamp
110
+ uuid
111
+ aReal
112
+ aSmallint
113
+ aDecimal
114
+ }
115
+ }
116
+ `
117
+ }
118
+ })
119
+ equal(res.statusCode, 200, 'getSimpleTypeById status code')
120
+ same(res.json(), {
121
+ data: {
122
+ getSimpleTypeById: {
123
+ id: 1,
124
+ published: true,
125
+ current: 42,
126
+ longText: 'abc',
127
+ bornAtDate: '2021-11-11',
128
+ bornAtTime: '12:42:00',
129
+ bornAtTimestamp: timestamp.toISOString(),
130
+ uuid: '12345678-1234-1234-1234-123456789012',
131
+ aReal: 1.2,
132
+ aSmallint: 42,
133
+ aDecimal: 42
134
+ }
135
+ }
136
+ }, 'getSimpleTypeById response')
137
+ }
138
+ })
139
+
140
+ test('[PG] - UUID', { skip: !isPg }, async ({ pass, teardown, same, equal }) => {
141
+ const app = fastify()
142
+ app.register(sqlMapper, {
143
+ ...connInfo,
144
+ async onDatabaseLoad (db, sql) {
145
+ pass('onDatabaseLoad called')
146
+
147
+ await clear(db, sql)
148
+
149
+ await db.query(sql`
150
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
151
+ CREATE TABLE pages (
152
+ id uuid PRIMARY KEY default uuid_generate_v1(),
153
+ title VARCHAR(42)
154
+ );`)
155
+ }
156
+ })
157
+ app.register(sqlGraphQL)
158
+ teardown(app.close.bind(app))
159
+
160
+ await app.ready()
161
+
162
+ let id
163
+ {
164
+ const res = await app.inject({
165
+ method: 'POST',
166
+ url: '/graphql',
167
+ body: {
168
+ query: `
169
+ mutation {
170
+ savePage(input: { title: "Hello" }) {
171
+ id
172
+ title
173
+ }
174
+ }
175
+ `
176
+ }
177
+ })
178
+ equal(res.statusCode, 200, 'savePage status code')
179
+ id = res.json().data.savePage.id
180
+ same(res.json(), {
181
+ data: {
182
+ savePage: {
183
+ id,
184
+ title: 'Hello'
185
+ }
186
+ }
187
+ }, 'savePage response')
188
+ }
189
+
190
+ {
191
+ const res = await app.inject({
192
+ method: 'POST',
193
+ url: '/graphql',
194
+ body: {
195
+ query: `
196
+ query {
197
+ getPageById(id: "${id}") {
198
+ id
199
+ title
200
+ }
201
+ }
202
+ `
203
+ }
204
+ })
205
+ equal(res.statusCode, 200, 'pages status code')
206
+ same(res.json(), {
207
+ data: {
208
+ getPageById: {
209
+ id,
210
+ title: 'Hello'
211
+ }
212
+ }
213
+ }, 'pages response')
214
+ }
215
+
216
+ {
217
+ const res = await app.inject({
218
+ method: 'POST',
219
+ url: '/graphql',
220
+ body: {
221
+ query: `
222
+ mutation {
223
+ savePage(input: { id: "${id}", title: "Hello World" }) {
224
+ id
225
+ title
226
+ }
227
+ }
228
+ `
229
+ }
230
+ })
231
+ equal(res.statusCode, 200, 'savePage status code')
232
+ same(res.json(), {
233
+ data: {
234
+ savePage: {
235
+ id,
236
+ title: 'Hello World'
237
+ }
238
+ }
239
+ }, 'savePage response')
240
+ }
241
+ })
242
+
243
+ test('[MySQL] simple db simple graphql schema', { skip: !isMysql }, async ({ pass, teardown, same, equal }) => {
244
+ const app = fastify()
245
+ app.register(sqlMapper, {
246
+ ...connInfo,
247
+ async onDatabaseLoad (db, sql) {
248
+ pass('onDatabaseLoad called')
249
+
250
+ await clear(db, sql)
251
+
252
+ const uuidType = db.isMariaDB ? sql.__dangerous__rawValue('uuid') : sql.__dangerous__rawValue('varchar(200)')
253
+
254
+ await db.query(sql`CREATE TABLE simple_types (
255
+ id SERIAL PRIMARY KEY,
256
+ published BOOL,
257
+ current DOUBLE PRECISION,
258
+ long_text TEXT,
259
+ born_at_date DATE,
260
+ born_at_time TIME,
261
+ born_at_timestamp TIMESTAMP,
262
+ uuid ${uuidType} UNIQUE,
263
+ a_real real,
264
+ a_smallint smallint,
265
+ a_decimal decimal
266
+ );`)
267
+ }
268
+ })
269
+ app.register(sqlGraphQL)
270
+ teardown(app.close.bind(app))
271
+
272
+ await app.ready()
273
+
274
+ const isMariaDB = app.platformatic.db.isMariaDB
275
+
276
+ const timestamp = new Date()
277
+ const bornAtTimestamp = new Date(Math[isMariaDB ? 'floor' : 'round'](timestamp.getTime() / 1000) * 1000).toISOString()
278
+
279
+ {
280
+ const res = await app.inject({
281
+ method: 'POST',
282
+ url: '/graphql',
283
+ body: {
284
+ query: `
285
+ mutation {
286
+ saveSimpleType(input: {
287
+ published: true,
288
+ current: 42,
289
+ longText: "abc",
290
+ bornAtDate: "2021-11-11",
291
+ bornAtTime: "12:42:00",
292
+ bornAtTimestamp: "${timestamp.toISOString()}",
293
+ uuid: "12345678-1234-1234-1234-123456789012",
294
+ aReal: 1.2,
295
+ aSmallint: 42,
296
+ aDecimal: 42 }) {
297
+ id
298
+ published
299
+ current
300
+ longText
301
+ bornAtDate
302
+ bornAtTime
303
+ bornAtTimestamp
304
+ uuid
305
+ aReal
306
+ aSmallint
307
+ aDecimal
308
+ }
309
+ }
310
+ `
311
+ }
312
+ })
313
+ equal(res.statusCode, 200, 'saveSimpleType status code')
314
+ same(res.json(), {
315
+ data: {
316
+ saveSimpleType: {
317
+ id: '1',
318
+ published: true,
319
+ current: 42,
320
+ longText: 'abc',
321
+ bornAtDate: '2021-11-11',
322
+ bornAtTime: '12:42:00',
323
+ bornAtTimestamp,
324
+ uuid: '12345678-1234-1234-1234-123456789012',
325
+ aReal: 1.2,
326
+ aSmallint: 42,
327
+ aDecimal: 42
328
+ }
329
+ }
330
+ }, 'saveSimpleType response')
331
+ }
332
+
333
+ {
334
+ const res = await app.inject({
335
+ method: 'POST',
336
+ url: '/graphql',
337
+ body: {
338
+ query: `
339
+ query {
340
+ getSimpleTypeById(id: 1) {
341
+ id
342
+ published
343
+ current
344
+ longText
345
+ bornAtDate
346
+ bornAtTime
347
+ bornAtTimestamp
348
+ uuid
349
+ aReal
350
+ aSmallint
351
+ aDecimal
352
+ }
353
+ }
354
+ `
355
+ }
356
+ })
357
+ equal(res.statusCode, 200, 'getSimpleTypeById status code')
358
+ same(res.json(), {
359
+ data: {
360
+ getSimpleTypeById: {
361
+ id: 1,
362
+ published: true,
363
+ current: 42,
364
+ longText: 'abc',
365
+ bornAtDate: '2021-11-11',
366
+ bornAtTime: '12:42:00',
367
+ bornAtTimestamp,
368
+ uuid: '12345678-1234-1234-1234-123456789012',
369
+ aReal: 1.2,
370
+ aSmallint: 42,
371
+ aDecimal: 42
372
+ }
373
+ }
374
+ }, 'getSimpleTypeById response')
375
+ }
376
+ })
377
+
378
+ test('[MySQL] - UUID', { skip: !isMysql }, async ({ pass, teardown, same, equal, skip }) => {
379
+ const app = fastify()
380
+ app.register(sqlMapper, {
381
+ ...connInfo,
382
+ async onDatabaseLoad (db, sql) {
383
+ pass('onDatabaseLoad called')
384
+ if (!db.isMariaDB) {
385
+ return
386
+ }
387
+
388
+ await clear(db, sql)
389
+
390
+ await db.query(sql`
391
+ CREATE TABLE pages (
392
+ id uuid PRIMARY KEY default UUID(),
393
+ title VARCHAR(42)
394
+ );`)
395
+ }
396
+ })
397
+ app.register(sqlGraphQL)
398
+ teardown(app.close.bind(app))
399
+
400
+ await app.ready()
401
+
402
+ if (!app.platformatic.db.isMariaDB) {
403
+ skip('MySQL does not support UUID, only MariaDB does')
404
+ return
405
+ }
406
+
407
+ let id
408
+ {
409
+ const res = await app.inject({
410
+ method: 'POST',
411
+ url: '/graphql',
412
+ body: {
413
+ query: `
414
+ mutation {
415
+ savePage(input: { title: "Hello" }) {
416
+ id
417
+ title
418
+ }
419
+ }
420
+ `
421
+ }
422
+ })
423
+ equal(res.statusCode, 200, 'savePage status code')
424
+ id = res.json().data.savePage.id
425
+ same(res.json(), {
426
+ data: {
427
+ savePage: {
428
+ id,
429
+ title: 'Hello'
430
+ }
431
+ }
432
+ }, 'savePage response')
433
+ }
434
+
435
+ {
436
+ const res = await app.inject({
437
+ method: 'POST',
438
+ url: '/graphql',
439
+ body: {
440
+ query: `
441
+ query {
442
+ getPageById(id: "${id}") {
443
+ id
444
+ title
445
+ }
446
+ }
447
+ `
448
+ }
449
+ })
450
+ equal(res.statusCode, 200, 'pages status code')
451
+ same(res.json(), {
452
+ data: {
453
+ getPageById: {
454
+ id,
455
+ title: 'Hello'
456
+ }
457
+ }
458
+ }, 'pages response')
459
+ }
460
+
461
+ {
462
+ const res = await app.inject({
463
+ method: 'POST',
464
+ url: '/graphql',
465
+ body: {
466
+ query: `
467
+ mutation {
468
+ savePage(input: { id: "${id}", title: "Hello World" }) {
469
+ id
470
+ title
471
+ }
472
+ }
473
+ `
474
+ }
475
+ })
476
+ equal(res.statusCode, 200, 'savePage status code')
477
+ same(res.json(), {
478
+ data: {
479
+ savePage: {
480
+ id,
481
+ title: 'Hello World'
482
+ }
483
+ }
484
+ }, 'savePage response')
485
+ }
486
+ })
487
+
488
+ test('[SQLite] simple db simple graphql schema', { skip: !isSQLite }, async ({ pass, teardown, same, equal }) => {
489
+ const app = fastify()
490
+ app.register(sqlMapper, {
491
+ ...connInfo,
492
+ async onDatabaseLoad (db, sql) {
493
+ pass('onDatabaseLoad called')
494
+
495
+ await clear(db, sql)
496
+
497
+ await db.query(sql`CREATE TABLE simple_types (
498
+ id INTEGER PRIMARY KEY,
499
+ published BOOL,
500
+ current DOUBLE PRECISION,
501
+ long_text TEXT,
502
+ born_at_timestamp TIMESTAMP,
503
+ uuid UUID UNIQUE,
504
+ a_real real,
505
+ a_smallint smallint,
506
+ a_decimal decimal
507
+ );`)
508
+ }
509
+ })
510
+ teardown(app.close.bind(app))
511
+
512
+ app.register(sqlGraphQL)
513
+
514
+ await app.ready()
515
+
516
+ const timestamp = new Date()
517
+
518
+ {
519
+ const res = await app.inject({
520
+ method: 'POST',
521
+ url: '/graphql',
522
+ body: {
523
+ query: `
524
+ mutation {
525
+ saveSimpleType(input: {
526
+ published: true,
527
+ current: 42,
528
+ longText: "abc",
529
+ bornAtTimestamp: "${timestamp.toISOString()}",
530
+ uuid: "12345678-1234-1234-1234-123456789012",
531
+ aReal: 1.2,
532
+ aSmallint: 42,
533
+ aDecimal: 42 }) {
534
+ id
535
+ published
536
+ current
537
+ longText
538
+ bornAtTimestamp
539
+ uuid
540
+ aReal
541
+ aSmallint
542
+ aDecimal
543
+ }
544
+ }
545
+ `
546
+ }
547
+ })
548
+ equal(res.statusCode, 200, 'saveSimpleType status code')
549
+ same(res.json(), {
550
+ data: {
551
+ saveSimpleType: {
552
+ id: '1',
553
+ published: true,
554
+ current: 42,
555
+ longText: 'abc',
556
+ bornAtTimestamp: timestamp.toISOString(),
557
+ uuid: '12345678-1234-1234-1234-123456789012',
558
+ aReal: 1.2,
559
+ aSmallint: 42,
560
+ aDecimal: 42
561
+ }
562
+ }
563
+ }, 'saveSimpleType response')
564
+ }
565
+
566
+ {
567
+ const res = await app.inject({
568
+ method: 'POST',
569
+ url: '/graphql',
570
+ body: {
571
+ query: `
572
+ query {
573
+ getSimpleTypeById(id: 1) {
574
+ id
575
+ published
576
+ current
577
+ longText
578
+ bornAtTimestamp
579
+ uuid
580
+ aReal
581
+ aSmallint
582
+ aDecimal
583
+ }
584
+ }
585
+ `
586
+ }
587
+ })
588
+ equal(res.statusCode, 200, 'getSimpleTypeById status code')
589
+ same(res.json(), {
590
+ data: {
591
+ getSimpleTypeById: {
592
+ id: 1,
593
+ published: true,
594
+ current: 42,
595
+ longText: 'abc',
596
+ bornAtTimestamp: timestamp.toISOString(),
597
+ uuid: '12345678-1234-1234-1234-123456789012',
598
+ aReal: 1.2,
599
+ aSmallint: 42,
600
+ aDecimal: 42
601
+ }
602
+ }
603
+ }, 'getSimpleTypeById response')
604
+ }
605
+ })
606
+
607
+ test('[SQLite] - UUID', { skip: !isSQLite }, async ({ pass, teardown, same, equal }) => {
608
+ const app = fastify()
609
+ app.register(sqlMapper, {
610
+ ...connInfo,
611
+ async onDatabaseLoad (db, sql) {
612
+ pass('onDatabaseLoad called')
613
+
614
+ await clear(db, sql)
615
+
616
+ await db.query(sql`
617
+ CREATE TABLE pages (
618
+ id uuid PRIMARY KEY,
619
+ title VARCHAR(42)
620
+ );`)
621
+ }
622
+ })
623
+ app.register(sqlGraphQL)
624
+
625
+ teardown(app.close.bind(app))
626
+
627
+ await app.ready()
628
+
629
+ let id
630
+ {
631
+ const res = await app.inject({
632
+ method: 'POST',
633
+ url: '/graphql',
634
+ body: {
635
+ query: `
636
+ mutation {
637
+ savePage(input: { title: "Hello" }) {
638
+ id
639
+ title
640
+ }
641
+ }
642
+ `
643
+ }
644
+ })
645
+ equal(res.statusCode, 200, 'savePage status code')
646
+ id = res.json().data.savePage.id
647
+ same(res.json(), {
648
+ data: {
649
+ savePage: {
650
+ id,
651
+ title: 'Hello'
652
+ }
653
+ }
654
+ }, 'savePage response')
655
+ }
656
+
657
+ {
658
+ const res = await app.inject({
659
+ method: 'POST',
660
+ url: '/graphql',
661
+ body: {
662
+ query: `
663
+ query {
664
+ getPageById(id: "${id}") {
665
+ id
666
+ title
667
+ }
668
+ }
669
+ `
670
+ }
671
+ })
672
+ equal(res.statusCode, 200, 'pages status code')
673
+ same(res.json(), {
674
+ data: {
675
+ getPageById: {
676
+ id,
677
+ title: 'Hello'
678
+ }
679
+ }
680
+ }, 'pages response')
681
+ }
682
+
683
+ {
684
+ const res = await app.inject({
685
+ method: 'POST',
686
+ url: '/graphql',
687
+ body: {
688
+ query: `
689
+ mutation {
690
+ savePage(input: { id: "${id}", title: "Hello World" }) {
691
+ id
692
+ title
693
+ }
694
+ }
695
+ `
696
+ }
697
+ })
698
+ equal(res.statusCode, 200, 'savePage status code')
699
+ same(res.json(), {
700
+ data: {
701
+ savePage: {
702
+ id,
703
+ title: 'Hello World'
704
+ }
705
+ }
706
+ }, 'savePage response')
707
+ }
708
+ })