├── .gitignore ├── .travis.yml ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── everit │ │ └── jira │ │ └── worklog │ │ └── query │ │ └── plugin │ │ ├── DateTimeConverterUtil.java │ │ ├── FindWorklogsByIssuesParam.java │ │ ├── IssueBeanWithTimespent.java │ │ ├── SearchResultsBeanWithTimespent.java │ │ ├── WorklogQueryCore.java │ │ ├── WorklogQueryCoreImpl.java │ │ ├── WorklogQueryException.java │ │ ├── WorklogQueryResource.java │ │ └── query │ │ ├── FindWorklogsByIssuesQuery.java │ │ ├── FindWorklogsQuery.java │ │ └── JsonWorklog.java └── resources │ ├── atlassian-plugin.xml │ └── icons │ ├── e_logo16.png │ ├── e_logo72.png │ ├── jwqp144.png │ └── jwqp16.png └── test ├── java └── org │ └── everit │ └── jira │ └── worklog │ └── query │ └── test │ ├── DatabaseSupport.java │ ├── MockTest.java │ └── WorklogQueryTest.java └── resources └── expectedResults.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse and Maven Files # 2 | .classpath 3 | .settings 4 | .project 5 | .checkstyle 6 | target 7 | .fbExcludeFilterFile 8 | .pmd 9 | .pmdruleset.xml 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | script: mvn verify 5 | install: true 6 | sudo: false 7 | cache: 8 | directories: 9 | - $HOME/.m2 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jira-worklog-query-plugin 2 | ========================= 3 | 4 | Worklog Query Plugin is a JIRA plugin enabling its users to run worklog 5 | queries and apply parameters on these queries through RESTful services. The 6 | plugin handles queries according to user roles, therefore, these queries can 7 | only be run on worklogs the user has appropriate permissions to access. 8 | Various filtering parameters can also be set up to narrow down the search 9 | results to a certain interval of time, user, user group and/or project. The 10 | result of the worklog query is served in JSON format. 11 | 12 | The documentation of the plugin can be found [here][1]. 13 | 14 | The version history can be found on the [Atlassian Marketplace][2]. 15 | 16 | [![Analytics](https://ga-beacon.appspot.com/UA-15041869-4/everit-org/jira-worklog-query-plugin)](https://github.com/igrigorik/ga-beacon) 17 | 18 | [1]: http://www.everit.org/jira-worklog-query-plugin/ 19 | [2]: https://marketplace.atlassian.com/plugins/org.everit.jira.worklog.query.plugin.core/versions 20 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 22 | 23 | 4.0.0 24 | 25 | 26 | org.everit.config 27 | org.everit.config.oss 28 | 7.1.0 29 | 30 | 31 | org.everit.jira 32 | org.everit.jira.worklog.query.plugin 33 | 3.0.2 34 | 35 | bundle 36 | 37 | 2013 38 | 39 | 40 | 8.0.0 41 | 1.8 42 | 1.8 43 | 6.2.4 44 | 45 | 46 | 47 | scm:git:git://github.com/everit-org/jira-worklog-query-plugin.git 48 | scm:git:https://github.com/everit-org/jira-worklog-query-plugin.git 49 | https://github.com/everit-org/jira-worklog-query-plugin 50 | 51 | 52 | 53 | Travis CI 54 | https://travis-ci.org/everit-org/jira-worklog-query-plugin 55 | 56 | 57 | 58 | GitHub 59 | https://github.com/everit-org/jira-worklog-query-plugin/issues 60 | 61 | 62 | 63 | 64 | atlassian-public 65 | https://maven.atlassian.com/repository/public 66 | 67 | true 68 | never 69 | warn 70 | 71 | 72 | true 73 | warn 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.apache.felix 82 | maven-bundle-plugin 83 | true 84 | 85 | 86 | ${project.artifactId} 87 | 88 | com.atlassian.jira.rest.v2.*;version="${jira.version}", 89 | com.atlassian.jira.rest.api.*;version="${jira.version}", 90 | * 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-checkstyle-plugin 99 | 100 | org/everit/jira/worklog/query/plugin/WorklogQueryResource* 101 | 102 | 103 | 104 | com.atlassian.maven.plugins 105 | maven-jira-plugin 106 | ${maven.jira.plugin.version} 107 | false 108 | 109 | 110 | generate-obr-artifact 111 | package 112 | 113 | generate-obr-artifact 114 | 115 | 116 | 117 | 118 | 119 | 120 | org.everit.jira 121 | jira-querydsl-support 122 | 123 | 124 | 125 | <_manifest>${project.build.outputdirectory}/META-INF/MANIFEST.MF 126 | 127 | 128 | 129 | 130 | org.apache.maven.plugins 131 | maven-surefire-plugin 132 | 133 | 134 | UTC 135 | 136 | 137 | 138 | 139 | 140 | 141 | src/main/resources 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | com.atlassian.jira 150 | jira-rest-plugin 151 | ${jira.version} 152 | provided 153 | 154 | 155 | com.atlassian.jira 156 | jira-core 157 | ${jira.version} 158 | provided 159 | 160 | 161 | jndi 162 | jndi 163 | 164 | 165 | jta 166 | jta 167 | 168 | 169 | 170 | 171 | com.atlassian.jira 172 | jira-rest-api 173 | ${jira.version} 174 | provided 175 | 176 | 177 | 178 | org.everit.jira 179 | jira-querydsl-support 180 | 2.0.0 181 | provided 182 | 183 | 184 | 185 | com.sun.jersey 186 | jersey-client 187 | 1.12 188 | 189 | 190 | 191 | junit 192 | junit 193 | 4.12 194 | test 195 | 196 | 197 | com.atlassian.jira 198 | jira-tests 199 | ${jira.version} 200 | test 201 | 202 | 203 | org.powermock 204 | powermock-module-junit4 205 | 2.0.0 206 | test 207 | 208 | 209 | org.powermock 210 | powermock-api-mockito2 211 | 2.0.0 212 | test 213 | 214 | 215 | org.apache.geronimo.components 216 | geronimo-transaction 217 | 3.1.3 218 | test 219 | 220 | 221 | org.apache.commons 222 | commons-dbcp2 223 | 2.1 224 | test 225 | 226 | 227 | com.atlassian.plugins.rest 228 | atlassian-rest-module 229 | 6.0.0 230 | test 231 | 232 | 233 | 234 | 235 | 236 | 237 | com.google.guava 238 | guava 239 | 29.0-jre 240 | 241 | 242 | 243 | 244 | 245 | 246 | 3dparty 247 | https://maven.atlassian.com/3rdparty 248 | 249 | true 250 | 251 | 252 | false 253 | 254 | 255 | 256 | atlassian-public 257 | https://maven.atlassian.com/repository/public 258 | 259 | true 260 | never 261 | warn 262 | 263 | 264 | true 265 | warn 266 | 267 | 268 | 269 | everit.release 270 | everit-releases 271 | https://repo.everit.biz/artifactory/public-release/ 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/DateTimeConverterUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.sql.Timestamp; 19 | import java.text.DateFormat; 20 | import java.text.ParseException; 21 | import java.text.SimpleDateFormat; 22 | import java.util.Calendar; 23 | import java.util.Date; 24 | 25 | /** 26 | * The utility class of date and time conversions. 27 | */ 28 | public final class DateTimeConverterUtil { 29 | 30 | /** 31 | * The date format of the input parameters. 32 | */ 33 | private static final String INPUT_DATE_FORMAT = "yyyy-MM-dd"; 34 | 35 | /** 36 | * The date format of JIRA. 37 | */ 38 | private static final String JIRA_OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.s"; 39 | 40 | /** 41 | * The date format of the output. 42 | */ 43 | private static final String OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ"; 44 | 45 | /** 46 | * Convert the date to String ({@value #OUTPUT_DATE_TIME_FORMAT}). 47 | * 48 | * @param date 49 | * The Date to convert. 50 | * @return The result time. 51 | */ 52 | private static String dateToString(final Date date) { 53 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT); 54 | String dateString = simpleDateFormat.format(date); 55 | return dateString; 56 | } 57 | 58 | /** 59 | * Convert String ({@value #INPUT_DATE_FORMAT}) to Calendar. 60 | * 61 | * @param dateString 62 | * The String date to convert. 63 | * @return The result Date. 64 | * @throws ParseException 65 | * If can't parse the date. 66 | */ 67 | public static Calendar inputStringToCalendar(final String dateString) throws ParseException { 68 | DateFormat dateFormat = new SimpleDateFormat(INPUT_DATE_FORMAT); 69 | Calendar calendar = Calendar.getInstance(); 70 | calendar.setTime(dateFormat.parse(dateString)); 71 | return calendar; 72 | } 73 | 74 | /** 75 | * Set the calendar hour, minute and second value. 76 | * 77 | * @param originalCalendar 78 | * The original calendar. 79 | * @param hourOfDay 80 | * The hour of the day to set. 81 | * @param minute 82 | * The minute to set. 83 | * @param second 84 | * The second to set. 85 | * @return The new calendar object. 86 | */ 87 | public static Calendar setCalendarHourMinSec(final Calendar originalCalendar, 88 | final int hourOfDay, 89 | final int minute, final int second) { 90 | Calendar calendar = Calendar.getInstance(); 91 | calendar.set(Calendar.MILLISECOND, 0); 92 | calendar.set( 93 | originalCalendar.get(Calendar.YEAR), 94 | originalCalendar.get(Calendar.MONTH), 95 | originalCalendar.get(Calendar.DAY_OF_MONTH), 96 | hourOfDay, 97 | minute, 98 | second); 99 | return calendar; 100 | } 101 | 102 | /** 103 | * Format a String date to valid ISO-8601 format String date. 104 | * 105 | * @param dateString 106 | * The date. 107 | * @return The formated String date. 108 | * @throws ParseException 109 | * If cannot parse the String to Date. 110 | */ 111 | public static String stringDateToISO8601FormatString(final String dateString) 112 | throws ParseException { 113 | DateFormat dateFormat = new SimpleDateFormat(JIRA_OUTPUT_DATE_TIME_FORMAT); 114 | Date date = dateFormat.parse(dateString); 115 | return DateTimeConverterUtil.dateToString(date); 116 | } 117 | 118 | /** 119 | * Format a timestamp to valid ISO-8601 format String date. 120 | * 121 | * @param timestamp 122 | * The timestamp. 123 | * @return The formated String date. 124 | * @throws ParseException 125 | * If cannot parse the String to Date. 126 | */ 127 | public static String stringDateToISO8601FormatString(final Timestamp timestamp) { 128 | Date date = new Date(timestamp.getTime()); 129 | return DateTimeConverterUtil.dateToString(date); 130 | } 131 | 132 | /** 133 | * Private constructor. 134 | */ 135 | private DateTimeConverterUtil() { 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/FindWorklogsByIssuesParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.io.Serializable; 19 | import java.util.List; 20 | 21 | import com.atlassian.jira.rest.api.util.StringList; 22 | 23 | /** 24 | * FindWorklogsByIssues method parameter container class. 25 | */ 26 | public class FindWorklogsByIssuesParam implements Serializable { 27 | 28 | /** 29 | * Serial Version UID. 30 | */ 31 | private static final long serialVersionUID = -4947183929460600358L; 32 | /** 33 | * The query start date parameter. 34 | */ 35 | public String startDate; 36 | /** 37 | * The query end date parameter. 38 | */ 39 | public String endDate; 40 | /** 41 | * The query user parameter. 42 | */ 43 | public String user; 44 | /** 45 | * The query group parameter. 46 | */ 47 | public String group; 48 | /** 49 | * The query jql parameter. 50 | */ 51 | public String jql; 52 | /** 53 | * The query start At parameter. 54 | */ 55 | public int startAt; 56 | /** 57 | * The query max Result parameter. 58 | */ 59 | public int maxResults; 60 | /** 61 | * The query fields parameter. 62 | */ 63 | public List fields; 64 | 65 | public FindWorklogsByIssuesParam endDate(final String endDate) { 66 | this.endDate = endDate; 67 | return this; 68 | } 69 | 70 | public FindWorklogsByIssuesParam fields(final List fields) { 71 | this.fields = fields; 72 | return this; 73 | } 74 | 75 | public FindWorklogsByIssuesParam group(final String group) { 76 | this.group = group; 77 | return this; 78 | } 79 | 80 | public FindWorklogsByIssuesParam jql(final String jql) { 81 | this.jql = jql; 82 | return this; 83 | } 84 | 85 | public FindWorklogsByIssuesParam maxResults(final int maxResults) { 86 | this.maxResults = maxResults; 87 | return this; 88 | } 89 | 90 | private void readObject(final java.io.ObjectInputStream stream) throws java.io.IOException, 91 | ClassNotFoundException { 92 | stream.close(); 93 | throw new java.io.NotSerializableException(getClass().getName()); 94 | } 95 | 96 | public FindWorklogsByIssuesParam startAt(final int startAt) { 97 | this.startAt = startAt; 98 | return this; 99 | } 100 | 101 | public FindWorklogsByIssuesParam startDate(final String startDate) { 102 | this.startDate = startDate; 103 | return this; 104 | } 105 | 106 | public FindWorklogsByIssuesParam user(final String user) { 107 | this.user = user; 108 | return this; 109 | } 110 | 111 | private void writeObject(final java.io.ObjectOutputStream stream) throws java.io.IOException { 112 | stream.close(); 113 | throw new java.io.NotSerializableException(getClass().getName()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/IssueBeanWithTimespent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.net.URI; 19 | import java.net.URISyntaxException; 20 | 21 | import javax.xml.bind.annotation.XmlElement; 22 | 23 | import com.atlassian.jira.rest.v2.issue.IssueBean; 24 | 25 | /** 26 | * IssueBeanWithTimespent extends the original IssueBean class with spent time value. 27 | */ 28 | public class IssueBeanWithTimespent extends IssueBean { 29 | @XmlElement 30 | private Long timespent = 0L; 31 | 32 | public IssueBeanWithTimespent(final Long id, final String key, final String selfUri, 33 | final Long timespent) throws URISyntaxException { 34 | super(id, key, new URI(selfUri)); 35 | this.timespent = timespent; 36 | } 37 | 38 | public Long getTimeSpent() { 39 | return timespent; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/SearchResultsBeanWithTimespent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.util.List; 19 | 20 | import javax.xml.bind.annotation.XmlElement; 21 | import javax.xml.bind.annotation.XmlRootElement; 22 | 23 | import org.codehaus.jackson.map.annotate.JsonSerialize; 24 | 25 | import com.atlassian.jira.rest.v2.search.SearchResultsBean; 26 | 27 | /** 28 | * SearchResultsBeanWithTimespent extends the original SearchResultsBean class with issues 29 | * timespent. 30 | */ 31 | @XmlRootElement 32 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 33 | public class SearchResultsBeanWithTimespent extends SearchResultsBean { 34 | 35 | @XmlElement 36 | private List issues; 37 | 38 | /** 39 | * SearchResultsBeanWithTimespent constructor with fields. 40 | * 41 | * @param startAt 42 | * Start the result list from. 43 | * @param maxResults 44 | * Max number of results. 45 | * @param total 46 | * Total number of found result. 47 | * @param issues 48 | * List of the found issues. 49 | */ 50 | public SearchResultsBeanWithTimespent(final Integer startAt, final Integer maxResults, 51 | final Integer total, final List issues) { 52 | this.startAt = startAt; 53 | this.maxResults = maxResults; 54 | this.total = total; 55 | setIssues(issues); 56 | } 57 | 58 | @SuppressWarnings("unused") 59 | public List getIssues() { 60 | return issues; 61 | } 62 | 63 | public void setIssues(final List issues) { 64 | this.issues = issues; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/WorklogQueryCore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.util.List; 19 | 20 | import javax.ws.rs.core.Response; 21 | 22 | import com.atlassian.jira.rest.api.util.StringList; 23 | 24 | /** 25 | * The interface of the core part of the WorklogQueryResource class. 26 | */ 27 | public interface WorklogQueryCore { 28 | 29 | Response findUpdatedWorklogs(String startDate, String endDate, String user, String group, 30 | String project, List fields) throws WorklogQueryException; 31 | 32 | Response findWorklogs(String startDate, String endDate, String user, String group, 33 | String project, List fields) throws WorklogQueryException; 34 | 35 | SearchResultsBeanWithTimespent findWorklogsByIssues( 36 | FindWorklogsByIssuesParam findWorklogsByIssuesParam) 37 | throws WorklogQueryException; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/WorklogQueryCoreImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.text.ParseException; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.Calendar; 22 | import java.util.Collection; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Set; 27 | 28 | import javax.ws.rs.core.Response; 29 | 30 | import org.everit.jira.querydsl.support.QuerydslSupport; 31 | import org.everit.jira.querydsl.support.ri.QuerydslSupportImpl; 32 | import org.everit.jira.worklog.query.plugin.query.FindWorklogsByIssuesQuery; 33 | import org.everit.jira.worklog.query.plugin.query.FindWorklogsQuery; 34 | import org.everit.jira.worklog.query.plugin.query.JsonWorklog; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | import com.atlassian.jira.bc.issue.search.SearchService; 39 | import com.atlassian.jira.bc.issue.search.SearchService.ParseResult; 40 | import com.atlassian.jira.component.ComponentAccessor; 41 | import com.atlassian.jira.config.properties.APKeys; 42 | import com.atlassian.jira.issue.Issue; 43 | import com.atlassian.jira.issue.fields.Field; 44 | import com.atlassian.jira.issue.fields.FieldException; 45 | import com.atlassian.jira.issue.fields.NavigableField; 46 | import com.atlassian.jira.issue.fields.OrderableField; 47 | import com.atlassian.jira.issue.fields.ProjectSystemField; 48 | import com.atlassian.jira.issue.fields.layout.field.FieldLayout; 49 | import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem; 50 | import com.atlassian.jira.issue.fields.rest.FieldJsonRepresentation; 51 | import com.atlassian.jira.issue.fields.rest.RestAwareField; 52 | import com.atlassian.jira.issue.search.SearchException; 53 | import com.atlassian.jira.issue.search.SearchResults; 54 | import com.atlassian.jira.jql.parser.JqlParseException; 55 | import com.atlassian.jira.permission.ProjectPermissions; 56 | import com.atlassian.jira.project.Project; 57 | import com.atlassian.jira.rest.api.util.StringList; 58 | import com.atlassian.jira.rest.v2.issue.IncludedFields; 59 | import com.atlassian.jira.rest.v2.issue.IssueBean; 60 | import com.atlassian.jira.rest.v2.issue.RESTException; 61 | import com.atlassian.jira.security.JiraAuthenticationContext; 62 | import com.atlassian.jira.user.ApplicationUser; 63 | import com.atlassian.jira.util.collect.CollectionBuilder; 64 | import com.atlassian.jira.util.json.JSONArray; 65 | import com.atlassian.jira.web.bean.PagerFilter; 66 | 67 | /** 68 | * The implementations of the WorklogQueryCore. 69 | */ 70 | public class WorklogQueryCoreImpl implements WorklogQueryCore { 71 | 72 | private static final int DEFAULT_MAXRESULT_PARAM = 25; 73 | 74 | private static final int DEFAULT_STARTAT_PARAM = 0; 75 | 76 | /** 77 | * The last hour of a day. 78 | */ 79 | private static final int LAST_HOUR_OF_DAY = 23; 80 | 81 | /** 82 | * The last minute of an hour. 83 | */ 84 | private static final int LAST_MINUTE_OF_HOUR = 59; 85 | 86 | /** 87 | * The last second of a minute. 88 | */ 89 | private static final int LAST_SECOND_OF_MINUTE = 59; 90 | 91 | /** 92 | * The logger used to log. 93 | */ 94 | private static final Logger LOGGER = LoggerFactory.getLogger(WorklogQueryCoreImpl.class); 95 | 96 | private QuerydslSupport querydslSupport; 97 | 98 | /** 99 | * Simple constructor. Create {@link QuerydslSupport} instance. 100 | */ 101 | public WorklogQueryCoreImpl() { 102 | try { 103 | querydslSupport = new QuerydslSupportImpl(); 104 | } catch (Exception e) { 105 | throw new RuntimeException("Cannot create Worklog Query instance.", e); 106 | } 107 | } 108 | 109 | private void addFields(final Issue issue, final IssueBean bean) { 110 | // iterate over all the visible layout items from the field layout for this issue and attempt to 111 | // add them 112 | // to the result 113 | ApplicationUser loggedInUser = 114 | ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(); 115 | FieldLayout layout = ComponentAccessor.getFieldLayoutManager().getFieldLayout(issue); 116 | List fieldLayoutItems = 117 | layout.getVisibleLayoutItems(issue.getProjectObject(), 118 | CollectionBuilder.list(issue.getIssueType().getId())); 119 | for (FieldLayoutItem fieldLayoutItem : fieldLayoutItems) { 120 | OrderableField field = fieldLayoutItem.getOrderableField(); 121 | FieldJsonRepresentation fieldValue = getFieldValue(fieldLayoutItem, issue); 122 | if ((fieldValue != null) && (fieldValue.getStandardData() != null)) { 123 | bean.addField(field, fieldValue, false); 124 | } 125 | } 126 | // Then we try to add "NavigableFields" which aren't "OrderableFields" unless they ae special 127 | // ones. 128 | // These aren't included in the Field Layout. 129 | // This is a bit crap because "getAvailableNavigableFields" doesn't take the issue into account. 130 | // All it means is the field is not hidden in at least one project the user has BROWSE 131 | // permission on. 132 | try { 133 | Set fields = ComponentAccessor.getFieldManager() 134 | .getAvailableNavigableFields(loggedInUser); 135 | for (NavigableField field : fields) { 136 | if (!bean.hasField(field.getId()) 137 | && (!(field instanceof OrderableField) || (field instanceof ProjectSystemField)) 138 | && (field instanceof RestAwareField)) { 139 | addRestAwareField(issue, bean, field, (RestAwareField) field); 140 | } 141 | } 142 | } catch (FieldException e) { 143 | // ignored...display as much as we can. 144 | } 145 | 146 | } 147 | 148 | private void addFieldsToIssueBeans(final List fields, 149 | final Map issueIdIssue, final List issueBeans) { 150 | IncludedFields includedFields = IncludedFields.includeNavigableByDefault(fields); 151 | boolean isEmptyField = StringList.joinLists(fields) 152 | .asList().contains("emptyFieldValue"); 153 | for (IssueBeanWithTimespent issueBean : issueBeans) { 154 | issueBean.fieldsToInclude(includedFields); 155 | if (!isEmptyField) { 156 | addFields(issueIdIssue.get(Long.valueOf(issueBean.getId())), issueBean); 157 | } 158 | } 159 | } 160 | 161 | private void addRestAwareField(final Issue issue, final IssueBean bean, final Field field, 162 | final RestAwareField restAware) { 163 | FieldJsonRepresentation fieldJsonFromIssue = restAware.getJsonFromIssue(issue, false, null); 164 | if ((fieldJsonFromIssue != null) && (fieldJsonFromIssue.getStandardData() != null)) { 165 | bean.addField(field, fieldJsonFromIssue, false); 166 | } 167 | } 168 | 169 | /** 170 | * Check the required (or optional) parameters. If any parameter missing or conflict return with 171 | * the right Response what describes the problem. If everything is right then return with null. 172 | * 173 | * @param startDate 174 | * The startDate parameter. 175 | * @param endDate 176 | * The endDate parameter. 177 | * @param user 178 | * The user parameter. 179 | * @param group 180 | * The group parameter. 181 | * 182 | * @return If a bad parameter was found then return with Response else null. 183 | */ 184 | private void checkRequiredFindWorklogsByIssuesParameter(final String startDate, 185 | final String endDate, 186 | final String user, final String group) { 187 | if (isStringEmpty(startDate)) { 188 | throw new RESTException(Response.Status.BAD_REQUEST, "The 'startDate' parameter is missing!"); 189 | } 190 | if (isStringEmpty(endDate)) { 191 | throw new RESTException(Response.Status.BAD_REQUEST, "The 'endDate' parameter is missing!"); 192 | } 193 | if ((isStringEmpty(user)) && (isStringEmpty(group))) { 194 | throw new RESTException(Response.Status.BAD_REQUEST, 195 | "The 'user' or the 'group' parameter is missing!"); 196 | } 197 | if ((!isStringEmpty(user)) && (!isStringEmpty(group))) { 198 | throw new RESTException(Response.Status.BAD_REQUEST, 199 | "The 'user' and the 'group' parameters cannot be present at the same time."); 200 | } 201 | } 202 | 203 | /** 204 | * Check the required (or optional) parameters. If any parameter missing or conflict return with 205 | * the right Response what describes the problem. If everything is right then return with null. 206 | * 207 | * @param startDate 208 | * The findWorklogs startDate parameter. 209 | * @param user 210 | * The findWorklogs user parameter. 211 | * @param group 212 | * The findWorklogs group parameter. 213 | * @return If find bad parameter then return with Response else null. 214 | */ 215 | private Response checkRequiredFindWorklogsParameter(final String startDate, final String user, 216 | final String group) { 217 | if (isStringEmpty(startDate)) { 218 | return Response.status(Response.Status.BAD_REQUEST) 219 | .entity("The 'startDate' parameter is missing!").build(); 220 | } 221 | if ((isStringEmpty(user)) && (isStringEmpty(group))) { 222 | return Response.status(Response.Status.BAD_REQUEST) 223 | .entity("The 'user' or the 'group' parameter is missing!").build(); 224 | } 225 | if ((!isStringEmpty(user)) && (!isStringEmpty(group))) { 226 | return Response.status(Response.Status.BAD_REQUEST) 227 | .entity("The 'user' and the 'group' parameters cannot be present at the same time.") 228 | .build(); 229 | } 230 | return null; 231 | } 232 | 233 | private Map collectIssueIds(final List issues) { 234 | Map result = new HashMap<>(); 235 | for (Issue issue : issues) { 236 | result.put(issue.getId(), issue); 237 | } 238 | return result; 239 | } 240 | 241 | /** 242 | * Convert the endDate String to Calendar. 243 | * 244 | * @param endDateString 245 | * The endDate parameter. 246 | * @return The formated, valid calendar. 247 | * @throws ParseException 248 | * If cannot parse the String to Calendar. 249 | */ 250 | private Calendar convertEndDate(final String endDateString) throws WorklogQueryException { 251 | Calendar endDate; 252 | if ((endDateString == null) || (endDateString.length() == 0)) { 253 | endDate = Calendar.getInstance(); 254 | } else { 255 | try { 256 | endDate = DateTimeConverterUtil.inputStringToCalendar(endDateString); 257 | } catch (ParseException e) { 258 | LOGGER.error("Failed to convert end date", e); 259 | throw new WorklogQueryException("Cannot parse the 'endDate' parameter: " + endDateString, 260 | e); 261 | } 262 | } 263 | endDate = DateTimeConverterUtil.setCalendarHourMinSec(endDate, 264 | LAST_HOUR_OF_DAY, LAST_MINUTE_OF_HOUR, LAST_SECOND_OF_MINUTE); 265 | return endDate; 266 | } 267 | 268 | /** 269 | * Convert the startDate String to Calendar. 270 | * 271 | * @param startDateString 272 | * The startDate parameter. 273 | * @return The formated, valid calendar. 274 | * @throws ParseException 275 | * Id cannot parse the String to Calendar. 276 | */ 277 | private Calendar convertStartDate(final String startDateString) throws WorklogQueryException { 278 | Calendar startDate; 279 | try { 280 | startDate = DateTimeConverterUtil.inputStringToCalendar(startDateString); 281 | } catch (ParseException e) { 282 | LOGGER.error("Failed to convert start date", e); 283 | throw new WorklogQueryException("Cannot parse the 'startDate' parameter: " + startDateString, 284 | e); 285 | } 286 | startDate = DateTimeConverterUtil.setCalendarHourMinSec(startDate, 0, 0, 0); 287 | return startDate; 288 | } 289 | 290 | /** 291 | * Creates a list of project Id's. Filtering based on project permission and the query 292 | * projectString parameter. 293 | * 294 | * @param projectString 295 | * The query projectString parameter. 296 | * @param user 297 | * The logged user. 298 | * 299 | * @return The list of the issues conditions. 300 | */ 301 | private List createProjects(final String projectString, final ApplicationUser user) { 302 | 303 | Collection projects = ComponentAccessor.getPermissionManager() 304 | .getProjects(ProjectPermissions.BROWSE_PROJECTS, user); 305 | 306 | List projectList = new ArrayList<>(); 307 | for (Project project : projects) { 308 | if ((projectString != null) && (projectString.length() != 0)) { 309 | if (projectString.equals(project.getKey())) { 310 | projectList.add(project.getId()); 311 | } 312 | } else { 313 | projectList.add(project.getId()); 314 | } 315 | } 316 | return projectList; 317 | } 318 | 319 | private List createUsers(final String userName, final String group) { 320 | List users = new ArrayList<>(); 321 | if ((group != null) && (group.length() != 0)) { 322 | Set groupUsers = ComponentAccessor.getUserUtil() 323 | .getAllUsersInGroupNames( 324 | Arrays.asList(new String[] { group })); 325 | for (ApplicationUser groupUser : groupUsers) { 326 | users.add(groupUser.getKey()); 327 | } 328 | } else if ((userName != null) && (userName.length() != 0)) { 329 | ApplicationUser user = ComponentAccessor.getUserManager().getUserByName(userName); 330 | if (user != null) { 331 | users.add(user.getKey()); 332 | } 333 | } 334 | return users; 335 | } 336 | 337 | /** 338 | * The findUpdatedWorklogs REST method core implementation. 339 | * 340 | * @param startDate 341 | * The start Date parameter of the REST. 342 | * @param endDate 343 | * The end Date parameter of the REST. 344 | * @param user 345 | * The user parameter of the REST. 346 | * @param group 347 | * The group parameter of the REST. 348 | * @param project 349 | * The project parameter of the REST. 350 | * @param fields 351 | * The fields parameter of the REST. 352 | * @return The founded worklogs. 353 | * 354 | */ 355 | @Override 356 | public Response findUpdatedWorklogs(final String startDate, final String endDate, 357 | final String user, final String group, 358 | final String project, final List fields) throws WorklogQueryException { 359 | Response checkRequiredFindWorklogsParamResponse = checkRequiredFindWorklogsParameter(startDate, 360 | user, group); 361 | if (checkRequiredFindWorklogsParamResponse != null) { 362 | return checkRequiredFindWorklogsParamResponse; 363 | } 364 | Calendar startDateCalendar = convertStartDate(startDate); 365 | Calendar endDateCalendar = convertEndDate(endDate); 366 | try { 367 | return worklogQuery(startDateCalendar, endDateCalendar, user, group, project, fields, true); 368 | } catch (Exception e) { 369 | LOGGER.error("Failed to query the worklogs", e); 370 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR) 371 | .entity(e.getMessage()).build(); 372 | } 373 | } 374 | 375 | /** 376 | * The findWorklogs REST method core implementation. 377 | * 378 | * @param startDate 379 | * The start Date parameter of the REST. 380 | * @param endDate 381 | * The end Date parameter of the REST. 382 | * @param user 383 | * The user parameter of the REST. 384 | * @param group 385 | * The group parameter of the REST. 386 | * @param project 387 | * The project parameter of the REST. 388 | * @param fields 389 | * The fields parameter of the REST. 390 | * @return The founded worklogs. 391 | */ 392 | @Override 393 | public Response findWorklogs(final String startDate, final String endDate, final String user, 394 | final String group, final String project, final List fields) 395 | throws WorklogQueryException { 396 | Response checkRequiredFindWorklogsParamResponse = checkRequiredFindWorklogsParameter(startDate, 397 | user, group); 398 | if (checkRequiredFindWorklogsParamResponse != null) { 399 | return checkRequiredFindWorklogsParamResponse; 400 | } 401 | Calendar startDateCalendar = convertStartDate(startDate); 402 | Calendar endDateCalendar = convertEndDate(endDate); 403 | try { 404 | return worklogQuery(startDateCalendar, endDateCalendar, user, group, project, fields, false); 405 | } catch (Exception e) { 406 | LOGGER.error("Failed to query the worklogs", e); 407 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR) 408 | .entity(e.getMessage()).build(); 409 | } 410 | } 411 | 412 | /** 413 | * 414 | * The findWorklogsByIssues REST method core implementation. 415 | * 416 | * @param findWorklogsByIssuesParam 417 | * The parameters object of the findWorklogsByIssues method parameters. 418 | * @return The search result. 419 | */ 420 | @Override 421 | public SearchResultsBeanWithTimespent findWorklogsByIssues( 422 | final FindWorklogsByIssuesParam findWorklogsByIssuesParam) 423 | throws WorklogQueryException { 424 | int tmpStartAt = findWorklogsByIssuesParam.startAt; 425 | int tmpMaxResults = findWorklogsByIssuesParam.maxResults; 426 | checkRequiredFindWorklogsByIssuesParameter(findWorklogsByIssuesParam.startDate, 427 | findWorklogsByIssuesParam.endDate, findWorklogsByIssuesParam.user, 428 | findWorklogsByIssuesParam.group); 429 | 430 | Calendar startDateCalendar = convertStartDate(findWorklogsByIssuesParam.startDate); 431 | Calendar endDateCalendar = convertEndDate(findWorklogsByIssuesParam.endDate); 432 | if (tmpStartAt < 0) { 433 | tmpStartAt = DEFAULT_STARTAT_PARAM; 434 | } 435 | if (tmpMaxResults < 0) { 436 | tmpMaxResults = DEFAULT_MAXRESULT_PARAM; 437 | } 438 | List users = 439 | createUsers(findWorklogsByIssuesParam.user, findWorklogsByIssuesParam.group); 440 | if (users.isEmpty()) { 441 | throw new WorklogQueryException( 442 | "Error running search: There is no group or user matching the given parameters."); 443 | } 444 | List issues = null; 445 | try { 446 | issues = getIssuesByJQL(findWorklogsByIssuesParam.jql); 447 | } catch (SearchException e) { 448 | LOGGER.error("Failed to query the worklogs", e); 449 | throw new WorklogQueryException("Error running search: ", e); 450 | } catch (JqlParseException e) { 451 | LOGGER.error("Failed to parse the JQL", e); 452 | throw new WorklogQueryException(e.getMessage(), e); 453 | } 454 | 455 | Map issueIdIssue = collectIssueIds(issues); 456 | 457 | List issueBeans = null; 458 | try { 459 | String jiraBaseUrl = ComponentAccessor.getApplicationProperties() 460 | .getString(APKeys.JIRA_BASEURL) + "/rest/api/2/issue/"; 461 | issueBeans = querydslSupport.execute(new FindWorklogsByIssuesQuery(startDateCalendar, 462 | endDateCalendar, users, issueIdIssue.keySet(), tmpStartAt, tmpMaxResults, jiraBaseUrl)); 463 | 464 | addFieldsToIssueBeans(findWorklogsByIssuesParam.fields, issueIdIssue, issueBeans); 465 | } catch (Exception e) { 466 | LOGGER.error("Error when try collectig issue beans.", e); 467 | throw new WorklogQueryException("Error when try collectig issue beans.", e); 468 | } 469 | SearchResultsBeanWithTimespent searchResultsBean = 470 | new SearchResultsBeanWithTimespent(tmpStartAt, tmpMaxResults, issueBeans.size(), 471 | issueBeans); 472 | 473 | return searchResultsBean; 474 | } 475 | 476 | private FieldJsonRepresentation getFieldValue(final FieldLayoutItem fieldLayoutItem, 477 | final Issue issue) { 478 | OrderableField field = fieldLayoutItem.getOrderableField(); 479 | 480 | if (field instanceof RestAwareField) { 481 | RestAwareField restAware = (RestAwareField) field; 482 | return restAware.getJsonFromIssue(issue, false, fieldLayoutItem); 483 | } else { 484 | return null; 485 | } 486 | } 487 | 488 | /** 489 | * Returns the selected issues based on the given JQL filter. 490 | * 491 | * @param jql 492 | * JQL filter the search is based on. 493 | * @return List of the matching JIRA Issues. 494 | * @throws SearchException 495 | * Atlassian Search Service excaption. 496 | * @throws JqlParseException 497 | * Thrown when the given JQL is not valid. 498 | */ 499 | private List getIssuesByJQL(final String jql) 500 | throws SearchException, 501 | JqlParseException { 502 | JiraAuthenticationContext authenticationContext = ComponentAccessor 503 | .getJiraAuthenticationContext(); 504 | ApplicationUser loggedInUser = authenticationContext.getLoggedInUser(); 505 | List issues = null; 506 | SearchService searchService = ComponentAccessor.getComponentOfType(SearchService.class); 507 | ParseResult parseResult = searchService.parseQuery(loggedInUser, jql); 508 | if (parseResult.isValid()) { 509 | SearchResults results = searchService.search(loggedInUser, 510 | parseResult.getQuery(), PagerFilter.getUnlimitedFilter()); 511 | issues = results.getResults(); 512 | } else { 513 | throw new JqlParseException(null, parseResult.getErrors().toString()); 514 | } 515 | return issues; 516 | } 517 | 518 | /** 519 | * Check the given String is empty. 520 | * 521 | * @param theString 522 | * The String variable. 523 | * @return If the String is null or the String length equals whit 0 then true, else false. 524 | */ 525 | private boolean isStringEmpty(final String theString) { 526 | if ((theString == null) || (theString.length() == 0)) { 527 | return true; 528 | } 529 | return false; 530 | } 531 | 532 | /** 533 | * The method to query worklogs. 534 | * 535 | * @param startDate 536 | * The startDate calendar parameter. 537 | * @param endDate 538 | * The endDate calendar parameter. 539 | * @param userString 540 | * The user String parameter. 541 | * @param groupString 542 | * The group String parameter. 543 | * @param projectString 544 | * The project String parameter. 545 | * @param updated 546 | * True if the method give back the worklogs which were created or updated in the given 547 | * period, else false. The false give back the worklogs of the period. 548 | * @return JSONString what contains a list of queried worklogs. 549 | */ 550 | private Response worklogQuery(final Calendar startDate, final Calendar endDate, 551 | final String userString, final String groupString, final String projectString, 552 | final List fields, final boolean updated) { 553 | 554 | JiraAuthenticationContext authenticationContext = ComponentAccessor 555 | .getJiraAuthenticationContext(); 556 | ApplicationUser loggedInUser = authenticationContext.getLoggedInUser(); 557 | 558 | List projects = createProjects(projectString, loggedInUser); 559 | if ((projectString != null) && projects.isEmpty()) { 560 | return Response 561 | .status(Response.Status.BAD_REQUEST) 562 | .entity( 563 | "Error running search: There is no project matching the given 'project' parameter: " 564 | + projectString) 565 | .build(); 566 | } 567 | 568 | List users = createUsers(userString, groupString); 569 | if (users.isEmpty()) { 570 | return Response.status(Response.Status.BAD_REQUEST) 571 | .entity("Error running search: There is no group or user matching the given parameters.") 572 | .build(); 573 | } 574 | 575 | List jsonWorklogs = 576 | querydslSupport.execute(new FindWorklogsQuery(startDate, endDate, fields, 577 | users, projects, updated)); 578 | JSONArray jsonArrayResult = new JSONArray(); 579 | jsonArrayResult.put(jsonWorklogs); 580 | 581 | return Response.ok(jsonArrayResult.toString()).build(); 582 | } 583 | 584 | } 585 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/WorklogQueryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | /** 19 | * The exception to handle Worklog Query Plugin exceptions. 20 | */ 21 | public class WorklogQueryException extends RuntimeException { 22 | 23 | /** 24 | * The generated serial version UID. 25 | */ 26 | private static final long serialVersionUID = -3704417971282723535L; 27 | 28 | /** 29 | * The simple constructor. 30 | * 31 | * @param msg 32 | * the detail message. 33 | */ 34 | protected WorklogQueryException(final String msg) { 35 | super(msg); 36 | } 37 | 38 | /** 39 | * The simple constructor. 40 | * 41 | * @param msg 42 | * the detail message. 43 | * @param cause 44 | * the cause. 45 | */ 46 | protected WorklogQueryException(final String msg, final Throwable cause) { 47 | super(msg, cause); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/WorklogQueryResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin; 17 | 18 | import java.util.List; 19 | 20 | import javax.ws.rs.DefaultValue; 21 | import javax.ws.rs.GET; 22 | import javax.ws.rs.Path; 23 | import javax.ws.rs.Produces; 24 | import javax.ws.rs.QueryParam; 25 | import javax.ws.rs.core.MediaType; 26 | import javax.ws.rs.core.Response; 27 | 28 | import com.atlassian.jira.rest.api.util.StringList; 29 | 30 | /** 31 | * The WorklogQueryResource class. The class contains the findWorklogs method. The class grant the 32 | * JIRA worklog query. 33 | * 34 | */ 35 | @Path("/find") 36 | public class WorklogQueryResource { 37 | 38 | private final WorklogQueryCore worklogQueryResource = new WorklogQueryCoreImpl(); 39 | 40 | /** 41 | * The updatedWorklogs restful api method. 42 | * 43 | * @param startDate 44 | * The query startDate parameter. 45 | * @param endDate 46 | * The query endDate parameter, optional. Default value is the current time. 47 | * @param user 48 | * The query user parameter, optional. This or the group parameter is required. 49 | * @param group 50 | * The query group parameter, optional. This or the user parameter is required. 51 | * @param project 52 | * The query project parameter, optional. Default is all project. 53 | * @return {@link Response} what contains the result of the query. If the method parameters was 54 | * wrong then a message what contains the description of the bad request. In case of any 55 | * exception return {@link Response} with INTERNAL_SERVER_ERROR status what contains the 56 | * original exception message. 57 | */ 58 | @GET 59 | @Produces("*/*") 60 | @Path("/updatedWorklogs") 61 | public Response findUpdatedWorklogs( 62 | @QueryParam("startDate") final String startDate, 63 | @QueryParam("endDate") final String endDate, 64 | @QueryParam("user") final String user, 65 | @QueryParam("group") final String group, 66 | @QueryParam("project") final String project, 67 | @QueryParam("fields") final List fields) { 68 | try { 69 | return worklogQueryResource.findUpdatedWorklogs(startDate, endDate, user, group, project, 70 | fields); 71 | } catch (WorklogQueryException e) { 72 | return Response.status(Response.Status.BAD_REQUEST) 73 | .entity(e.getMessage()).build(); 74 | } 75 | } 76 | 77 | /** 78 | * The worklogs restful api method. 79 | * 80 | * @param startDate 81 | * The query startDate parameter. 82 | * @param endDate 83 | * The query endDate parameter, optional. Default value is the current time. 84 | * @param user 85 | * The query user parameter, optional. This or the group parameter is required. 86 | * @param group 87 | * The query group parameter, optional. This or the user parameter is required. 88 | * @param project 89 | * The query project parameter, optional. Default is all project. 90 | * @return {@link Response} what contains the result of the query. If the method parameters was 91 | * wrong then a message what contains the description of the bad request. In case of any 92 | * exception return {@link Response} with INTERNAL_SERVER_ERROR status what contains the 93 | * original exception message. 94 | */ 95 | @GET 96 | @Produces({ MediaType.APPLICATION_JSON }) 97 | @Path("/worklogs") 98 | public Response findWorklogs( 99 | @QueryParam("startDate") final String startDate, 100 | @QueryParam("endDate") final String endDate, 101 | @QueryParam("user") final String user, 102 | @QueryParam("group") final String group, 103 | @QueryParam("project") final String project, 104 | @QueryParam("fields") final List fields) { 105 | try { 106 | return worklogQueryResource.findWorklogs(startDate, endDate, user, group, project, fields); 107 | } catch (WorklogQueryException e) { 108 | return Response.status(Response.Status.BAD_REQUEST) 109 | .entity(e.getMessage()).build(); 110 | } 111 | } 112 | 113 | /** 114 | * FindWorklogsByIssues REST method. 115 | * 116 | * @param startDate 117 | * The query start date. 118 | * @param endDate 119 | * The query end date. 120 | * @param user 121 | * The searched user. Optional. 122 | * @param group 123 | * The searched group. Optional. 124 | * @param jql 125 | * Plus jql. Default empty String. 126 | * @param startAt 127 | * Start the query result list from this element. Default 0. 128 | * @param maxResults 129 | * Max number of results. Default 25. 130 | * @param fields 131 | * List of the queried fields. 132 | * @return The found worklogs. 133 | */ 134 | @GET 135 | @Path("/worklogsByIssues") 136 | @Produces({ MediaType.APPLICATION_JSON }) 137 | public Response findWorklogsByIssues( 138 | @QueryParam("startDate") final String startDate, 139 | @QueryParam("endDate") final String endDate, 140 | @QueryParam("user") final String user, 141 | @QueryParam("group") final String group, 142 | @DefaultValue("") @QueryParam("jql") final String jql, 143 | @DefaultValue("0") @QueryParam("startAt") final int startAt, 144 | @DefaultValue("25") @QueryParam("maxResults") final int maxResults, 145 | @DefaultValue("emptyFieldValue") @QueryParam("fields") final List fields) { 146 | FindWorklogsByIssuesParam findWorklogsByIssuesParam = 147 | new FindWorklogsByIssuesParam() 148 | .startDate(startDate) 149 | .endDate(endDate) 150 | .user(user) 151 | .group(group) 152 | .jql(jql) 153 | .startAt(startAt) 154 | .maxResults(maxResults) 155 | .fields(fields); 156 | try { 157 | return Response.ok(worklogQueryResource.findWorklogsByIssues(findWorklogsByIssuesParam)) 158 | .build(); 159 | } catch (WorklogQueryException e) { 160 | return Response.status(Response.Status.BAD_REQUEST) 161 | .entity(e.getMessage()).build(); 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/query/FindWorklogsByIssuesQuery.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin.query; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | import java.sql.Timestamp; 21 | import java.util.Calendar; 22 | import java.util.List; 23 | import java.util.Set; 24 | 25 | import org.everit.jira.querydsl.schema.QJiraissue; 26 | import org.everit.jira.querydsl.schema.QProject; 27 | import org.everit.jira.querydsl.schema.QWorklog; 28 | import org.everit.jira.querydsl.support.QuerydslCallable; 29 | import org.everit.jira.worklog.query.plugin.IssueBeanWithTimespent; 30 | 31 | import com.querydsl.core.types.Expression; 32 | import com.querydsl.core.types.Projections; 33 | import com.querydsl.core.types.dsl.Expressions; 34 | import com.querydsl.core.types.dsl.SimpleExpression; 35 | import com.querydsl.core.types.dsl.StringExpression; 36 | import com.querydsl.core.types.dsl.StringExpressions; 37 | import com.querydsl.sql.Configuration; 38 | import com.querydsl.sql.SQLExpressions; 39 | import com.querydsl.sql.SQLQuery; 40 | 41 | /** 42 | * Query to find worklogs by issues. 43 | */ 44 | public class FindWorklogsByIssuesQuery implements QuerydslCallable> { 45 | 46 | private final Calendar endDate; 47 | 48 | private final Set issueIds; 49 | 50 | private final String jiraBaseUrl; 51 | 52 | private final long limit; 53 | 54 | private final long offset; 55 | 56 | private final Calendar startDate; 57 | 58 | private final List userKeys; 59 | 60 | /** 61 | * Simple constructor. 62 | * 63 | * @param startDate 64 | * the start date of worklogs. 65 | * @param endDate 66 | * the end date of worklogs 67 | * @param userKeys 68 | * a list of user keys. 69 | * @param issueIds 70 | * a collection of user ids. 71 | * @param offset 72 | * the offset for the query results. 73 | * @param limit 74 | * the limit / max results for the query results. 75 | * @param jiraBaseUrl 76 | * the JIRA base url. 77 | */ 78 | public FindWorklogsByIssuesQuery(final Calendar startDate, final Calendar endDate, 79 | final List userKeys, final Set issueIds, final long offset, final long limit, 80 | final String jiraBaseUrl) { 81 | this.endDate = endDate; 82 | this.startDate = startDate; 83 | this.userKeys = userKeys; 84 | this.issueIds = issueIds; 85 | this.offset = offset; 86 | this.limit = limit; 87 | this.jiraBaseUrl = jiraBaseUrl; 88 | } 89 | 90 | @Override 91 | public List call(final Connection connection, 92 | final Configuration configuration) 93 | throws SQLException { 94 | QWorklog worklog = new QWorklog("worklog"); 95 | QJiraissue issue = new QJiraissue("issue"); 96 | QProject project = new QProject("project"); 97 | 98 | Timestamp startTimestamp = new Timestamp(startDate.getTimeInMillis()); 99 | Timestamp endTimestamp = new Timestamp(endDate.getTimeInMillis()); 100 | 101 | StringExpression issueKey = project.pkey.concat("-").concat(issue.issuenum.stringValue()); 102 | SimpleExpression timeworked = SQLExpressions.sum(worklog.timeworked).as("timeworked"); 103 | Expression jiraBaseUrlExpression = Expressions.constant(jiraBaseUrl); 104 | StringExpression jiraBaseUrlStringExpression = StringExpressions.ltrim(jiraBaseUrlExpression); 105 | 106 | StringExpression concat = jiraBaseUrlStringExpression.concat(issue.id.stringValue()); 107 | return new SQLQuery>(connection, configuration) 108 | .select(Projections.constructor(IssueBeanWithTimespent.class, 109 | issue.id, 110 | issueKey, 111 | concat, 112 | timeworked)) 113 | .from(worklog) 114 | .join(issue).on(issue.id.eq(worklog.issueid)) 115 | .join(project).on(project.id.eq(issue.project)) 116 | .where(worklog.startdate.goe(startTimestamp) 117 | .and(worklog.startdate.lt(endTimestamp)) 118 | .and(worklog.author.in(userKeys)) 119 | .and(worklog.issueid.in(issueIds))) 120 | .groupBy(issue.id, project.pkey, issue.issuenum) 121 | .offset(offset) 122 | .limit(limit) 123 | .orderBy(issue.id.asc()) 124 | .fetch(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/query/FindWorklogsQuery.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin.query; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | import java.sql.Timestamp; 21 | import java.util.Arrays; 22 | import java.util.Calendar; 23 | import java.util.List; 24 | 25 | import org.everit.jira.querydsl.schema.QAppUser; 26 | import org.everit.jira.querydsl.schema.QCwdUser; 27 | import org.everit.jira.querydsl.schema.QJiraissue; 28 | import org.everit.jira.querydsl.schema.QProject; 29 | import org.everit.jira.querydsl.schema.QWorklog; 30 | import org.everit.jira.querydsl.support.QuerydslCallable; 31 | 32 | import com.atlassian.jira.rest.api.util.StringList; 33 | import com.querydsl.core.types.dsl.BooleanExpression; 34 | import com.querydsl.core.types.dsl.StringExpression; 35 | import com.querydsl.sql.Configuration; 36 | import com.querydsl.sql.SQLExpressions; 37 | import com.querydsl.sql.SQLQuery; 38 | 39 | /** 40 | * Query to find worklogs. 41 | */ 42 | public class FindWorklogsQuery implements QuerydslCallable> { 43 | 44 | private final Calendar endDate; 45 | 46 | private final List fields; 47 | 48 | private List projectIds; 49 | 50 | private final Calendar startDate; 51 | 52 | private final boolean updated; 53 | 54 | private final List userKeys; 55 | 56 | /** 57 | * Simple constructor. 58 | * 59 | * @param startDate 60 | * the start date of worklogs. 61 | * @param endDate 62 | * the end date of worklogs 63 | * @param fields 64 | * a list of additional fields. 65 | * @param userKeys 66 | * a list of user keys. 67 | * @param projectIds 68 | * a list of project ids. 69 | * @param updated 70 | * True if the method give back the worklogs which were created or updated in the given 71 | * period, else false. The false give back the worklogs of the period. 72 | */ 73 | public FindWorklogsQuery(final Calendar startDate, final Calendar endDate, 74 | final List fields, final List userKeys, final List projectIds, 75 | final boolean updated) { 76 | this.startDate = startDate; 77 | this.endDate = endDate; 78 | this.fields = fields; 79 | this.userKeys = userKeys; 80 | this.projectIds = projectIds; 81 | this.updated = updated; 82 | } 83 | 84 | @Override 85 | public List call(final Connection connection, final Configuration configuration) 86 | throws SQLException { 87 | QWorklog worklog = new QWorklog("worklog"); 88 | QJiraissue issue = new QJiraissue("issue"); 89 | QProject project = new QProject("project"); 90 | QCwdUser cwduser = new QCwdUser("cwd_user"); 91 | QAppUser appuser = new QAppUser("app_user"); 92 | 93 | StringExpression issueKey = project.pkey.concat("-").concat(issue.issuenum.stringValue()); 94 | 95 | Timestamp startTimestamp = new Timestamp(startDate.getTimeInMillis()); 96 | Timestamp endTimestamp = new Timestamp(endDate.getTimeInMillis()); 97 | 98 | List fieldsAsList = 99 | Arrays.asList(StringList.joinLists(fields).toQueryParam().split(",")); 100 | final boolean useComment = fieldsAsList.contains("comment"); 101 | final boolean useUpdated = fieldsAsList.contains("updated"); 102 | 103 | BooleanExpression intervalPredicate = null; 104 | if (updated) { 105 | intervalPredicate = worklog.updated.goe(startTimestamp) 106 | .and(worklog.updated.lt(endTimestamp)); 107 | } else { 108 | intervalPredicate = worklog.startdate.goe(startTimestamp) 109 | .and(worklog.startdate.lt(endTimestamp)); 110 | } 111 | 112 | return new SQLQuery(connection, configuration) 113 | .select(JsonWorklog.createProjection(worklog.id, 114 | worklog.startdate, 115 | issueKey, 116 | SQLExpressions.select(cwduser.userName) 117 | .from(cwduser) 118 | .join(appuser).on(cwduser.lowerUserName.eq(appuser.lowerUserName)) 119 | .where(appuser.userKey.eq(worklog.author)) 120 | .distinct(), 121 | worklog.timeworked, 122 | useComment ? worklog.worklogbody : null, 123 | useUpdated ? worklog.updated : null)) 124 | .from(worklog) 125 | .join(issue).on(issue.id.eq(worklog.issueid)) 126 | .join(project).on(project.id.eq(issue.project)) 127 | .where(intervalPredicate 128 | .and(worklog.author.in(userKeys)) 129 | .and(issue.project.in(projectIds))) 130 | .orderBy(worklog.id.asc()) 131 | .fetch(); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/everit/jira/worklog/query/plugin/query/JsonWorklog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.plugin.query; 17 | 18 | import java.sql.Timestamp; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.everit.jira.worklog.query.plugin.DateTimeConverterUtil; 23 | 24 | import com.atlassian.jira.util.json.JSONException; 25 | import com.atlassian.jira.util.json.JSONObject; 26 | import com.querydsl.core.types.Projections; 27 | import com.querydsl.core.types.QBean; 28 | import com.querydsl.core.types.dsl.DateTimeExpression; 29 | import com.querydsl.core.types.dsl.NumberExpression; 30 | import com.querydsl.core.types.dsl.SimpleExpression; 31 | import com.querydsl.core.types.dsl.StringExpression; 32 | import com.querydsl.sql.SQLQuery; 33 | 34 | /** 35 | * JsonWorklog is an unordered collection of name/value pairs. Contains information of worklog. 36 | */ 37 | public class JsonWorklog extends JSONObject { 38 | 39 | private static final String COMMENT = "commentBody"; 40 | 41 | private static final String DURATION = "duration"; 42 | 43 | private static final String ID = "id"; 44 | 45 | private static final String ISSUE_KEY = "issueKey"; 46 | 47 | private static final String START_DATE = "startDate"; 48 | 49 | private static final String UPDATED = "updated"; 50 | 51 | private static final String USER_ID = "userId"; 52 | 53 | /** 54 | * Create a JsonWorklog Bean populating projection for the given type and expressions. 55 | * 56 | * @param worklogId 57 | * the id of the worklog expression. 58 | * @param startDate 59 | * the worklog startdate expression. 60 | * @param issueKey 61 | * the issue key expression. 62 | * @param userId 63 | * the user id SQL subquery. 64 | * @param duration 65 | * the worklog duration expression. 66 | * @param comment 67 | * the worklog comment expression. 68 | * @param updated 69 | * the worklog updated date expression. 70 | * @return the JsonWorklog Bean population projection. 71 | */ 72 | public static QBean createProjection(final NumberExpression worklogId, 73 | final DateTimeExpression startDate, final StringExpression issueKey, 74 | final SQLQuery userId, final NumberExpression duration, 75 | final StringExpression comment, final DateTimeExpression updated) { 76 | 77 | List> expressionList = new ArrayList>(); 78 | expressionList.add(worklogId.as(ID)); 79 | expressionList.add(startDate.as(START_DATE)); 80 | expressionList.add(issueKey.as(ISSUE_KEY)); 81 | expressionList.add(userId.as(USER_ID)); 82 | expressionList.add(duration.as(DURATION)); 83 | if (comment != null) { 84 | expressionList.add(comment.as(COMMENT)); 85 | } 86 | if (updated != null) { 87 | expressionList.add(updated.as(UPDATED)); 88 | } 89 | 90 | SimpleExpression[] expressions = new SimpleExpression[expressionList.size()]; 91 | 92 | return Projections.bean(JsonWorklog.class, expressionList.toArray(expressions)); 93 | } 94 | 95 | /** 96 | * Create a JsonWorklog Bean populating projection for the given type and expressions. 97 | * 98 | * @param worklogId 99 | * the id of the worklog expression. 100 | * @param startDate 101 | * the worklog startdate expression. 102 | * @param issueKey 103 | * the issue key expression. 104 | * @param userId 105 | * the user id expression. 106 | * @param duration 107 | * the worklog duration expression. 108 | * @param comment 109 | * the worklog comment expression. 110 | * @param updated 111 | * the worklog updated date expression. 112 | * @return the JsonWorklog Bean population projection. 113 | */ 114 | public static QBean createProjection(final NumberExpression worklogId, 115 | final DateTimeExpression startDate, final StringExpression issueKey, 116 | final StringExpression userId, final NumberExpression duration, 117 | final StringExpression comment, final DateTimeExpression updated) { 118 | 119 | List> expressionList = new ArrayList>(); 120 | expressionList.add(worklogId.as(ID)); 121 | expressionList.add(startDate.as(START_DATE)); 122 | expressionList.add(issueKey.as(ISSUE_KEY)); 123 | expressionList.add(userId.as(USER_ID)); 124 | expressionList.add(duration.as(DURATION)); 125 | if (comment != null) { 126 | expressionList.add(comment.as(COMMENT)); 127 | } 128 | if (updated != null) { 129 | expressionList.add(updated.as(UPDATED)); 130 | } 131 | 132 | SimpleExpression[] expressions = new SimpleExpression[expressionList.size()]; 133 | 134 | return Projections.bean(JsonWorklog.class, expressionList.toArray(expressions)); 135 | } 136 | 137 | public void setCommentBody(final String comment) throws JSONException { 138 | put("comment", comment); 139 | } 140 | 141 | public void setDuration(final long duration) throws JSONException { 142 | put(DURATION, duration); 143 | } 144 | 145 | public void setId(final long id) throws JSONException { 146 | put(ID, Long.valueOf(id)); 147 | } 148 | 149 | public void setIssueKey(final String issueKey) throws JSONException { 150 | put(ISSUE_KEY, issueKey); 151 | } 152 | 153 | public void setStartDate(final Timestamp startDate) throws JSONException { 154 | put(START_DATE, DateTimeConverterUtil.stringDateToISO8601FormatString(startDate)); 155 | } 156 | 157 | public void setUpdated(final Timestamp updated) throws JSONException { 158 | put(UPDATED, DateTimeConverterUtil.stringDateToISO8601FormatString(updated)); 159 | } 160 | 161 | public void setUserId(final String userId) throws JSONException { 162 | put(USER_ID, userId); 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/resources/atlassian-plugin.xml: -------------------------------------------------------------------------------- 1 | 23 | 25 | 26 | 27 | ${project.version} 28 | Query worklogs in JSON format via an authenticated RESTful service. Using the 29 | Worklog Query Plugin is easy to retrieve time tracking data from any application. 30 | 31 | 32 | 33 | true 34 | icons/jwqp16.png 35 | icons/jwqp144.png 36 | icons/e_logo16.png 37 | icons/e_logo72.png 38 | 39 | 40 | 41 | Provides the REST resource for the Worklog Query plugin. 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/resources/icons/e_logo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everit-org/jira-worklog-query-plugin/d4f167eb6ff4e76a040d98b45d99bcc720cd85f4/src/main/resources/icons/e_logo16.png -------------------------------------------------------------------------------- /src/main/resources/icons/e_logo72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everit-org/jira-worklog-query-plugin/d4f167eb6ff4e76a040d98b45d99bcc720cd85f4/src/main/resources/icons/e_logo72.png -------------------------------------------------------------------------------- /src/main/resources/icons/jwqp144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everit-org/jira-worklog-query-plugin/d4f167eb6ff4e76a040d98b45d99bcc720cd85f4/src/main/resources/icons/jwqp144.png -------------------------------------------------------------------------------- /src/main/resources/icons/jwqp16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everit-org/jira-worklog-query-plugin/d4f167eb6ff4e76a040d98b45d99bcc720cd85f4/src/main/resources/icons/jwqp16.png -------------------------------------------------------------------------------- /src/test/java/org/everit/jira/worklog/query/test/DatabaseSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.test; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | import java.sql.Statement; 21 | 22 | import javax.sql.DataSource; 23 | 24 | public final class DatabaseSupport { 25 | 26 | private static void createAppUserTable(final Statement createStatement) throws SQLException { 27 | createStatement.execute("CREATE TABLE \"PUBLIC\".APP_USER (" 28 | + " ID BIGINT NOT NULL," 29 | + " USER_KEY VARCHAR(2147483647)," 30 | + " LOWER_USER_NAME VARCHAR(2147483647)," 31 | + " CONSTRAINT PK_APP_USER PRIMARY KEY (ID)" 32 | + " );"); 33 | } 34 | 35 | private static void createCwdUserTable(final Statement createStatement) throws SQLException { 36 | createStatement.execute("CREATE TABLE \"PUBLIC\".CWD_USER (" 37 | + " ID BIGINT NOT NULL," 38 | + " DIRECTORY_ID BIGINT," 39 | + " USER_NAME VARCHAR(2147483647)," 40 | + " LOWER_USER_NAME VARCHAR(2147483647)," 41 | + " ACTIVE INTEGER," 42 | + " CREATED_DATE TIMESTAMP," 43 | + " UPDATED_DATE TIMESTAMP," 44 | + " FIRST_NAME VARCHAR(2147483647)," 45 | + " LOWER_FIRST_NAME VARCHAR(2147483647)," 46 | + " LAST_NAME VARCHAR(2147483647)," 47 | + " LOWER_LAST_NAME VARCHAR(2147483647)," 48 | + " DISPLAY_NAME VARCHAR(2147483647)," 49 | + " LOWER_DISPLAY_NAME VARCHAR(2147483647)," 50 | + " EMAIL_ADDRESS VARCHAR(2147483647)," 51 | + " LOWER_EMAIL_ADDRESS VARCHAR(2147483647)," 52 | + " CREDENTIAL VARCHAR(2147483647)," 53 | + " DELETED_EXTERNALLY INTEGER," 54 | + " EXTERNAL_ID VARCHAR(2147483647)," 55 | + " CONSTRAINT PK_CWD_USER PRIMARY KEY (ID)" 56 | + " );"); 57 | } 58 | 59 | private static void createJiraIssueTable(final Statement createStatement) throws SQLException { 60 | createStatement.execute("CREATE TABLE \"PUBLIC\".JIRAISSUE (" 61 | + " ID BIGINT NOT NULL," 62 | + " PKEY VARCHAR(2147483647)," 63 | + " ISSUENUM BIGINT," 64 | + " PROJECT BIGINT," 65 | + " REPORTER VARCHAR(2147483647)," 66 | + " ASSIGNEE VARCHAR(2147483647)," 67 | + " CREATOR VARCHAR(2147483647)," 68 | + " ISSUETYPE VARCHAR(2147483647)," 69 | + " SUMMARY VARCHAR(2147483647)," 70 | + " DESCRIPTION VARCHAR(2147483647)," 71 | + " ENVIRONMENT VARCHAR(2147483647)," 72 | + " PRIORITY VARCHAR(2147483647)," 73 | + " RESOLUTION VARCHAR(2147483647)," 74 | + " ISSUESTATUS VARCHAR(2147483647)," 75 | + " CREATED TIMESTAMP," 76 | + " UPDATED TIMESTAMP," 77 | + " DUEDATE TIMESTAMP," 78 | + " RESOLUTIONDATE TIMESTAMP," 79 | + " VOTES BIGINT," 80 | + " WATCHES BIGINT," 81 | + " TIMEORIGINALESTIMATE BIGINT," 82 | + " TIMEESTIMATE BIGINT," 83 | + " TIMESPENT BIGINT," 84 | + " WORKFLOW_ID BIGINT," 85 | + " \"SECURITY\" BIGINT," 86 | + " FIXFOR BIGINT," 87 | + " COMPONENT BIGINT," 88 | + " CONSTRAINT PK_JIRAISSUE PRIMARY KEY (ID)" 89 | + " );"); 90 | } 91 | 92 | private static void createProjectTable(final Statement createStatement) throws SQLException { 93 | createStatement.execute("CREATE TABLE \"PUBLIC\".PROJECT (" 94 | + " ID BIGINT NOT NULL," 95 | + " PNAME VARCHAR(2147483647)," 96 | + " URL VARCHAR(2147483647)," 97 | + " LEAD VARCHAR(2147483647)," 98 | + " DESCRIPTION VARCHAR(2147483647)," 99 | + " PKEY VARCHAR(2147483647)," 100 | + " PCOUNTER BIGINT," 101 | + " ASSIGNEETYPE BIGINT," 102 | + " AVATAR BIGINT," 103 | + " ORIGINALKEY VARCHAR(2147483647)," 104 | + " PROJECTTYPE VARCHAR(2147483647)," 105 | + " CONSTRAINT PK_PROJECT PRIMARY KEY (ID)" 106 | + " );"); 107 | } 108 | 109 | private static void createWorklogTable(final Statement createStatement) throws SQLException { 110 | createStatement.execute("CREATE TABLE \"PUBLIC\".WORKLOG (" 111 | + " ID BIGINT NOT NULL," 112 | + " ISSUEID BIGINT," 113 | + " AUTHOR VARCHAR(2147483647)," 114 | + " GROUPLEVEL VARCHAR(2147483647)," 115 | + " ROLELEVEL BIGINT," 116 | + " WORKLOGBODY VARCHAR(2147483647)," 117 | + " CREATED TIMESTAMP," 118 | + " UPDATEAUTHOR VARCHAR(2147483647)," 119 | + " UPDATED TIMESTAMP," 120 | + " STARTDATE TIMESTAMP," 121 | + " TIMEWORKED BIGINT," 122 | + " CONSTRAINT PK_WORKLOG PRIMARY KEY (ID)" 123 | + " );"); 124 | } 125 | 126 | public static void dropTables(final DataSource datasource) throws SQLException { 127 | try (Connection connection = datasource.getConnection(); 128 | Statement createStatement = connection.createStatement();) { 129 | createStatement.execute("DROP TABLE \"PUBLIC\".WORKLOG"); 130 | createStatement.execute("DROP TABLE \"PUBLIC\".PROJECT"); 131 | createStatement.execute("DROP TABLE \"PUBLIC\".JIRAISSUE"); 132 | createStatement.execute("DROP TABLE \"PUBLIC\".CWD_USER"); 133 | createStatement.execute("DROP TABLE \"PUBLIC\".APP_USER"); 134 | } 135 | } 136 | 137 | public static void initializeDatabase(final DataSource datasource) throws SQLException { 138 | try (Connection connection = datasource.getConnection(); 139 | Statement createStatement = connection.createStatement();) { 140 | DatabaseSupport.createWorklogTable(createStatement); 141 | 142 | DatabaseSupport.createJiraIssueTable(createStatement); 143 | 144 | DatabaseSupport.createProjectTable(createStatement); 145 | 146 | DatabaseSupport.createCwdUserTable(createStatement); 147 | 148 | DatabaseSupport.createAppUserTable(createStatement); 149 | 150 | DatabaseSupport.insertJiraIssueRows(createStatement); 151 | 152 | DatabaseSupport.insertWorklogRows(createStatement); 153 | 154 | DatabaseSupport.insertProjectRows(createStatement); 155 | 156 | DatabaseSupport.insertCwdUserRows(createStatement); 157 | 158 | DatabaseSupport.insertAppUserRows(createStatement); 159 | } 160 | } 161 | 162 | private static void insertAppUserRows(final Statement createStatement) throws SQLException { 163 | createStatement.execute("INSERT INTO app_user VALUES (10000, " 164 | + "'test-user@everit.biz', 'test-user@everit.biz');"); 165 | } 166 | 167 | private static void insertCwdUserRows(final Statement createStatement) throws SQLException { 168 | createStatement.execute( 169 | "INSERT INTO cwd_user VALUES (10000, 1, 'test-user@everit.biz', " 170 | + "'test-user@everit.biz', 1, '2016-03-07 14:06:16.89', " 171 | + "'2016-03-07 14:06:16.89', 'Zsigmond', 'zsigmond', 'Czine', 'czine', " 172 | + "'Zsigmond Czine', 'zsigmond czine', 'test-user@everit.biz', " 173 | + "'test-user@everit.biz', " 174 | + "'asdf', NULL, " 175 | + "'7c717a76-20bc-4d80-9087-a073e057cf78');"); 176 | } 177 | 178 | private static void insertJiraIssueRows(final Statement createStatement) throws SQLException { 179 | createStatement.execute( 180 | "INSERT INTO jiraissue VALUES (10002, NULL, 3, 10000, 'test-user@everit.biz', NULL, " 181 | + "'test-user@everit.biz', '10001', 'harom', NULL, NULL, '3', NULL, '10000', " 182 | + "'2016-03-07 14:07:41.156', '2016-03-07 14:07:41.156', NULL, NULL, 0, 1," 183 | + " NULL, NULL, NULL, 10002, NULL, NULL, NULL);"); 184 | createStatement.execute( 185 | "INSERT INTO jiraissue VALUES (10001, NULL, 2, 10000, 'test-user@everit.biz', NULL, " 186 | + "'test-user@everit.biz', '10001', 'ketto', NULL, NULL, '3', NULL, '10000', " 187 | + "'2016-03-07 14:07:38.683', '2016-03-07 14:08:09.022', NULL, NULL, 0, 1, " 188 | + "NULL, 0, 22020, 10001, NULL, NULL, NULL);"); 189 | createStatement.execute( 190 | "INSERT INTO jiraissue VALUES (10003, NULL, 4, 10000, 'test-user@everit.biz', NULL, " 191 | + "'test-user@everit.biz', '10001', 'negy', NULL, NULL, '3', NULL, '10000', " 192 | + "'2016-03-07 14:07:43.854', '2016-03-07 14:08:24.627', NULL, NULL, 0, 1, " 193 | + "NULL, 0, 22080, 10003, NULL, NULL, NULL);"); 194 | createStatement.execute( 195 | "INSERT INTO jiraissue VALUES (10004, NULL, 5, 10000, 'test-user@everit.biz', NULL, " 196 | + "'test-user@everit.biz', '10001', 'ot', NULL, NULL, '3', NULL, '10000', " 197 | + "'2016-03-07 14:07:45.756', '2016-03-07 14:08:33.903', NULL, NULL, 0, 1, " 198 | + "NULL, 0, 22080, 10004, NULL, NULL, NULL);"); 199 | createStatement.execute( 200 | "INSERT INTO jiraissue VALUES (10000, NULL, 1, 10000, 'test-user@everit.biz', NULL, " 201 | + "'test-user@everit.biz', '10001', 'egy', NULL, NULL, '3', NULL, '10000', " 202 | + "'2016-03-07 14:07:33.368', '2016-03-11 10:46:46.357', NULL, NULL, 0, 1, " 203 | + "NULL, 0, 22020, 10000, NULL, NULL, NULL);"); 204 | } 205 | 206 | private static void insertProjectRows(final Statement createStatement) throws SQLException { 207 | createStatement.execute("INSERT INTO project VALUES (10000, 'SAMPLE', '', " 208 | + "'test-user@everit.biz', '', 'SAM', 5, 3, 10324, 'SAM', 'software');"); 209 | } 210 | 211 | private static void insertWorklogRows(final Statement createStatement) throws SQLException { 212 | createStatement.execute( 213 | "INSERT INTO worklog VALUES (10001, 10001, 'test-user@everit.biz', NULL, NULL, '', " 214 | + "'2016-03-07 14:08:08.967', 'test-user@everit.biz', '2016-03-07 14:08:08.967', " 215 | + "'2016-03-01 08:00:00', 22020);"); 216 | createStatement.execute( 217 | "INSERT INTO worklog VALUES (10002, 10003, 'test-user@everit.biz', NULL, NULL, '', " 218 | + "'2016-03-07 14:08:24.536', 'test-user@everit.biz', '2016-03-07 14:08:24.536', " 219 | + "'2016-02-24 08:00:00', 22080);"); 220 | createStatement.execute( 221 | "INSERT INTO worklog VALUES (10003, 10004, 'test-user@everit.biz', NULL, NULL, '', " 222 | + "'2016-03-07 14:08:33.899', 'test-user@everit.biz', '2016-03-07 14:08:33.899', " 223 | + "'2016-02-23 08:00:00', 22080);"); 224 | createStatement.execute( 225 | "INSERT INTO worklog VALUES (10000, 10000, 'test-user@everit.biz', NULL, NULL, 'asdfasf', " 226 | + "'2016-03-07 14:07:55.675', 'test-user@everit.biz', '2016-03-11 10:46:46.277', " 227 | + "'2016-03-07 08:00:00', 22020);"); 228 | } 229 | 230 | private DatabaseSupport() { 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/test/java/org/everit/jira/worklog/query/test/MockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.test; 17 | 18 | import java.io.IOException; 19 | import java.sql.Array; 20 | import java.sql.Blob; 21 | import java.sql.CallableStatement; 22 | import java.sql.Clob; 23 | import java.sql.Connection; 24 | import java.sql.DatabaseMetaData; 25 | import java.sql.NClob; 26 | import java.sql.PreparedStatement; 27 | import java.sql.SQLClientInfoException; 28 | import java.sql.SQLException; 29 | import java.sql.SQLWarning; 30 | import java.sql.SQLXML; 31 | import java.sql.Savepoint; 32 | import java.sql.Statement; 33 | import java.sql.Struct; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Properties; 38 | import java.util.TimeZone; 39 | import java.util.concurrent.Executor; 40 | 41 | import javax.sql.XADataSource; 42 | import javax.transaction.xa.XAException; 43 | import javax.ws.rs.core.Response; 44 | 45 | import org.apache.commons.dbcp2.managed.BasicManagedDataSource; 46 | import org.apache.geronimo.transaction.manager.GeronimoTransactionManager; 47 | import org.everit.jira.worklog.query.plugin.FindWorklogsByIssuesParam; 48 | import org.everit.jira.worklog.query.plugin.IssueBeanWithTimespent; 49 | import org.everit.jira.worklog.query.plugin.SearchResultsBeanWithTimespent; 50 | import org.everit.jira.worklog.query.plugin.WorklogQueryCoreImpl; 51 | import org.h2.jdbcx.JdbcDataSource; 52 | import org.junit.After; 53 | import org.junit.Assert; 54 | import org.junit.Before; 55 | import org.junit.Test; 56 | import org.junit.runner.RunWith; 57 | import org.mockito.ArgumentMatchers; 58 | import org.mockito.Mockito; 59 | import org.ofbiz.core.entity.config.DatasourceInfo; 60 | import org.powermock.api.mockito.PowerMockito; 61 | import org.powermock.core.classloader.annotations.PowerMockIgnore; 62 | import org.powermock.core.classloader.annotations.PrepareForTest; 63 | import org.powermock.modules.junit4.PowerMockRunner; 64 | 65 | import com.atlassian.jira.bc.issue.search.SearchService; 66 | import com.atlassian.jira.bc.issue.search.SearchService.ParseResult; 67 | import com.atlassian.jira.exception.DataAccessException; 68 | import com.atlassian.jira.issue.Issue; 69 | import com.atlassian.jira.issue.search.SearchException; 70 | import com.atlassian.jira.issue.search.SearchResults; 71 | import com.atlassian.jira.mock.component.MockComponentWorker; 72 | import com.atlassian.jira.mock.issue.MockIssue; 73 | import com.atlassian.jira.ofbiz.DefaultOfBizConnectionFactory; 74 | import com.atlassian.jira.permission.ProjectPermissions; 75 | import com.atlassian.jira.project.Project; 76 | import com.atlassian.jira.rest.api.util.StringList; 77 | import com.atlassian.jira.security.JiraAuthenticationContext; 78 | import com.atlassian.jira.security.PermissionManager; 79 | import com.atlassian.jira.user.ApplicationUser; 80 | import com.atlassian.jira.user.util.UserManager; 81 | 82 | @RunWith(PowerMockRunner.class) 83 | @PowerMockIgnore("javax.security.*") 84 | @PrepareForTest({ DefaultOfBizConnectionFactory.class, DatasourceInfo.class, ParseResult.class }) 85 | public class MockTest { 86 | 87 | public class SinkConnection implements Connection { 88 | 89 | private Connection conn; 90 | 91 | @Override 92 | public void abort(final Executor executor) throws SQLException { 93 | getConnection().abort(executor); 94 | } 95 | 96 | @Override 97 | public void clearWarnings() throws SQLException { 98 | getConnection().clearWarnings(); 99 | } 100 | 101 | @Override 102 | public void close() throws SQLException { 103 | getConnection().close(); 104 | } 105 | 106 | @Override 107 | public void commit() throws SQLException { 108 | getConnection().commit(); 109 | } 110 | 111 | @Override 112 | public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { 113 | return getConnection().createArrayOf(typeName, elements); 114 | } 115 | 116 | @Override 117 | public Blob createBlob() throws SQLException { 118 | return getConnection().createBlob(); 119 | } 120 | 121 | @Override 122 | public Clob createClob() throws SQLException { 123 | return getConnection().createClob(); 124 | } 125 | 126 | @Override 127 | public NClob createNClob() throws SQLException { 128 | return getConnection().createNClob(); 129 | } 130 | 131 | @Override 132 | public SQLXML createSQLXML() throws SQLException { 133 | return getConnection().createSQLXML(); 134 | } 135 | 136 | @Override 137 | public Statement createStatement() throws SQLException { 138 | return getConnection().createStatement(); 139 | } 140 | 141 | @Override 142 | public Statement createStatement(final int resultSetType, final int resultSetConcurrency) 143 | throws SQLException { 144 | return getConnection().createStatement(resultSetType, resultSetConcurrency); 145 | } 146 | 147 | @Override 148 | public Statement createStatement(final int resultSetType, final int resultSetConcurrency, 149 | final int resultSetHoldability) throws SQLException { 150 | return getConnection().createStatement(resultSetType, resultSetConcurrency, 151 | resultSetHoldability); 152 | } 153 | 154 | @Override 155 | public Struct createStruct(final String typeName, final Object[] attributes) 156 | throws SQLException { 157 | return getConnection().createStruct(typeName, attributes); 158 | } 159 | 160 | @Override 161 | public boolean getAutoCommit() throws SQLException { 162 | return getConnection().getAutoCommit(); 163 | } 164 | 165 | @Override 166 | public String getCatalog() throws SQLException { 167 | return getConnection().getCatalog(); 168 | } 169 | 170 | @Override 171 | public Properties getClientInfo() throws SQLException { 172 | return getConnection().getClientInfo(); 173 | } 174 | 175 | @Override 176 | public String getClientInfo(final String name) throws SQLException { 177 | return getConnection().getClientInfo(name); 178 | } 179 | 180 | private Connection getConnection() { 181 | try { 182 | if ((conn == null) || conn.isClosed()) { 183 | conn = createXADatasource().getConnection(); 184 | } 185 | } catch (SQLException e) { 186 | } 187 | return conn; 188 | } 189 | 190 | @Override 191 | public int getHoldability() throws SQLException { 192 | return getConnection().getHoldability(); 193 | } 194 | 195 | @Override 196 | public DatabaseMetaData getMetaData() throws SQLException { 197 | return getConnection().getMetaData(); 198 | } 199 | 200 | @Override 201 | public int getNetworkTimeout() throws SQLException { 202 | return getConnection().getNetworkTimeout(); 203 | } 204 | 205 | @Override 206 | public String getSchema() throws SQLException { 207 | return getConnection().getSchema(); 208 | } 209 | 210 | @Override 211 | public int getTransactionIsolation() throws SQLException { 212 | return getConnection().getTransactionIsolation(); 213 | } 214 | 215 | @Override 216 | public Map> getTypeMap() throws SQLException { 217 | return getConnection().getTypeMap(); 218 | } 219 | 220 | @Override 221 | public SQLWarning getWarnings() throws SQLException { 222 | return getConnection().getWarnings(); 223 | } 224 | 225 | @Override 226 | public boolean isClosed() throws SQLException { 227 | return getConnection().isClosed(); 228 | } 229 | 230 | @Override 231 | public boolean isReadOnly() throws SQLException { 232 | return getConnection().isReadOnly(); 233 | } 234 | 235 | @Override 236 | public boolean isValid(final int timeout) throws SQLException { 237 | return getConnection().isValid(timeout); 238 | } 239 | 240 | @Override 241 | public boolean isWrapperFor(final Class iface) throws SQLException { 242 | return getConnection().isWrapperFor(iface); 243 | } 244 | 245 | @Override 246 | public String nativeSQL(final String sql) throws SQLException { 247 | return getConnection().nativeSQL(sql); 248 | } 249 | 250 | @Override 251 | public CallableStatement prepareCall(final String sql) throws SQLException { 252 | return getConnection().prepareCall(sql); 253 | } 254 | 255 | @Override 256 | public CallableStatement prepareCall(final String sql, final int resultSetType, 257 | final int resultSetConcurrency) 258 | throws SQLException { 259 | return getConnection().prepareCall(sql, resultSetType, resultSetConcurrency); 260 | } 261 | 262 | @Override 263 | public CallableStatement prepareCall(final String sql, final int resultSetType, 264 | final int resultSetConcurrency, 265 | final int resultSetHoldability) throws SQLException { 266 | return getConnection().prepareCall(sql, resultSetType, resultSetConcurrency, 267 | resultSetHoldability); 268 | } 269 | 270 | @Override 271 | public PreparedStatement prepareStatement(final String sql) throws SQLException { 272 | return getConnection().prepareStatement(sql); 273 | } 274 | 275 | @Override 276 | public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) 277 | throws SQLException { 278 | return getConnection().prepareStatement(sql, autoGeneratedKeys); 279 | } 280 | 281 | @Override 282 | public PreparedStatement prepareStatement(final String sql, final int resultSetType, 283 | final int resultSetConcurrency) throws SQLException { 284 | return getConnection().prepareStatement(sql, resultSetType, resultSetConcurrency); 285 | } 286 | 287 | @Override 288 | public PreparedStatement prepareStatement(final String sql, final int resultSetType, 289 | final int resultSetConcurrency, final int resultSetHoldability) throws SQLException { 290 | return getConnection().prepareCall(sql, resultSetType, resultSetConcurrency, 291 | resultSetHoldability); 292 | } 293 | 294 | @Override 295 | public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) 296 | throws SQLException { 297 | return getConnection().prepareStatement(sql, columnIndexes); 298 | } 299 | 300 | @Override 301 | public PreparedStatement prepareStatement(final String sql, final String[] columnNames) 302 | throws SQLException { 303 | return getConnection().prepareStatement(sql, columnNames); 304 | } 305 | 306 | @Override 307 | public void releaseSavepoint(final Savepoint savepoint) throws SQLException { 308 | getConnection().releaseSavepoint(savepoint); 309 | } 310 | 311 | @Override 312 | public void rollback() throws SQLException { 313 | getConnection().rollback(); 314 | } 315 | 316 | @Override 317 | public void rollback(final Savepoint savepoint) throws SQLException { 318 | getConnection().rollback(savepoint); 319 | } 320 | 321 | @Override 322 | public void setAutoCommit(final boolean autoCommit) throws SQLException { 323 | getConnection().setAutoCommit(autoCommit); 324 | } 325 | 326 | @Override 327 | public void setCatalog(final String catalog) throws SQLException { 328 | getConnection().setCatalog(catalog); 329 | } 330 | 331 | @Override 332 | public void setClientInfo(final Properties properties) throws SQLClientInfoException { 333 | getConnection().setClientInfo(properties); 334 | } 335 | 336 | @Override 337 | public void setClientInfo(final String name, final String value) throws SQLClientInfoException { 338 | getConnection().setClientInfo(name, value); 339 | } 340 | 341 | @Override 342 | public void setHoldability(final int holdability) throws SQLException { 343 | getConnection().setHoldability(holdability); 344 | } 345 | 346 | @Override 347 | public void setNetworkTimeout(final Executor executor, final int milliseconds) 348 | throws SQLException { 349 | getConnection().setNetworkTimeout(executor, milliseconds); 350 | } 351 | 352 | @Override 353 | public void setReadOnly(final boolean readOnly) throws SQLException { 354 | getConnection().setReadOnly(readOnly); 355 | } 356 | 357 | @Override 358 | public Savepoint setSavepoint() throws SQLException { 359 | return getConnection().setSavepoint(); 360 | } 361 | 362 | @Override 363 | public Savepoint setSavepoint(final String name) throws SQLException { 364 | return getConnection().setSavepoint(name); 365 | } 366 | 367 | @Override 368 | public void setSchema(final String schema) throws SQLException { 369 | getConnection().setSchema(schema); 370 | } 371 | 372 | @Override 373 | public void setTransactionIsolation(final int level) throws SQLException { 374 | getConnection().setTransactionIsolation(level); 375 | } 376 | 377 | @Override 378 | public void setTypeMap(final Map> map) throws SQLException { 379 | getConnection().setTypeMap(map); 380 | } 381 | 382 | @Override 383 | public T unwrap(final Class iface) throws SQLException { 384 | return getConnection().unwrap(iface); 385 | } 386 | 387 | } 388 | 389 | private static final long N_10000 = 10000L; 390 | 391 | private BasicManagedDataSource managedDataSource = null; 392 | 393 | private String TEST_USER = "test-user@everit.biz"; 394 | 395 | private WorklogQueryCoreImpl worklogQuery; 396 | 397 | @After 398 | public void after() throws SQLException { 399 | DatabaseSupport.dropTables(managedDataSource); 400 | 401 | if (managedDataSource != null) { 402 | try { 403 | managedDataSource.close(); 404 | } catch (SQLException e) { 405 | throw new RuntimeException(e); 406 | } 407 | } 408 | } 409 | 410 | @Before 411 | public void before() throws DataAccessException, SQLException, SearchException { 412 | GeronimoTransactionManager transactionManager = null; 413 | try { 414 | transactionManager = new GeronimoTransactionManager(6000); 415 | } catch (XAException e) { 416 | throw new RuntimeException(e); 417 | } 418 | 419 | managedDataSource = createManagedDataSource(transactionManager, createXADatasource()); 420 | 421 | ApplicationUser testUser = mockApplicationUser(); 422 | Project project = mockProject(); 423 | 424 | JiraAuthenticationContext jiraAuthenticationContext = mockJiraAuthenticationContext(testUser); 425 | 426 | PermissionManager permissionManager = mockPermissionManager(testUser, project); 427 | 428 | UserManager userManager = mockUserManager(testUser); 429 | 430 | SearchService searchService = Mockito.mock(SearchService.class); 431 | PowerMockito.mockStatic(ParseResult.class); 432 | ParseResult parseResult = Mockito.mock(ParseResult.class); 433 | Mockito.when(searchService.parseQuery(testUser, "")).thenReturn(parseResult); 434 | Mockito.when(parseResult.isValid()).thenReturn(Boolean.TRUE); 435 | 436 | SearchResults searchResults = Mockito.mock(SearchResults.class); 437 | List issues = new ArrayList<>(); 438 | issues.add(new MockIssue(10000)); 439 | issues.add(new MockIssue(10001)); 440 | issues.add(new MockIssue(10002)); 441 | issues.add(new MockIssue(10003)); 442 | issues.add(new MockIssue(10004)); 443 | Mockito.when(searchResults.getResults()).thenReturn(issues); 444 | 445 | // Mockito.when( 446 | // searchService.search(ArgumentMatchers.any(ApplicationUser.class), 447 | // ArgumentMatchers.any(Query.class), 448 | // ArgumentMatchers.any(PagerFilter.class))) 449 | Mockito.when( 450 | searchService.search(ArgumentMatchers.any(), 451 | ArgumentMatchers.any(), 452 | ArgumentMatchers.any())) 453 | .thenReturn(searchResults); 454 | 455 | mockDefaultOfBizConnectionFactory(); 456 | 457 | new MockComponentWorker() 458 | .addMock(JiraAuthenticationContext.class, jiraAuthenticationContext) 459 | .addMock(PermissionManager.class, permissionManager) 460 | .addMock(UserManager.class, userManager) 461 | .addMock(SearchService.class, searchService) 462 | .init(); 463 | 464 | worklogQuery = new WorklogQueryCoreImpl(); 465 | DatabaseSupport.initializeDatabase(managedDataSource); 466 | 467 | System.setProperty("user.timezone", "UTC"); 468 | TimeZone.setDefault(null); 469 | } 470 | 471 | private BasicManagedDataSource createManagedDataSource( 472 | final GeronimoTransactionManager transactionManager, final XADataSource xaDataSource) { 473 | BasicManagedDataSource lManagedDataSource = new BasicManagedDataSource(); 474 | lManagedDataSource.setTransactionManager(transactionManager); 475 | lManagedDataSource.setXaDataSourceInstance(xaDataSource); 476 | return lManagedDataSource; 477 | } 478 | 479 | private JdbcDataSource createXADatasource() { 480 | JdbcDataSource xaDatasource = new JdbcDataSource(); 481 | xaDatasource.setURL("jdbc:h2:mem:test"); 482 | // xaDatasource.setURL("jdbc:h2:tcp://localhost:9092/~/test"); 483 | xaDatasource.setUser("sa"); 484 | xaDatasource.setPassword(""); 485 | return xaDatasource; 486 | } 487 | 488 | private Properties loadExpectedResultProperties() throws IOException { 489 | Properties properties = new Properties(); 490 | properties.load(this.getClass().getResourceAsStream("/expectedResults.properties")); 491 | return properties; 492 | } 493 | 494 | private ApplicationUser mockApplicationUser() { 495 | ApplicationUser testUser = Mockito.mock(ApplicationUser.class); 496 | Mockito.when(testUser.getKey()).thenReturn(TEST_USER); 497 | Mockito.when(testUser.getId()).thenReturn(N_10000); 498 | return testUser; 499 | } 500 | 501 | private void mockDefaultOfBizConnectionFactory() throws SQLException { 502 | PowerMockito.mockStatic(DefaultOfBizConnectionFactory.class); 503 | DefaultOfBizConnectionFactory defaultOfBizConnectionFactory = 504 | Mockito.mock(DefaultOfBizConnectionFactory.class); 505 | PowerMockito.when(DefaultOfBizConnectionFactory.getInstance()) 506 | .thenReturn(defaultOfBizConnectionFactory); 507 | 508 | PowerMockito.mockStatic(DatasourceInfo.class); 509 | DatasourceInfo datasourceInfo = Mockito.mock(DatasourceInfo.class); 510 | PowerMockito.when(defaultOfBizConnectionFactory.getDatasourceInfo()).thenReturn(datasourceInfo); 511 | Mockito.when(datasourceInfo.getSchemaName()).thenReturn("PUBLIC"); 512 | Mockito.when(defaultOfBizConnectionFactory.getConnection()) 513 | .thenReturn(new SinkConnection()); 514 | } 515 | 516 | private JiraAuthenticationContext mockJiraAuthenticationContext(final ApplicationUser testUser) { 517 | JiraAuthenticationContext jiraAuthenticationContext = 518 | Mockito.mock(JiraAuthenticationContext.class); 519 | Mockito.when(jiraAuthenticationContext.getLoggedInUser()).thenReturn(testUser); 520 | return jiraAuthenticationContext; 521 | } 522 | 523 | private PermissionManager mockPermissionManager(final ApplicationUser testUser, 524 | final Project project) { 525 | ArrayList projects = new ArrayList<>(); 526 | projects.add(project); 527 | PermissionManager permissionManager = Mockito.mock(PermissionManager.class); 528 | Mockito.when(permissionManager.getProjects(ProjectPermissions.BROWSE_PROJECTS, testUser)) 529 | .thenReturn(projects); 530 | return permissionManager; 531 | } 532 | 533 | private Project mockProject() { 534 | Project project = Mockito.mock(Project.class); 535 | Mockito.when(project.getId()).thenReturn(N_10000); 536 | Mockito.when(project.getKey()).thenReturn("SAM"); 537 | return project; 538 | } 539 | 540 | private UserManager mockUserManager(final ApplicationUser testUser) { 541 | UserManager userManager = Mockito.mock(UserManager.class); 542 | Mockito.when(userManager.getUserByName(TEST_USER)).thenReturn(testUser); 543 | return userManager; 544 | } 545 | 546 | @Test 547 | public void testFindWorklogs() throws IOException { 548 | Response findWorklogs = worklogQuery.findWorklogs("2016-02-24", "2016-03-12", TEST_USER, "", "", 549 | new ArrayList()); 550 | String json = findWorklogs.getEntity().toString(); 551 | Properties properties = loadExpectedResultProperties(); 552 | Assert.assertEquals(properties.get("findWorklogs"), json); 553 | } 554 | 555 | @Test 556 | public void testUpdateWorklogs() throws IOException { 557 | Response findUpdatedWorklogs = 558 | worklogQuery.findUpdatedWorklogs("2016-02-24", "2016-03-12", TEST_USER, "", "", 559 | new ArrayList()); 560 | String json = findUpdatedWorklogs.getEntity().toString(); 561 | Properties properties = loadExpectedResultProperties(); 562 | Assert.assertEquals(properties.get("findUpdatedWorklogs"), json); 563 | } 564 | 565 | @Test 566 | public void testWorklogsByIssues() throws IOException { 567 | List fields = new ArrayList<>(); 568 | fields.add(StringList.fromList("emptyFieldValue")); 569 | FindWorklogsByIssuesParam findWorklogsByIssuesParam = new FindWorklogsByIssuesParam() 570 | .startDate("2016-02-24") 571 | .endDate("2016-03-12") 572 | .user(TEST_USER) 573 | .jql("") 574 | .startAt(0) 575 | .maxResults(25) 576 | .fields(fields); 577 | SearchResultsBeanWithTimespent findWorklogsByIssues = 578 | worklogQuery.findWorklogsByIssues(findWorklogsByIssuesParam); 579 | 580 | Assert.assertEquals(3, findWorklogsByIssues.total.intValue()); 581 | List issues = findWorklogsByIssues.getIssues(); 582 | Assert.assertEquals("10000", issues.get(0).getId()); 583 | Assert.assertEquals("SAM-1", issues.get(0).getKey()); 584 | Assert.assertEquals("10001", issues.get(1).getId()); 585 | Assert.assertEquals("SAM-2", issues.get(1).getKey()); 586 | Assert.assertEquals("10003", issues.get(2).getId()); 587 | Assert.assertEquals("SAM-4", issues.get(2).getKey()); 588 | } 589 | } 590 | -------------------------------------------------------------------------------- /src/test/java/org/everit/jira/worklog/query/test/WorklogQueryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | package org.everit.jira.worklog.query.test; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.sun.jersey.api.client.Client; 22 | import com.sun.jersey.api.client.ClientResponse; 23 | import com.sun.jersey.api.client.WebResource; 24 | import com.sun.jersey.core.util.Base64; 25 | 26 | /** 27 | * The WorklogQueryTest class help test the plugin authorization and the plugin query. 28 | */ 29 | public final class WorklogQueryTest { 30 | /** 31 | * The logger used to log. 32 | */ 33 | private static final Logger LOGGER = LoggerFactory.getLogger(WorklogQueryTest.class); 34 | 35 | /** 36 | * The status code of the unsuccessful authorization. 37 | */ 38 | public static final int INVALID_AUTHOR_STATUS = 401; 39 | /** 40 | * The user name for authentication. 41 | */ 42 | public static final String USERNAME = "admin"; 43 | /** 44 | * The password for authentication. 45 | */ 46 | public static final String PASSWORD = "admin_ps"; 47 | 48 | /** 49 | * The WorklogQueryTest class main method. 50 | * 51 | * @param args 52 | * The main args. 53 | */ 54 | public static void main(final String[] args) { 55 | try { 56 | WorklogQueryTest.simpleClientTest(); 57 | WorklogQueryTest.simpleClientUpdateTest(); 58 | } catch (Exception e) { 59 | LOGGER.error("Fail to test jira-worklog-query", e); 60 | } 61 | } 62 | 63 | /** 64 | * The jira-worklog-query HTTP BASIC AUTHORIZATION test. 65 | * 66 | * @throws Exception 67 | * If any Exception happen. 68 | */ 69 | public static void simpleClientTest() throws Exception { 70 | String url = 71 | "http://localhost:8080rest/jira-worklog-query/1.1.0/find/" 72 | + "worklogs?startDate=2012-12-12&user=admin"; 73 | LOGGER.info("Start the simple test"); 74 | byte[] authByteArray = Base64.encode(USERNAME + ":" + PASSWORD); 75 | String auth = new String(authByteArray, "UTF8"); 76 | Client client = Client.create(); 77 | WebResource webResource = client.resource(url); 78 | ClientResponse response = 79 | webResource.header("Authorization", "Basic " + auth).type("application/json") 80 | .accept("application/json").get(ClientResponse.class); 81 | int statusCode = response.getStatus(); 82 | 83 | if (statusCode == INVALID_AUTHOR_STATUS) { 84 | throw new Exception("Invalid Username or Password"); 85 | } 86 | final String stringResponse = response.getEntity(String.class); 87 | LOGGER.info("sr: " + stringResponse); 88 | 89 | } 90 | 91 | /** 92 | * The jira-worklog-query HTTP BASIC AUTHORIZATION test. 93 | * 94 | * @throws Exception 95 | * If any Exception happen. 96 | */ 97 | public static void simpleClientUpdateTest() throws Exception { 98 | String url = 99 | "http://localhost:8080rest/jira-worklog-query/1.1.0/find/" 100 | + "updatedWorklogs?startDate=2013-04-15&user=admin"; 101 | LOGGER.info("Start the simple test"); 102 | byte[] authByteArray = Base64.encode(USERNAME + ":" + PASSWORD); 103 | String auth = new String(authByteArray, "UTF8"); 104 | Client client = Client.create(); 105 | WebResource webResource = client.resource(url); 106 | ClientResponse response = 107 | webResource.header("Authorization", "Basic " + auth).type("application/json") 108 | .accept("application/json").get(ClientResponse.class); 109 | int statusCode = response.getStatus(); 110 | 111 | if (statusCode == INVALID_AUTHOR_STATUS) { 112 | throw new Exception("Invalid Username or Password"); 113 | } 114 | final String stringResponse = response.getEntity(String.class); 115 | LOGGER.info("sr: " + stringResponse); 116 | 117 | } 118 | 119 | /** 120 | * Simple private constructor. 121 | */ 122 | private WorklogQueryTest() { 123 | 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/test/resources/expectedResults.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Everit Kft. (http://www.everit.org) 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 | findWorklogs=[[{"id":10000,"startDate":"2016-03-07T08:00:00+0000","issueKey":"SAM-1","userId":"test-user@everit.biz","duration":22020},{"id":10001,"startDate":"2016-03-01T08:00:00+0000","issueKey":"SAM-2","userId":"test-user@everit.biz","duration":22020},{"id":10002,"startDate":"2016-02-24T08:00:00+0000","issueKey":"SAM-4","userId":"test-user@everit.biz","duration":22080}]] 18 | findUpdatedWorklogs=[[{"id":10000,"startDate":"2016-03-07T08:00:00+0000","issueKey":"SAM-1","userId":"test-user@everit.biz","duration":22020},{"id":10001,"startDate":"2016-03-01T08:00:00+0000","issueKey":"SAM-2","userId":"test-user@everit.biz","duration":22020},{"id":10002,"startDate":"2016-02-24T08:00:00+0000","issueKey":"SAM-4","userId":"test-user@everit.biz","duration":22080},{"id":10003,"startDate":"2016-02-23T08:00:00+0000","issueKey":"SAM-5","userId":"test-user@everit.biz","duration":22080}]] 19 | --------------------------------------------------------------------------------