embulk-input-jira 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +10 -5
- data/.travis.yml +4 -34
- data/CHANGELOG.md +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +5 -4
- data/build.gradle +116 -0
- data/config/checkstyle/checkstyle.xml +128 -0
- data/config/checkstyle/default.xml +108 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +5 -0
- data/gradlew +172 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/guess/jira.rb +24 -0
- data/lib/embulk/input/jira.rb +3 -169
- data/src/main/java/org/embulk/input/jira/AuthenticateMethod.java +27 -0
- data/src/main/java/org/embulk/input/jira/Constant.java +17 -0
- data/src/main/java/org/embulk/input/jira/Issue.java +150 -0
- data/src/main/java/org/embulk/input/jira/JiraInputPlugin.java +226 -0
- data/src/main/java/org/embulk/input/jira/client/JiraClient.java +254 -0
- data/src/main/java/org/embulk/input/jira/util/JiraException.java +18 -0
- data/src/main/java/org/embulk/input/jira/util/JiraUtil.java +264 -0
- data/src/test/java/org/embulk/input/jira/IssueTest.java +278 -0
- data/src/test/java/org/embulk/input/jira/JiraInputPluginTest.java +204 -0
- data/src/test/java/org/embulk/input/jira/JiraPluginTestRuntime.java +133 -0
- data/src/test/java/org/embulk/input/jira/TestHelpers.java +41 -0
- data/src/test/java/org/embulk/input/jira/client/JiraClientTest.java +222 -0
- data/src/test/java/org/embulk/input/jira/util/JiraUtilTest.java +318 -0
- data/src/test/resources/config.yml +13 -0
- data/src/test/resources/issue_flatten.json +129 -0
- data/src/test/resources/issue_flatten_expected.json +73 -0
- data/src/test/resources/issue_get.json +36 -0
- data/src/test/resources/issue_get_expected.json +62 -0
- data/src/test/resources/jira_client.json +81 -0
- data/src/test/resources/jira_input_plugin.json +114 -0
- data/src/test/resources/jira_util.json +26 -0
- metadata +55 -175
- data/Gemfile +0 -3
- data/LICENSE +0 -13
- data/Rakefile +0 -15
- data/embulk-input-jira.gemspec +0 -27
- data/gemfiles/embulk-0.8.0-latest +0 -4
- data/gemfiles/embulk-0.8.7 +0 -4
- data/gemfiles/embulk-0.8.8 +0 -4
- data/gemfiles/embulk-latest +0 -4
- data/gemfiles/template.erb +0 -4
- data/lib/embulk/input/jira_api.rb +0 -9
- data/lib/embulk/input/jira_api/client.rb +0 -144
- data/lib/embulk/input/jira_api/issue.rb +0 -133
- data/lib/embulk/input/jira_input_plugin_utils.rb +0 -58
- data/spec/embulk/input/jira-input-plugin-utils_spec.rb +0 -89
- data/spec/embulk/input/jira_api/client_spec.rb +0 -224
- data/spec/embulk/input/jira_api/issue_spec.rb +0 -394
- data/spec/embulk/input/jira_spec.rb +0 -322
- data/spec/embulk_spec.rb +0 -32
- data/spec/spec_helper.rb +0 -26
- data/spec/support/stdout_and_err_capture.rb +0 -45
@@ -0,0 +1,318 @@
|
|
1
|
+
package org.embulk.input.jira.util;
|
2
|
+
|
3
|
+
import com.google.gson.JsonObject;
|
4
|
+
|
5
|
+
import org.embulk.config.ConfigException;
|
6
|
+
import org.embulk.config.ConfigSource;
|
7
|
+
import org.embulk.input.jira.Issue;
|
8
|
+
import org.embulk.input.jira.JiraInputPlugin.PluginTask;
|
9
|
+
import org.embulk.input.jira.TestHelpers;
|
10
|
+
import org.embulk.spi.Column;
|
11
|
+
import org.embulk.spi.PageBuilder;
|
12
|
+
import org.embulk.spi.Schema;
|
13
|
+
import org.embulk.spi.json.JsonParser;
|
14
|
+
import org.embulk.spi.time.Timestamp;
|
15
|
+
import org.embulk.spi.time.TimestampParser;
|
16
|
+
import org.junit.BeforeClass;
|
17
|
+
import org.junit.Test;
|
18
|
+
import org.mockito.Mockito;
|
19
|
+
import org.msgpack.value.Value;
|
20
|
+
|
21
|
+
import java.io.IOException;
|
22
|
+
|
23
|
+
import static org.junit.Assert.assertEquals;
|
24
|
+
import static org.junit.Assert.assertNotNull;
|
25
|
+
import static org.junit.Assert.assertNull;
|
26
|
+
import static org.junit.Assert.assertTrue;
|
27
|
+
import static org.mockito.Mockito.times;
|
28
|
+
import static org.mockito.Mockito.verify;
|
29
|
+
|
30
|
+
public class JiraUtilTest
|
31
|
+
{
|
32
|
+
private static JsonObject data;
|
33
|
+
private static PluginTask pluginTask;
|
34
|
+
private static Schema schema;
|
35
|
+
private static Column booleanColumn;
|
36
|
+
private static Column longColumn;
|
37
|
+
private static Column doubleColumn;
|
38
|
+
private static Column stringColumn;
|
39
|
+
private static Column dateColumn;
|
40
|
+
private static Column jsonColumn;
|
41
|
+
|
42
|
+
@BeforeClass
|
43
|
+
public static void setUp() throws IOException
|
44
|
+
{
|
45
|
+
data = TestHelpers.getJsonFromFile("jira_util.json");
|
46
|
+
pluginTask = TestHelpers.config().loadConfig(PluginTask.class);
|
47
|
+
schema = pluginTask.getColumns().toSchema();
|
48
|
+
booleanColumn = schema.getColumn(0);
|
49
|
+
longColumn = schema.getColumn(1);
|
50
|
+
doubleColumn = schema.getColumn(2);
|
51
|
+
stringColumn = schema.getColumn(3);
|
52
|
+
dateColumn = schema.getColumn(4);
|
53
|
+
jsonColumn = schema.getColumn(5);
|
54
|
+
}
|
55
|
+
@Test
|
56
|
+
public void test_calculateTotalPage()
|
57
|
+
{
|
58
|
+
int resultPerPage = 50;
|
59
|
+
int expected = 0;
|
60
|
+
int totalCount = 0;
|
61
|
+
int actual = JiraUtil.calculateTotalPage(totalCount, resultPerPage);
|
62
|
+
assertEquals(expected, actual);
|
63
|
+
|
64
|
+
expected = 1;
|
65
|
+
totalCount = resultPerPage - 1;
|
66
|
+
actual = JiraUtil.calculateTotalPage(totalCount, resultPerPage);
|
67
|
+
assertEquals(expected, actual);
|
68
|
+
|
69
|
+
expected = 1;
|
70
|
+
totalCount = resultPerPage;
|
71
|
+
actual = JiraUtil.calculateTotalPage(totalCount, resultPerPage);
|
72
|
+
assertEquals(expected, actual);
|
73
|
+
|
74
|
+
expected = 2;
|
75
|
+
totalCount = resultPerPage + 1;
|
76
|
+
actual = JiraUtil.calculateTotalPage(totalCount, resultPerPage);
|
77
|
+
assertEquals(expected, actual);
|
78
|
+
}
|
79
|
+
|
80
|
+
@Test
|
81
|
+
public void test_buildPermissionUrl()
|
82
|
+
{
|
83
|
+
String url = "https://example.com";
|
84
|
+
String expected = "https://example.com/rest/api/latest/myself";
|
85
|
+
String actual = JiraUtil.buildPermissionUrl(url);
|
86
|
+
assertEquals(expected, actual);
|
87
|
+
|
88
|
+
url = "https://example.com/";
|
89
|
+
expected = "https://example.com/rest/api/latest/myself";
|
90
|
+
actual = JiraUtil.buildPermissionUrl(url);
|
91
|
+
assertEquals(expected, actual);
|
92
|
+
|
93
|
+
url = "https://example.com//";
|
94
|
+
expected = "https://example.com//rest/api/latest/myself";
|
95
|
+
actual = JiraUtil.buildPermissionUrl(url);
|
96
|
+
assertEquals(expected, actual);
|
97
|
+
|
98
|
+
url = "https://example.com/sub/subsub";
|
99
|
+
expected = "https://example.com/sub/subsub/rest/api/latest/myself";
|
100
|
+
actual = JiraUtil.buildPermissionUrl(url);
|
101
|
+
assertEquals(expected, actual);
|
102
|
+
}
|
103
|
+
|
104
|
+
@Test
|
105
|
+
public void test_buildSearchUrl() throws IOException
|
106
|
+
{
|
107
|
+
PluginTask task = TestHelpers.config().loadConfig(PluginTask.class);
|
108
|
+
String expected = "https://example.com/rest/api/latest/search";
|
109
|
+
String actual = JiraUtil.buildSearchUrl(task.getUri());
|
110
|
+
assertEquals(expected, actual);
|
111
|
+
}
|
112
|
+
|
113
|
+
@Test
|
114
|
+
public void test_validateTaskConfig() throws IOException
|
115
|
+
{
|
116
|
+
// Happy case
|
117
|
+
ConfigSource configSource = TestHelpers.config();
|
118
|
+
PluginTask task = configSource.loadConfig(PluginTask.class);
|
119
|
+
Exception exception = null;
|
120
|
+
try {
|
121
|
+
JiraUtil.validateTaskConfig(task);
|
122
|
+
}
|
123
|
+
catch (Exception e) {
|
124
|
+
exception = e;
|
125
|
+
}
|
126
|
+
assertNull(exception);
|
127
|
+
|
128
|
+
// empty username
|
129
|
+
configSource = TestHelpers.config();
|
130
|
+
configSource.set("username", "");
|
131
|
+
task = configSource.loadConfig(PluginTask.class);
|
132
|
+
exception = null;
|
133
|
+
try {
|
134
|
+
JiraUtil.validateTaskConfig(task);
|
135
|
+
}
|
136
|
+
catch (Exception e) {
|
137
|
+
exception = e;
|
138
|
+
}
|
139
|
+
assertNotNull(exception);
|
140
|
+
assertTrue(exception instanceof ConfigException);
|
141
|
+
assertEquals("Username or email could not be empty", exception.getMessage());
|
142
|
+
|
143
|
+
// empty password
|
144
|
+
configSource = TestHelpers.config();
|
145
|
+
configSource.set("password", "");
|
146
|
+
task = configSource.loadConfig(PluginTask.class);
|
147
|
+
exception = null;
|
148
|
+
try {
|
149
|
+
JiraUtil.validateTaskConfig(task);
|
150
|
+
}
|
151
|
+
catch (Exception e) {
|
152
|
+
exception = e;
|
153
|
+
}
|
154
|
+
assertNotNull(exception);
|
155
|
+
assertTrue(exception instanceof ConfigException);
|
156
|
+
assertEquals("Password could not be empty", exception.getMessage());
|
157
|
+
|
158
|
+
// empty uri
|
159
|
+
configSource = TestHelpers.config();
|
160
|
+
configSource.set("uri", "");
|
161
|
+
task = configSource.loadConfig(PluginTask.class);
|
162
|
+
exception = null;
|
163
|
+
try {
|
164
|
+
JiraUtil.validateTaskConfig(task);
|
165
|
+
}
|
166
|
+
catch (Exception e) {
|
167
|
+
exception = e;
|
168
|
+
}
|
169
|
+
assertNotNull(exception);
|
170
|
+
assertTrue(exception instanceof ConfigException);
|
171
|
+
assertEquals("JIRA API endpoint could not be empty", exception.getMessage());
|
172
|
+
|
173
|
+
// invalid uri
|
174
|
+
configSource = TestHelpers.config();
|
175
|
+
configSource.set("uri", "https://not-existed-domain");
|
176
|
+
task = configSource.loadConfig(PluginTask.class);
|
177
|
+
exception = null;
|
178
|
+
try {
|
179
|
+
JiraUtil.validateTaskConfig(task);
|
180
|
+
}
|
181
|
+
catch (Exception e) {
|
182
|
+
exception = e;
|
183
|
+
}
|
184
|
+
assertNotNull(exception);
|
185
|
+
assertTrue(exception instanceof ConfigException);
|
186
|
+
assertEquals("JIRA API endpoint is incorrect or not available", exception.getMessage());
|
187
|
+
|
188
|
+
// empty jql
|
189
|
+
configSource = TestHelpers.config();
|
190
|
+
configSource.set("jql", "");
|
191
|
+
task = configSource.loadConfig(PluginTask.class);
|
192
|
+
exception = null;
|
193
|
+
try {
|
194
|
+
JiraUtil.validateTaskConfig(task);
|
195
|
+
}
|
196
|
+
catch (Exception e) {
|
197
|
+
exception = e;
|
198
|
+
}
|
199
|
+
assertNotNull(exception);
|
200
|
+
assertTrue(exception instanceof ConfigException);
|
201
|
+
assertEquals("JQL could not be empty", exception.getMessage());
|
202
|
+
|
203
|
+
// initial_retry_interval_millis = 0
|
204
|
+
configSource = TestHelpers.config();
|
205
|
+
configSource.set("initial_retry_interval_millis", 0);
|
206
|
+
task = configSource.loadConfig(PluginTask.class);
|
207
|
+
exception = null;
|
208
|
+
try {
|
209
|
+
JiraUtil.validateTaskConfig(task);
|
210
|
+
}
|
211
|
+
catch (Exception e) {
|
212
|
+
exception = e;
|
213
|
+
}
|
214
|
+
assertNotNull(exception);
|
215
|
+
assertTrue(exception instanceof ConfigException);
|
216
|
+
assertEquals("Initial retry delay should be equal or greater than 1", exception.getMessage());
|
217
|
+
|
218
|
+
// retry_limit = -1
|
219
|
+
configSource = TestHelpers.config();
|
220
|
+
configSource.set("retry_limit", -1);
|
221
|
+
task = configSource.loadConfig(PluginTask.class);
|
222
|
+
exception = null;
|
223
|
+
try {
|
224
|
+
JiraUtil.validateTaskConfig(task);
|
225
|
+
}
|
226
|
+
catch (Exception e) {
|
227
|
+
exception = e;
|
228
|
+
}
|
229
|
+
assertNotNull(exception);
|
230
|
+
assertTrue(exception instanceof ConfigException);
|
231
|
+
assertEquals("Retry limit should between 0 and 10", exception.getMessage());
|
232
|
+
|
233
|
+
// retry_limit = 100
|
234
|
+
configSource = TestHelpers.config();
|
235
|
+
configSource.set("retry_limit", 100);
|
236
|
+
task = configSource.loadConfig(PluginTask.class);
|
237
|
+
exception = null;
|
238
|
+
try {
|
239
|
+
JiraUtil.validateTaskConfig(task);
|
240
|
+
}
|
241
|
+
catch (Exception e) {
|
242
|
+
exception = e;
|
243
|
+
}
|
244
|
+
assertNotNull(exception);
|
245
|
+
assertTrue(exception instanceof ConfigException);
|
246
|
+
assertEquals("Retry limit should between 0 and 10", exception.getMessage());
|
247
|
+
}
|
248
|
+
|
249
|
+
@Test
|
250
|
+
public void test_addRecord_allRight()
|
251
|
+
{
|
252
|
+
String testName = "allRight";
|
253
|
+
Issue issue = new Issue(data.get(testName).getAsJsonObject());
|
254
|
+
PageBuilder mock = Mockito.mock(PageBuilder.class);
|
255
|
+
|
256
|
+
Boolean boolValue = Boolean.TRUE;
|
257
|
+
Long longValue = Long.valueOf(1);
|
258
|
+
Double doubleValue = Double.valueOf(1);
|
259
|
+
String stringValue = "string";
|
260
|
+
Timestamp dateValue = TimestampParser.of("%Y-%m-%dT%H:%M:%S.%L%z", "UTC").parse("2019-01-01T00:00:00.000Z");
|
261
|
+
Value jsonValue = new JsonParser().parse("{}");
|
262
|
+
|
263
|
+
JiraUtil.addRecord(issue, schema, pluginTask, mock);
|
264
|
+
|
265
|
+
verify(mock, times(1)).setBoolean(booleanColumn, boolValue);
|
266
|
+
verify(mock, times(1)).setLong(longColumn, longValue);
|
267
|
+
verify(mock, times(1)).setDouble(doubleColumn, doubleValue);
|
268
|
+
verify(mock, times(1)).setString(stringColumn, stringValue);
|
269
|
+
verify(mock, times(1)).setTimestamp(dateColumn, dateValue);
|
270
|
+
verify(mock, times(1)).setJson(jsonColumn, jsonValue);
|
271
|
+
}
|
272
|
+
|
273
|
+
@Test
|
274
|
+
public void test_addRecord_allWrong()
|
275
|
+
{
|
276
|
+
String testName = "allWrong";
|
277
|
+
Issue issue = new Issue(data.get(testName).getAsJsonObject());
|
278
|
+
PageBuilder mock = Mockito.mock(PageBuilder.class);
|
279
|
+
|
280
|
+
String stringValue = "{}";
|
281
|
+
Value jsonValue = new JsonParser().parse("{}");
|
282
|
+
|
283
|
+
JiraUtil.addRecord(issue, schema, pluginTask, mock);
|
284
|
+
|
285
|
+
verify(mock, times(1)).setNull(booleanColumn);
|
286
|
+
verify(mock, times(1)).setNull(longColumn);
|
287
|
+
verify(mock, times(1)).setNull(doubleColumn);
|
288
|
+
verify(mock, times(1)).setString(stringColumn, stringValue);
|
289
|
+
verify(mock, times(1)).setNull(dateColumn);
|
290
|
+
verify(mock, times(1)).setJson(jsonColumn, jsonValue);
|
291
|
+
}
|
292
|
+
|
293
|
+
@Test
|
294
|
+
public void test_addRecord_allMissing()
|
295
|
+
{
|
296
|
+
String testName = "allMissing";
|
297
|
+
Issue issue = new Issue(data.get(testName).getAsJsonObject());
|
298
|
+
PageBuilder mock = Mockito.mock(PageBuilder.class);
|
299
|
+
|
300
|
+
JiraUtil.addRecord(issue, schema, pluginTask, mock);
|
301
|
+
|
302
|
+
verify(mock, times(6)).setNull(Mockito.any(Column.class));
|
303
|
+
}
|
304
|
+
|
305
|
+
@Test
|
306
|
+
public void test_addRecord_arrayAsString()
|
307
|
+
{
|
308
|
+
String testName = "arrayAsString";
|
309
|
+
Issue issue = new Issue(data.get(testName).getAsJsonObject());
|
310
|
+
PageBuilder mock = Mockito.mock(PageBuilder.class);
|
311
|
+
|
312
|
+
String stringValue = "1,{},[]";
|
313
|
+
|
314
|
+
JiraUtil.addRecord(issue, schema, pluginTask, mock);
|
315
|
+
|
316
|
+
verify(mock, times(1)).setString(stringColumn, stringValue);
|
317
|
+
}
|
318
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
type: jira
|
2
|
+
username: example@example.com
|
3
|
+
password: XXXXXXXXXXXXXXXXX
|
4
|
+
uri: "https://example.com/"
|
5
|
+
jql: project = example
|
6
|
+
retry_limit: 3
|
7
|
+
columns:
|
8
|
+
- {name: boolean, type: boolean}
|
9
|
+
- {name: long, type: long}
|
10
|
+
- {name: double, type: double}
|
11
|
+
- {name: string, type: string}
|
12
|
+
- {name: date, type: timestamp, format: '%Y-%m-%dT%H:%M:%S.%L%z'}
|
13
|
+
- {name: json, type: json}
|
@@ -0,0 +1,129 @@
|
|
1
|
+
{
|
2
|
+
"simple": {
|
3
|
+
"primitive": 1,
|
4
|
+
"string": "string",
|
5
|
+
"array": [],
|
6
|
+
"object": {},
|
7
|
+
"null": null
|
8
|
+
},
|
9
|
+
"twoLevels": {
|
10
|
+
"1stLevel": {
|
11
|
+
"2ndLevelPrimitive": 1,
|
12
|
+
"2ndLevelString": "string",
|
13
|
+
"2ndLevelArray": [],
|
14
|
+
"2ndLevelObject": {},
|
15
|
+
"2ndLevelNull": null
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"threeLevels": {
|
19
|
+
"1stLevel": {
|
20
|
+
"2ndLevel": {
|
21
|
+
"3rdLevelPrimitive": 1,
|
22
|
+
"3rdLevelString": "string",
|
23
|
+
"3rdLevelArray": [],
|
24
|
+
"3rdLevelObject": {},
|
25
|
+
"3rdLevelNull": null
|
26
|
+
}
|
27
|
+
}
|
28
|
+
},
|
29
|
+
"threeLevelsWithoutKeys": {
|
30
|
+
"1stLevel": {
|
31
|
+
"2ndLevel": {
|
32
|
+
"3rdLevelPrimitive": 1,
|
33
|
+
"3rdLevelString": "string",
|
34
|
+
"3rdLevelArray": [],
|
35
|
+
"3rdLevelObject": {
|
36
|
+
"notKey": "notKey"
|
37
|
+
},
|
38
|
+
"3rdLevelNull": null
|
39
|
+
}
|
40
|
+
}
|
41
|
+
},
|
42
|
+
"threeLevelsWithKeys": {
|
43
|
+
"1stLevel": {
|
44
|
+
"2ndLevel": {
|
45
|
+
"3rdLevelPrimitive": 1,
|
46
|
+
"3rdLevelString": "string",
|
47
|
+
"3rdLevelArray": [],
|
48
|
+
"3rdLevelObject": {
|
49
|
+
"id": "id",
|
50
|
+
"name": "name",
|
51
|
+
"key": "key",
|
52
|
+
"notKey": "notKey"
|
53
|
+
},
|
54
|
+
"3rdLevelNull": null
|
55
|
+
}
|
56
|
+
}
|
57
|
+
},
|
58
|
+
"threeLevelsWithNullKeys": {
|
59
|
+
"1stLevel": {
|
60
|
+
"2ndLevel": {
|
61
|
+
"3rdLevelPrimitive": 1,
|
62
|
+
"3rdLevelString": "string",
|
63
|
+
"3rdLevelArray": [],
|
64
|
+
"3rdLevelObject": {
|
65
|
+
"id": "id",
|
66
|
+
"name": "name",
|
67
|
+
"key": null
|
68
|
+
},
|
69
|
+
"3rdLevelNull": null
|
70
|
+
}
|
71
|
+
}
|
72
|
+
},
|
73
|
+
"arrayWithAllJsonObjectWithSameKeysAndEmptyObject": {
|
74
|
+
"array": [
|
75
|
+
{
|
76
|
+
"primitive": 1,
|
77
|
+
"string": "string",
|
78
|
+
"array": [],
|
79
|
+
"object": {},
|
80
|
+
"null": null
|
81
|
+
},
|
82
|
+
{
|
83
|
+
"primitive": 2,
|
84
|
+
"string": "string2",
|
85
|
+
"array": [],
|
86
|
+
"object": {},
|
87
|
+
"null": null
|
88
|
+
}
|
89
|
+
]
|
90
|
+
},
|
91
|
+
"arrayWithAllJsonObjectWithSameKeysAndNotEmptyObject": {
|
92
|
+
"array": [
|
93
|
+
{
|
94
|
+
"primitive": 1,
|
95
|
+
"string": "string",
|
96
|
+
"array": [],
|
97
|
+
"object": {
|
98
|
+
"primitive": 1
|
99
|
+
},
|
100
|
+
"null": null
|
101
|
+
},
|
102
|
+
{
|
103
|
+
"primitive": 2,
|
104
|
+
"string": "string2",
|
105
|
+
"array": [],
|
106
|
+
"object": {},
|
107
|
+
"null": null
|
108
|
+
}
|
109
|
+
]
|
110
|
+
},
|
111
|
+
"arrayWithAllJsonObjectWithoutSameKeys": {
|
112
|
+
"array": [
|
113
|
+
{
|
114
|
+
"primitive": 1,
|
115
|
+
"string": "string",
|
116
|
+
"array": [],
|
117
|
+
"object": {},
|
118
|
+
"null": null
|
119
|
+
},
|
120
|
+
{
|
121
|
+
"primitive1": 2,
|
122
|
+
"string1": "string2",
|
123
|
+
"array1": [],
|
124
|
+
"object1": {},
|
125
|
+
"null1": null
|
126
|
+
}
|
127
|
+
]
|
128
|
+
}
|
129
|
+
}
|