View Javadoc

1   /*
2    * Copyright (C) 2012 Atlassian
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package it;
18  
19  import com.atlassian.jira.nimblefunctests.annotation.JiraBuildNumberDependent;
20  import com.atlassian.jira.rest.client.IntegrationTestUtil;
21  import com.atlassian.jira.rest.client.api.GetCreateIssueMetadataOptionsBuilder;
22  import com.atlassian.jira.rest.client.api.IssueRestClient;
23  import com.atlassian.jira.rest.client.api.RestClientException;
24  import com.atlassian.jira.rest.client.api.domain.BasicComponent;
25  import com.atlassian.jira.rest.client.api.domain.BasicIssue;
26  import com.atlassian.jira.rest.client.api.domain.BasicPriority;
27  import com.atlassian.jira.rest.client.api.domain.BasicUser;
28  import com.atlassian.jira.rest.client.api.domain.BulkOperationResult;
29  import com.atlassian.jira.rest.client.api.domain.CimFieldInfo;
30  import com.atlassian.jira.rest.client.api.domain.CimIssueType;
31  import com.atlassian.jira.rest.client.api.domain.CimProject;
32  import com.atlassian.jira.rest.client.api.domain.CustomFieldOption;
33  import com.atlassian.jira.rest.client.api.domain.EntityHelper;
34  import com.atlassian.jira.rest.client.api.domain.Issue;
35  import com.atlassian.jira.rest.client.api.domain.IssueFieldId;
36  import com.atlassian.jira.rest.client.api.domain.IssueType;
37  import com.atlassian.jira.rest.client.api.domain.Subtask;
38  import com.atlassian.jira.rest.client.api.domain.TimeTracking;
39  import com.atlassian.jira.rest.client.api.domain.User;
40  import com.atlassian.jira.rest.client.api.domain.input.CannotTransformValueException;
41  import com.atlassian.jira.rest.client.api.domain.input.ComplexIssueInputFieldValue;
42  import com.atlassian.jira.rest.client.api.domain.input.FieldInput;
43  import com.atlassian.jira.rest.client.api.domain.input.IssueInput;
44  import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder;
45  import com.atlassian.jira.rest.client.api.domain.input.PropertyInput;
46  import com.atlassian.jira.rest.client.api.domain.util.ErrorCollection;
47  import com.atlassian.jira.rest.client.internal.json.JsonParseUtil;
48  import com.atlassian.jira.rest.client.internal.json.TestConstants;
49  import com.google.common.base.Function;
50  import com.google.common.base.Predicate;
51  import com.google.common.collect.ImmutableList;
52  import com.google.common.collect.ImmutableMap;
53  import com.google.common.collect.ImmutableSet;
54  import com.google.common.collect.Iterables;
55  import com.google.common.collect.Lists;
56  import com.google.common.collect.Sets;
57  import org.codehaus.jettison.json.JSONArray;
58  import org.codehaus.jettison.json.JSONException;
59  import org.codehaus.jettison.json.JSONObject;
60  import org.hamcrest.Matchers;
61  import org.hamcrest.collection.IsIterableWithSize;
62  import org.joda.time.DateTime;
63  import org.junit.Before;
64  import org.junit.Rule;
65  import org.junit.Test;
66  import org.junit.rules.ExpectedException;
67  
68  import java.text.MessageFormat;
69  import java.util.ArrayList;
70  import java.util.Collection;
71  import java.util.Collections;
72  import java.util.Date;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Set;
76  
77  import static com.atlassian.jira.rest.client.api.domain.EntityHelper.findEntityByName;
78  import static com.atlassian.jira.rest.client.internal.ServerVersionConstants.BN_JIRA_5;
79  import static com.atlassian.jira.rest.client.internal.ServerVersionConstants.BN_JIRA_6;
80  import static com.google.common.collect.Iterables.toArray;
81  import static org.hamcrest.Matchers.greaterThanOrEqualTo;
82  import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
83  import static org.hamcrest.core.IsCollectionContaining.hasItems;
84  import static org.junit.Assert.assertEquals;
85  import static org.junit.Assert.assertFalse;
86  import static org.junit.Assert.assertNotNull;
87  import static org.junit.Assert.assertThat;
88  import static org.junit.Assert.assertTrue;
89  import static org.junit.Assert.fail;
90  
91  // Ignore "May produce NPE" warnings, as we know what we are doing in tests
92  @SuppressWarnings("ConstantConditions")
93  // Restore data only once as we just creates issues here - tests doesn't change any settings and doesn't rely on other issues
94  public class AsynchronousIssueRestClientCreateIssueTest extends AbstractAsynchronousRestClientTest {
95  
96      @Rule
97      public ExpectedException thrown = ExpectedException.none();
98  
99      private static boolean alreadyRestored;
100 
101     @Before
102     public void setup() {
103         if (!alreadyRestored) {
104             IntegrationTestUtil.restoreAppropriateJiraData(TestConstants.JIRA_DUMP_CREATING_ISSUE_TESTS_FILE, administration);
105             alreadyRestored = true;
106         }
107     }
108 
109     @JiraBuildNumberDependent(BN_JIRA_5)
110     @Test
111     public void testCreateIssue() throws JSONException {
112         // collect CreateIssueMetadata for project with key TST
113         final IssueRestClient issueClient = client.getIssueClient();
114         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
115                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
116 
117         // select project and issue
118         assertEquals(1, Iterables.size(metadataProjects));
119         final CimProject project = metadataProjects.iterator().next();
120         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Bug");
121 
122         // grab the first component
123         final Iterable<Object> allowedValuesForComponents = issueType.getField(IssueFieldId.COMPONENTS_FIELD).getAllowedValues();
124         assertNotNull(allowedValuesForComponents);
125         assertTrue(allowedValuesForComponents.iterator().hasNext());
126         final BasicComponent component = (BasicComponent) allowedValuesForComponents.iterator().next();
127 
128         // grab the first priority
129         final Iterable<Object> allowedValuesForPriority = issueType.getField(IssueFieldId.PRIORITY_FIELD).getAllowedValues();
130         assertNotNull(allowedValuesForPriority);
131         assertTrue(allowedValuesForPriority.iterator().hasNext());
132         final BasicPriority priority = (BasicPriority) allowedValuesForPriority.iterator().next();
133 
134         // build issue input
135         final String summary = "My new issue!";
136         final String description = "Some description";
137         final BasicUser assignee = IntegrationTestUtil.USER1;
138         final List<String> affectedVersionsNames = Collections.emptyList();
139         final DateTime dueDate = new DateTime(new Date().getTime());
140         final ArrayList<String> fixVersionsNames = Lists.newArrayList("1.1");
141 
142         // prepare IssueInput
143         final String multiUserCustomFieldId = "customfield_10031";
144         final ImmutableList<BasicUser> multiUserCustomFieldValues = ImmutableList.of(IntegrationTestUtil.USER1, IntegrationTestUtil.USER2);
145         final IssueInputBuilder issueInputBuilder = new IssueInputBuilder(project, issueType, summary)
146                 .setDescription(description)
147                 .setAssignee(assignee)
148                 .setAffectedVersionsNames(affectedVersionsNames)
149                 .setFixVersionsNames(fixVersionsNames)
150                 .setComponents(component)
151                 .setDueDate(dueDate)
152                 .setPriority(priority)
153                 .setFieldValue(multiUserCustomFieldId, multiUserCustomFieldValues);
154 
155         // create
156         final BasicIssue basicCreatedIssue = issueClient.createIssue(issueInputBuilder.build()).claim();
157         assertNotNull(basicCreatedIssue.getKey());
158 
159         // get issue and check if everything was set as we expected
160         final Issue createdIssue = issueClient.getIssue(basicCreatedIssue.getKey()).claim();
161         assertNotNull(createdIssue);
162 
163         assertEquals(basicCreatedIssue.getKey(), createdIssue.getKey());
164         assertEquals(project.getKey(), createdIssue.getProject().getKey());
165         assertEquals(issueType.getId(), createdIssue.getIssueType().getId());
166         assertEquals(summary, createdIssue.getSummary());
167         assertEquals(description, createdIssue.getDescription());
168 
169         final User actualAssignee = createdIssue.getAssignee();
170         assertNotNull(actualAssignee);
171         assertEquals(assignee.getSelf(), actualAssignee.getSelf());
172         // TODO we need some users for integration tests!
173         assertEquals(actualAssignee.getEmailAddress(), "wojciech.seliga@spartez.com");
174 
175         final Iterable<String> actualAffectedVersionsNames = EntityHelper.toNamesList(createdIssue.getAffectedVersions());
176         assertThat(affectedVersionsNames, containsInAnyOrder(toArray(actualAffectedVersionsNames, String.class)));
177 
178         final Iterable<String> actualFixVersionsNames = EntityHelper.toNamesList(createdIssue.getFixVersions());
179         assertThat(fixVersionsNames, containsInAnyOrder(toArray(actualFixVersionsNames, String.class)));
180 
181         assertTrue(createdIssue.getComponents().iterator().hasNext());
182         assertEquals(component.getId(), createdIssue.getComponents().iterator().next().getId());
183 
184         // strip time from dueDate
185         final DateTime expectedDueDate = JsonParseUtil.parseDate(JsonParseUtil.formatDate(dueDate));
186         assertEquals(expectedDueDate, createdIssue.getDueDate());
187 
188         final BasicPriority actualPriority = createdIssue.getPriority();
189         assertNotNull(actualPriority);
190         assertEquals(priority.getId(), actualPriority.getId());
191 
192         // check value of MultiUserSelect field
193         final Object multiUserValue = createdIssue.getField(multiUserCustomFieldId).getValue();
194         // ideally this should be Iterable<User>, but for now it's just an JSONArray...
195         assertThat(multiUserValue, Matchers.instanceOf(JSONArray.class));
196         final JSONArray multiUserArray = (JSONArray) multiUserValue;
197         final List<String> actualMultiUserNames = Lists.newArrayListWithCapacity(multiUserArray.length());
198         for (int i = 0; i < multiUserArray.length(); i++) {
199             final JSONObject jsonUser = (JSONObject) multiUserArray.get(i);
200             actualMultiUserNames.add((String) jsonUser.get("name"));
201         }
202         assertThat(actualMultiUserNames, containsInAnyOrder(
203                 toArray(EntityHelper.toNamesList(multiUserCustomFieldValues), String.class)));
204     }
205 
206     @JiraBuildNumberDependent(BN_JIRA_5)
207     @Test
208     public void testCreateSubtask() {
209         // collect CreateIssueMetadata for project with key TST
210         final IssueRestClient issueClient = client.getIssueClient();
211         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
212                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
213 
214         // select project and issue
215         assertEquals(1, Iterables.size(metadataProjects));
216         final CimProject project = metadataProjects.iterator().next();
217         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Sub-task");
218 
219         // grab the first component
220         final Iterable<Object> allowedValuesForComponents = issueType.getField(IssueFieldId.COMPONENTS_FIELD).getAllowedValues();
221         assertNotNull(allowedValuesForComponents);
222         assertTrue(allowedValuesForComponents.iterator().hasNext());
223         final BasicComponent component = (BasicComponent) allowedValuesForComponents.iterator().next();
224 
225         // grab the first priority
226         final Iterable<Object> allowedValuesForPriority = issueType.getField(IssueFieldId.PRIORITY_FIELD).getAllowedValues();
227         assertNotNull(allowedValuesForPriority);
228         assertTrue(allowedValuesForPriority.iterator().hasNext());
229         final BasicPriority priority = (BasicPriority) allowedValuesForPriority.iterator().next();
230 
231         // build issue input
232         final String summary = "My first substask!";
233         final String description = "Some description for substask";
234         final BasicUser assignee = IntegrationTestUtil.USER1;
235         final List<String> affectedVersionsNames = Collections.emptyList();
236         final DateTime dueDate = new DateTime(new Date().getTime());
237         final ArrayList<String> fixVersionsNames = Lists.newArrayList("1.1");
238 
239         // prepare IssueInput
240         final IssueInputBuilder issueInputBuilder = new IssueInputBuilder(project, issueType, summary)
241                 .setDescription(description)
242                 .setAssignee(assignee)
243                 .setAffectedVersionsNames(affectedVersionsNames)
244                 .setFixVersionsNames(fixVersionsNames)
245                 .setComponents(component)
246                 .setDueDate(dueDate)
247                 .setPriority(priority)
248                 .setFieldValue("parent", ComplexIssueInputFieldValue.with("key", "TST-1"));
249 
250         // create
251         final BasicIssue basicCreatedIssue = issueClient.createIssue(issueInputBuilder.build()).claim();
252         assertNotNull(basicCreatedIssue.getKey());
253 
254         // get issue and check if everything was set as we expected
255         final Issue createdIssue = issueClient.getIssue(basicCreatedIssue.getKey()).claim();
256         assertNotNull(createdIssue);
257 
258         assertEquals(basicCreatedIssue.getKey(), createdIssue.getKey());
259         assertEquals(project.getKey(), createdIssue.getProject().getKey());
260         assertEquals(issueType.getId(), createdIssue.getIssueType().getId());
261         assertEquals(summary, createdIssue.getSummary());
262         assertEquals(description, createdIssue.getDescription());
263 
264         final BasicUser actualAssignee = createdIssue.getAssignee();
265         assertNotNull(actualAssignee);
266         assertEquals(assignee.getSelf(), actualAssignee.getSelf());
267 
268         final Iterable<String> actualAffectedVersionsNames = EntityHelper.toNamesList(createdIssue.getAffectedVersions());
269         assertThat(affectedVersionsNames, containsInAnyOrder(toArray(actualAffectedVersionsNames, String.class)));
270 
271         final Iterable<String> actualFixVersionsNames = EntityHelper.toNamesList(createdIssue.getFixVersions());
272         assertThat(fixVersionsNames, containsInAnyOrder(toArray(actualFixVersionsNames, String.class)));
273 
274         assertTrue(createdIssue.getComponents().iterator().hasNext());
275         assertEquals(component.getId(), createdIssue.getComponents().iterator().next().getId());
276 
277         // strip time from dueDate
278         final DateTime expectedDueDate = JsonParseUtil.parseDate(JsonParseUtil.formatDate(dueDate));
279         assertEquals(expectedDueDate, createdIssue.getDueDate());
280 
281         final BasicPriority actualPriority = createdIssue.getPriority();
282         assertNotNull(actualPriority);
283         assertEquals(priority.getId(), actualPriority.getId());
284     }
285 
286     @JiraBuildNumberDependent(value = BN_JIRA_6)
287     @Test
288     public void testCreateManySubtasksInGivenOrder() throws NoSuchFieldException, IllegalAccessException {
289         // collect CreateIssueMetadata for project with key TST
290         final IssueRestClient issueClient = client.getIssueClient();
291         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
292                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
293 
294         // select project and issue
295         assertEquals(1, Iterables.size(metadataProjects));
296         final CimProject project = metadataProjects.iterator().next();
297         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Sub-task");
298 
299         // grab the first component
300         final Iterable<Object> allowedValuesForComponents = issueType.getField(IssueFieldId.COMPONENTS_FIELD).getAllowedValues();
301         assertNotNull(allowedValuesForComponents);
302         assertTrue(allowedValuesForComponents.iterator().hasNext());
303         final BasicComponent component = (BasicComponent) allowedValuesForComponents.iterator().next();
304 
305         // grab the first priority
306         final Iterable<Object> allowedValuesForPriority = issueType.getField(IssueFieldId.PRIORITY_FIELD).getAllowedValues();
307         assertNotNull(allowedValuesForPriority);
308         assertTrue(allowedValuesForPriority.iterator().hasNext());
309         final BasicPriority priority = (BasicPriority) allowedValuesForPriority.iterator().next();
310 
311         // build issue input
312         final String description = "Some description for substask";
313         final BasicUser assignee = IntegrationTestUtil.USER1;
314         final List<String> affectedVersionsNames = Collections.emptyList();
315         final DateTime dueDate = new DateTime(new Date().getTime());
316         final ArrayList<String> fixVersionsNames = Lists.newArrayList("1.1");
317 
318         final Set<String> summaries = ImmutableSet.of("Summary 1", "Summary 2", "Summary 3", "Summary 4", "Summary 5");
319 
320         // prepare IssueInput
321         final List<IssueInput> issuesToCreate = Lists.newArrayList();
322         for (final String summary : summaries) {
323 
324             final IssueInputBuilder issueInputBuilder =
325                     new IssueInputBuilder(project, issueType, summary)
326                             .setDescription(description)
327                             .setAssignee(assignee)
328                             .setAffectedVersionsNames(affectedVersionsNames)
329                             .setFixVersionsNames(fixVersionsNames)
330                             .setComponents(component)
331                             .setDueDate(dueDate)
332                             .setPriority(priority)
333                             .setFieldValue("parent", ComplexIssueInputFieldValue.with("key", "TST-1"));
334 
335             issuesToCreate.add(issueInputBuilder.build());
336         }
337         assertEquals(summaries.size(), issuesToCreate.size());
338 
339         // create
340         final BulkOperationResult<BasicIssue> createdIssues = issueClient.createIssues(issuesToCreate).claim();
341         assertEquals(summaries.size(), Iterables.size(createdIssues.getIssues()));
342         assertEquals(0, Iterables.size(createdIssues.getErrors()));
343 
344         //check order
345         final Set<String> createdSummariesOrder = ImmutableSet.copyOf(Iterables.transform(createdIssues
346                 .getIssues(), new Function<BasicIssue, String>() {
347             @Override
348             public String apply(final BasicIssue basicIssue) {
349                 return issueClient.getIssue(basicIssue.getKey()).claim().getSummary();
350             }
351         }));
352 
353         assertEquals(summaries, createdSummariesOrder);
354 
355         final Issue parentIssue = issueClient.getIssue("TST-1").claim();
356         final Set<String> subtaskKeys = ImmutableSet.copyOf(Iterables.transform(parentIssue
357                 .getSubtasks(), new Function<Subtask, String>() {
358             @Override
359             public String apply(final Subtask subtask) {
360                 return subtask.getIssueKey();
361             }
362         }));
363 
364         for (final BasicIssue basicIssue : createdIssues.getIssues()) {
365 
366             // get issue and check if everything was set as we expected
367             final Issue createdIssue = issueClient.getIssue(basicIssue.getKey()).claim();
368             assertNotNull(createdIssue);
369 
370             assertEquals(basicIssue.getKey(), createdIssue.getKey());
371             assertEquals(project.getKey(), createdIssue.getProject().getKey());
372             assertEquals(issueType.getId(), createdIssue.getIssueType().getId());
373             assertTrue(summaries.contains(createdIssue.getSummary()));
374             assertEquals(description, createdIssue.getDescription());
375 
376             final BasicUser actualAssignee = createdIssue.getAssignee();
377             assertNotNull(actualAssignee);
378             assertEquals(assignee.getSelf(), actualAssignee.getSelf());
379 
380             assertTrue(subtaskKeys.contains(createdIssue.getKey()));
381         }
382     }
383 
384 
385     @JiraBuildNumberDependent(value = BN_JIRA_6)
386     @Test
387     public void testCreateManySubtasksInGivenOrderWithSomeFailing() throws NoSuchFieldException, IllegalAccessException {
388         // collect CreateIssueMetadata for project with key TST
389         final IssueRestClient issueClient = client.getIssueClient();
390         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
391                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
392 
393         // select project and issue
394         assertEquals(1, Iterables.size(metadataProjects));
395         final CimProject project = metadataProjects.iterator().next();
396         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Sub-task");
397 
398         // grab the first component
399         final Iterable<Object> allowedValuesForComponents = issueType.getField(IssueFieldId.COMPONENTS_FIELD).getAllowedValues();
400         assertNotNull(allowedValuesForComponents);
401         assertTrue(allowedValuesForComponents.iterator().hasNext());
402         final BasicComponent component = (BasicComponent) allowedValuesForComponents.iterator().next();
403 
404         // grab the first priority
405         final Iterable<Object> allowedValuesForPriority = issueType.getField(IssueFieldId.PRIORITY_FIELD).getAllowedValues();
406         assertNotNull(allowedValuesForPriority);
407         assertTrue(allowedValuesForPriority.iterator().hasNext());
408         final BasicPriority priority = (BasicPriority) allowedValuesForPriority.iterator().next();
409 
410         // build issue input
411         final String description = "Some description for substask";
412         final BasicUser assignee = IntegrationTestUtil.USER1;
413         final List<String> affectedVersionsNames = Collections.emptyList();
414         final DateTime dueDate = new DateTime(new Date().getTime());
415         final ArrayList<String> fixVersionsNames = Lists.newArrayList("1.1");
416 
417 
418         final Set<String> summaries = ImmutableSet.of("Summary 1", "Summary 2", "Summary 3", "Summary 4", "Summary 5");
419         final Set<String> summariesWithError = ImmutableSet.of("Summary 1", "Summary 4");
420         final Set<String> expectedSummariesOrder = Sets.difference(summaries, summariesWithError);
421 
422         final int issuecToCreateCount = summaries.size() - summariesWithError.size();
423         final int issuesInErrorCount = summariesWithError.size();
424 
425         final List<IssueInput> issuesToCreate = Lists.newArrayList();
426         // prepare IssueInput
427         for (final String summary : summaries) {
428             String currentProjectKey = project.getKey();
429             //last issue to create will have a non existing project - to simulate creation error
430             if (summariesWithError.contains(summary)) {
431                 currentProjectKey = "FAKE_KEY";
432             }
433 
434             final IssueInputBuilder issueInputBuilder =
435                     new IssueInputBuilder(currentProjectKey, issueType.getId(), summary)
436                             .setDescription(description)
437                             .setAssignee(assignee)
438                             .setAffectedVersionsNames(affectedVersionsNames)
439                             .setFixVersionsNames(fixVersionsNames)
440                             .setComponents(component)
441                             .setDueDate(dueDate)
442                             .setPriority(priority)
443                             .setFieldValue("parent", ComplexIssueInputFieldValue.with("key", "TST-1"));
444 
445             issuesToCreate.add(issueInputBuilder.build());
446         }
447         assertEquals(summaries.size(), issuesToCreate.size());
448 
449         // create
450         final BulkOperationResult<BasicIssue> createdIssues = issueClient.createIssues(issuesToCreate).claim();
451         assertEquals(issuecToCreateCount, Iterables.size(createdIssues.getIssues()));
452         assertEquals(issuesInErrorCount, Iterables.size(createdIssues.getErrors()));
453 
454         //check order
455         final Set<String> createdSummariesOrder = ImmutableSet.copyOf(Iterables.transform(createdIssues
456                 .getIssues(), new Function<BasicIssue, String>() {
457             @Override
458             public String apply(final BasicIssue basicIssue) {
459                 return issueClient.getIssue(basicIssue.getKey()).claim().getSummary();
460             }
461         }));
462 
463         assertEquals(expectedSummariesOrder, createdSummariesOrder);
464 
465         final Issue parentIssue = issueClient.getIssue("TST-1").claim();
466         final Set<String> subtaskKeys = ImmutableSet.copyOf(Iterables.transform(parentIssue
467                 .getSubtasks(), new Function<Subtask, String>() {
468             @Override
469             public String apply(Subtask subtask) {
470                 return subtask.getIssueKey();
471             }
472         }));
473 
474         for (final BasicIssue basicIssue : createdIssues.getIssues()) {
475 
476             // get issue and check if everything was set as we expected
477             final Issue createdIssue = issueClient.getIssue(basicIssue.getKey()).claim();
478             assertNotNull(createdIssue);
479 
480             assertEquals(basicIssue.getKey(), createdIssue.getKey());
481             assertEquals(project.getKey(), createdIssue.getProject().getKey());
482             assertEquals(issueType.getId(), createdIssue.getIssueType().getId());
483             assertEquals(description, createdIssue.getDescription());
484 
485             final BasicUser actualAssignee = createdIssue.getAssignee();
486             assertNotNull(actualAssignee);
487             assertEquals(assignee.getSelf(), actualAssignee.getSelf());
488 
489             assertTrue(summaries.contains(createdIssue.getSummary()));
490             assertFalse(summariesWithError.contains(createdIssue.getSummary()));
491 
492             assertTrue(subtaskKeys.contains(createdIssue.getKey()));
493         }
494     }
495 
496     @JiraBuildNumberDependent(value = BN_JIRA_6)
497     @Test
498     public void testCreateManySubtasksInGivenOrderWithAllFailing() throws NoSuchFieldException, IllegalAccessException {
499         // collect CreateIssueMetadata for project with key TST
500         final IssueRestClient issueClient = client.getIssueClient();
501         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
502                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
503 
504         // select project and issue
505         assertEquals(1, Iterables.size(metadataProjects));
506         final CimProject project = metadataProjects.iterator().next();
507         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Sub-task");
508 
509         // grab the first component
510         final Iterable<Object> allowedValuesForComponents = issueType.getField(IssueFieldId.COMPONENTS_FIELD).getAllowedValues();
511         assertNotNull(allowedValuesForComponents);
512         assertTrue(allowedValuesForComponents.iterator().hasNext());
513         final BasicComponent component = (BasicComponent) allowedValuesForComponents.iterator().next();
514 
515         // grab the first priority
516         final Iterable<Object> allowedValuesForPriority = issueType.getField(IssueFieldId.PRIORITY_FIELD).getAllowedValues();
517         assertNotNull(allowedValuesForPriority);
518         assertTrue(allowedValuesForPriority.iterator().hasNext());
519         final BasicPriority priority = (BasicPriority) allowedValuesForPriority.iterator().next();
520 
521         // build issue input
522         final String description = "Some description for substask";
523         final BasicUser assignee = IntegrationTestUtil.USER1;
524         final List<String> affectedVersionsNames = Collections.emptyList();
525         final DateTime dueDate = new DateTime(new Date().getTime());
526         final ArrayList<String> fixVersionsNames = Lists.newArrayList("1.1");
527 
528         final Set<String> summaries = ImmutableSet.of("Summary 1", "Summary 2", "Summary 3", "Summary 4", "Summary 5");
529         final Set<String> summariesWithError = ImmutableSet.of("Summary 1", "Summary 2", "Summary 3", "Summary 4", "Summary 5");
530 
531         final int issuesInErrorCount = summariesWithError.size();
532 
533         final List<IssueInput> issuesToCreate = Lists.newArrayList();
534         // prepare IssueInput
535         for (final String summary : summaries) {
536             String currentProjectKey = project.getKey();
537             //last issue to create will have a non existing project - to simulate creation error
538             if (summariesWithError.contains(summary)) {
539                 currentProjectKey = "FAKE_KEY";
540             }
541 
542             final IssueInputBuilder issueInputBuilder =
543                     new IssueInputBuilder(currentProjectKey, issueType.getId(), summary)
544                             .setDescription(description)
545                             .setAssignee(assignee)
546                             .setAffectedVersionsNames(affectedVersionsNames)
547                             .setFixVersionsNames(fixVersionsNames)
548                             .setComponents(component)
549                             .setDueDate(dueDate)
550                             .setPriority(priority)
551                             .setFieldValue("parent", ComplexIssueInputFieldValue.with("key", "TST-1"));
552 
553             issuesToCreate.add(issueInputBuilder.build());
554         }
555         assertEquals(summaries.size(), issuesToCreate.size());
556 
557         // create
558         try {
559             issueClient.createIssues(issuesToCreate).claim();
560         } catch (RestClientException ex) {
561             assertEquals(issuesInErrorCount, ex.getErrorCollections().size());
562             for (final ErrorCollection errorCollection : ex.getErrorCollections()) {
563                 assertTrue("Unexpected error messages", errorCollection.getErrorMessages().isEmpty());
564                 final String message = errorCollection.getErrors().get("project");
565                 assertEquals("project is required", message);
566             }
567         }
568     }
569 
570     @JiraBuildNumberDependent(BN_JIRA_5)
571     @Test
572     public void testCreateIssueWithOnlyRequiredFields() {
573         // collect CreateIssueMetadata for project with key TST
574         final IssueRestClient issueClient = client.getIssueClient();
575         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
576                 new GetCreateIssueMetadataOptionsBuilder().withProjectKeys("TST").withExpandedIssueTypesFields().build()).claim();
577 
578         // select project and issue
579         assertEquals(1, Iterables.size(metadataProjects));
580         final CimProject project = metadataProjects.iterator().next();
581         final CimIssueType issueType = findEntityByName(project.getIssueTypes(), "Bug");
582 
583         // build issue input
584         final String summary = "My new issue!";
585 
586         // create
587         final IssueInput issueInput = new IssueInputBuilder(project, issueType, summary).build();
588         final BasicIssue basicCreatedIssue = issueClient.createIssue(issueInput).claim();
589         assertNotNull(basicCreatedIssue.getKey());
590 
591         // get issue and check if everything was set as we expected
592         final Issue createdIssue = issueClient.getIssue(basicCreatedIssue.getKey()).claim();
593         assertNotNull(createdIssue);
594 
595         assertEquals(basicCreatedIssue.getKey(), createdIssue.getKey());
596         assertEquals(project.getKey(), createdIssue.getProject().getKey());
597         assertEquals(issueType.getId(), createdIssue.getIssueType().getId());
598         assertEquals(summary, createdIssue.getSummary());
599     }
600 
601     @JiraBuildNumberDependent(BN_JIRA_5)
602     @Test
603     public void testCreateIssueWithoutSummary() {
604         final IssueRestClient issueClient = client.getIssueClient();
605 
606         thrown.expect(RestClientException.class);
607         thrown.expectMessage("You must specify a summary of the issue.");
608 
609         final IssueInput issueInput = new IssueInputBuilder("TST", 1L).build();
610         issueClient.createIssue(issueInput).claim();
611     }
612 
613     @JiraBuildNumberDependent(BN_JIRA_5)
614     @Test
615     public void testCreateIssueWithNotExistentProject() {
616         final IssueRestClient issueClient = client.getIssueClient();
617 
618         thrown.expect(RestClientException.class);
619         thrown.expectMessage("project is required");
620 
621         final IssueInput issueInput = new IssueInputBuilder("BAD", 1L, "Should fail").build();
622         issueClient.createIssue(issueInput).claim();
623     }
624 
625     @JiraBuildNumberDependent(BN_JIRA_5)
626     @Test
627     public void testCreateIssueWithNotExistentIssueType() {
628         final IssueRestClient issueClient = client.getIssueClient();
629 
630         thrown.expect(RestClientException.class);
631         thrown.expectMessage("valid issue type is required");
632 
633         final IssueInput issueInput = new IssueInputBuilder("TST", 666L, "Should fail").build();
634         issueClient.createIssue(issueInput).claim();
635     }
636 
637 
638     @JiraBuildNumberDependent(BN_JIRA_5)
639     @Test
640     public void testCreateIssueWithoutProject() {
641         final IssueRestClient issueClient = client.getIssueClient();
642 
643         thrown.expect(RestClientException.class);
644         thrown.expectMessage("project is required");
645 
646         final IssueInput issueInput = new IssueInput(ImmutableMap.of(
647                 "summary", new FieldInput("summary", "Summary"),
648                 "issuetype", new FieldInput("issuetype", ComplexIssueInputFieldValue.with("id", "1"))
649         ), ImmutableList.of(new PropertyInput("testKey", "{\"testValue\" : \"foo\"}")));
650         issueClient.createIssue(issueInput).claim();
651     }
652 
653     @JiraBuildNumberDependent(BN_JIRA_5)
654     @Test
655     public void testCreateIssueWithInvalidAdditionalField() {
656         final IssueRestClient issueClient = client.getIssueClient();
657         final String fieldId = "invalidField";
658 
659         thrown.expect(RestClientException.class);
660         thrown.expectMessage(String
661                 .format("Field '%s' cannot be set. It is not on the appropriate screen, or unknown.", fieldId));
662 
663         final IssueInput issueInput = new IssueInputBuilder("TST", 1L, "Should fail")
664                 .setFieldValue(fieldId, "test")
665                 .build();
666         issueClient.createIssue(issueInput).claim();
667     }
668 
669     @JiraBuildNumberDependent(BN_JIRA_5)
670     @Test
671     public void testCreateIssueWithFieldValueThatIsNotAllowed() {
672         final IssueRestClient issueClient = client.getIssueClient();
673         final BasicPriority invalidPriority = new BasicPriority(null, 666L, "Invalid Priority");
674 
675         thrown.expect(RestClientException.class);
676         thrown.expectMessage(String
677                 .format("Invalid value '%s' passed for customfield 'My Radio buttons'. Allowed values are: 10000[abc], 10001[Another], 10002[The last option], -1", invalidPriority
678                         .getId()));
679 
680         final IssueInput issueInput = new IssueInputBuilder("TST", 1L, "Should fail")
681                 .setFieldValue("customfield_10001", invalidPriority)
682                 .build();
683         issueClient.createIssue(issueInput).claim();
684     }
685 
686     @JiraBuildNumberDependent(BN_JIRA_5)
687     @Test
688     public void testCreateIssueAsAnonymous() {
689         setAnonymousMode();
690 
691         final IssueRestClient issueClient = client.getIssueClient();
692 
693         final IssueInput issueInput = new IssueInputBuilder("ANONEDIT", 1L, "Anonymously created issue").build();
694         final BasicIssue createdIssue = issueClient.createIssue(issueInput).claim();
695 
696         assertNotNull(createdIssue);
697         assertNotNull(createdIssue.getKey());
698     }
699 
700     @JiraBuildNumberDependent(BN_JIRA_5)
701     @Test
702     public void testCreateIssueAsAnonymousWhenNotAllowed() {
703         setAnonymousMode();
704         final IssueRestClient issueClient = client.getIssueClient();
705 
706         thrown.expect(RestClientException.class);
707         thrown.expectMessage("Anonymous users do not have permission to create issues in this project. Please try logging in first.");
708 
709         // TODO: add summary when JIRA bug is fixed (JRADEV-13412)
710         final IssueInput issueInput = new IssueInputBuilder("TST", 1L/*, "Issue created by testCreateIssueAsAnonymousWhenNotAllowed"*/)
711                 .build();
712         issueClient.createIssue(issueInput).claim();
713     }
714 
715     @JiraBuildNumberDependent(BN_JIRA_5)
716     @Test
717     public void testJiradev13412BugNotFixedIfThisFailsThenCorrectAffectedTests() {
718         // This test checks if JRADEV-13412 is fixed.
719         // TODO: When fixed please correct testCreateIssueAsAnonymousWhenNotAllowed, testCreateIssueWithoutCreateIssuePermission, testCreateIssueWithoutBrowseProjectPermission and remove this test.
720         //
721         // We should get something like that when this is fixed:
722         //    Anonymous users do not have permission to create issues in this project. Please try logging in first.
723         // instead of:
724         //    Field 'summary' cannot be set. It is not on the appropriate screen, or unknown.
725         setAnonymousMode();
726         final IssueRestClient issueClient = client.getIssueClient();
727 
728         thrown.expect(RestClientException.class);
729         thrown.expectMessage("Field 'summary' cannot be set. It is not on the appropriate screen, or unknown.");
730 
731         final IssueInput issueInput = new IssueInputBuilder("TST", 1L, "Sample summary").build();
732         issueClient.createIssue(issueInput).claim();
733     }
734 
735     @JiraBuildNumberDependent(BN_JIRA_5)
736     @Test
737     public void testCreateIssueWithAssigneeWhenNotAllowedToAssignIssue() {
738         setUser2();
739         final IssueRestClient issueClient = client.getIssueClient();
740 
741         thrown.expect(RestClientException.class);
742         thrown.expectMessage("Field 'assignee' cannot be set. It is not on the appropriate screen, or unknown.");
743 
744         final IssueInput issueInput = new IssueInputBuilder("TST", 1L, "Issue created by testCreateIssueWithAssigneeWhenNotAllowedToAssignIssue")
745                 .setAssignee(IntegrationTestUtil.USER_ADMIN)
746                 .build();
747         issueClient.createIssue(issueInput).claim();
748     }
749 
750     @JiraBuildNumberDependent(BN_JIRA_5)
751     @Test
752     public void testCreateIssueWithoutCreateIssuePermission() {
753         setUser1();
754         final IssueRestClient issueClient = client.getIssueClient();
755 
756         thrown.expect(RestClientException.class);
757         thrown.expectMessage("You do not have permission to create issues in this project.");
758 
759         // TODO: add summary when JIRA bug is fixed (JRADEV-13412)
760         final IssueInput issueInput = new IssueInputBuilder("NCIFU", 1L/*, "Issue created by testCreateIssueWithoutCreateIssuePermission"*/)
761                 .build();
762         issueClient.createIssue(issueInput).claim();
763     }
764 
765 
766     @JiraBuildNumberDependent(BN_JIRA_5)
767     @Test
768     public void testCreateIssueWithoutBrowseProjectPermission() {
769         setUser1();
770         final IssueRestClient issueClient = client.getIssueClient();
771 
772         thrown.expect(RestClientException.class);
773         thrown.expectMessage("You do not have permission to create issues in this project.");
774 
775         // TODO: add summary when JIRA bug is fixed (JRADEV-13412)
776         final IssueInput issueInput = new IssueInputBuilder("RST", 1L/*, "Issue created by testCreateIssueWithoutBrowseProjectPermission"*/)
777                 .build();
778         issueClient.createIssue(issueInput).claim();
779     }
780 
781     @JiraBuildNumberDependent(BN_JIRA_6)
782     @Test
783     public void testCreateMetaShouldReturnIssueTypeInFieldsListEvenIfIssueTypeIsNotOnCreateIssueScreen() {
784         final IssueRestClient issueClient = client.getIssueClient();
785         final Iterable<CimProject> cimProjects = issueClient.getCreateIssueMetadata(
786                 new GetCreateIssueMetadataOptionsBuilder().withExpandedIssueTypesFields().build()).claim();
787 
788         final CimProject testProject = findEntityByName(cimProjects, "Project With Create Issue Screen Without Issue Type");
789         assertThat(testProject.getIssueTypes(), IsIterableWithSize.<CimIssueType>iterableWithSize(greaterThanOrEqualTo(5)));
790         for (CimIssueType cimIssueType : testProject.getIssueTypes()) {
791             final CimFieldInfo issueType = cimIssueType.getField(IssueFieldId.ISSUE_TYPE_FIELD);
792             final String assertMessageIssueTypeNotPresent = String.format(
793                     "Issue type is missing for project %s (%s) and issue type %s (%s)!",
794                     testProject.getName(), testProject.getKey(), cimIssueType.getName(), cimIssueType.getId());
795             assertNotNull(assertMessageIssueTypeNotPresent, issueType);
796 
797             // check the allowed values
798             final Iterable<Object> allowedValues = issueType.getAllowedValues();
799             final String assertMessageAllowedValuesSizeNotMatch = String.format(
800                     "We expected exactly one allowed value - the issue type %s (%s) for project  %s (%s)",
801                     testProject.getName(), testProject.getKey(), cimIssueType.getName(), cimIssueType.getId());
802             assertEquals(assertMessageAllowedValuesSizeNotMatch, 1, Iterables.size(allowedValues));
803 
804             //noinspection unchecked
805             final IssueType firstAllowedValue = (IssueType) Iterables.getOnlyElement(allowedValues);
806             assertEquals(firstAllowedValue.getId(), cimIssueType.getId());
807         }
808     }
809 
810     @JiraBuildNumberDependent(BN_JIRA_5)
811     @Test
812     public void interactiveUseCase() throws CannotTransformValueException {
813         final IssueRestClient issueClient = client.getIssueClient();
814 
815         // get project list with fields expanded
816         final Iterable<CimProject> metadataProjects = issueClient.getCreateIssueMetadata(
817                 new GetCreateIssueMetadataOptionsBuilder().withExpandedIssueTypesFields().build()).claim();
818         log.log("Available projects: ");
819         for (CimProject p : metadataProjects) {
820             log.log(MessageFormat.format("\t* [{0}] {1}", p.getKey(), p.getName()));
821         }
822         log.log("");
823         assertTrue("There is no project to select!", metadataProjects.iterator().hasNext());
824 
825         // select project
826         final CimProject project = metadataProjects.iterator().next();
827         log.log(MessageFormat.format("Selected project: [{0}] {1}\n", project.getKey(), project.getName()));
828 
829         // select issue type
830         log.log("Available issue types for selected project:");
831         for (CimIssueType t : project.getIssueTypes()) {
832             log.log(MessageFormat.format("\t* [{0}] {1}", t.getId(), t.getName()));
833         }
834         log.log("");
835 
836         final CimIssueType issueType = project.getIssueTypes().iterator().next();
837         log.log(MessageFormat.format("Selected issue type: [{0}] {1}\n", issueType.getId(), issueType.getName()));
838 
839         final IssueInputBuilder builder = new IssueInputBuilder(project.getKey(), issueType.getId());
840 
841         // fill fields
842         log.log("Filling fields:");
843         for (Map.Entry<String, CimFieldInfo> entry : issueType.getFields().entrySet()) {
844             final CimFieldInfo fieldInfo = entry.getValue();
845             final String fieldCustomType = fieldInfo.getSchema().getCustom();
846             final String fieldType = fieldInfo.getSchema().getType();
847             final String fieldId = fieldInfo.getId();
848 
849             if ("project".equals(fieldId) || "issuetype".equals(fieldId)) {
850                 // this field was already set by IssueInputBuilder constructor - skip it
851                 continue;
852             }
853 
854             log.log(MessageFormat.format("\t* [{0}] {1}\n\t\t| schema: {2}\n\t\t| required: {3}", fieldId, fieldInfo
855                     .getName(), fieldInfo.getSchema(), fieldInfo.isRequired()));
856 
857             // choose value for this field
858             Object value = null;
859             final Iterable<Object> allowedValues = fieldInfo.getAllowedValues();
860             if (allowedValues != null) {
861                 log.log("\t\t| field only accepts those values:");
862                 for (Object val : allowedValues) {
863                     log.log("\t\t\t* " + val);
864                 }
865                 if (allowedValues.iterator().hasNext()) {
866                     final boolean expectedArray = "array".equals(fieldType);
867                     Object singleValue = allowedValues.iterator().next();
868 
869                     if ("com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect".equals(fieldCustomType)) {
870                         // select option with children - if any
871                         final Iterable<Object> optionsWithChildren = Iterables.filter(allowedValues, new Predicate<Object>() {
872                             @Override
873                             public boolean apply(Object input) {
874                                 return ((CustomFieldOption) input).getChildren().iterator().hasNext();
875                             }
876                         });
877 
878                         if (optionsWithChildren.iterator().hasNext()) {
879                             // there is option with children - set it
880                             final CustomFieldOption option = (CustomFieldOption) optionsWithChildren.iterator().next();
881                             value = new CustomFieldOption(option.getId(), option.getSelf(), option.getValue(),
882                                     Collections.<CustomFieldOption>emptyList(), option.getChildren().iterator().next());
883                         } else {
884                             // no sub-values available, set only top level value
885                             value = allowedValues.iterator().next();
886                         }
887                     } else {
888                         value = expectedArray ? Collections.singletonList(singleValue) : singleValue;
889                     }
890                     log.log("\t\t| selecting value: " + value);
891                 } else {
892                     log.log("\t\t| there is no allowed value - leaving field blank");
893                 }
894             } else {
895                 if ("com.atlassian.jirafisheyeplugin:jobcheckbox".equals(fieldCustomType)) {
896                     value = "false";
897                 } else if ("com.atlassian.jira.plugin.system.customfieldtypes:url".equals(fieldCustomType)) {
898                     value = "http://www.atlassian.com/";
899                 } else if ("string".equals(fieldType)) {
900                     value = "This is simple string value for field " + fieldId + " named " + fieldInfo.getName() + ".";
901                 } else if ("number".equals(fieldType)) {
902                     value = 124;
903                 } else if ("user".equals(fieldType)) {
904                     value = IntegrationTestUtil.USER_ADMIN;
905                 } else if ("array".equals(fieldType) && "user".equals(fieldInfo.getSchema().getItems())) {
906                     value = ImmutableList.of(IntegrationTestUtil.USER_ADMIN);
907                 } else if ("group".equals(fieldType)) {
908                     // TODO change to group object when implemented
909                     value = ComplexIssueInputFieldValue.with("name", IntegrationTestUtil.GROUP_JIRA_ADMINISTRATORS);
910                 } else if ("array".equals(fieldType) && "group".equals(fieldInfo.getSchema().getItems())) {
911                     // TODO change to group object when implemented
912                     value = ImmutableList.of(ComplexIssueInputFieldValue.with("name", IntegrationTestUtil.GROUP_JIRA_ADMINISTRATORS));
913                 } else if ("date".equals(fieldType)) {
914                     value = JsonParseUtil.formatDate(new DateTime());
915                 } else if ("datetime".equals(fieldType)) {
916                     value = JsonParseUtil.formatDateTime(new DateTime());
917                 } else if ("array".equals(fieldType) && "string".equals(fieldInfo.getSchema().getItems())) {
918                     value = ImmutableList.of("one", "two", "three");
919                 } else if ("timetracking".equals(fieldType)) {
920                     value = new TimeTracking(60, 40, null); // time spent is not allowed
921                 } else {
922                     if (fieldInfo.isRequired()) {
923                         fail("I don't know how to fill that required field, sorry.");
924                     } else {
925                         log.log("\t\t| field value is not required, leaving blank");
926                     }
927                 }
928             }
929             if (value == null) {
930                 log.log("\t\t| value is null, skipping filed");
931             } else {
932                 log.log(MessageFormat.format("\t\t| setting value => {0}", value));
933                 builder.setFieldValue(fieldId, value);
934             }
935         }
936         log.log("");
937 
938         // all required data is provided, let's create issue
939         final IssueInput issueInput = builder.build();
940 
941         final BasicIssue basicCreatedIssue = issueClient.createIssue(issueInput).claim();
942         assertNotNull(basicCreatedIssue);
943 
944         final Issue createdIssue = issueClient.getIssue(basicCreatedIssue.getKey()).claim();
945         assertNotNull(createdIssue);
946 
947         log.log("Created new issue successfully, key: " + basicCreatedIssue.getKey());
948 
949         // assert few fields
950         IssueInputBuilder actualBuilder = new IssueInputBuilder(createdIssue.getProject(), createdIssue
951                 .getIssueType(), createdIssue.getSummary())
952                 .setPriority(createdIssue.getPriority())
953                 .setReporter(createdIssue.getReporter())
954                 .setAssignee(createdIssue.getAssignee())
955                 .setDescription(createdIssue.getDescription());
956 
957         final Collection<FieldInput> actualValues = actualBuilder.build().getFields().values();
958         final Collection<FieldInput> expectedValues = issueInput.getFields().values();
959 
960         assertThat(expectedValues, hasItems(toArray(actualValues, FieldInput.class)));
961     }
962 }