diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95274b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.gradle +.idea +**/build/ +**/target/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +*.iml + +*.log + +*.toDelete \ No newline at end of file diff --git a/HEADER b/HEADER new file mode 100644 index 0000000..76f9222 --- /dev/null +++ b/HEADER @@ -0,0 +1,13 @@ +Copyright ${year} ${name} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bec5de8 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Mifos I/O Group Management + +[![Join the chat at https://gitter.im/mifos-initiative/mifos.io](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mifos-initiative/mifos.io?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This project provides Group management capabilities. + +## Versioning +The version numbers follow the [Semantic Versioning](http://semver.org/) scheme. + +In addition to MAJOR.MINOR.PATCH the following postfixes are used to indicate the development state. + +* BUILD-SNAPSHOT - A release currently in development. +* M - A _milestone_ release include specific sets of functions and are released as soon as the functionality is complete. +* RC - A _release candidate_ is a version with potential to be a final product, considered _code complete_. +* RELEASE - _General availability_ indicates that this release is the best available version and is recommended for all usage. + +The versioning layout is {MAJOR}.{MINOR}.{PATCH}-{INDICATOR}[.{PATCH}]. Only milestones and release candidates can have patch versions. Some examples: + +1.2.3.BUILD-SNAPSHOT +1.3.5.M.1 +1.5.7.RC.2 +2.0.0.RELEASE + +## License +See [LICENSE](LICENSE) file. diff --git a/api/build.gradle b/api/build.gradle new file mode 100644 index 0000000..5dc9656 --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,33 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE' } +} + +plugins { + id "com.github.hierynomus.license" version "0.13.1" +} + +apply from: '../shared.gradle' + +dependencies { + compile( + [group: 'org.springframework.cloud', name: 'spring-cloud-starter-feign'], + [group: 'io.mifos.core', name: 'api', version: versions.frameworkapi], + [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator] + ) +} + +publishing { + publications { + api(MavenPublication) { + from components.java + groupId project.group + artifactId project.name + version project.version + } + } +} diff --git a/api/settings.gradle b/api/settings.gradle new file mode 100644 index 0000000..5cd7dd3 --- /dev/null +++ b/api/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'api' diff --git a/api/src/main/java/io/mifos/group/api/v1/EventConstants.java b/api/src/main/java/io/mifos/group/api/v1/EventConstants.java new file mode 100644 index 0000000..f18fe33 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/EventConstants.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1; + +@SuppressWarnings("unused") +public interface EventConstants { + + String DESTINATION = "group-v1"; + String SELECTOR_NAME = "action"; + + String INITIALIZE = "initialize"; + String SELECTOR_INITIALIZE = SELECTOR_NAME + " = '" + INITIALIZE + "'"; + + String POST_GROUP_DEFINITION = "post-group-definition"; + String SELECTOR_POST_GROUP_DEFINITION = SELECTOR_NAME + " = '" + POST_GROUP_DEFINITION + "'"; + + String POST_GROUP = "post-group"; + String SELECTOR_POST_GROUP = SELECTOR_NAME + " = '" + POST_GROUP + "'"; + String PUT_GROUP = "put-group"; + String SELECTOR_PUT_GROUP = SELECTOR_NAME + " = '" + PUT_GROUP + "'"; + String ACTIVATE_GROUP = "activate-group"; + String SELECTOR_ACTIVATE_GROUP = SELECTOR_NAME + " = '" + ACTIVATE_GROUP + "'"; + String CLOSE_GROUP = "close-group"; + String SELECTOR_CLOSE_GROUP = SELECTOR_NAME + " = '" + CLOSE_GROUP + "'"; + String REOPEN_GROUP = "reopen-group"; + String SELECTOR_REOPEN_GROUP = SELECTOR_NAME + " = '" + REOPEN_GROUP + "'"; + + String POST_MEETING = "post-meeting"; + String SELECTOR_POST_MEETING = SELECTOR_NAME + " = '" + POST_MEETING + "'"; + String PUT_MEETING = "put-meeting"; + String SELECTOR_PUT_MEETING = SELECTOR_NAME + " = '" + PUT_MEETING + "'"; +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupAlreadyExists.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupAlreadyExists.java new file mode 100644 index 0000000..35fd6e2 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupAlreadyExists.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupAlreadyExists extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupClient.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupClient.java new file mode 100644 index 0000000..f1e44b9 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupClient.java @@ -0,0 +1,174 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +import io.mifos.core.api.annotation.ThrowsException; +import io.mifos.core.api.annotation.ThrowsExceptions; +import io.mifos.core.api.util.CustomFeignClientsConfiguration; +import io.mifos.group.api.v1.domain.AssignedEmployeeHolder; +import io.mifos.group.api.v1.domain.SignOffMeeting; +import io.mifos.group.api.v1.domain.Group; +import io.mifos.group.api.v1.domain.GroupCommand; +import io.mifos.group.api.v1.domain.GroupDefinition; +import io.mifos.group.api.v1.domain.GroupPage; +import io.mifos.group.api.v1.domain.Meeting; +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; +import java.util.Set; + +@SuppressWarnings("unused") +@FeignClient(name="group-v1", path="/group/v1", configuration=CustomFeignClientsConfiguration.class) +public interface GroupClient { + + @RequestMapping( + value = "/definitions", + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsExceptions({ + @ThrowsException(status = HttpStatus.CONFLICT, exception = GroupDefinitionAlreadyExists.class), + @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = GroupDefinitionValidation.class) + }) + void createGroupDefinition(@RequestBody final GroupDefinition groupDefinition); + + @RequestMapping( + value = "/definitions/{identifier}", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ResponseBody + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupDefinitionNotFound.class) + GroupDefinition findGroupDefinition(@PathVariable("identifier") final String identifier); + + @RequestMapping( + value = "/definitions", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ResponseBody + List fetchGroupDefinitions(); + + @RequestMapping( + value = "/groups", + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsExceptions({ + @ThrowsException(status = HttpStatus.CONFLICT, exception = GroupAlreadyExists.class), + @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = GroupValidationException.class) + }) + void createGroup(@RequestBody final Group group); + + @RequestMapping( + value = "/groups", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + GroupPage fetchGroups(@RequestParam("employee") final String employee, + @RequestParam("page") final Integer page, + @RequestParam("size") final Integer size, + @RequestParam("sortColumn") final String sortColumn, + @RequestParam("sortDirection") final String sortDirection); + + @RequestMapping( + value = "/groups/{identifier}", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + Group findGroup(@PathVariable("identifier") final String identifier); + + @RequestMapping( + value = "/groups/{identifier}/leaders", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + void updateLeaders(@PathVariable("identifier") final String identifier, final Set customerIdentifiers); + + @RequestMapping( + value = "/groups/{identifier}/members", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + void updateMembers(@PathVariable("identifier") final String identifier, final Set customerIdentifiers); + + @RequestMapping( + value = "/groups/{identifier}/employee", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + void updateAssignedEmployee(@PathVariable("identifier") final String identifier, + final AssignedEmployeeHolder assignedEmployeeHolder); + + @RequestMapping( + value = "/groups/{identifier}/commands", + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + void processGroupCommand(@PathVariable("identifier") final String identifier, final GroupCommand groupCommand); + + @RequestMapping( + value = "/groups/{identifier}/commands", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + List fetchGroupCommands(@PathVariable("identifier") final String identifier); + + @RequestMapping( + value = "/groups/{identifier}/meetings", + method = RequestMethod.GET, + produces = MediaType.ALL_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + List fetchMeetings( + @PathVariable("identifier") final String groupIdentifier, + @RequestParam("upcoming") final Boolean upcoming); + + @RequestMapping( + value = "/groups/{identifier}/meetings", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ThrowsException(status = HttpStatus.NOT_FOUND, exception = GroupNotFoundException.class) + void closeMeeting(@PathVariable("identifier") final String groupIdentifier, final SignOffMeeting signOffMeeting); +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionAlreadyExists.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionAlreadyExists.java new file mode 100644 index 0000000..261411a --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionAlreadyExists.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupDefinitionAlreadyExists extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionNotFound.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionNotFound.java new file mode 100644 index 0000000..99e2676 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionNotFound.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupDefinitionNotFound extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionValidation.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionValidation.java new file mode 100644 index 0000000..75be3a1 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupDefinitionValidation.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupDefinitionValidation extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupNotFoundException.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupNotFoundException.java new file mode 100644 index 0000000..629c5e6 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupNotFoundException.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupNotFoundException extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/client/GroupValidationException.java b/api/src/main/java/io/mifos/group/api/v1/client/GroupValidationException.java new file mode 100644 index 0000000..2a3bd0b --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/client/GroupValidationException.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.client; + +public class GroupValidationException extends RuntimeException { +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/Address.java b/api/src/main/java/io/mifos/group/api/v1/domain/Address.java new file mode 100644 index 0000000..902b255 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/Address.java @@ -0,0 +1,83 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import org.hibernate.validator.constraints.NotBlank; + +public class Address { + + @NotBlank + private String street; + @NotBlank + private String city; + private String region; + private String postalCode; + @NotBlank + private String countryCode; + private String country; + + public Address() { + super(); + } + + public String getStreet() { + return this.street; + } + + public void setStreet(final String street) { + this.street = street; + } + + public String getCity() { + return this.city; + } + + public void setCity(final String city) { + this.city = city; + } + + public String getRegion() { + return this.region; + } + + public void setRegion(final String region) { + this.region = region; + } + + public String getPostalCode() { + return this.postalCode; + } + + public void setPostalCode(final String postalCode) { + this.postalCode = postalCode; + } + + public String getCountryCode() { + return this.countryCode; + } + + public void setCountryCode(final String countryCode) { + this.countryCode = countryCode; + } + + public String getCountry() { + return this.country; + } + + public void setCountry(final String country) { + this.country = country; + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/AssignedEmployeeHolder.java b/api/src/main/java/io/mifos/group/api/v1/domain/AssignedEmployeeHolder.java new file mode 100644 index 0000000..e2ce5d8 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/AssignedEmployeeHolder.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +public class AssignedEmployeeHolder { + + private String identifier; + + public AssignedEmployeeHolder() { + super(); + } + + public String getIdentifier() { + return this.identifier; + } + + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/Attendee.java b/api/src/main/java/io/mifos/group/api/v1/domain/Attendee.java new file mode 100644 index 0000000..147be78 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/Attendee.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.constraints.NotNull; + +public class Attendee { + + @NotBlank + private String customerIdentifier; + @NotNull + private Status status; + + public Attendee() { + super(); + } + + public String getCustomerIdentifier() { + return this.customerIdentifier; + } + + public void setCustomerIdentifier(final String customerIdentifier) { + this.customerIdentifier = customerIdentifier; + } + + public String getStatus() { + return this.status.name(); + } + + public void setStatus(final String status) { + this.status = Status.valueOf(status); + } + + public enum Status { + EXPECTED, + ATTENDED, + MISSED + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/Cycle.java b/api/src/main/java/io/mifos/group/api/v1/domain/Cycle.java new file mode 100644 index 0000000..261c651 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/Cycle.java @@ -0,0 +1,67 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import javax.validation.constraints.NotNull; + +public class Cycle { + + @NotNull + private Integer numberOfMeetings; + @NotNull + private Frequency frequency; + private Adjustment adjustment; + + public Cycle() { + super(); + } + + public Integer getNumberOfMeetings() { + return this.numberOfMeetings; + } + + public void setNumberOfMeetings(final Integer numberOfMeetings) { + this.numberOfMeetings = numberOfMeetings; + } + + public String getFrequency() { + return this.frequency.name(); + } + + public void setFrequency(final String frequency) { + this.frequency = Frequency.valueOf(frequency); + } + + public String getAdjustment() { + return this.adjustment.name(); + } + + public void setAdjustment(final String adjustment) { + this.adjustment = Adjustment.valueOf(adjustment); + } + + public enum Frequency { + DAILY, + WEEKLY, + FORTNIGHTLY, + MONTHLY + } + + public enum Adjustment { + NEXT_BUSINESS_DAY, + SKIP + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/Group.java b/api/src/main/java/io/mifos/group/api/v1/domain/Group.java new file mode 100644 index 0000000..c38d955 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/Group.java @@ -0,0 +1,202 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Objects; +import java.util.Set; + +public class Group { + + @NotBlank + private String identifier; + @NotBlank + private String groupDefinitionIdentifier; + @NotBlank + private String name; + @Size(max = 15) + private Set leaders; + @Size(min = 1, max = 60) + private Set members; + @NotBlank + private String office; + @NotBlank + private String assignedEmployee; + @NotNull + private Integer weekday; + private Status status; + @Valid + private Address address; + private String createdOn; + private String createdBy; + private String lastModifiedOn; + private String lastModifiedBy; + + public Group() { + super(); + } + + public String getGroupDefinitionIdentifier() { + return this.groupDefinitionIdentifier; + } + + public void setGroupDefinitionIdentifier(final String groupDefinitionIdentifier) { + this.groupDefinitionIdentifier = groupDefinitionIdentifier; + } + + public String getIdentifier() { + return this.identifier; + } + + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + public String getName() { + return this.name; + } + + public void setName(final String name) { + this.name = name; + } + + public Set getLeaders() { + return this.leaders; + } + + public void setLeaders(final Set leaders) { + this.leaders = leaders; + } + + public Set getMembers() { + return this.members; + } + + public void setMembers(final Set members) { + this.members = members; + } + + public String getOffice() { + return this.office; + } + + public void setOffice(final String office) { + this.office = office; + } + + public String getAssignedEmployee() { + return this.assignedEmployee; + } + + public void setAssignedEmployee(final String assignedEmployee) { + this.assignedEmployee = assignedEmployee; + } + + public String getStatus() { + return this.status.name(); + } + + public void setStatus(final String status) { + this.status = Status.valueOf(status); + } + + public Integer getWeekday() { + return this.weekday; + } + + public void setWeekday(final Integer weekday) { + this.weekday = weekday; + } + + public Address getAddress() { + return this.address; + } + + public void setAddress(final Address address) { + this.address = address; + } + + public String getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final String createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } + + public String getLastModifiedOn() { + return this.lastModifiedOn; + } + + public void setLastModifiedOn(final String lastModifiedOn) { + this.lastModifiedOn = lastModifiedOn; + } + + public String getLastModifiedBy() { + return this.lastModifiedBy; + } + + public void setLastModifiedBy(final String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } + + public enum Weekday { + MONDAY(1), + TUESDAY(2), + WEDNESDAY(3), + THURSDAY(4), + FRIDAY(5), + SATURDAY(6), + SUNDAY(7); + + private Integer value; + + Weekday(final Integer value) { + this.value = value; + } + + public static Weekday from(final Integer dayOfWeek) { + for (Weekday weekday : Weekday.values()) { + if (Objects.equals(weekday.value, dayOfWeek)) { + return weekday; + } + } + throw new IllegalArgumentException("Unknown day of week '" + dayOfWeek + "'."); + } + + public Integer getValue() { + return this.value; + } + } + + public enum Status { + PENDING, + ACTIVE, + CLOSED + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/GroupCommand.java b/api/src/main/java/io/mifos/group/api/v1/domain/GroupCommand.java new file mode 100644 index 0000000..1941d47 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/GroupCommand.java @@ -0,0 +1,66 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +public class GroupCommand { + + private Action action; + private String note; + private String createdBy; + private String createdOn; + + public GroupCommand() { + super(); + } + + public String getAction() { + return this.action.name(); + } + + public void setAction(final String action) { + this.action = Action.valueOf(action); + } + + public String getNote() { + return this.note; + } + + public void setNote(final String note) { + this.note = note; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } + + public String getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final String createdOn) { + this.createdOn = createdOn; + } + + public enum Action { + ACTIVATE, + CLOSE, + REOPEN + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/GroupDefinition.java b/api/src/main/java/io/mifos/group/api/v1/domain/GroupDefinition.java new file mode 100644 index 0000000..70f7726 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/GroupDefinition.java @@ -0,0 +1,116 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +public class GroupDefinition { + + @NotBlank + private String identifier; + private String description; + @NotNull + @Min(1L) + private Integer minimalSize; + @Min(2L) + private Integer maximalSize; + @Valid + private Cycle cycle; + private String createOn; + private String createdBy; + private String lastModifiedOn; + private String lastModifiedBy; + + public GroupDefinition() { + super(); + } + + public String getIdentifier() { + return this.identifier; + } + + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public Integer getMinimalSize() { + return this.minimalSize; + } + + public void setMinimalSize(final Integer minimalSize) { + this.minimalSize = minimalSize; + } + + public Integer getMaximalSize() { + return this.maximalSize; + } + + public void setMaximalSize(final Integer maximalSize) { + this.maximalSize = maximalSize; + } + + public Cycle getCycle() { + return this.cycle; + } + + public void setCycle(final Cycle cycle) { + this.cycle = cycle; + } + + public String getCreateOn() { + return this.createOn; + } + + public void setCreateOn(final String createOn) { + this.createOn = createOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } + + public String getLastModifiedOn() { + return this.lastModifiedOn; + } + + public void setLastModifiedOn(final String lastModifiedOn) { + this.lastModifiedOn = lastModifiedOn; + } + + public String getLastModifiedBy() { + return this.lastModifiedBy; + } + + public void setLastModifiedBy(final String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/GroupPage.java b/api/src/main/java/io/mifos/group/api/v1/domain/GroupPage.java new file mode 100644 index 0000000..7ddd739 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/GroupPage.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import java.util.List; + +public class GroupPage { + + private List groups; + private Integer totalPages; + private Long totalElements; + + public GroupPage() { + super(); + } + + public List getGroups() { + return this.groups; + } + + public void setGroups(final List groups) { + this.groups = groups; + } + + public Integer getTotalPages() { + return this.totalPages; + } + + public void setTotalPages(final Integer totalPages) { + this.totalPages = totalPages; + } + + public Long getTotalElements() { + return this.totalElements; + } + + public void setTotalElements(final Long totalElements) { + this.totalElements = totalElements; + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/Meeting.java b/api/src/main/java/io/mifos/group/api/v1/domain/Meeting.java new file mode 100644 index 0000000..7770ac3 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/Meeting.java @@ -0,0 +1,127 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import org.hibernate.validator.constraints.NotBlank; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Set; + +public class Meeting { + + @NotBlank + private Integer meetingSequence; + @NotBlank + private String groupIdentifier; + @NotNull + private Integer currentCycle; + @NotEmpty + @Valid + private Set attendees; + private String scheduledFor; + @Valid + private Address location; + private String heldOn; + private Long duration; + private String createdOn; + private String createdBy; + + public Meeting() { + super(); + } + + public Integer getMeetingSequence() { + return this.meetingSequence; + } + + public void setMeetingSequence(final Integer meetingSequence) { + this.meetingSequence = meetingSequence; + } + + public String getGroupIdentifier() { + return this.groupIdentifier; + } + + public void setGroupIdentifier(final String groupIdentifier) { + this.groupIdentifier = groupIdentifier; + } + + public Integer getCurrentCycle() { + return this.currentCycle; + } + + public void setCurrentCycle(final Integer currentCycle) { + this.currentCycle = currentCycle; + } + + public Set getAttendees() { + return this.attendees; + } + + public void setAttendees(final Set attendees) { + this.attendees = attendees; + } + + public String getScheduledFor() { + return this.scheduledFor; + } + + public void setScheduledFor(final String scheduledFor) { + this.scheduledFor = scheduledFor; + } + + public Address getLocation() { + return this.location; + } + + public void setLocation(final Address location) { + this.location = location; + } + + public String getHeldOn() { + return this.heldOn; + } + + public void setHeldOn(final String heldOn) { + this.heldOn = heldOn; + } + + public Long getDuration() { + return this.duration; + } + + public void setDuration(final Long duration) { + this.duration = duration; + } + + public String getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final String createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } +} diff --git a/api/src/main/java/io/mifos/group/api/v1/domain/SignOffMeeting.java b/api/src/main/java/io/mifos/group/api/v1/domain/SignOffMeeting.java new file mode 100644 index 0000000..a8e71f7 --- /dev/null +++ b/api/src/main/java/io/mifos/group/api/v1/domain/SignOffMeeting.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.api.v1.domain; + +import javax.validation.Valid; +import java.util.Set; + +public class SignOffMeeting { + + @Valid + private Integer cycle; + private Integer sequence; + private Set attendees; + private Long duration; + + public SignOffMeeting() { + super(); + } + + public Integer getCycle() { + return this.cycle; + } + + public void setCycle(final Integer cycle) { + this.cycle = cycle; + } + + public Integer getSequence() { + return this.sequence; + } + + public void setSequence(final Integer sequence) { + this.sequence = sequence; + } + + public Set getAttendees() { + return this.attendees; + } + + public void setAttendees(final Set attendees) { + this.attendees = attendees; + } + + public Long getDuration() { + return this.duration; + } + + public void setDuration(final Long duration) { + this.duration = duration; + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b8c09e6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,29 @@ +group 'io.mifos' + +task publishApiToMavenLocal { + dependsOn gradle.includedBuild('api').task(':publishToMavenLocal') +} + +task publishServiceToMavenLocal { + mustRunAfter publishApiToMavenLocal + dependsOn gradle.includedBuild('service').task(':publishToMavenLocal') +} + +task publishComponentTestToMavenLocal { + mustRunAfter publishApiToMavenLocal + mustRunAfter publishServiceToMavenLocal + dependsOn gradle.includedBuild('component-test').task(':publishToMavenLocal') +} + +task publishToMavenLocal { + group 'all' + dependsOn publishApiToMavenLocal + dependsOn publishServiceToMavenLocal + dependsOn publishComponentTestToMavenLocal +} + +task prepareForTest { + group 'all' + dependsOn publishToMavenLocal + dependsOn gradle.includedBuild('component-test').task(':build') +} diff --git a/component-test/build.gradle b/component-test/build.gradle new file mode 100644 index 0000000..b9a16bc --- /dev/null +++ b/component-test/build.gradle @@ -0,0 +1,38 @@ +buildscript { + ext { + springBootVersion = '1.4.1.RELEASE' + } + + repositories { + jcenter() + } + + dependencies { + classpath ("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +plugins { + id "com.github.hierynomus.license" version "0.13.1" +} + +apply from: '../shared.gradle' + +dependencies { + compile( + [group: 'io.mifos.group', name: 'api', version: project.version], + [group: 'io.mifos.group', name: 'service', version: project.version], + [group: 'io.mifos.core', name: 'api', version: versions.frameworkapi], + [group: 'io.mifos.core', name: 'test', version: versions.frameworktest], + [group: 'io.mifos.anubis', name: 'test', version: versions.frameworkanubis], + [group: 'org.springframework.boot', name: 'spring-boot-starter-test'] + ) +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } +} diff --git a/component-test/settings.gradle b/component-test/settings.gradle new file mode 100644 index 0000000..b2e36e3 --- /dev/null +++ b/component-test/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'component-test' diff --git a/component-test/src/main/java/io/mifos/group/TestGroup.java b/component-test/src/main/java/io/mifos/group/TestGroup.java new file mode 100644 index 0000000..91110f9 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/TestGroup.java @@ -0,0 +1,275 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group; + +import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule; +import io.mifos.core.api.context.AutoUserContext; +import io.mifos.core.test.env.TestEnvironment; +import io.mifos.core.test.fixture.TenantDataStoreContextTestRule; +import io.mifos.core.test.fixture.cassandra.CassandraInitializer; +import io.mifos.core.test.fixture.mariadb.MariaDBInitializer; +import io.mifos.core.test.listener.EnableEventRecording; +import io.mifos.core.test.listener.EventRecorder; +import io.mifos.group.api.v1.EventConstants; +import io.mifos.group.api.v1.client.GroupClient; +import io.mifos.group.api.v1.domain.AssignedEmployeeHolder; +import io.mifos.group.api.v1.domain.Attendee; +import io.mifos.group.api.v1.domain.Group; +import io.mifos.group.api.v1.domain.GroupCommand; +import io.mifos.group.api.v1.domain.GroupDefinition; +import io.mifos.group.api.v1.domain.Meeting; +import io.mifos.group.api.v1.domain.SignOffMeeting; +import io.mifos.group.service.GroupConfiguration; +import io.mifos.group.util.GroupDefinitionGenerator; +import io.mifos.group.util.GroupGenerator; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.*; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.netflix.feign.EnableFeignClients; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.Clock; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +public class TestGroup { + private static final String APP_NAME = "group-v1"; + private static final String TEST_USER = "ranefer"; + + private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME); + private final static CassandraInitializer cassandraInitializer = new CassandraInitializer(); + private final static MariaDBInitializer mariaDBInitializer = new MariaDBInitializer(); + private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer); + + @ClassRule + public static TestRule orderClassRules = RuleChain + .outerRule(testEnvironment) + .around(cassandraInitializer) + .around(mariaDBInitializer) + .around(tenantDataStoreContext); + + @Rule + public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment + = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize); + @Autowired + private GroupClient groupClient; + @Autowired + private EventRecorder eventRecorder; + + private AutoUserContext userContext; + + public TestGroup() { + super(); + } + + @Before + public void prepTest() { + userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TestGroup.TEST_USER); + } + + @After + public void cleanTest() { + userContext.close(); + } + + public boolean waitForInitialize() { + try { + return this.eventRecorder.wait(EventConstants.INITIALIZE, EventConstants.INITIALIZE); + } catch (final InterruptedException e) { + throw new IllegalStateException(e); + } + } + + @Test + public void shouldCreateGroup() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + + this.groupClient.createGroupDefinition(randomGroupDefinition); + + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier()); + + this.groupClient.createGroup(randomGroup); + + this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier()); + + final Group fetchedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals(randomGroup.getIdentifier(), fetchedGroup.getIdentifier()); + Assert.assertEquals(randomGroup.getGroupDefinitionIdentifier(), fetchedGroup.getGroupDefinitionIdentifier()); + Assert.assertEquals(randomGroup.getName(), fetchedGroup.getName()); + Assert.assertEquals(randomGroup.getOffice(), fetchedGroup.getOffice()); + Assert.assertEquals(randomGroup.getAssignedEmployee(), fetchedGroup.getAssignedEmployee()); + Assert.assertEquals(randomGroup.getWeekday(), fetchedGroup.getWeekday()); + Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus()); + Assert.assertEquals(randomGroup.getLeaders().size(), fetchedGroup.getLeaders().size()); + Assert.assertEquals(randomGroup.getMembers().size(), fetchedGroup.getMembers().size()); + Assert.assertNotNull(fetchedGroup.getCreatedBy()); + Assert.assertNotNull(fetchedGroup.getCreatedOn()); + Assert.assertNull(fetchedGroup.getLastModifiedBy()); + Assert.assertNull(fetchedGroup.getLastModifiedOn()); + Assert.assertNotNull(fetchedGroup.getAddress()); + } + + @Test + public void shouldActivateCommand() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + this.groupClient.createGroupDefinition(randomGroupDefinition); + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier()); + this.groupClient.createGroup(randomGroup); + this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier()); + + final Group fetchedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus()); + + final GroupCommand activate = new GroupCommand(); + activate.setAction(GroupCommand.Action.ACTIVATE.name()); + activate.setNote(RandomStringUtils.randomAlphanumeric(256)); + activate.setCreatedBy(TestGroup.TEST_USER); + activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); + + this.groupClient.processGroupCommand(randomGroup.getIdentifier(), activate); + this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier()); + + final Group activatedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals(Group.Status.ACTIVE.name(), activatedGroup.getStatus()); + + final List groupCommands = this.groupClient.fetchGroupCommands(activatedGroup.getIdentifier()); + Assert.assertTrue(groupCommands.size() == 1); + final GroupCommand groupCommand = groupCommands.get(0); + Assert.assertEquals(activate.getAction(), groupCommand.getAction()); + Assert.assertEquals(activate.getNote(), groupCommand.getNote()); + Assert.assertEquals(activate.getCreatedBy(), groupCommand.getCreatedBy()); + Assert.assertNotNull(groupCommand.getCreatedOn()); + + final List meetings = this.groupClient.fetchMeetings(activatedGroup.getIdentifier(), Boolean.FALSE); + Assert.assertNotNull(meetings); + Assert.assertEquals(randomGroupDefinition.getCycle().getNumberOfMeetings(), Integer.valueOf(meetings.size())); + + final Meeting meeting2signOff = meetings.get(0); + final SignOffMeeting signOffMeeting = new SignOffMeeting(); + signOffMeeting.setCycle(meeting2signOff.getCurrentCycle()); + signOffMeeting.setSequence(meeting2signOff.getMeetingSequence()); + signOffMeeting.setDuration(120L); + signOffMeeting.setAttendees(meeting2signOff.getAttendees() + .stream() + .map(attendee -> { + attendee.setStatus(Attendee.Status.ATTENDED.name()); + return attendee; + }) + .collect(Collectors.toSet()) + ); + + this.groupClient.closeMeeting(activatedGroup.getIdentifier(), signOffMeeting); + this.eventRecorder.wait(EventConstants.PUT_GROUP, activatedGroup.getIdentifier()); + } + + @Test + public void shouldUpdateLeaders() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + this.groupClient.createGroupDefinition(randomGroupDefinition); + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier()); + this.groupClient.createGroup(randomGroup); + this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier()); + + final int currentLeadersSize = randomGroup.getLeaders().size(); + randomGroup.getLeaders().add(RandomStringUtils.randomAlphanumeric(32)); + this.groupClient.updateLeaders(randomGroup.getIdentifier(), randomGroup.getLeaders()); + this.eventRecorder.wait(EventConstants.PUT_GROUP, randomGroup.getIdentifier()); + + final Group fetchedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals((currentLeadersSize + 1), fetchedGroup.getLeaders().size()); + } + + @Test + public void shouldUpdateMembers() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + this.groupClient.createGroupDefinition(randomGroupDefinition); + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier()); + this.groupClient.createGroup(randomGroup); + this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier()); + + final int currentMembersSize = randomGroup.getMembers().size(); + randomGroup.getMembers().addAll(Arrays.asList( + RandomStringUtils.randomAlphanumeric(32), + RandomStringUtils.randomAlphanumeric(32) + )); + this.groupClient.updateMembers(randomGroup.getIdentifier(), randomGroup.getMembers()); + this.eventRecorder.wait(EventConstants.PUT_GROUP, randomGroup.getIdentifier()); + + final Group fetchedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals((currentMembersSize + 2), fetchedGroup.getMembers().size()); + } + + @Test + public void shouldUpdateAssignedEmployee() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + this.groupClient.createGroupDefinition(randomGroupDefinition); + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier()); + this.groupClient.createGroup(randomGroup); + this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier()); + + final AssignedEmployeeHolder anotherEmployee = new AssignedEmployeeHolder(); + anotherEmployee.setIdentifier(RandomStringUtils.randomAlphanumeric(32)); + + this.groupClient.updateAssignedEmployee(randomGroup.getIdentifier(), anotherEmployee); + this.eventRecorder.wait(EventConstants.PUT_GROUP, randomGroup.getIdentifier()); + + final Group fetchedGroup = this.groupClient.findGroup(randomGroup.getIdentifier()); + Assert.assertEquals(anotherEmployee.getIdentifier(), fetchedGroup.getAssignedEmployee()); + } + + @Configuration + @EnableEventRecording + @EnableFeignClients(basePackages = {"io.mifos.group.api.v1.client"}) + @RibbonClient(name = APP_NAME) + @Import({GroupConfiguration.class}) + @ComponentScan("io.mifos.group.listener") + public static class TestConfiguration { + public TestConfiguration() { + super(); + } + + @Bean() + public Logger logger() { + return LoggerFactory.getLogger("test-logger"); + } + } +} diff --git a/component-test/src/main/java/io/mifos/group/TestGroupDefinition.java b/component-test/src/main/java/io/mifos/group/TestGroupDefinition.java new file mode 100644 index 0000000..b33ad72 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/TestGroupDefinition.java @@ -0,0 +1,139 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group; + +import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule; +import io.mifos.core.api.context.AutoUserContext; +import io.mifos.core.lang.TenantContextHolder; +import io.mifos.core.test.env.TestEnvironment; +import io.mifos.core.test.fixture.TenantDataStoreContextTestRule; +import io.mifos.core.test.fixture.cassandra.CassandraInitializer; +import io.mifos.core.test.fixture.mariadb.MariaDBInitializer; +import io.mifos.core.test.listener.EnableEventRecording; +import io.mifos.core.test.listener.EventRecorder; +import io.mifos.group.api.v1.EventConstants; +import io.mifos.group.api.v1.client.GroupClient; +import io.mifos.group.api.v1.domain.GroupDefinition; +import io.mifos.group.service.GroupConfiguration; +import io.mifos.group.util.GroupDefinitionGenerator; +import org.junit.*; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.netflix.feign.EnableFeignClients; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +public class TestGroupDefinition { + private static final String APP_NAME = "group-v1"; + private static final String TEST_USER = "ranefer"; + + private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME); + private final static CassandraInitializer cassandraInitializer = new CassandraInitializer(); + private final static MariaDBInitializer mariaDBInitializer = new MariaDBInitializer(); + private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer); + + @ClassRule + public static TestRule orderClassRules = RuleChain + .outerRule(testEnvironment) + .around(cassandraInitializer) + .around(mariaDBInitializer) + .around(tenantDataStoreContext); + + @Rule + public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment + = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize); + + @Autowired + private GroupClient groupClient; + @Autowired + private EventRecorder eventRecorder; + + private AutoUserContext userContext; + + public TestGroupDefinition() { + super(); + } + + @Before + public void prepTest() { + userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TestGroupDefinition.TEST_USER); + } + + @After + public void cleanTest() { + TenantContextHolder.clear(); + userContext.close(); + } + + public boolean waitForInitialize() { + try { + return this.eventRecorder.wait(EventConstants.INITIALIZE, EventConstants.INITIALIZE); + } catch (final InterruptedException e) { + throw new IllegalStateException(e); + } + } + + @Test + public void shouldCreateGroupDefinition() throws Exception { + final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition(); + this.groupClient.createGroupDefinition(randomGroupDefinition); + + this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier()); + + final GroupDefinition fetchedGroupDefinition = this.groupClient.findGroupDefinition(randomGroupDefinition.getIdentifier()); + + Assert.assertEquals(randomGroupDefinition.getIdentifier(), fetchedGroupDefinition.getIdentifier()); + Assert.assertEquals(randomGroupDefinition.getDescription(), fetchedGroupDefinition.getDescription()); + Assert.assertEquals(randomGroupDefinition.getMinimalSize(), fetchedGroupDefinition.getMinimalSize()); + Assert.assertEquals(randomGroupDefinition.getMaximalSize(), fetchedGroupDefinition.getMaximalSize()); + Assert.assertNotNull(fetchedGroupDefinition.getCycle()); + Assert.assertEquals(randomGroupDefinition.getCycle().getNumberOfMeetings(), fetchedGroupDefinition.getCycle().getNumberOfMeetings()); + Assert.assertEquals(randomGroupDefinition.getCycle().getFrequency(), fetchedGroupDefinition.getCycle().getFrequency()); + Assert.assertEquals(randomGroupDefinition.getCycle().getAdjustment(), fetchedGroupDefinition.getCycle().getAdjustment()); + Assert.assertNotNull(fetchedGroupDefinition.getCreatedBy()); + Assert.assertNotNull(fetchedGroupDefinition.getCreateOn()); + Assert.assertNull(fetchedGroupDefinition.getLastModifiedBy()); + Assert.assertNull(fetchedGroupDefinition.getLastModifiedOn()); + } + + @Configuration + @EnableEventRecording + @EnableFeignClients(basePackages = {"io.mifos.group.api.v1.client"}) + @RibbonClient(name = APP_NAME) + @Import({GroupConfiguration.class}) + @ComponentScan("io.mifos.group.listener") + public static class TestConfiguration { + public TestConfiguration() { + super(); + } + + @Bean() + public Logger logger() { + return LoggerFactory.getLogger("test-logger"); + } + } +} diff --git a/component-test/src/main/java/io/mifos/group/listener/GroupDefinitionEventListener.java b/component-test/src/main/java/io/mifos/group/listener/GroupDefinitionEventListener.java new file mode 100644 index 0000000..a64b355 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/listener/GroupDefinitionEventListener.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.listener; + +import io.mifos.core.lang.config.TenantHeaderFilter; +import io.mifos.core.test.listener.EventRecorder; +import io.mifos.group.api.v1.EventConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +@SuppressWarnings("unused") +@Component +public class GroupDefinitionEventListener { + + private final EventRecorder eventRecorder; + + @Autowired + public GroupDefinitionEventListener(final EventRecorder eventRecorder) { + super(); + this.eventRecorder = eventRecorder; + } + + @JmsListener( + subscription = EventConstants.DESTINATION, + destination = EventConstants.DESTINATION, + selector = EventConstants.SELECTOR_POST_GROUP_DEFINITION + ) + public void onGroupDefinitionCreated(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant, + final String payload) { + this.eventRecorder.event(tenant, EventConstants.POST_GROUP_DEFINITION, payload, String.class); + } +} diff --git a/component-test/src/main/java/io/mifos/group/listener/GroupEventListener.java b/component-test/src/main/java/io/mifos/group/listener/GroupEventListener.java new file mode 100644 index 0000000..32446b3 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/listener/GroupEventListener.java @@ -0,0 +1,67 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.listener; + +import io.mifos.core.lang.config.TenantHeaderFilter; +import io.mifos.core.test.listener.EventRecorder; +import io.mifos.group.api.v1.EventConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +@SuppressWarnings("unused") +@Component +public class GroupEventListener { + + private final EventRecorder eventRecorder; + + @Autowired + public GroupEventListener(final EventRecorder eventRecorder) { + super(); + this.eventRecorder = eventRecorder; + } + + @JmsListener( + subscription = EventConstants.DESTINATION, + destination = EventConstants.DESTINATION, + selector = EventConstants.SELECTOR_POST_GROUP + ) + public void onGroupCreated(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant, + final String payload) { + this.eventRecorder.event(tenant, EventConstants.POST_GROUP, payload, String.class); + } + + @JmsListener( + subscription = EventConstants.DESTINATION, + destination = EventConstants.DESTINATION, + selector = EventConstants.SELECTOR_ACTIVATE_GROUP + ) + public void onGroupActivated(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant, + final String payload) { + this.eventRecorder.event(tenant, EventConstants.ACTIVATE_GROUP, payload, String.class); + } + + @JmsListener( + subscription = EventConstants.DESTINATION, + destination = EventConstants.DESTINATION, + selector = EventConstants.SELECTOR_PUT_GROUP + ) + public void onGroupUpdated(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant, + final String payload) { + this.eventRecorder.event(tenant, EventConstants.PUT_GROUP, payload, String.class); + } +} diff --git a/component-test/src/main/java/io/mifos/group/listener/MigrationEventListener.java b/component-test/src/main/java/io/mifos/group/listener/MigrationEventListener.java new file mode 100644 index 0000000..df7c8fb --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/listener/MigrationEventListener.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.listener; + +import io.mifos.core.lang.config.TenantHeaderFilter; +import io.mifos.core.test.listener.EventRecorder; +import io.mifos.group.api.v1.EventConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +@SuppressWarnings("unused") +@Component +public class MigrationEventListener { + + private final EventRecorder eventRecorder; + + @Autowired + public MigrationEventListener(final EventRecorder eventRecorder) { + super(); + this.eventRecorder = eventRecorder; + } + + @JmsListener( + subscription = EventConstants.DESTINATION, + destination = EventConstants.DESTINATION, + selector = EventConstants.SELECTOR_INITIALIZE + ) + public void onInitialization(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant, + final String payload) { + this.eventRecorder.event(tenant, EventConstants.INITIALIZE, payload, String.class); + } +} diff --git a/component-test/src/main/java/io/mifos/group/util/GroupDefinitionGenerator.java b/component-test/src/main/java/io/mifos/group/util/GroupDefinitionGenerator.java new file mode 100644 index 0000000..7024152 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/util/GroupDefinitionGenerator.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.util; + +import io.mifos.group.api.v1.domain.Cycle; +import io.mifos.group.api.v1.domain.GroupDefinition; +import org.apache.commons.lang3.RandomStringUtils; + +public class GroupDefinitionGenerator { + + private GroupDefinitionGenerator() { + super(); + } + + public static GroupDefinition createRandomGroupDefinition() { + final GroupDefinition groupDefinition = new GroupDefinition(); + groupDefinition.setIdentifier(RandomStringUtils.randomAlphanumeric(32)); + groupDefinition.setDescription(RandomStringUtils.randomAlphabetic(2048)); + groupDefinition.setMinimalSize(10); + groupDefinition.setMaximalSize(30); + final Cycle cycle = new Cycle(); + cycle.setNumberOfMeetings(25); + cycle.setFrequency(Cycle.Frequency.WEEKLY.name()); + cycle.setAdjustment(Cycle.Adjustment.NEXT_BUSINESS_DAY.name()); + groupDefinition.setCycle(cycle); + return groupDefinition; + } +} diff --git a/component-test/src/main/java/io/mifos/group/util/GroupGenerator.java b/component-test/src/main/java/io/mifos/group/util/GroupGenerator.java new file mode 100644 index 0000000..d00bde4 --- /dev/null +++ b/component-test/src/main/java/io/mifos/group/util/GroupGenerator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2017 The Mifos Initiative + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.util; + +import io.mifos.group.api.v1.domain.Address; +import io.mifos.group.api.v1.domain.Group; +import org.apache.commons.lang3.RandomStringUtils; + +import java.util.Arrays; +import java.util.HashSet; + +public class GroupGenerator { + + private GroupGenerator() { + super(); + } + + public static Group createRandomGroup(final String definitionIdentifier) { + final Group group = new Group(); + group.setIdentifier(RandomStringUtils.randomAlphanumeric(32)); + group.setGroupDefinitionIdentifier(definitionIdentifier); + group.setName(RandomStringUtils.randomAlphanumeric(256)); + group.setOffice(RandomStringUtils.randomAlphanumeric(32)); + group.setAssignedEmployee(RandomStringUtils.randomAlphanumeric(32)); + group.setLeaders(new HashSet<>(Arrays.asList( + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32) + ))); + group.setMembers(new HashSet<>(Arrays.asList( + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32), + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32), + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32), + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32), + RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32) + ))); + group.setWeekday(Group.Weekday.WEDNESDAY.getValue()); + final Address address = new Address(); + address.setStreet(RandomStringUtils.randomAlphanumeric(256)); + address.setCity(RandomStringUtils.randomAlphanumeric(256)); + address.setRegion(RandomStringUtils.randomAlphanumeric(256)); + address.setPostalCode(RandomStringUtils.randomAlphanumeric(2)); + address.setCountry(RandomStringUtils.randomAlphanumeric(256)); + address.setCountryCode(RandomStringUtils.randomAlphanumeric(2)); + group.setAddress(address); + return group; + } +} diff --git a/component-test/src/main/resources/logback.xml b/component-test/src/main/resources/logback.xml new file mode 100644 index 0000000..e0b1e5e --- /dev/null +++ b/component-test/src/main/resources/logback.xml @@ -0,0 +1,28 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..c733004 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4aca373 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Mar 16 14:47:20 CET 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4453cce --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/service/build.gradle b/service/build.gradle new file mode 100644 index 0000000..09e8a22 --- /dev/null +++ b/service/build.gradle @@ -0,0 +1,63 @@ +buildscript { + ext { + springBootVersion = '1.4.1.RELEASE' + } + + repositories { + jcenter() + } + + dependencies { + classpath ("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +plugins { + id "com.github.hierynomus.license" version "0.13.1" +} + +apply from: '../shared.gradle' + +apply plugin: 'spring-boot' + +springBoot { + executable = true + classifier = 'boot' +} + +dependencies { + compile( + [group: 'org.springframework.cloud', name: 'spring-cloud-starter-config'], + [group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka'], + [group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'], + [group: 'io.mifos.group', name: 'api', version: project.version], + [group: 'io.mifos.anubis', name: 'library', version: versions.frameworkanubis], + [group: 'com.google.code.gson', name: 'gson'], + [group: 'io.mifos.core', name: 'lang', version: versions.frameworklang], + [group: 'io.mifos.core', name: 'async', version: versions.frameworkasync], + [group: 'io.mifos.core', name: 'cassandra', version: versions.frameworkcassandra], + [group: 'io.mifos.core', name: 'mariadb', version: versions.frameworkmariadb], + [group: 'io.mifos.core', name: 'command', version: versions.frameworkcommand], + [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator] + ) +} + +publishToMavenLocal.dependsOn bootRepackage + +publishing { + publications { + service(MavenPublication) { + from components.java + groupId project.group + artifactId project.name + version project.version + } + bootService(MavenPublication) { + // "boot" jar + artifact ("$buildDir/libs/$project.name-$version-boot.jar") + groupId project.group + artifactId ("$project.name-boot") + version project.version + } + } +} diff --git a/service/settings.gradle b/service/settings.gradle new file mode 100644 index 0000000..1ed471d --- /dev/null +++ b/service/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'service' diff --git a/service/src/main/java/io/mifos/group/service/GroupApplication.java b/service/src/main/java/io/mifos/group/service/GroupApplication.java new file mode 100644 index 0000000..1dac37d --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/GroupApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service; + +import org.springframework.boot.SpringApplication; + +public class GroupApplication { + + public GroupApplication() { + super(); + } + + public static void main(String[] args) { + SpringApplication.run(GroupConfiguration.class, args); + } +} diff --git a/service/src/main/java/io/mifos/group/service/GroupConfiguration.java b/service/src/main/java/io/mifos/group/service/GroupConfiguration.java new file mode 100644 index 0000000..0ecceeb --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/GroupConfiguration.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service; + +import io.mifos.anubis.config.EnableAnubis; +import io.mifos.core.async.config.EnableAsync; +import io.mifos.core.cassandra.config.EnableCassandra; +import io.mifos.core.command.config.EnableCommandProcessing; +import io.mifos.core.lang.config.EnableServiceException; +import io.mifos.core.lang.config.EnableTenantContext; +import io.mifos.core.mariadb.config.EnableMariaDB; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@SuppressWarnings("WeakerAccess") +@Configuration +@EnableAutoConfiguration +@EnableDiscoveryClient +@EnableAsync +@EnableTenantContext +@EnableCassandra +@EnableMariaDB +@EnableCommandProcessing +@EnableAnubis +@EnableServiceException +@ComponentScan({ + "io.mifos.group.service.rest", + "io.mifos.group.service.internal.service", + "io.mifos.group.service.internal.repository", + "io.mifos.group.service.internal.command.handler" +}) +@EnableJpaRepositories({ + "io.mifos.group.service.internal.repository" +}) +public class GroupConfiguration { + + public GroupConfiguration() { + super(); + } + + @Bean(name = ServiceConstants.LOGGER_NAME) + public Logger logger() { + return LoggerFactory.getLogger(ServiceConstants.LOGGER_NAME); + } +} diff --git a/service/src/main/java/io/mifos/group/service/ServiceConstants.java b/service/src/main/java/io/mifos/group/service/ServiceConstants.java new file mode 100644 index 0000000..07ae9a0 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/ServiceConstants.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service; + +public interface ServiceConstants { + String LOGGER_NAME = "group-logger"; +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/ActivateGroupCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/ActivateGroupCommand.java new file mode 100644 index 0000000..6e03951 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/ActivateGroupCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.GroupCommand; + +public class ActivateGroupCommand { + + private final String identifier; + private final GroupCommand groupCommand; + + public ActivateGroupCommand(final String identifier, final GroupCommand groupCommand) { + super(); + this.identifier = identifier; + this.groupCommand = groupCommand; + } + + public String identifier() { + return this.identifier; + } + + public GroupCommand groupCommand() { + return this.groupCommand; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/CloseGroupCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/CloseGroupCommand.java new file mode 100644 index 0000000..e599458 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/CloseGroupCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.GroupCommand; + +public class CloseGroupCommand { + + private final String identifier; + private final GroupCommand groupCommand; + + public CloseGroupCommand(final String identifier, final GroupCommand groupCommand) { + super(); + this.identifier = identifier; + this.groupCommand = groupCommand; + } + + public String identifier() { + return this.identifier; + } + + public GroupCommand groupCommand() { + return this.groupCommand; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupCommand.java new file mode 100644 index 0000000..88515db --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupCommand.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.Group; + +public class CreateGroupCommand { + + private final Group group; + + public CreateGroupCommand(final Group group) { + super(); + this.group = group; + } + + public Group group() { + return this.group; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupDefinitionCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupDefinitionCommand.java new file mode 100644 index 0000000..5c85f8d --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/CreateGroupDefinitionCommand.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.GroupDefinition; + +public class CreateGroupDefinitionCommand { + + private final GroupDefinition groupDefinition; + + public CreateGroupDefinitionCommand(final GroupDefinition groupDefinition) { + super(); + this.groupDefinition = groupDefinition; + } + + public GroupDefinition groupDefinition() { + return this.groupDefinition; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/InitializeServiceCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/InitializeServiceCommand.java new file mode 100644 index 0000000..a5e28da --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/InitializeServiceCommand.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +public class InitializeServiceCommand { + + public InitializeServiceCommand() { + super(); + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/ReopenGroupCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/ReopenGroupCommand.java new file mode 100644 index 0000000..0e83570 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/ReopenGroupCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.GroupCommand; + +public class ReopenGroupCommand { + + private final String identifier; + private final GroupCommand groupCommand; + + public ReopenGroupCommand(final String identifier, final GroupCommand groupCommand) { + super(); + this.identifier = identifier; + this.groupCommand = groupCommand; + } + + public String identifier() { + return this.identifier; + } + + public GroupCommand groupCommand() { + return this.groupCommand; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/SignOffMeetingCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/SignOffMeetingCommand.java new file mode 100644 index 0000000..0144b32 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/SignOffMeetingCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import io.mifos.group.api.v1.domain.SignOffMeeting; + +public class SignOffMeetingCommand { + + private final String groupIdentifier; + private final SignOffMeeting signOffMeeting; + + public SignOffMeetingCommand(final String groupIdentifier, final SignOffMeeting signOffMeeting) { + super(); + this.groupIdentifier = groupIdentifier; + this.signOffMeeting = signOffMeeting; + } + + public String groupIdentifier() { + return this.groupIdentifier; + } + + public SignOffMeeting signOffMeeting() { + return this.signOffMeeting; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/UpdateAssignedEmployeeCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/UpdateAssignedEmployeeCommand.java new file mode 100644 index 0000000..e664ac1 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/UpdateAssignedEmployeeCommand.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +public class UpdateAssignedEmployeeCommand { + + private final String identifier; + private final String employeeIdentifier; + + public UpdateAssignedEmployeeCommand(final String identifier, final String employeeIdentifier) { + super(); + this.identifier = identifier; + this.employeeIdentifier = employeeIdentifier; + } + + public String identifier() { + return this.identifier; + } + + public String employeeIdentifier() { + return this.employeeIdentifier; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/UpdateLeadersCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/UpdateLeadersCommand.java new file mode 100644 index 0000000..7167aeb --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/UpdateLeadersCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import java.util.Set; + +public class UpdateLeadersCommand { + + private final String identifier; + private final Set customerIdentifiers; + + public UpdateLeadersCommand(final String identifier, final Set customerIdentifiers) { + super(); + this.identifier = identifier; + this.customerIdentifiers = customerIdentifiers; + } + + public String identifier() { + return this.identifier; + } + + public Set customerIdentifiers() { + return this.customerIdentifiers; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/UpdateMembersCommand.java b/service/src/main/java/io/mifos/group/service/internal/command/UpdateMembersCommand.java new file mode 100644 index 0000000..935257a --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/UpdateMembersCommand.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command; + +import java.util.Set; + +public class UpdateMembersCommand { + + private final String identifier; + private final Set customerIdentifiers; + + public UpdateMembersCommand(final String identifier, final Set customerIdentifiers) { + super(); + this.identifier = identifier; + this.customerIdentifiers = customerIdentifiers; + } + + public String identifier() { + return this.identifier; + } + + public Set customerIdentifiers() { + return this.customerIdentifiers; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/handler/GroupAggregate.java b/service/src/main/java/io/mifos/group/service/internal/command/handler/GroupAggregate.java new file mode 100644 index 0000000..49074ce --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/handler/GroupAggregate.java @@ -0,0 +1,332 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command.handler; + +import io.mifos.core.api.util.UserContextHolder; +import io.mifos.core.command.annotation.Aggregate; +import io.mifos.core.command.annotation.CommandHandler; +import io.mifos.core.command.annotation.EventEmitter; +import io.mifos.core.lang.DateConverter; +import io.mifos.core.lang.ServiceException; +import io.mifos.group.service.internal.command.ActivateGroupCommand; +import io.mifos.group.service.internal.command.CloseGroupCommand; +import io.mifos.group.service.internal.command.CreateGroupCommand; +import io.mifos.group.service.internal.command.CreateGroupDefinitionCommand; +import io.mifos.group.service.internal.command.ReopenGroupCommand; +import io.mifos.group.service.internal.command.SignOffMeetingCommand; +import io.mifos.group.service.internal.command.UpdateAssignedEmployeeCommand; +import io.mifos.group.service.internal.command.UpdateLeadersCommand; +import io.mifos.group.service.internal.command.UpdateMembersCommand; +import io.mifos.group.service.internal.repository.AddressEntity; +import io.mifos.group.service.internal.repository.AddressRepository; +import io.mifos.group.service.internal.repository.AttendeeEntity; +import io.mifos.group.service.internal.repository.AttendeeRepository; +import io.mifos.group.service.internal.repository.GroupCommandEntity; +import io.mifos.group.service.internal.repository.GroupCommandRepository; +import io.mifos.group.service.internal.repository.GroupDefinitionEntity; +import io.mifos.group.service.internal.repository.GroupDefinitionRepository; +import io.mifos.group.service.internal.repository.GroupEntity; +import io.mifos.group.service.internal.repository.GroupRepository; +import io.mifos.group.service.internal.repository.MeetingEntity; +import io.mifos.group.service.internal.repository.MeetingRepository; +import io.mifos.group.api.v1.EventConstants; +import io.mifos.group.api.v1.domain.*; +import io.mifos.group.service.internal.mapper.AddressMapper; +import io.mifos.group.service.internal.mapper.GroupCommandMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.Clock; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoField; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@SuppressWarnings("unused") +@Aggregate +public class GroupAggregate { + + private final GroupDefinitionRepository groupDefinitionRepository; + private final GroupRepository groupRepository; + private final GroupCommandRepository groupCommandRepository; + private final MeetingRepository meetingRepository; + private final AttendeeRepository attendeeRepository; + private final AddressRepository addressRepository; + + @Autowired + public GroupAggregate(final GroupDefinitionRepository groupDefinitionRepository, + final GroupRepository groupRepository, + final GroupCommandRepository groupCommandRepository, + final MeetingRepository meetingRepository, + final AttendeeRepository attendeeRepository, + final AddressRepository addressRepository) { + super(); + this.groupDefinitionRepository = groupDefinitionRepository; + this.groupRepository = groupRepository; + this.groupCommandRepository = groupCommandRepository; + this.meetingRepository = meetingRepository; + this.attendeeRepository = attendeeRepository; + this.addressRepository = addressRepository; + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.POST_GROUP_DEFINITION) + public String createDefinition(final CreateGroupDefinitionCommand createGroupDefinitionCommand) { + final GroupDefinition groupDefinition = createGroupDefinitionCommand.groupDefinition(); + final GroupDefinitionEntity groupDefinitionEntity = new GroupDefinitionEntity(); + groupDefinitionEntity.setIdentifier(groupDefinition.getIdentifier()); + groupDefinitionEntity.setDescription(groupDefinition.getDescription()); + groupDefinitionEntity.setMinimalSize(groupDefinition.getMinimalSize()); + groupDefinitionEntity.setMaximalSize(groupDefinition.getMaximalSize()); + final Cycle cycle = groupDefinition.getCycle(); + groupDefinitionEntity.setNumberOfMeetings(cycle.getNumberOfMeetings()); + groupDefinitionEntity.setFrequency(cycle.getFrequency()); + groupDefinitionEntity.setAdjustment(cycle.getAdjustment()); + groupDefinitionEntity.setCreatedBy(UserContextHolder.checkedGetUser()); + groupDefinitionEntity.setCreatedOn(LocalDateTime.now(Clock.systemUTC())); + this.groupDefinitionRepository.save(groupDefinitionEntity); + + return groupDefinition.getIdentifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.POST_GROUP) + public String createGroup(final CreateGroupCommand createGroupCommand) { + final Group group = createGroupCommand.group(); + final GroupDefinitionEntity groupDefinitionEntity = + this.groupDefinitionRepository.findByIdentifier(group.getGroupDefinitionIdentifier()) + .orElseThrow( + () -> ServiceException.notFound("Group definition {0} not found.", group.getGroupDefinitionIdentifier()) + ); + + final AddressEntity savedAddress = this.addressRepository.save(AddressMapper.map(group.getAddress())); + + final GroupEntity groupEntity = new GroupEntity(); + groupEntity.setGroupDefinition(groupDefinitionEntity); + groupEntity.setIdentifier(group.getIdentifier()); + groupEntity.setName(group.getName()); + groupEntity.setLeaders(StringUtils.collectionToCommaDelimitedString(group.getLeaders())); + groupEntity.setMembers(StringUtils.collectionToCommaDelimitedString(group.getMembers())); + groupEntity.setOffice(group.getOffice()); + groupEntity.setAddressEntity(savedAddress); + groupEntity.setAssignedEmployee(group.getAssignedEmployee()); + groupEntity.setWeekday(group.getWeekday()); + groupEntity.setGroupStatus(Group.Status.PENDING.name()); + groupEntity.setCreatedBy(UserContextHolder.checkedGetUser()); + groupEntity.setCreatedOn(LocalDateTime.now(Clock.systemUTC())); + this.groupRepository.save(groupEntity); + return group.getIdentifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.ACTIVATE_GROUP) + public String activateGroup(final ActivateGroupCommand activateGroupCommand) { + this.groupRepository.findByIdentifier(activateGroupCommand.identifier()) + .ifPresent(groupEntity -> { + final GroupEntity savedGroupEntity = this.processCommandInternally(groupEntity, activateGroupCommand.groupCommand()); + this.createMeetingSchedule(groupEntity.getGroupDefinition(), savedGroupEntity); + }); + return activateGroupCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.CLOSE_GROUP) + public String closeGroup(final CloseGroupCommand closeGroupCommand) { + this.groupRepository.findByIdentifier(closeGroupCommand.identifier()) + .ifPresent(groupEntity -> { + final List currentMeetings = + this.meetingRepository.findByGroupEntityAndCurrentCycleOrderByMeetingSequenceDesc(groupEntity, groupEntity.getCurrentCycle()); + if (currentMeetings.stream().anyMatch(meetingEntity -> meetingEntity.getHeldOn() == null)) { + throw ServiceException.conflict("Not all meetings for group {0} signed off.", closeGroupCommand.identifier()); + } + this.processCommandInternally(groupEntity, closeGroupCommand.groupCommand()); + }); + return closeGroupCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.REOPEN_GROUP) + public String reopenGroup(final ReopenGroupCommand reopenGroupCommand) { + this.groupRepository.findByIdentifier(reopenGroupCommand.identifier()) + .ifPresent(groupEntity -> { + final GroupEntity savedGroupEntity = this.processCommandInternally(groupEntity, reopenGroupCommand.groupCommand()); + this.createMeetingSchedule(groupEntity.getGroupDefinition(), savedGroupEntity); + }); + return reopenGroupCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_GROUP) + public String updateLeaders(final UpdateLeadersCommand updateLeadersCommand) { + this.groupRepository.findByIdentifier(updateLeadersCommand.identifier()) + .ifPresent(groupEntity -> { + groupEntity.setLeaders(StringUtils.collectionToCommaDelimitedString(updateLeadersCommand.customerIdentifiers())); + groupEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); + groupEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); + this.groupRepository.save(groupEntity); + }); + return updateLeadersCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_GROUP) + public String updateMembers(final UpdateMembersCommand updateMembersCommand) { + this.groupRepository.findByIdentifier(updateMembersCommand.identifier()) + .ifPresent(groupEntity -> { + groupEntity.setMembers(StringUtils.collectionToCommaDelimitedString(updateMembersCommand.customerIdentifiers())); + groupEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); + groupEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); + this.groupRepository.save(groupEntity); + }); + return updateMembersCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_GROUP) + public String updateAssignedEmployee(final UpdateAssignedEmployeeCommand updateAssignedEmployeeCommand) { + this.groupRepository.findByIdentifier(updateAssignedEmployeeCommand.identifier()) + .ifPresent(groupEntity -> { + groupEntity.setAssignedEmployee(updateAssignedEmployeeCommand.employeeIdentifier()); + groupEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); + groupEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); + this.groupRepository.save(groupEntity); + }); + return updateAssignedEmployeeCommand.identifier(); + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_GROUP) + public String signOffMeeting(final SignOffMeetingCommand signOffMeetingCommand) { + this.groupRepository.findByIdentifier(signOffMeetingCommand.groupIdentifier()) + .ifPresent(groupEntity -> { + final SignOffMeeting signOffMeeting = signOffMeetingCommand.signOffMeeting(); + this.meetingRepository + .findByGroupEntityAndCurrentCycleAndMeetingSequence(groupEntity, + signOffMeeting.getCycle(), signOffMeeting.getSequence()) + .ifPresent(meetingEntity -> { + meetingEntity.setDuration(signOffMeeting.getDuration()); + meetingEntity.setHeldOn(LocalDate.now(Clock.systemUTC())); + this.meetingRepository.save(meetingEntity); + + final List attendeeEntities = this.attendeeRepository.findByMeeting(meetingEntity); + attendeeEntities.forEach(attendeeEntity -> signOffMeeting.getAttendees() + .stream() + .filter(attendee -> attendee.getCustomerIdentifier().equals(attendeeEntity.getCustomerIdentifier())) + .findFirst() + .ifPresent(attendee -> { + attendeeEntity.setStatus(attendee.getStatus()); + this.attendeeRepository.save(attendeeEntity); + })); + }); + }); + return signOffMeetingCommand.groupIdentifier(); + } + + private void createMeetingSchedule(final GroupDefinitionEntity groupDefinitionEntity, final GroupEntity groupEntity) { + final Integer numberOfMeetings = groupDefinitionEntity.getNumberOfMeetings(); + final Cycle.Frequency frequency = Cycle.Frequency.valueOf(groupDefinitionEntity.getFrequency()); + final Cycle.Adjustment adjustment = Cycle.Adjustment.valueOf(groupDefinitionEntity.getAdjustment()); + final Group.Weekday weekday = Group.Weekday.from(groupEntity.getWeekday()); + + final Set members = StringUtils.commaDelimitedListToSet(groupEntity.getMembers()); + + LocalDate meeting = LocalDate.now(Clock.systemUTC()); + if (frequency != Cycle.Frequency.DAILY) { + meeting = meeting.with(ChronoField.DAY_OF_WEEK, weekday.getValue()); + } + + for (int i = 0; i < numberOfMeetings; i++) { + switch (frequency) { + case DAILY: + meeting = meeting.plusDays(1L); + break; + case WEEKLY: + meeting = meeting.plusWeeks(1L); + break; + case FORTNIGHTLY: + meeting = meeting.plusWeeks(2L); + break; + case MONTHLY: + meeting.plusMonths(1L); + break; + } + + final MeetingEntity meetingEntity = new MeetingEntity(); + meetingEntity.setGroupEntity(groupEntity); + meetingEntity.setCurrentCycle(groupEntity.getCurrentCycle()); + meetingEntity.setMeetingSequence((i + 1)); + meetingEntity.setScheduledFor(meeting); + meetingEntity.setCreatedBy(UserContextHolder.checkedGetUser()); + meetingEntity.setCreatedOn(LocalDateTime.now(Clock.systemUTC())); + final MeetingEntity savedMeeting = this.meetingRepository.save(meetingEntity); + + this.attendeeRepository.save( + members + .stream() + .map(member -> { + final AttendeeEntity attendeeEntity = new AttendeeEntity(); + attendeeEntity.setMeeting(savedMeeting); + attendeeEntity.setCustomerIdentifier(member); + attendeeEntity.setStatus(Attendee.Status.EXPECTED.name()); + return attendeeEntity; + }) + .collect(Collectors.toList()) + ); + } + } + + private GroupEntity processCommandInternally(final GroupEntity groupEntity, final GroupCommand groupCommand) { + this.saveGroupCommand(groupEntity, groupCommand); + + final GroupCommand.Action action = GroupCommand.Action.valueOf(groupCommand.getAction()); + switch (action) { + case ACTIVATE: + groupEntity.setGroupStatus(Group.Status.ACTIVE.name()); + groupEntity.setCurrentCycle(groupEntity.getCurrentCycle() + 1); + break; + case CLOSE: + groupEntity.setGroupStatus(Group.Status.CLOSED.name()); + break; + case REOPEN: + groupEntity.setGroupStatus(Group.Status.PENDING.name()); + groupEntity.setCurrentCycle(groupEntity.getCurrentCycle() + 1); + break; + default: + throw ServiceException.badRequest("Unsupported command {}.", action.name()); + } + groupEntity.setLastModifiedBy(groupCommand.getCreatedBy()); + groupEntity.setLastModifiedOn(DateConverter.fromIsoString(groupCommand.getCreatedOn())); + return this.groupRepository.save(groupEntity); + } + + private void saveGroupCommand(final GroupEntity groupEntity, final GroupCommand groupCommand) { + final GroupCommandEntity groupCommandEntity = GroupCommandMapper.map(groupCommand); + groupCommandEntity.setGroup(groupEntity); + this.groupCommandRepository.save(groupCommandEntity); + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/command/handler/MigrationAggregate.java b/service/src/main/java/io/mifos/group/service/internal/command/handler/MigrationAggregate.java new file mode 100644 index 0000000..3d93d3b --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/command/handler/MigrationAggregate.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.command.handler; + +import io.mifos.core.command.annotation.Aggregate; +import io.mifos.core.command.annotation.CommandHandler; +import io.mifos.core.command.annotation.EventEmitter; +import io.mifos.core.mariadb.domain.FlywayFactoryBean; +import io.mifos.group.api.v1.EventConstants; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.command.InitializeServiceCommand; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.transaction.annotation.Transactional; + +import javax.sql.DataSource; + +@SuppressWarnings("unused") +@Aggregate +public class MigrationAggregate { + + private final Logger logger; + private final DataSource dataSource; + private final FlywayFactoryBean flywayFactoryBean; + + @Autowired + public MigrationAggregate(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final DataSource dataSource, + final FlywayFactoryBean flywayFactoryBean) { + super(); + this.logger = logger; + this.dataSource = dataSource; + this.flywayFactoryBean = flywayFactoryBean; + } + + @Transactional + @CommandHandler + @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.INITIALIZE) + public String initialize(final InitializeServiceCommand initializeServiceCommand) { + this.logger.debug("Start service migration."); + this.flywayFactoryBean.create(this.dataSource).migrate(); + return EventConstants.INITIALIZE; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/AddressMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/AddressMapper.java new file mode 100644 index 0000000..7ff897a --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/AddressMapper.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.group.service.internal.repository.AddressEntity; +import io.mifos.group.api.v1.domain.Address; + +public class AddressMapper { + + private AddressMapper() { + super(); + } + + public static Address map(final AddressEntity addressEntity) { + final Address address = new Address(); + address.setStreet(addressEntity.getStreet()); + address.setCity(addressEntity.getCity()); + address.setRegion(addressEntity.getRegion()); + address.setPostalCode(addressEntity.getPostalCode()); + address.setCountry(addressEntity.getCountry()); + address.setCountryCode(addressEntity.getCountryCode()); + return address; + } + + public static AddressEntity map(final Address address) { + final AddressEntity addressEntity = new AddressEntity(); + addressEntity.setStreet(address.getStreet()); + addressEntity.setCity(address.getCity()); + addressEntity.setRegion(address.getRegion()); + addressEntity.setPostalCode(address.getPostalCode()); + addressEntity.setCountry(address.getCountry()); + addressEntity.setCountryCode(address.getCountryCode()); + return addressEntity; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/AttendeeMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/AttendeeMapper.java new file mode 100644 index 0000000..58cc7bb --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/AttendeeMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.group.api.v1.domain.Attendee; +import io.mifos.group.service.internal.repository.AttendeeEntity; + +public class AttendeeMapper { + + private AttendeeMapper() { + super(); + } + + public static Attendee map(final AttendeeEntity attendeeEntity) { + final Attendee attendee = new Attendee(); + attendee.setCustomerIdentifier(attendeeEntity.getCustomerIdentifier()); + attendee.setStatus(attendeeEntity.getStatus()); + return attendee; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/GroupCommandMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupCommandMapper.java new file mode 100644 index 0000000..c1bbca0 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupCommandMapper.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.core.api.util.UserContextHolder; +import io.mifos.core.lang.DateConverter; +import io.mifos.group.service.internal.repository.GroupCommandEntity; +import io.mifos.group.api.v1.domain.GroupCommand; + +import java.time.Clock; +import java.time.LocalDateTime; + +public class GroupCommandMapper { + + private GroupCommandMapper() { + super(); + } + + public static GroupCommand map(final GroupCommandEntity groupCommandEntity) { + final GroupCommand groupCommand = new GroupCommand(); + groupCommand.setAction(groupCommandEntity.getAction()); + groupCommand.setNote(groupCommandEntity.getNote()); + groupCommand.setCreatedBy(groupCommandEntity.getCreatedBy()); + groupCommand.setCreatedOn(DateConverter.toIsoString(groupCommandEntity.getCreatedOn())); + return groupCommand; + } + + public static GroupCommandEntity map(final GroupCommand groupCommand) { + final GroupCommandEntity groupCommandEntity = new GroupCommandEntity(); + groupCommandEntity.setAction(groupCommand.getAction()); + groupCommandEntity.setNote(groupCommand.getNote()); + groupCommandEntity.setCreatedBy(UserContextHolder.checkedGetUser()); + groupCommandEntity.setCreatedOn(LocalDateTime.now(Clock.systemUTC())); + return groupCommandEntity; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/GroupDefinitionMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupDefinitionMapper.java new file mode 100644 index 0000000..88cf2c4 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupDefinitionMapper.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.core.lang.DateConverter; +import io.mifos.group.service.internal.repository.GroupDefinitionEntity; +import io.mifos.group.api.v1.domain.Cycle; +import io.mifos.group.api.v1.domain.GroupDefinition; + +public class GroupDefinitionMapper { + + private GroupDefinitionMapper() { + super(); + } + + public static GroupDefinition map(final GroupDefinitionEntity groupDefinitionEntity) { + final GroupDefinition groupDefinition = new GroupDefinition(); + groupDefinition.setIdentifier(groupDefinitionEntity.getIdentifier()); + groupDefinition.setDescription(groupDefinitionEntity.getDescription()); + groupDefinition.setMinimalSize(groupDefinitionEntity.getMinimalSize()); + groupDefinition.setMaximalSize(groupDefinitionEntity.getMaximalSize()); + groupDefinition.setCreateOn(DateConverter.toIsoString(groupDefinitionEntity.getCreatedOn())); + groupDefinition.setCreatedBy(groupDefinitionEntity.getCreatedBy()); + + if (groupDefinitionEntity.getLastModifiedOn() != null) { + groupDefinition.setLastModifiedOn(DateConverter.toIsoString(groupDefinitionEntity.getLastModifiedOn())); + groupDefinition.setLastModifiedBy(groupDefinitionEntity.getLastModifiedBy()); + } + + final Cycle cycle = new Cycle(); + cycle.setNumberOfMeetings(groupDefinitionEntity.getNumberOfMeetings()); + cycle.setFrequency(groupDefinitionEntity.getFrequency()); + cycle.setAdjustment(groupDefinitionEntity.getAdjustment()); + groupDefinition.setCycle(cycle); + + return groupDefinition; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/GroupMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupMapper.java new file mode 100644 index 0000000..a345c2a --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/GroupMapper.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.core.lang.DateConverter; +import io.mifos.group.service.internal.repository.GroupEntity; +import io.mifos.group.api.v1.domain.Group; +import org.springframework.util.StringUtils; + +public class GroupMapper { + + private GroupMapper() { + super(); + } + + public static Group map(final GroupEntity groupEntity) { + final Group group = new Group(); + group.setIdentifier(groupEntity.getIdentifier()); + group.setGroupDefinitionIdentifier(groupEntity.getGroupDefinition().getIdentifier()); + group.setName(groupEntity.getName()); + group.setLeaders(StringUtils.commaDelimitedListToSet(groupEntity.getLeaders())); + group.setMembers(StringUtils.commaDelimitedListToSet(groupEntity.getMembers())); + group.setOffice(groupEntity.getOffice()); + group.setAssignedEmployee(groupEntity.getAssignedEmployee()); + group.setWeekday(groupEntity.getWeekday()); + group.setStatus(groupEntity.getGroupStatus()); + group.setCreatedOn(DateConverter.toIsoString(groupEntity.getCreatedOn())); + group.setCreatedBy(groupEntity.getCreatedBy()); + if (groupEntity.getLastModifiedOn() != null) { + group.setLastModifiedOn(DateConverter.toIsoString(groupEntity.getLastModifiedOn())); + group.setLastModifiedBy(groupEntity.getLastModifiedBy()); + } + return group; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/mapper/MeetingMapper.java b/service/src/main/java/io/mifos/group/service/internal/mapper/MeetingMapper.java new file mode 100644 index 0000000..9e67548 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/mapper/MeetingMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.mapper; + +import io.mifos.core.lang.DateConverter; +import io.mifos.group.api.v1.domain.Meeting; +import io.mifos.group.service.internal.repository.MeetingEntity; + +public class MeetingMapper { + + private MeetingMapper() { + super(); + } + + public static Meeting map(final MeetingEntity meetingEntity) { + final Meeting meeting = new Meeting(); + meeting.setCurrentCycle(meetingEntity.getCurrentCycle()); + meeting.setMeetingSequence(meetingEntity.getMeetingSequence()); + meeting.setScheduledFor(DateConverter.toIsoString(meetingEntity.getScheduledFor())); + if (meetingEntity.getHeldOn() != null) { + meeting.setHeldOn(DateConverter.toIsoString(meetingEntity.getHeldOn())); + } + if (meetingEntity.getCreatedBy() != null) { + meeting.setCreatedBy(meetingEntity.getCreatedBy()); + meeting.setCreatedOn(DateConverter.toIsoString(meetingEntity.getCreatedOn())); + } + meeting.setDuration(meetingEntity.getDuration()); + return meeting; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/AddressEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/AddressEntity.java new file mode 100644 index 0000000..f456da2 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/AddressEntity.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "ptah_addresses") +public class AddressEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + @Column(name = "street") + private String street; + @Column(name = "city") + private String city; + @Column(name = "postal_code") + private String postalCode; + @Column(name = "region") + private String region; + @Column(name = "country_code") + private String countryCode; + @Column(name = "country") + private String country; + + public AddressEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getStreet() { + return this.street; + } + + public void setStreet(final String street) { + this.street = street; + } + + public String getCity() { + return this.city; + } + + public void setCity(final String city) { + this.city = city; + } + + public String getPostalCode() { + return this.postalCode; + } + + public void setPostalCode(final String postalCode) { + this.postalCode = postalCode; + } + + public String getRegion() { + return this.region; + } + + public void setRegion(final String region) { + this.region = region; + } + + public String getCountryCode() { + return this.countryCode; + } + + public void setCountryCode(final String countryCode) { + this.countryCode = countryCode; + } + + public String getCountry() { + return this.country; + } + + public void setCountry(final String country) { + this.country = country; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/AddressRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/AddressRepository.java new file mode 100644 index 0000000..b3dfdfb --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/AddressRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AddressRepository extends JpaRepository { +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeEntity.java new file mode 100644 index 0000000..f0b37db --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeEntity.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "ptah_attendees") +public class AttendeeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "meeting_id", nullable = false) + private MeetingEntity meeting; + @Column(name = "customer_identifier", nullable = false) + private String customerIdentifier; + @Column(name = "a_status", nullable = false) + private String status; + + public AttendeeEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public MeetingEntity getMeeting() { + return this.meeting; + } + + public void setMeeting(final MeetingEntity meeting) { + this.meeting = meeting; + } + + public String getCustomerIdentifier() { + return this.customerIdentifier; + } + + public void setCustomerIdentifier(final String customerIdentifier) { + this.customerIdentifier = customerIdentifier; + } + + public String getStatus() { + return this.status; + } + + public void setStatus(final String status) { + this.status = status; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeRepository.java new file mode 100644 index 0000000..b718aa6 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/AttendeeRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AttendeeRepository extends JpaRepository { + + List findByMeeting(final MeetingEntity meetingEntity); +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandEntity.java new file mode 100644 index 0000000..332c02e --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandEntity.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import io.mifos.core.mariadb.util.LocalDateTimeConverter; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Table(name = "ptah_group_commands") +public class GroupCommandEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id", nullable = false) + private GroupEntity group; + @Column(name = "a_action") + private String action; + @Column(name = "note") + private String note; + @Column(name = "created_on", nullable = false) + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime createdOn; + @Column(name = "created_by", nullable = false) + private String createdBy; + + public GroupCommandEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public GroupEntity getGroup() { + return this.group; + } + + public void setGroup(final GroupEntity group) { + this.group = group; + } + + public String getAction() { + return this.action; + } + + public void setAction(final String action) { + this.action = action; + } + + public String getNote() { + return this.note; + } + + public void setNote(final String note) { + this.note = note; + } + + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final LocalDateTime createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandRepository.java new file mode 100644 index 0000000..9313514 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupCommandRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface GroupCommandRepository extends JpaRepository { + + List findByGroup(final GroupEntity group); +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionEntity.java new file mode 100644 index 0000000..926e832 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionEntity.java @@ -0,0 +1,161 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import io.mifos.core.mariadb.util.LocalDateTimeConverter; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Table(name = "ptah_group_definitions") +public class GroupDefinitionEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @Column(name = "identifier", nullable = false) + private String identifier; + @Column(name = "description") + private String description; + @Column(name = "minimal_size", nullable = false) + private Integer minimalSize; + @Column(name = "maximal_size", nullable = false) + private Integer maximalSize; + @Column(name = "number_of_meetings", nullable = false) + private Integer numberOfMeetings; + @Column(name = "frequency", nullable = false) + private String frequency; + @Column(name = "adjustment") + private String adjustment; + @Column(name = "created_on", nullable = false) + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime createdOn; + @Column(name = "created_by", nullable = false) + private String createdBy; + @Column(name = "last_modified_on") + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime lastModifiedOn; + @Column(name = "last_modified_by") + private String lastModifiedBy; + + public GroupDefinitionEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getIdentifier() { + return this.identifier; + } + + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public Integer getMinimalSize() { + return this.minimalSize; + } + + public void setMinimalSize(final Integer minimalSize) { + this.minimalSize = minimalSize; + } + + public Integer getMaximalSize() { + return this.maximalSize; + } + + public void setMaximalSize(final Integer maximalSize) { + this.maximalSize = maximalSize; + } + + public Integer getNumberOfMeetings() { + return this.numberOfMeetings; + } + + public void setNumberOfMeetings(final Integer numberOfMeetings) { + this.numberOfMeetings = numberOfMeetings; + } + + public String getFrequency() { + return this.frequency; + } + + public void setFrequency(final String frequency) { + this.frequency = frequency; + } + + public String getAdjustment() { + return this.adjustment; + } + + public void setAdjustment(final String adjustment) { + this.adjustment = adjustment; + } + + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final LocalDateTime createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getLastModifiedOn() { + return this.lastModifiedOn; + } + + public void setLastModifiedOn(final LocalDateTime lastModifiedOn) { + this.lastModifiedOn = lastModifiedOn; + } + + public String getLastModifiedBy() { + return this.lastModifiedBy; + } + + public void setLastModifiedBy(final String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionRepository.java new file mode 100644 index 0000000..e274b95 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupDefinitionRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface GroupDefinitionRepository extends JpaRepository { + + Optional findByIdentifier(final String identifier); +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupEntity.java new file mode 100644 index 0000000..da5a423 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupEntity.java @@ -0,0 +1,207 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import io.mifos.core.mariadb.util.LocalDateTimeConverter; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Table(name = "ptah_groups") +public class GroupEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @Column(name = "identifier", nullable = false) + private String identifier; + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "group_definition_id", nullable = false) + private GroupDefinitionEntity groupDefinition; + @Column(name = "a_name", nullable = false) + private String name; + @Column(name = "leaders") + private String leaders; + @Column(name = "members", nullable = false) + private String members; + @Column(name = "office", nullable = false) + private String office; + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "address_id", nullable = false) + private AddressEntity addressEntity; + @Column(name = "assigned_employee", nullable = false) + private String assignedEmployee; + @Column(name = "weekday", nullable = false) + private Integer weekday; + @Column(name = "group_status", nullable = false) + private String groupStatus; + @Column(name = "current_cycle", nullable = false) + private Integer currentCycle = Integer.valueOf(0); + @Column(name = "created_on", nullable = false) + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime createdOn; + @Column(name = "created_by", nullable = false) + private String createdBy; + @Column(name = "last_modified_on") + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime lastModifiedOn; + @Column(name = "last_modified_by") + private String lastModifiedBy; + + public GroupEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getIdentifier() { + return this.identifier; + } + + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + public GroupDefinitionEntity getGroupDefinition() { + return this.groupDefinition; + } + + public void setGroupDefinition(final GroupDefinitionEntity groupDefinition) { + this.groupDefinition = groupDefinition; + } + + public String getName() { + return this.name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getLeaders() { + return this.leaders; + } + + public void setLeaders(final String leaders) { + this.leaders = leaders; + } + + public String getMembers() { + return this.members; + } + + public void setMembers(final String members) { + this.members = members; + } + + public String getOffice() { + return this.office; + } + + public void setOffice(final String office) { + this.office = office; + } + + public String getAssignedEmployee() { + return this.assignedEmployee; + } + + public void setAssignedEmployee(final String assignedEmployee) { + this.assignedEmployee = assignedEmployee; + } + + public AddressEntity getAddressEntity() { + return this.addressEntity; + } + + public void setAddressEntity(final AddressEntity addressEntity) { + this.addressEntity = addressEntity; + } + + public Integer getWeekday() { + return this.weekday; + } + + public void setWeekday(final Integer weekday) { + this.weekday = weekday; + } + + public String getGroupStatus() { + return this.groupStatus; + } + + public void setGroupStatus(final String groupStatus) { + this.groupStatus = groupStatus; + } + + public Integer getCurrentCycle() { + return this.currentCycle; + } + + public void setCurrentCycle(final Integer currentCycle) { + this.currentCycle = currentCycle; + } + + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final LocalDateTime createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getLastModifiedOn() { + return this.lastModifiedOn; + } + + public void setLastModifiedOn(final LocalDateTime lastModifiedOn) { + this.lastModifiedOn = lastModifiedOn; + } + + public String getLastModifiedBy() { + return this.lastModifiedBy; + } + + public void setLastModifiedBy(final String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/GroupRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/GroupRepository.java new file mode 100644 index 0000000..7398865 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/GroupRepository.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface GroupRepository extends JpaRepository { + + Optional findByIdentifier(final String identifier); + + Page findByAssignedEmployee(final String employee, final Pageable pageable); +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/MeetingEntity.java b/service/src/main/java/io/mifos/group/service/internal/repository/MeetingEntity.java new file mode 100644 index 0000000..2733c5a --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/MeetingEntity.java @@ -0,0 +1,138 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import io.mifos.core.mariadb.util.LocalDateConverter; +import io.mifos.core.mariadb.util.LocalDateTimeConverter; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Entity +@Table(name = "ptah_meetings") +public class MeetingEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @Column(name = "meeting_sequence", nullable = false) + private Integer meetingSequence; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id", nullable = false) + private GroupEntity groupEntity; + @Column(name = "current_cycle", nullable = false) + private Integer currentCycle; + @Column(name = "scheduled_for", nullable = false) + @Convert(converter = LocalDateConverter.class) + private LocalDate scheduledFor; + @Column(name = "held_on", nullable = true) + @Convert(converter = LocalDateConverter.class) + private LocalDate heldOn; + @Column(name = "duration", nullable = true) + private Long duration; + @Column(name = "created_on", nullable = false) + @Convert(converter = LocalDateTimeConverter.class) + private LocalDateTime createdOn; + @Column(name = "created_by", nullable = false) + private String createdBy; + + public MeetingEntity() { + super(); + } + + public Long getId() { + return this.id; + } + + public void setId(final Long id) { + this.id = id; + } + + public Integer getMeetingSequence() { + return this.meetingSequence; + } + + public void setMeetingSequence(final Integer meetingSequence) { + this.meetingSequence = meetingSequence; + } + + public GroupEntity getGroupEntity() { + return this.groupEntity; + } + + public void setGroupEntity(final GroupEntity groupEntity) { + this.groupEntity = groupEntity; + } + + public Integer getCurrentCycle() { + return this.currentCycle; + } + + public void setCurrentCycle(final Integer currentCycle) { + this.currentCycle = currentCycle; + } + + public LocalDate getScheduledFor() { + return this.scheduledFor; + } + + public void setScheduledFor(final LocalDate scheduledFor) { + this.scheduledFor = scheduledFor; + } + + public LocalDate getHeldOn() { + return this.heldOn; + } + + public void setHeldOn(final LocalDate heldOn) { + this.heldOn = heldOn; + } + + public Long getDuration() { + return this.duration; + } + + public void setDuration(final Long duration) { + this.duration = duration; + } + + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public void setCreatedOn(final LocalDateTime createdOn) { + this.createdOn = createdOn; + } + + public String getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(final String createdBy) { + this.createdBy = createdBy; + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/repository/MeetingRepository.java b/service/src/main/java/io/mifos/group/service/internal/repository/MeetingRepository.java new file mode 100644 index 0000000..6875ba7 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/repository/MeetingRepository.java @@ -0,0 +1,39 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.repository; + +import io.mifos.core.mariadb.util.LocalDateConverter; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import javax.persistence.Convert; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Repository +public interface MeetingRepository extends JpaRepository { + + @Convert(converter = LocalDateConverter.class) + List findTopByGroupEntityAndScheduledForAfter(final GroupEntity groupEntity, final LocalDate date); + + List findByGroupEntityAndCurrentCycleOrderByMeetingSequenceDesc( + final GroupEntity groupEntity, final Integer currentCycle); + + List findByGroupEntityOrderByCurrentCycleDescMeetingSequenceDesc(final GroupEntity groupEntity); + + Optional findByGroupEntityAndCurrentCycleAndMeetingSequence(final GroupEntity groupEntity, final Integer cycle, final Integer sequence); +} diff --git a/service/src/main/java/io/mifos/group/service/internal/service/GroupDefinitionService.java b/service/src/main/java/io/mifos/group/service/internal/service/GroupDefinitionService.java new file mode 100644 index 0000000..2bc03cb --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/service/GroupDefinitionService.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.service; + +import io.mifos.group.api.v1.domain.GroupDefinition; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.mapper.GroupDefinitionMapper; +import io.mifos.group.service.internal.repository.GroupDefinitionRepository; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class GroupDefinitionService { + + private final Logger logger; + private final GroupDefinitionRepository groupDefinitionRepository; + + @Autowired + public GroupDefinitionService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final GroupDefinitionRepository groupDefinitionRepository) { + super(); + this.logger = logger; + this.groupDefinitionRepository = groupDefinitionRepository; + } + + public Optional findByIdentifier(final String identifier) { + return this.groupDefinitionRepository.findByIdentifier(identifier).map(GroupDefinitionMapper::map); + } + + public List fetchAllGroupDefinitions() { + return this.groupDefinitionRepository.findAll() + .stream() + .map(GroupDefinitionMapper::map) + .collect(Collectors.toList()); + } +} diff --git a/service/src/main/java/io/mifos/group/service/internal/service/GroupService.java b/service/src/main/java/io/mifos/group/service/internal/service/GroupService.java new file mode 100644 index 0000000..76ce37e --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/internal/service/GroupService.java @@ -0,0 +1,134 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.internal.service; + +import io.mifos.core.lang.ServiceException; +import io.mifos.group.service.internal.repository.AttendeeRepository; +import io.mifos.group.service.internal.repository.GroupRepository; +import io.mifos.group.api.v1.domain.Group; +import io.mifos.group.api.v1.domain.GroupCommand; +import io.mifos.group.api.v1.domain.GroupPage; +import io.mifos.group.api.v1.domain.Meeting; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.mapper.AddressMapper; +import io.mifos.group.service.internal.mapper.AttendeeMapper; +import io.mifos.group.service.internal.mapper.GroupCommandMapper; +import io.mifos.group.service.internal.mapper.GroupMapper; +import io.mifos.group.service.internal.mapper.MeetingMapper; +import io.mifos.group.service.internal.repository.GroupCommandRepository; +import io.mifos.group.service.internal.repository.GroupEntity; +import io.mifos.group.service.internal.repository.MeetingEntity; +import io.mifos.group.service.internal.repository.MeetingRepository; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.time.Clock; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class GroupService { + + private final Logger logger; + private final GroupRepository groupRepository; + private final GroupCommandRepository groupCommandRepository; + private final MeetingRepository meetingRepository; + private final AttendeeRepository attendeeRepository; + + @Autowired + public GroupService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final GroupRepository groupRepository, + final GroupCommandRepository groupCommandRepository, + final MeetingRepository meetingRepository, + final AttendeeRepository attendeeRepository) { + super(); + this.logger = logger; + this.groupRepository = groupRepository; + this.groupCommandRepository = groupCommandRepository; + this.meetingRepository = meetingRepository; + this.attendeeRepository = attendeeRepository; + } + + public Optional findByIdentifier(final String identifier) { + final Optional optionalGroup = this.groupRepository.findByIdentifier(identifier); + if (optionalGroup.isPresent()) { + final GroupEntity groupEntity = optionalGroup.get(); + final Group group = GroupMapper.map(groupEntity); + group.setAddress(AddressMapper.map(groupEntity.getAddressEntity())); + return Optional.of(group); + } else { + return Optional.empty(); + } + } + + public GroupPage fetchGroups(final String employee, final Pageable pageable) { + final Page page; + if (employee != null) { + page = this.groupRepository.findByAssignedEmployee(employee, pageable); + } else { + page = this.groupRepository.findAll(pageable); + } + + final GroupPage groupPage = new GroupPage(); + groupPage.setGroups(page.map(GroupMapper::map).getContent()); + groupPage.setTotalPages(page.getTotalPages()); + groupPage.setTotalElements(page.getTotalElements()); + + return groupPage; + } + + public List findCommandsByIdentifier(final String identifier) { + final GroupEntity groupEntity = + this.groupRepository.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + return this.groupCommandRepository.findByGroup(groupEntity) + .stream() + .map(GroupCommandMapper::map) + .collect(Collectors.toList()); + } + + public List findMeetings(final String identifier, final Boolean upcoming) { + final GroupEntity groupEntity = this.groupRepository.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + + final List meetings; + if (upcoming) { + meetings = this.meetingRepository.findTopByGroupEntityAndScheduledForAfter(groupEntity, LocalDate.now(Clock.systemUTC())); + } else { + meetings = this.meetingRepository.findByGroupEntityOrderByCurrentCycleDescMeetingSequenceDesc(groupEntity); + } + + return meetings + .stream() + .map(meetingEntity -> { + final Meeting meeting = MeetingMapper.map(meetingEntity); + meeting.setGroupIdentifier(groupEntity.getIdentifier()); + meeting.setAttendees( + this.attendeeRepository.findByMeeting(meetingEntity) + .stream().map(AttendeeMapper::map).collect(Collectors.toSet()) + ); + meeting.setLocation(AddressMapper.map(groupEntity.getAddressEntity())); + return meeting; + }) + .collect(Collectors.toList()); + } +} diff --git a/service/src/main/java/io/mifos/group/service/rest/GroupDefinitionRestController.java b/service/src/main/java/io/mifos/group/service/rest/GroupDefinitionRestController.java new file mode 100644 index 0000000..7b78586 --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/rest/GroupDefinitionRestController.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.rest; + +import io.mifos.anubis.annotation.AcceptedTokenType; +import io.mifos.anubis.annotation.Permittable; +import io.mifos.core.command.gateway.CommandGateway; +import io.mifos.core.lang.ServiceException; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.command.CreateGroupDefinitionCommand; +import io.mifos.group.service.internal.service.GroupDefinitionService; +import io.mifos.group.api.v1.domain.GroupDefinition; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +@RestController +@RequestMapping("/definitions") +public class GroupDefinitionRestController { + + private final Logger logger; + private final CommandGateway commandGateway; + private final GroupDefinitionService groupDefinitionService; + + @Autowired + public GroupDefinitionRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final CommandGateway commandGateway, + final GroupDefinitionService groupDefinitionService) { + super(); + this.logger = logger; + this.commandGateway = commandGateway; + this.groupDefinitionService = groupDefinitionService; + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity createDefinition(@RequestBody @Valid final GroupDefinition groupDefinition) { + this.groupDefinitionService.findByIdentifier(groupDefinition.getIdentifier()) + .ifPresent(gd -> { + throw ServiceException.conflict("Group definition {0} already exists.", gd.getIdentifier()); + }); + + this.commandGateway.process(new CreateGroupDefinitionCommand(groupDefinition)); + return ResponseEntity.accepted().build(); + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity> fetchAllGroupDefinitions() { + return ResponseEntity.ok(this.groupDefinitionService.fetchAllGroupDefinitions()); + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + value = "/{identifier}", + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity findGroupDefinitionByIdentifier( + @PathVariable("identifier") final String identifier) { + return this.groupDefinitionService.findByIdentifier(identifier) + .map(ResponseEntity::ok) + .orElseThrow(() -> ServiceException.notFound("Group definition {0} not found.", identifier)); + } +} diff --git a/service/src/main/java/io/mifos/group/service/rest/GroupRestController.java b/service/src/main/java/io/mifos/group/service/rest/GroupRestController.java new file mode 100644 index 0000000..7aad78b --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/rest/GroupRestController.java @@ -0,0 +1,269 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.rest; + +import io.mifos.anubis.annotation.AcceptedTokenType; +import io.mifos.anubis.annotation.Permittable; +import io.mifos.core.command.gateway.CommandGateway; +import io.mifos.core.lang.ServiceException; +import io.mifos.group.service.internal.command.SignOffMeetingCommand; +import io.mifos.group.service.internal.command.UpdateLeadersCommand; +import io.mifos.group.service.internal.command.UpdateMembersCommand; +import io.mifos.group.service.internal.service.GroupDefinitionService; +import io.mifos.group.service.internal.service.GroupService; +import io.mifos.group.api.v1.domain.AssignedEmployeeHolder; +import io.mifos.group.api.v1.domain.Group; +import io.mifos.group.api.v1.domain.GroupCommand; +import io.mifos.group.api.v1.domain.GroupPage; +import io.mifos.group.api.v1.domain.Meeting; +import io.mifos.group.api.v1.domain.SignOffMeeting; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.command.ActivateGroupCommand; +import io.mifos.group.service.internal.command.CloseGroupCommand; +import io.mifos.group.service.internal.command.CreateGroupCommand; +import io.mifos.group.service.internal.command.ReopenGroupCommand; +import io.mifos.group.service.internal.command.UpdateAssignedEmployeeCommand; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; +import java.util.Set; + +@RestController +@RequestMapping("/groups") +public class GroupRestController { + + private final Logger logger; + private final CommandGateway commandGateway; + private final GroupService groupService; + private final GroupDefinitionService groupDefinitionService; + + + @Autowired + public GroupRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final CommandGateway commandGateway, + final GroupService groupService, + final GroupDefinitionService groupDefinitionService) { + super(); + this.logger = logger; + this.commandGateway = commandGateway; + this.groupService = groupService; + this.groupDefinitionService = groupDefinitionService; + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity createGroup(@RequestBody @Valid final Group group) { + this.groupService.findByIdentifier(group.getIdentifier()) + .ifPresent(g -> { + throw ServiceException.conflict("Group {0} already exists.", g.getIdentifier()); + }); + + if (!this.groupDefinitionService.findByIdentifier(group.getGroupDefinitionIdentifier()).isPresent()) { + throw ServiceException.notFound("Unknown group definition {0}.", group.getGroupDefinitionIdentifier()); + } + + this.commandGateway.process(new CreateGroupCommand(group)); + return ResponseEntity.accepted().build(); + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity fetchGroups( + @RequestParam("employee") final String employee, + @RequestParam("page") final Integer page, + @RequestParam("size") final Integer size, + @RequestParam("sortColumn") final String sortColumn, + @RequestParam("sortDirection") final String sortDirection) { + return ResponseEntity.ok( + this.groupService.fetchGroups(employee, this.createPageRequest(page, size, sortColumn, sortDirection)) + ); + } + + @Permittable(AcceptedTokenType.TENANT) + @RequestMapping( + value = "/{identifier}", + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity findByIdentifier(@PathVariable("identifier") final String identifier) { + return this.groupService.findByIdentifier(identifier) + .map(ResponseEntity::ok) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + } + + @RequestMapping( + value = "/{identifier}/commands", + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity processGroupCommand(@PathVariable("identifier") final String identifier, @RequestBody final GroupCommand groupCommand) { + this.groupService.findByIdentifier(identifier).orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + final GroupCommand.Action action = GroupCommand.Action.valueOf(groupCommand.getAction()); + switch (action) { + case ACTIVATE: + this.commandGateway.process(new ActivateGroupCommand(identifier, groupCommand)); + break; + case CLOSE: + this.commandGateway.process(new CloseGroupCommand(identifier, groupCommand)); + break; + case REOPEN: + this.commandGateway.process(new ReopenGroupCommand(identifier, groupCommand)); + break; + default: + throw ServiceException.badRequest("Unsupported command {0}.", action.name()); + } + return ResponseEntity.accepted().build(); + } + + @RequestMapping( + value = "/{identifier}/commands", + method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.ALL_VALUE + ) + public + @ResponseBody + ResponseEntity> getGroupCommands(@PathVariable("identifier") final String identifier) { + return ResponseEntity.ok(this.groupService.findCommandsByIdentifier(identifier)); + } + + @RequestMapping( + value = "/{identifier}/leaders", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity updateLeaders(@PathVariable("identifier") final String identifier, + @RequestBody final Set customerIdentifiers) { + this.groupService.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + + this.commandGateway.process(new UpdateLeadersCommand(identifier, customerIdentifiers)); + + return ResponseEntity.accepted().build(); + } + + @RequestMapping( + value = "/{identifier}/members", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity updateMembers(@PathVariable("identifier") final String identifier, + @RequestBody final Set customerIdentifiers) { + this.groupService.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + + this.commandGateway.process(new UpdateMembersCommand(identifier, customerIdentifiers)); + + return ResponseEntity.accepted().build(); + } + + @RequestMapping( + value = "/{identifier}/employee", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity updateAssignedEmployee(@PathVariable("identifier") final String identifier, + @RequestBody final AssignedEmployeeHolder assignedEmployeeHolder) { + + this.groupService.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", identifier)); + + this.commandGateway.process(new UpdateAssignedEmployeeCommand(identifier, assignedEmployeeHolder.getIdentifier())); + + return ResponseEntity.accepted().build(); + } + + @RequestMapping( + value = "/{identifier}/meetings", + method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.ALL_VALUE + ) + public + @ResponseBody + ResponseEntity> fetchMeetings(@PathVariable("identifier") final String groupIdentifier, + @RequestParam(value = "upcoming", required = false, defaultValue = "false") final Boolean upcoming) { + this.groupService.findByIdentifier(groupIdentifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", groupIdentifier)); + return ResponseEntity.ok(this.groupService.findMeetings(groupIdentifier, upcoming)); + } + + @RequestMapping( + value = "/{identifier}/meetings", + method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + public + ResponseEntity closeMeeting(@PathVariable("identifier") final String groupIdentifier, + @RequestBody final SignOffMeeting signOffMeeting) { + this.groupService.findByIdentifier(groupIdentifier) + .orElseThrow(() -> ServiceException.notFound("Group {0} not found.", groupIdentifier)); + + this.commandGateway.process(new SignOffMeetingCommand(groupIdentifier, signOffMeeting)); + return ResponseEntity.accepted().build(); + } + + private Pageable createPageRequest(final Integer page, final Integer size, final String sortColumn, final String sortDirection) { + final Integer pageToUse = page != null ? page : 0; + final Integer sizeToUse = size != null ? size : 20; + final String sortColumnToUse = sortColumn != null ? sortColumn : "identifier"; + final Sort.Direction direction = sortDirection != null ? Sort.Direction.valueOf(sortDirection.toUpperCase()) : Sort.Direction.ASC; + return new PageRequest(pageToUse, sizeToUse, direction, sortColumnToUse); + } +} diff --git a/service/src/main/java/io/mifos/group/service/rest/MigrationRestController.java b/service/src/main/java/io/mifos/group/service/rest/MigrationRestController.java new file mode 100644 index 0000000..e656b2a --- /dev/null +++ b/service/src/main/java/io/mifos/group/service/rest/MigrationRestController.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 The Mifos Initiative. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.mifos.group.service.rest; + +import io.mifos.anubis.annotation.AcceptedTokenType; +import io.mifos.anubis.annotation.Permittable; +import io.mifos.core.command.gateway.CommandGateway; +import io.mifos.group.service.ServiceConstants; +import io.mifos.group.service.internal.command.InitializeServiceCommand; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/initialize") +public class MigrationRestController { + + private final Logger logger; + private final CommandGateway commandGateway; + + @Autowired + public MigrationRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, + final CommandGateway commandGateway) { + super(); + this.logger = logger; + this.commandGateway = commandGateway; + } + + @Permittable(AcceptedTokenType.SYSTEM) + @RequestMapping( + method = RequestMethod.POST, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity initialize() throws InterruptedException { + this.commandGateway.process(new InitializeServiceCommand()); + return ResponseEntity.accepted().build(); + } +} diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml new file mode 100644 index 0000000..fbbfa68 --- /dev/null +++ b/service/src/main/resources/application.yml @@ -0,0 +1,67 @@ + +# +# Copyright 2016 The Mifos Initiative. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +spring: + cloud: + discovery: + enabled: false + config: + enabled: false + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ + +server: + port: 8081 + contextPath: /group/v1/* + +cassandra: + clusterName: staging_cluster + contactPoints: 127.0.0.1:9042,127.0.0.2:9042,127.0.0.3:9042 + keyspace: seshat + cl: + read: LOCAL_QUORUM + write: LOCAL_QUORUM + delete: LOCAL_QUORUM + +mariadb: + driverClass: org.mariadb.jdbc.Driver + database: seshat + host: localhost + port: 3306 + user: root + password: mysql + +bonecp: + idleMaxAgeInMinutes: 240 + idleConnectionTestPeriodInMinutes: 60 + maxConnectionsPerPartition: 10 + minConnectionsPerPartition: 1 + partitionCount: 2 + acquireIncrement: 5 + statementsCacheSize: 100 + +async: + corePoolSize: 32 + maxPoolSize: 16384 + queueCapacity: 0 + threadName: async-processor- + +flyway: + enabled: false diff --git a/service/src/main/resources/bootstrap.yml b/service/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..93080e3 --- /dev/null +++ b/service/src/main/resources/bootstrap.yml @@ -0,0 +1,19 @@ +# +# Copyright 2016 The Mifos Initiative. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +spring: + application: + name: group-api diff --git a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql new file mode 100644 index 0000000..ab45c4b --- /dev/null +++ b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql @@ -0,0 +1,100 @@ +-- +-- Copyright 2016 The Mifos Initiative. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +CREATE TABLE ptah_addresses ( + id BIGINT NOT NULL AUTO_INCREMENT, + street VARCHAR(256) NOT NULL, + city VARCHAR(256) NOT NULL, + postal_code VARCHAR(32) NULL, + region VARCHAR(256) NULL, + country_code VARCHAR(2) NOT NULL, + country VARCHAR(256) NOT NULL, + CONSTRAINT ptah_addresses_pk PRIMARY KEY (id) +); + +CREATE TABLE ptah_group_definitions ( + id BIGINT NOT NULL AUTO_INCREMENT, + identifier VARCHAR(32) NOT NULL, + description VARCHAR(2048) NULL, + minimal_size INTEGER NOT NULL, + maximal_size INTEGER NOT NULL, + number_of_meetings INTEGER NOT NULL, + frequency VARCHAR(32) NOT NULL, + adjustment VARCHAR(32) NULL, + created_on TIMESTAMP(3) NOT NULL, + created_by VARCHAR(32) NOT NULL, + last_modified_on TIMESTAMP(3) NULL, + last_modified_by VARCHAR(32) NULL, + CONSTRAINT ptah_group_definitions_pk PRIMARY KEY (id), + CONSTRAINT ptah_group_definitions_identifier_uq UNIQUE (identifier) +); + +CREATE TABLE ptah_groups ( + id BIGINT NOT NULL AUTO_INCREMENT, + identifier VARCHAR(32) NOT NULL, + group_definition_id BIGINT NOT NULL, + a_name VARCHAR(256) NOT NULL, + leaders VARCHAR(512) NULL, + members VARCHAR(2048) NOT NULL, + office VARCHAR(32) NOT NULL, + assigned_employee VARCHAR(32) NOT NULL, + weekday INTEGER NOT NULL, + group_status VARCHAR(32) NOT NULL, + current_cycle BIGINT NOT NULL , + address_id BIGINT NOT NULL, + created_on TIMESTAMP(3) NOT NULL, + created_by VARCHAR(32) NOT NULL, + last_modified_on TIMESTAMP(3) NULL, + last_modified_by VARCHAR(32) NULL, + CONSTRAINT ptah_groups_pk PRIMARY KEY (id), + CONSTRAINT ptah_groups_identifier_uq UNIQUE (identifier), + CONSTRAINT ptah_groups_definitions_fk FOREIGN KEY (group_definition_id) REFERENCES ptah_group_definitions (id), + CONSTRAINT ptah_group_addresses_fk FOREIGN KEY (address_id) REFERENCES ptah_addresses (id) +); + +CREATE TABLE ptah_group_commands ( + id BIGINT NOT NULL AUTO_INCREMENT, + group_id BIGINT NOT NULL, + a_action VARCHAR(32) NOT NULL, + note VARCHAR(256) NOT NULL, + created_on TIMESTAMP(3) NOT NULL, + created_by VARCHAR(32) NOT NULL, + CONSTRAINT ptah_group_commands_pk PRIMARY KEY (id), + CONSTRAINT ptah_group_commands_definitions_fk FOREIGN KEY (group_id) REFERENCES ptah_groups (id) +); + +CREATE TABLE ptah_meetings ( + id BIGINT NOT NULL AUTO_INCREMENT, + group_id BIGINT NOT NULL, + meeting_sequence BIGINT NOT NULL, + current_cycle BIGINT NOT NULL, + scheduled_for DATE NOT NULL, + held_on DATE NULL, + duration BIGINT NULL, + created_on TIMESTAMP(3) NOT NULL, + created_by VARCHAR(32) NOT NULL, + CONSTRAINT ptah_meetings_pk PRIMARY KEY (id), + CONSTRAINT ptah_group_meetings_fk FOREIGN KEY (group_id) REFERENCES ptah_groups (id) +); + +CREATE TABLE ptah_attendees ( + id BIGINT NOT NULL AUTO_INCREMENT, + meeting_id BIGINT NOT NULL, + customer_identifier VARCHAR(32) NOT NULL, + a_status VARCHAR(256) NOT NULL, + CONSTRAINT ptah_attendees_pk PRIMARY KEY (id), + CONSTRAINT ptah_meeting_attendees_fk FOREIGN KEY (meeting_id) REFERENCES ptah_attendees (id) +); diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c82a6db --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'group' + +includeBuild 'api' +includeBuild 'service' +includeBuild 'component-test' diff --git a/shared.gradle b/shared.gradle new file mode 100644 index 0000000..c54ad58 --- /dev/null +++ b/shared.gradle @@ -0,0 +1,64 @@ +group 'io.mifos.group' +version '0.1.0-BUILD-SNAPSHOT' + +ext.versions = [ + frameworkapi : '0.1.0-BUILD-SNAPSHOT', + frameworklang : '0.1.0-BUILD-SNAPSHOT', + frameworkasync : '0.1.0-BUILD-SNAPSHOT', + frameworkcassandra : '0.1.0-BUILD-SNAPSHOT', + frameworkmariadb : '0.1.0-BUILD-SNAPSHOT', + frameworkcommand : '0.1.0-BUILD-SNAPSHOT', + frameworktest: '0.1.0-BUILD-SNAPSHOT', + frameworkanubis: '0.1.0-BUILD-SNAPSHOT', + validator : '5.3.0.Final' +] + +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'maven-publish' +apply plugin: 'io.spring.dependency-management' + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +repositories { + jcenter() + mavenLocal() +} + +dependencyManagement { + imports { + mavenBom 'io.spring.platform:platform-bom:Athens-RELEASE' + mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR1' + } +} + +// override certain dependency provided by Spring platform using newer releases +ext['cassandra.version'] = '3.6' +ext['cassandra-driver.version'] = '3.1.2' +ext['activemq.version'] = '5.13.2' +ext['spring-data-releasetrain.version'] = 'Gosling-SR2A' + +dependencies { + compile( + [group: 'com.google.code.findbugs', name: 'jsr305'] + ) + + testCompile( + [group: 'org.springframework.boot', name: 'spring-boot-starter-test'] + ) +} + +license { + header rootProject.file('../HEADER') + strictCheck true + mapping { + java = 'SLASHSTAR_STYLE' + xml = 'XML_STYLE' + yml = 'SCRIPT_STYLE' + yaml = 'SCRIPT_STYLE' + uxf = 'XML_STYLE' + } + ext.year = Calendar.getInstance().get(Calendar.YEAR) + ext.name = 'The Mifos Initiative' +}